Hey there,
I've been lurking on this forum for a while now. After all I got from it, I think it's now my turn to contribute
This post will describe my 1+ year journey making a bot, and will reference other useful links. I hope this thread can be the starting point for other people to start making wow bots :-)
About me: I'm a software engineer in a big tech company everyone knows, with 5+ year of experience. I've always been amazed by the World of Warcraft game. I've started making very uneffective bots when I was younger. Now, making bots that really work is not only a way to learn a lot, but also to avoid spending hours killing pigs in the game :-)
1. The pixel bot
Before diving into implementation, I've first did some investigations about how to make a bot. I've read a couple of books, and I came to the conclusion that making memory editing bots was too risky (Blizzard detection leading to ban) and too complex.
So I first started to make a pixel bot whose goal was to farm mobs and level up my character.
What is a pixel bot ?
It's a program which reads pixel in the game as input, to take decisions. I'm not going to spend a lot of time on that part, because making a pixel bot is actually not very complex and there are already plenty very useful resources on that forum.
For instance: How to make a bot kind of tutorial =>
wow...-tutorial.html
Example to add colored pixel in a wow lua addon:
Happy-Pixels/DataToColor at master . MicahRCM/Happy-Pixels . GitHub
There are 2 interesting parts here that I'd like to mention:
A/ Navigation system
The bot was getting the position from pixels in the game representing the lua code: UnitPosition("Player"). To build a navigation system, I've made a tool to record the position of the player. This tool had a GUI which allowed me to "click" to mark a cell (= a (x,y) position) as non walkable. So given a zone where I wanted my character to farm, I first had to walk through that zone and mark the non walkable parts of it. Recording those points allowed me to find the walkable / non walkable part, hence providing enough data for my character to randomly walk in a given zone.
The algorithm was that one:
* Find a random destination in the current zone
* Go to that destination (use A* algorithm)
* While walking, if there are mobs, start a fight.
The mob selection was simply made by pressing tab regularly & checking the target of my character.
The facing algorithm (given a position A and an Orientation, which angle do you turn to face position B?) can be deduced for the other "kinda tutorial" thread
Code:
double getangle(double y1, double y2, double x1, double x2)
{
double ang = Math.Atan2(x1 - x2, y1 - y2)/Math.PI;
if (ang < 0) ang += 2; // this is used to avoind negative numbers.
return Math.Round(ang*1000);
}
You'll find this method in other posts as well.
B/ Looting mobs
To loot mobs, I've used an image hashing library to recognize the mouse icon. So once the fight was done, I just moved the mouse around the middle of the screen and compared the mouse icon to icon I had previously saved.
The algorithm was that one:
* Move the mouse
* Get current mouse icon
* ImageHash(current mouse icon) == ImageHash(loot icon) ? => then loot
You can find more about this technique here:
Blog (see the looting part). The used library is that one:
GitHub - jforshee/ImageHashing.
How do you find the mouse icon in [langage]?
I believe you can type this in google to find it out
(this is how I actually did)
C/ Sending mouse and keyboard events to the game
This was the hardest part of the pixel bot. To avoid detection, I've made a driver which runs in kernel mode. And I spent a lot of time trying to test it, because I was using an Hyper V virtual machine and it is actually not supported
So even if my driver was working, I couldn't test it through the virtual machine.
A nice resource to build a windows driver:
Write a Hello World Windows Driver (KMDF) - Windows drivers | Microsoft Docs
Another one:
https://docs.microsoft.com/en-us/win...client-drivers
Third one:
https://github.com/MicrosoftDocs/win...ent-drivers.md
You can also find github example of HID keyboard & mouse kernel mode driver.
Overall, the pixel bot did a great job and I successfully reached level 70 and got rich enough to buy the fast flying mount.
But there were some drawbacks:
* The bot could not work for pvp battleground, because some lua apis are disabled in battleground (like the position)
* The navigation system was not really precise. Z coordinate can't be retrieved using the LUA API, and the manual recording part was not only tedious but also led to not super precise cells. The navigation was clearly random and I've seen other player detecting my character as a bot because not fighting well enough when attacked in PVP. Actually the bot had no knowledge of its environment.
2. The memory editing bot
This is where things get interesting. A memory editing bot is getting information from the wow process memory. It is actually reading the WOW's memory. Such bot can also write to the WOW's memory, for instance to call existing method (example: click to move, providing the position where you want to move to).
There are kings on this forum (hello Razzue!) who are really good and write posts to give offsets from where you can read the data you want (example:
wor...d-43638-a.html).
The thing is, it's very hard to understand how these people are actually doing to retrieve those offsets. There are some tutorials (like this one:
wor...e-stuff-5.html), but overall I think there are missing resources on that topic.
What are offsets?
They are actually numbers you can add from a memory pointer to get either a value or another pointer. The nice thing is that they change each time you build a new version of the WoW client, so the offsets have to be looked for again for each client update...
One way of finding these offset is runtime analysis: analyse wow process memory at runtime. A software like cheat engine can be used to monitor the WoW process memory. The big idea is to look for a value using that tool (for instance, your health) to find out where in memory this value resides. Then, by looking at how this value is accessed (for instance, by attaching a debugger), you can reproduce the value access in an external tool (ex: your bot).
The goal of this thread is not to make you learn how to use tools like Cheat engine, I believe you can find plenty of existing resources online. I think Blizzard detects the use of cheat engine and this usage can get you banned. To avoid detection, you might rebuild locally a modified version of the software (and change a few things like the software name before rebuilding). But attaching a debugger may trigger Blizzard anti cheat system... (aka Warden)
Another way to find the offset is to do static analysis. A disassembler is used to transform the binary to "readable" code. So by disassembling the wow client, you get the actual code which gets executed by the WoW client. Then, you can browse it to understand how it works. The nice thing is that once you get a non obfuscated version of the client (see here:
https://github.com/maikel233/WoWIDB/.../wow%20classic and there:
https://www.ownedcore.com/forums/wor...-memory-5.html and there
wor...bfuscator.html), then there is 0 risk for you to get banned while you analyze the binary.
Once you've conducted your static analysis, you can then try to find an opcode pattern to recognize an offset. If you're a king legend like Razzue, you can then make a tool to automatically detect offsets. As you've understood, this is way out of my knowledge currently.
What I've done to learn reverse engineering is to download the Ghidra software (
https://github.com/NationalSecurityAgency/ghidra) and tried to disassemble an old version of the game (WOW 3.3.5a). Using an old version of the client is easier than the current version because it is obfuscated (see this thread:
wor...on-coming.html).
Overall, I was able thanks to some posts on this forum to understand from Ghidra how to iterate through the object manager, but this was not easy.
The issue with Ghidra is that it fails at disassembling the actual version of the game unobfuscated (I get a NullReferenceException
). So a concurrent product should be used, for instance, IDA Free version. Many kings on this forum are using IDA Pro, which is expansive. So these kings may actually be professional reverse engineer...
What is the object manager?
The object manager is a structure used by WoW to represent the game objects. In this structure, you'll find all players, mobs, herbs, ... So for instance if you want to get all surrounding players, you'll walk the object manager to find those. To iterate through the object manager in wow classic TBC, the Razzue's post linked above can be helpful. For other versions of the game, you can find the answer on this forum (or on existing bots on github...).
To understand how to use the offsets for WoW classic TBC, you can have a look at existing bot to see the class & enums used:
https://github.com/mmalka/TheNoobBot
Here are key interesting points:
A/ Navigation system
This system is based on the pixel bot one. I recommend reading the Pixel bot part first to have a better understanding.
Overall I recommend having a look at this thread (
https://drewkestell.us/Article/6/Chapter/20) which gives a lot of information. To build an effective navigation system, the idea is to use the game map files to build a navigation mesh. I've reused the tools provided in the above article to build the maps used by my bot, but I plan to dig deeper in this part later.
Currently, the navigation system is working pretty well:
* Get the position of the player by reading wow process memory (see king Razzue's post)
* Using Detour, find a path to a destination. You can find more on how to use Detour in drewkestell blog.
The maps I've used are the ones which were with WoW 3.3.5a version (they are still up to date for Wow classic TBC).
Here is a nice code to avoid finger injury to convert the map id retrieved using Razzue's offset to a map id used by the blog shared above. I believe you can find the zone id directly in memory, but...I haven't looked for the offset
(as mentioned the offset are game version dependent and the WowClassicMapId is freely provided by Razzue in its offset list).
Code:
public enum WowClassicMapId
{
Ragefire_Chasm_Dungeon_Orgrimmar = 213,
Zul_Farrak_Orphan_Tanaris = 219,
The_Temple_of_Atal_Hakkar_Dungeon_Swamp_of_Sorrows = 220,
Blackfathom_Deeps_Dungeon_Ashenvale_1 = 221,
Blackfathom_Deeps_Dungeon_Ashenvale_2 = 222,
Blackfathom_Deeps_Dungeon_Ashenvale_3 = 223,
The_Stockade_Dungeon_Stormwind_City = 225,
Gnomeregan_Dungeon_Dun_Morogh_1 = 226,
Gnomeregan_Dungeon_Dun_Morogh_2 = 227,
Gnomeregan_Dungeon_Dun_Morogh_3 = 228,
Gnomeregan_Dungeon_Dun_Morogh_4 = 229,
Uldaman_Dungeon_Badlands_1 = 230,
Uldaman_Dungeon_Badlands_2 = 231,
Molten_Core_Dungeon_Burning_Steppes = 232,
Dire_Maul_Orphan_Feralas = 234,
Dire_Maul_Dungeon_Feralas_1 = 235,
Dire_Maul_Dungeon_Feralas_2 = 236,
Dire_Maul_Dungeon_Feralas_3 = 237,
Dire_Maul_Dungeon_Feralas_4 = 238,
Dire_Maul_Dungeon_Feralas_5 = 239,
Dire_Maul_Dungeon_Feralas_6 = 240,
Blackrock_Depths_Dungeon_Searing_Gorge_1 = 242,
Blackrock_Depths_Dungeon_Searing_Gorge_2 = 243,
The_Shattered_Halls_Dungeon_Hellfire_Peninsula = 246,
Ruins_of_Ahn_Qiraj_Orphan_Silithus = 247,
Onyxia_s_Lair_Dungeon_Dustwallow_Marsh = 248,
Blackrock_Spire_Dungeon_Burning_Steppes_1 = 250,
Blackrock_Spire_Dungeon_Burning_Steppes_2 = 251,
Blackrock_Spire_Dungeon_Burning_Steppes_3 = 252,
Blackrock_Spire_Dungeon_Burning_Steppes_4 = 253,
Blackrock_Spire_Dungeon_Burning_Steppes_5 = 254,
Blackrock_Spire_Dungeon_Burning_Steppes_6 = 255,
Auchenai_Crypts_Dungeon_Terokkar_Forest_7 = 256,
Auchenai_Crypts_Dungeon_Terokkar_Forest_8 = 257,
Sethekk_Halls_Dungeon_Terokkar_Forest_1 = 258,
Sethekk_Halls_Dungeon_Terokkar_Forest_2 = 259,
Shadow_Labyrinth_Dungeon_Terokkar_Forest = 260,
The_Blood_Furnace_Dungeon_Hellfire_Peninsula = 261,
The_Underbog_Dungeon_Zangarmarsh = 262,
The_Steamvault_Dungeon_Zangarmarsh_1 = 263,
The_Steamvault_Dungeon_Zangarmarsh_2 = 264,
The_Slave_Pens_Dungeon_Zangarmarsh = 265,
The_Botanica_Dungeon_Netherstorm = 266,
The_Mechanar_Dungeon_Netherstorm_1 = 267,
The_Mechanar_Dungeon_Netherstorm_2 = 268,
The_Arcatraz_Dungeon_Netherstorm_3 = 269,
The_Arcatraz_Dungeon_Netherstorm_4 = 270,
The_Arcatraz_Dungeon_Netherstorm_5 = 271,
ManaTombs_Dungeon_Terokkar_Forest = 272,
The_Black_Morass_Orphan_Tanaris = 273,
Old_Hillsbrad_Foothills_Orphan_Tanaris = 274,
Wailing_Caverns_Dungeon_The_Barrens = 279,
Maraudon_Dungeon_Desolace_1 = 280,
Maraudon_Dungeon_Desolace_2 = 281,
Blackwing_Lair_Dungeon_Burning_Steppes_1 = 287,
Blackwing_Lair_Dungeon_Burning_Steppes_2 = 288,
Blackwing_Lair_Dungeon_Burning_Steppes_3 = 289,
Blackwing_Lair_Dungeon_Burning_Steppes_4 = 290,
The_Deadmines_Dungeon_Westfall_1 = 291,
The_Deadmines_Dungeon_Westfall_2 = 292,
Razorfen_Downs_Dungeon_Thousand_Needles = 300,
Razorfen_Kraul_Dungeon_The_Barrens = 301,
Scarlet_Monastery_Dungeon_Tirisfal_Glades_1 = 302,
Scarlet_Monastery_Dungeon_Tirisfal_Glades_2 = 303,
Scarlet_Monastery_Dungeon_Tirisfal_Glades_3 = 304,
Scarlet_Monastery_Dungeon_Tirisfal_Glades_4 = 305,
ScholomanceOLD_Dungeon_Western_Plaguelands_1 = 306,
ScholomanceOLD_Dungeon_Western_Plaguelands_2 = 307,
ScholomanceOLD_Dungeon_Western_Plaguelands_3 = 308,
ScholomanceOLD_Dungeon_Western_Plaguelands_4 = 309,
Shadowfang_Keep_Dungeon_Silverpine_Forest_1 = 310,
Shadowfang_Keep_Dungeon_Silverpine_Forest_2 = 311,
Shadowfang_Keep_Dungeon_Silverpine_Forest_3 = 312,
Shadowfang_Keep_Dungeon_Silverpine_Forest_4 = 313,
Shadowfang_Keep_Dungeon_Silverpine_Forest_5 = 314,
Shadowfang_Keep_Dungeon_Silverpine_Forest_6 = 315,
Shadowfang_Keep_Dungeon_Silverpine_Forest_7 = 316,
Stratholme_Dungeon_Eastern_Plaguelands_1 = 317,
Stratholme_Dungeon_Eastern_Plaguelands_2 = 318,
Ahn_Qiraj_Dungeon_Silithus_1 = 319,
Ahn_Qiraj_Dungeon_Silithus_2 = 320,
Ahn_Qiraj_Dungeon_Silithus_3 = 321,
Hyjal_Summit_Orphan_Tanaris = 329,
Gruul_s_Lair_Dungeon_Blade_s_Edge_Mountains = 330,
Magtheridon_s_Lair_Dungeon_Hellfire_Peninsula = 331,
Zul_Aman_Orphan_Ghostlands = 333,
Tempest_Keep_Dungeon_Netherstorm_1 = 334,
Tempest_Keep_Dungeon_Netherstorm_2 = 1555,
Sunwell_Plateau_Dungeon_Isle_of_Quel_Danas = 335,
Sunwell_Plateau_Dungeon_Sunwell_Plateau = 336,
Zul_Gurub_Orphan_Stranglethorn_Vale_1 = 233,
Zul_Gurub_Orphan_Stranglethorn_Vale_2 = 337,
Black_Temple_Dungeon_Shadowmoon_Valley = 339,
Hellfire_Ramparts_Dungeon_Hellfire_Peninsula = 347,
Karazhan_Dungeon_Deadwind_Pass_1 = 350,
Karazhan_Dungeon_Deadwind_Pass_2 = 351,
Karazhan_Dungeon_Deadwind_Pass_3 = 352,
Karazhan_Dungeon_Deadwind_Pass_4 = 353,
Karazhan_Dungeon_Deadwind_Pass_5 = 354,
Karazhan_Dungeon_Deadwind_Pass_6 = 355,
Karazhan_Dungeon_Deadwind_Pass_7 = 356,
Karazhan_Dungeon_Deadwind_Pass_8 = 357,
Karazhan_Dungeon_Deadwind_Pass_9 = 358,
Karazhan_Dungeon_Deadwind_Pass_10 = 359,
Karazhan_Dungeon_Deadwind_Pass_11 = 360,
Karazhan_Dungeon_Deadwind_Pass_12 = 361,
Karazhan_Dungeon_Deadwind_Pass_13 = 362,
Karazhan_Dungeon_Deadwind_Pass_14 = 363,
Karazhan_Dungeon_Deadwind_Pass_15 = 364,
Karazhan_Dungeon_Deadwind_Pass_16 = 365,
Karazhan_Dungeon_Deadwind_Pass_17 = 366,
Cosmic_Cosmic = 946,
Azeroth_World_Cosmic = 947,
Outland_Continent = 987,
Durotar_Zone_Kalimdor = 1411,
Mulgore_Zone_Kalimdor = 1412,
The_Barrens_Zone_Kalimdor = 1413,
Kalimdor_Continent_Azeroth = 1414,
Eastern_Kingdoms_Continent_Azeroth = 1415,
Alterac_Mountains_Zone_Eastern_Kingdoms = 1416,
Arathi_Highlands_Zone_Eastern_Kingdoms = 1417,
Badlands_Zone_Eastern_Kingdoms = 1418,
Blasted_Lands_Zone_Eastern_Kingdoms = 1419,
Tirisfal_Glades_Zone_Eastern_Kingdoms = 1420,
Silverpine_Forest_Zone_Eastern_Kingdoms = 1421,
Western_Plaguelands_Zone_Eastern_Kingdoms = 1422,
Eastern_Plaguelands_Zone_Eastern_Kingdoms = 1423,
Hillsbrad_Foothills_Zone_Eastern_Kingdoms = 1424,
The_Hinterlands_Zone_Eastern_Kingdoms = 1425,
Dun_Morogh_Zone_Eastern_Kingdoms = 1426,
Searing_Gorge_Zone_Eastern_Kingdoms = 1427,
Burning_Steppes_Zone_Eastern_Kingdoms = 1428,
Elwynn_Forest_Zone_Eastern_Kingdoms = 1429,
Deadwind_Pass_Zone_Eastern_Kingdoms = 1430,
Duskwood_Zone_Eastern_Kingdoms = 1431,
Loch_Modan_Zone_Eastern_Kingdoms = 1432,
Redridge_Mountains_Zone_Eastern_Kingdoms = 1433,
Stranglethorn_Vale_Zone_Eastern_Kingdoms = 1434,
Swamp_of_Sorrows_Zone_Eastern_Kingdoms = 1435,
Westfall_Zone_Eastern_Kingdoms = 1436,
Wetlands_Zone_Eastern_Kingdoms = 1437,
Teldrassil_Zone_Kalimdor = 1438,
Darkshore_Zone_Kalimdor = 1439,
Ashenvale_Zone_Kalimdor = 1440,
Thousand_Needles_Zone_Kalimdor = 1441,
Stonetalon_Mountains_Zone_Kalimdor = 1442,
Desolace_Zone_Kalimdor = 1443,
Feralas_Zone_Kalimdor = 1444,
Dustwallow_Marsh_Zone_Kalimdor = 1445,
Tanaris_Zone_Kalimdor = 1446,
Azshara_Zone_Kalimdor = 1447,
Felwood_Zone_Kalimdor = 1448,
Un_Goro_Crater_Zone_Kalimdor = 1449,
Moonglade_Zone_Kalimdor = 1450,
Silithus_Zone_Kalimdor = 1451,
Winterspring_Zone_Kalimdor = 1452,
Stormwind_City_Zone_Eastern_Kingdoms = 1453,
Orgrimmar_Zone_Kalimdor = 1454,
Ironforge_Zone_Eastern_Kingdoms = 1455,
Thunder_Bluff_Zone_Kalimdor = 1456,
Darnassus_Zone_Kalimdor = 1457,
Undercity_Zone_Eastern_Kingdoms = 1458,
Alterac_Valley_Zone_Azeroth = 1459,
Warsong_Gulch_Zone_Azeroth = 1460,
Arathi_Basin_Zone_Azeroth = 1461,
Eastern_Kingdoms_Continent = 1463,
Kalimdor_Continent = 1464,
Serpentshrine_Cavern_Dungeon_Zangarmarsh_1 = 332,
Serpentshrine_Cavern_Dungeon_Zangarmarsh_2 = 1554,
Eversong_Woods_Zone_Eastern_Kingdoms = 1941,
Ghostlands_Zone_Eastern_Kingdoms = 1942,
Azuremyst_Isle_Zone_Kalimdor = 1943,
Hellfire_Peninsula_Zone_Outland = 1944,
Outland_Continent_Cosmic = 1945,
Zangarmarsh_Zone_Outland = 1946,
The_Exodar_Zone_Kalimdor = 1947,
Shadowmoon_Valley_Zone_Outland = 1948,
Blade_s_Edge_Mountains_Zone_Outland = 1949,
Bloodmyst_Isle_Zone_Kalimdor = 1950,
Nagrand_Zone_Outland = 1951,
Terokkar_Forest_Zone_Outland = 1952,
Netherstorm_Zone_Outland = 1953,
Silvermoon_City_Zone_Eastern_Kingdoms = 1954,
Shattrath_City_Zone_Outland = 1955,
Eye_of_the_Storm_Zone_Netherstorm = 1956,
Isle_of_Quel_Danas_Zone_Eastern_Kingdoms = 1957,
}
public enum WowMapId
{
AhnkahetTheOldKingdom = 619, // Azjol_LowerCity
AhnQirajTemple = 531, // AhnQirajTemple
AlteracValley = 30, // PVPZone01
ArathiBasin = 529, // PVPZone04
AuchenaiCrypts = 558, // AuchindounDraenei
AzjolNerub = 601, // Azjol_Uppercity
AzsharaCrater = 37, // PVPZone02
BlackfathomDeeps = 48, // Blackfathom
BlackMorass = 269, // CavernsOfTime
BlackrockDepths = 230, // BlackrockDepths
BlackrockSpire = 229, // BlackRockSpire
BlackTemple = 564, // BlackTemple
BlackwingLair = 469, // BlackwingLair
BladesEdgeArena = 562, // bladesedgearena
ChampionsHall = 449, // AlliancePVPBarracks
DalaranSewers = 617, // DalaranArena
Deadmines = 36, // DeadminesInstance
DeeprunTram = 369, // DeeprunTram
DireMaul = 429, // DireMaul
DrakTharonKeep = 600, // DrakTheronKeep
EasternKingdoms = 0, // Azeroth
EbonHold = 609, // DeathKnightStart
EmeraldDream = 169, // EmeraldDream
EyeOfTheStorm = 566, // NetherstormBG
Gnomeregan = 90, // GnomeragonInstance
GruulsLair = 565, // GruulsLair
Gundrak = 604, // GunDrak
HallOfLegends = 450, // HordePVPBarracks
HallsOfLighting = 602, // Ulduar80
HallsOfReflection = 668, // HallsOfReflection
HallsOfStone = 599, // Ulduar70
HellfireRamparts = 543, // HellfireRampart
IcecrownCitadel = 631, // IcecrownCitadel
IsleOfConquest = 628, // IsleofConquest
Kalimdor = 1, // Kalimdor
Karazhan = 532, // Karazahn
MagistersTerrace = 585, // Sunwell5ManFix
MagtheridonsLair = 544, // HellfireRaid
ManaTombs = 557, // AuchindounEthereal
Mauradon = 349, // Mauradon
MoltenCore = 409, // MoltenCore
NagrandArena = 559, // PVPZone05
Naxxramas = 533, // Stratholme Raid
Northrend = 571, // Northrend
OldHillsbradFoothills = 560, // HillsbradPast
OnyxiasLair = 249, // OnyxiaLairInstance
Outland = 530, // Expansion01
PitOfSaron = 658, // QuarryOfTears
RagefireChasm = 389, // OrgrimmarInstance
RazorfenDowns = 129, // RazorfenDowns
RazorfenKraul = 47, // RazorfenKraulInstance
RuinsOfAhnQiraj = 509, // AhnQiraj
RuinsOfLordaeron = 572, // PVPLordaeron
ScarletMonastery = 189, // MonasteryInstances
Scholomance = 289, // SchoolofNecromancy
SerpentshrineCavern = 548, // CoilfangRaid
SethekkHalls = 556, // AuchindounDemon
ShadowfangKeep = 33, // Shadowfang
ShadowLabyrinth = 555, // AuchindounShadow
Stormwind = 723, // Stormwind
StormwindStockade = 34, // StormwindJail
StrandOfTheAncients = 607, // NorthrendBG
SunkenTemple = 109, // SunkenTemple
TempestKeep = 550, // TempestKeepRaid
TheArcatraz = 552, // TempestKeepArcane
TheBattleForMountHyjal = 534, // HyjalPast
TheBloodFurnace = 542, // HellfireDemon
TheBotanica = 553, // TempestKeepAtrium
TheCullingOfStratholme = 595, // StratholmeCOT
TheEyeOfEternity = 616, // NexusRaid
TheForgeOfSouls = 632, // IcecrownCitadel5Man
TheMechanar = 554, // TempestKeepFactory
TheNexus = 576, // Nexus70
TheObsidianSanctum = 615, // ChamberOfAspectsBlack
TheOculus = 578, // Nexus80
TheRingOfValor = 618, // OrgrimmarArena
TheRubySanctum = 724, // ChamberOfAspectsRed
TheShatteredHalls = 540, // HellfireMilitary
TheSlavePens = 547, // CoilfangDraenei
TheSteamvault = 545, // CoilfangPumping
TheSunwell = 580, // SunwellPlateau
TheUnderbog = 546, // CoilfangMarsh
TrialOfTheChampion = 650, // ArgentTournamentDungeon
TrialOfTheCrusader = 649, // ArgentTournamentRaid
Uldaman = 70, // Uldaman
Ulduar = 603, // UlduarRaid
UnderMine = 2, // UnderMine
UtgardeKeep = 574, // Valgarde70
UtgardePinnacle = 575, // UtgardePinnacle
VaultOfArchavon = 624, // WintergraspRaid
VioletHold = 608, // DalaranPrison
WailingCaverns = 43, // WailingCaverns
WarsongGulch = 489, // PVPZone03
ZulAman = 568, // ZulAman
ZulFarrak = 209, // TanarisInstance
ZulGurub = 309 // Zul'gurub
}
private WowMapId ToWowMapId(WowClassicMapId mapId)
{
switch (mapId)
{
case WowClassicMapId.Ragefire_Chasm_Dungeon_Orgrimmar:
return WowMapId.RagefireChasm;
case WowClassicMapId.Zul_Farrak_Orphan_Tanaris:
return WowMapId.ZulFarrak;
case WowClassicMapId.Blackfathom_Deeps_Dungeon_Ashenvale_1:
case WowClassicMapId.Blackfathom_Deeps_Dungeon_Ashenvale_2:
case WowClassicMapId.Blackfathom_Deeps_Dungeon_Ashenvale_3:
return WowMapId.BlackfathomDeeps;
case WowClassicMapId.The_Stockade_Dungeon_Stormwind_City:
return WowMapId.StormwindStockade;
case WowClassicMapId.Gnomeregan_Dungeon_Dun_Morogh_1:
case WowClassicMapId.Gnomeregan_Dungeon_Dun_Morogh_2:
case WowClassicMapId.Gnomeregan_Dungeon_Dun_Morogh_3:
case WowClassicMapId.Gnomeregan_Dungeon_Dun_Morogh_4:
return WowMapId.Gnomeregan;
case WowClassicMapId.Uldaman_Dungeon_Badlands_1:
case WowClassicMapId.Uldaman_Dungeon_Badlands_2:
return WowMapId.Uldaman;
case WowClassicMapId.Molten_Core_Dungeon_Burning_Steppes:
return WowMapId.MoltenCore;
case WowClassicMapId.Dire_Maul_Orphan_Feralas:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_1:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_2:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_3:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_4:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_5:
case WowClassicMapId.Dire_Maul_Dungeon_Feralas_6:
return WowMapId.DireMaul;
case WowClassicMapId.Blackrock_Depths_Dungeon_Searing_Gorge_1:
case WowClassicMapId.Blackrock_Depths_Dungeon_Searing_Gorge_2:
return WowMapId.BlackrockDepths;
case WowClassicMapId.The_Shattered_Halls_Dungeon_Hellfire_Peninsula:
return WowMapId.TheShatteredHalls;
case WowClassicMapId.Ruins_of_Ahn_Qiraj_Orphan_Silithus:
return WowMapId.RuinsOfAhnQiraj;
case WowClassicMapId.Onyxia_s_Lair_Dungeon_Dustwallow_Marsh:
return WowMapId.OnyxiasLair;
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_1:
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_2:
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_3:
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_4:
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_5:
case WowClassicMapId.Blackrock_Spire_Dungeon_Burning_Steppes_6:
return WowMapId.BlackrockSpire;
case WowClassicMapId.Auchenai_Crypts_Dungeon_Terokkar_Forest_7:
case WowClassicMapId.Auchenai_Crypts_Dungeon_Terokkar_Forest_8:
return WowMapId.AuchenaiCrypts;
case WowClassicMapId.Sethekk_Halls_Dungeon_Terokkar_Forest_1:
case WowClassicMapId.Sethekk_Halls_Dungeon_Terokkar_Forest_2:
return WowMapId.SethekkHalls;
case WowClassicMapId.Shadow_Labyrinth_Dungeon_Terokkar_Forest:
return WowMapId.ShadowLabyrinth;
case WowClassicMapId.The_Blood_Furnace_Dungeon_Hellfire_Peninsula:
return WowMapId.TheBloodFurnace;
case WowClassicMapId.The_Underbog_Dungeon_Zangarmarsh:
return WowMapId.TheUnderbog;
case WowClassicMapId.The_Steamvault_Dungeon_Zangarmarsh_1:
case WowClassicMapId.The_Steamvault_Dungeon_Zangarmarsh_2:
return WowMapId.TheSteamvault;
case WowClassicMapId.The_Slave_Pens_Dungeon_Zangarmarsh:
return WowMapId.TheSlavePens;
case WowClassicMapId.The_Botanica_Dungeon_Netherstorm:
return WowMapId.TheBotanica;
case WowClassicMapId.The_Mechanar_Dungeon_Netherstorm_1:
case WowClassicMapId.The_Mechanar_Dungeon_Netherstorm_2:
return WowMapId.TheMechanar;
case WowClassicMapId.The_Arcatraz_Dungeon_Netherstorm_3:
case WowClassicMapId.The_Arcatraz_Dungeon_Netherstorm_4:
case WowClassicMapId.The_Arcatraz_Dungeon_Netherstorm_5:
return WowMapId.TheArcatraz;
case WowClassicMapId.ManaTombs_Dungeon_Terokkar_Forest:
return WowMapId.ManaTombs;
case WowClassicMapId.The_Black_Morass_Orphan_Tanaris:
return WowMapId.BlackMorass;
case WowClassicMapId.Old_Hillsbrad_Foothills_Orphan_Tanaris:
return WowMapId.OldHillsbradFoothills;
case WowClassicMapId.Wailing_Caverns_Dungeon_The_Barrens:
return WowMapId.WailingCaverns;
case WowClassicMapId.Blackwing_Lair_Dungeon_Burning_Steppes_1:
case WowClassicMapId.Blackwing_Lair_Dungeon_Burning_Steppes_2:
case WowClassicMapId.Blackwing_Lair_Dungeon_Burning_Steppes_3:
case WowClassicMapId.Blackwing_Lair_Dungeon_Burning_Steppes_4:
return WowMapId.BlackwingLair;
case WowClassicMapId.The_Deadmines_Dungeon_Westfall_1:
case WowClassicMapId.The_Deadmines_Dungeon_Westfall_2:
return WowMapId.Deadmines;
case WowClassicMapId.Razorfen_Downs_Dungeon_Thousand_Needles:
return WowMapId.RazorfenDowns;
case WowClassicMapId.Razorfen_Kraul_Dungeon_The_Barrens:
return WowMapId.RazorfenKraul;
case WowClassicMapId.Scarlet_Monastery_Dungeon_Tirisfal_Glades_1:
case WowClassicMapId.Scarlet_Monastery_Dungeon_Tirisfal_Glades_2:
case WowClassicMapId.Scarlet_Monastery_Dungeon_Tirisfal_Glades_3:
case WowClassicMapId.Scarlet_Monastery_Dungeon_Tirisfal_Glades_4:
return WowMapId.ScarletMonastery;
case WowClassicMapId.ScholomanceOLD_Dungeon_Western_Plaguelands_1:
case WowClassicMapId.ScholomanceOLD_Dungeon_Western_Plaguelands_2:
case WowClassicMapId.ScholomanceOLD_Dungeon_Western_Plaguelands_3:
case WowClassicMapId.ScholomanceOLD_Dungeon_Western_Plaguelands_4:
return WowMapId.Scholomance;
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_1:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_2:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_3:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_4:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_5:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_6:
case WowClassicMapId.Shadowfang_Keep_Dungeon_Silverpine_Forest_7:
return WowMapId.ShadowfangKeep;
case WowClassicMapId.Stratholme_Dungeon_Eastern_Plaguelands_1:
case WowClassicMapId.Stratholme_Dungeon_Eastern_Plaguelands_2:
return WowMapId.TheCullingOfStratholme;
case WowClassicMapId.Ahn_Qiraj_Dungeon_Silithus_1:
case WowClassicMapId.Ahn_Qiraj_Dungeon_Silithus_2:
case WowClassicMapId.Ahn_Qiraj_Dungeon_Silithus_3:
return WowMapId.AhnQirajTemple;
case WowClassicMapId.Hyjal_Summit_Orphan_Tanaris:
return WowMapId.TheBattleForMountHyjal;
case WowClassicMapId.Gruul_s_Lair_Dungeon_Blade_s_Edge_Mountains:
return WowMapId.GruulsLair;
case WowClassicMapId.Magtheridon_s_Lair_Dungeon_Hellfire_Peninsula:
return WowMapId.MagtheridonsLair;
case WowClassicMapId.Zul_Aman_Orphan_Ghostlands:
return WowMapId.ZulAman;
case WowClassicMapId.Tempest_Keep_Dungeon_Netherstorm_1:
case WowClassicMapId.Tempest_Keep_Dungeon_Netherstorm_2:
return WowMapId.TempestKeep;
case WowClassicMapId.Sunwell_Plateau_Dungeon_Isle_of_Quel_Danas:
case WowClassicMapId.Sunwell_Plateau_Dungeon_Sunwell_Plateau:
return WowMapId.TheSunwell;
case WowClassicMapId.Zul_Gurub_Orphan_Stranglethorn_Vale_1:
case WowClassicMapId.Zul_Gurub_Orphan_Stranglethorn_Vale_2:
return WowMapId.ZulGurub;
case WowClassicMapId.Black_Temple_Dungeon_Shadowmoon_Valley:
return WowMapId.BlackTemple;
case WowClassicMapId.Hellfire_Ramparts_Dungeon_Hellfire_Peninsula:
return WowMapId.HellfireRamparts;
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_1:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_2:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_3:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_4:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_5:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_6:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_7:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_8:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_9:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_10:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_11:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_12:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_13:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_14:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_15:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_16:
case WowClassicMapId.Karazhan_Dungeon_Deadwind_Pass_17:
return WowMapId.Karazhan;
case WowClassicMapId.Azeroth_World_Cosmic:
case WowClassicMapId.Kalimdor_Continent_Azeroth:
case WowClassicMapId.Eastern_Kingdoms_Continent_Azeroth:
case WowClassicMapId.Alterac_Mountains_Zone_Eastern_Kingdoms:
case WowClassicMapId.Arathi_Highlands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Badlands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Blasted_Lands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Tirisfal_Glades_Zone_Eastern_Kingdoms:
case WowClassicMapId.Silverpine_Forest_Zone_Eastern_Kingdoms:
case WowClassicMapId.Western_Plaguelands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Eastern_Plaguelands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Hillsbrad_Foothills_Zone_Eastern_Kingdoms:
case WowClassicMapId.The_Hinterlands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Dun_Morogh_Zone_Eastern_Kingdoms:
case WowClassicMapId.Searing_Gorge_Zone_Eastern_Kingdoms:
case WowClassicMapId.Burning_Steppes_Zone_Eastern_Kingdoms:
case WowClassicMapId.Elwynn_Forest_Zone_Eastern_Kingdoms:
case WowClassicMapId.Deadwind_Pass_Zone_Eastern_Kingdoms:
case WowClassicMapId.Duskwood_Zone_Eastern_Kingdoms:
case WowClassicMapId.Loch_Modan_Zone_Eastern_Kingdoms:
case WowClassicMapId.Redridge_Mountains_Zone_Eastern_Kingdoms:
case WowClassicMapId.Stranglethorn_Vale_Zone_Eastern_Kingdoms:
case WowClassicMapId.Swamp_of_Sorrows_Zone_Eastern_Kingdoms:
case WowClassicMapId.Westfall_Zone_Eastern_Kingdoms:
case WowClassicMapId.Wetlands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Stormwind_City_Zone_Eastern_Kingdoms:
case WowClassicMapId.Ironforge_Zone_Eastern_Kingdoms:
case WowClassicMapId.Undercity_Zone_Eastern_Kingdoms:
case WowClassicMapId.Eastern_Kingdoms_Continent:
case WowClassicMapId.Eversong_Woods_Zone_Eastern_Kingdoms:
case WowClassicMapId.Ghostlands_Zone_Eastern_Kingdoms:
case WowClassicMapId.Silvermoon_City_Zone_Eastern_Kingdoms:
case WowClassicMapId.Isle_of_Quel_Danas_Zone_Eastern_Kingdoms:
return WowMapId.EasternKingdoms;
case WowClassicMapId.Outland_Continent:
case WowClassicMapId.Hellfire_Peninsula_Zone_Outland:
case WowClassicMapId.Outland_Continent_Cosmic:
case WowClassicMapId.Zangarmarsh_Zone_Outland:
case WowClassicMapId.Shadowmoon_Valley_Zone_Outland:
case WowClassicMapId.Blade_s_Edge_Mountains_Zone_Outland:
case WowClassicMapId.Nagrand_Zone_Outland:
case WowClassicMapId.Terokkar_Forest_Zone_Outland:
case WowClassicMapId.Netherstorm_Zone_Outland:
case WowClassicMapId.Shattrath_City_Zone_Outland:
return WowMapId.Outland;
case WowClassicMapId.Durotar_Zone_Kalimdor:
case WowClassicMapId.Mulgore_Zone_Kalimdor:
case WowClassicMapId.The_Barrens_Zone_Kalimdor:
case WowClassicMapId.Teldrassil_Zone_Kalimdor:
case WowClassicMapId.Darkshore_Zone_Kalimdor:
case WowClassicMapId.Ashenvale_Zone_Kalimdor:
case WowClassicMapId.Thousand_Needles_Zone_Kalimdor:
case WowClassicMapId.Stonetalon_Mountains_Zone_Kalimdor:
case WowClassicMapId.Desolace_Zone_Kalimdor:
case WowClassicMapId.Feralas_Zone_Kalimdor:
case WowClassicMapId.Dustwallow_Marsh_Zone_Kalimdor:
case WowClassicMapId.Tanaris_Zone_Kalimdor:
case WowClassicMapId.Azshara_Zone_Kalimdor:
case WowClassicMapId.Felwood_Zone_Kalimdor:
case WowClassicMapId.Un_Goro_Crater_Zone_Kalimdor:
case WowClassicMapId.Moonglade_Zone_Kalimdor:
case WowClassicMapId.Silithus_Zone_Kalimdor:
case WowClassicMapId.Winterspring_Zone_Kalimdor:
case WowClassicMapId.Orgrimmar_Zone_Kalimdor:
case WowClassicMapId.Thunder_Bluff_Zone_Kalimdor:
case WowClassicMapId.Darnassus_Zone_Kalimdor:
case WowClassicMapId.Kalimdor_Continent:
case WowClassicMapId.Azuremyst_Isle_Zone_Kalimdor:
case WowClassicMapId.The_Exodar_Zone_Kalimdor:
case WowClassicMapId.Bloodmyst_Isle_Zone_Kalimdor:
return WowMapId.Kalimdor;
case WowClassicMapId.Alterac_Valley_Zone_Azeroth:
return WowMapId.AlteracValley;
case WowClassicMapId.Warsong_Gulch_Zone_Azeroth:
return WowMapId.WarsongGulch;
case WowClassicMapId.Arathi_Basin_Zone_Azeroth:
return WowMapId.ArathiBasin;
case WowClassicMapId.Serpentshrine_Cavern_Dungeon_Zangarmarsh_1:
case WowClassicMapId.Serpentshrine_Cavern_Dungeon_Zangarmarsh_2:
return WowMapId.SerpentshrineCavern;
case WowClassicMapId.Eye_of_the_Storm_Zone_Netherstorm:
return WowMapId.EyeOfTheStorm;
case WowClassicMapId.Cosmic_Cosmic:
case WowClassicMapId.Maraudon_Dungeon_Desolace_1:
case WowClassicMapId.Maraudon_Dungeon_Desolace_2:
case WowClassicMapId.The_Temple_of_Atal_Hakkar_Dungeon_Swamp_of_Sorrows:
default:
throw new ArgumentOutOfRangeException(nameof(mapId), mapId, null);
}
}
Future learnings:
* I'd like to understand how are people doing to find game offsets. With my free IDA version, I'm not able to find all LUA APIs in the WoW classic TBC client (it may come from the obfuscation) so I find it really hard to understand the logic & to find the offsets by myself. I wonder if people with the Pro version have the same issue... I've started to quickly have a look (here:
https://www.ownedcore.com/forums/wor...ed-binary.html) but I need to spend more time on that part.
* When time allows, I'll dig into the recast / detour usage (and actually understand that page:
https://wowdev.wiki/ADT/v18 because I really don't know how to interpret the games files thanks to this wiki
). The final goal would be to be able to build a navmesh for at least a zone by myself.
Side note: I won't provide the code I've written for my bot. I believe the information shared here can lead you on the road to build your own bot :-)