-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathAddon.lua
402 lines (344 loc) · 13.3 KB
/
Addon.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
--[[--------------------------------------------------------------------
MountMe
One button to mount, dismount, and use travel forms.
Copyright 2014-2018 Phanx <[email protected]>. All rights reserved.
https://github.com/phanx-wow/MountMe
------------------------------------------------------------------------
TODO:
- Cancel transformation buffs that block mounting?
- Ignore garrison stables training mounts
----------------------------------------------------------------------]]
local MOD_TRAVEL_FORM = "ctrl"
local MOD_DISMOUNT_FLYING = "shift"
------------------------------------------------------------------------
local _, ns = ...
local _, PLAYER_CLASS = UnitClass("player")
local LibFlyable = LibStub("LibFlyable")
local GetItemCount, GetSpellInfo, GetZoneAbilitySpellInfo, IsOutdoors, IsPlayerMoving
= GetItemCount, GetSpellInfo, GetZoneAbilitySpellInfo, IsOutdoors, IsPlayerMoving
local IsPlayerSpell, IsSpellKnown, IsSubmerged, SecureCmdOptionParse
= IsPlayerSpell, IsSpellKnown, IsSubmerged, SecureCmdOptionParse
local MOUNT_CONDITION = "[nocombat,outdoors,nomounted,novehicleui,nomod:" .. MOD_TRAVEL_FORM .. "]"
local GARRISON_MOUNT_CONDITION = "[outdoors,nomounted,novehicleui,nomod:" .. MOD_TRAVEL_FORM .. "]"
local SAFE_DISMOUNT = "/stopmacro [flying,nomod:" .. MOD_DISMOUNT_FLYING .. "]"
local DISMOUNT = [[
/leavevehicle [canexitvehicle]
/dismount [mounted]
]]
local SpellID = {
["Cat Form"] = 768,
["Darkflight"] = 68992,
["Garrison Ability"] = 161691,
["Ghost Wolf"] = 2645,
["Flight Form"] = 165962,
["Summon Mechashredder 5000"] = 164050,
["Travel Form"] = 783,
}
local SpellName = {}
for name, id in pairs(SpellID) do
SpellName[name] = GetSpellInfo(id)
end
local ItemID = {
["Magic Broom"] = 37011,
}
local ItemName = {}
for name, id in pairs(ItemID) do
ItemName[name] = GetItemInfo(id)
end
------------------------------------------------------------------------
------------------------------------------------------------------------
local function GetOverrideMount()
local combat = UnitAffectingCombat("player")
-- Magic Broom
-- Instant but not usable in combat
if not combat and GetItemCount(ItemID["Magic Broom"]) > 0 then
return "/use " .. ItemName["Magic Broom"]
end
-- Nagrand garrison mounts: Frostwolf War Wolf, Telaari Talbuk
-- Can be summoned in combat
if GetZoneAbilitySpellInfo() == SpellID["Garrison Ability"] then
local _, _, _, _, _, _, id = GetSpellInfo(SpellName["Garrison Ability"])
if (id == 164222 or id == 165803)
and SecureCmdOptionParse(GARRISON_MOUNT_CONDITION)
and (UnitAffectingCombat("player") or not LibFlyable:IsFlyableArea()) then
return "/cast " .. SpellName["Garrison Ability"]
end
end
end
------------------------------------------------------------------------
------------------------------------------------------------------------
local GetMount
do
local GROUND, FLYING, SWIMMING = 1, 2, 3
local GetMountInfoByID = C_MountJournal.GetMountInfoByID
local GetMountInfoExtraByID = C_MountJournal.GetMountInfoExtraByID
local mountTypeInfo = {
[230] = {100,99,0}, -- ground -- 99 flying to use in flying areas if the player doesn't have any flying mounts as favorites
[231] = {20,0,60}, -- aquatic
[232] = {0,0,450}, -- Abyssal Seahorse -- only in Vashj'ir
[241] = {101,0,0}, -- Qiraji Battle Tanks -- only in Temple of Ahn'Qiraj
[247] = {99,310,0}, -- Red Flying Cloud
[248] = {99,310,0}, -- flying -- 99 ground to deprioritize in non-flying zones if any non-flying mounts are favorites
[254] = {0,0,60}, -- Subdued Seahorse -- +300% swim speed in Vashj'ir, +60% swim speed elsewhere
[269] = {100,0,0}, -- Water Striders
[284] = {60,0,0}, -- Chauffeured Chopper
}
local flexMounts = { -- flying mounts that look OK on the ground
[530] = true, -- Armored Skyscreamer
[864] = true, -- Ban-Lu, Grandmaster's Companion
[376] = true, -- Celestial Steed
[600] = true, -- Dread Raven
[888] = true, -- Fraseer's Raging Tempest
[532] = true, -- Ghastly Charger
[594] = true, -- Grinning Reaver
[219] = true, -- Headless Horseman's Mount
[547] = true, -- Hearthsteed
[885] = true, -- Highlord's Golden Charger
[894] = true, -- Highlord's Valorous Charger
[892] = true, -- Highlord's Vengeful Charger
[983] = true, -- Highlord's Vigilant Charger
[763] = true, -- Illidari Felstalker
[468] = true, -- Imperial Quilen
[363] = true, -- Invincible
[552] = true, -- Ironbound Wraithcharger
[457] = true, -- Jade Panther
[451] = true, -- Jeweled Onyx Panther
[932] = true, -- Lightforged Warframe
[949] = true, -- Luminous Starseeker
[845] = true, -- Mechanized Lumber Extractor
[741] = true, -- Mystic Runesaber
[931] = true, -- Netherlord's Accursed Wrathsteed
[930] = true, -- Netherlord's Brimstone Wrathsteed
[898] = true, -- Netherlord's Chaotic Wrathsteed
[455] = true, -- Obsidian Panther
[458] = true, -- Ruby Panther
[456] = true, -- Sapphire Panther
[522] = true, -- Sky Golem
[868] = true, -- SLayer's Felbroken Shrieker
[779] = true, -- Spirit of Eche'ro
[459] = true, -- Sunstone Panther
[523] = true, -- Swift Windsteed
[439] = true, -- Tyrael's Charger
[593] = true, -- Warforged Nightmare
[421] = true, -- Winged Guardian
}
local zoneMounts = { -- special mounts that don't need to be favorites
[678] = true, -- Chauffeured Mechano-Hog
[679] = true, -- Chauffeured Mekgineer's Chopper
[312] = true, -- Sea Turtle
[420] = true, -- Subdued Seahorse
[373] = true, -- Vashj'ir Seahorse
[117] = true, -- Blue Qiraji Battle Tank
[120] = true, -- Green Qiraji Battle Tank
[118] = true, -- Red Qiraji Battle Tank
[119] = true, -- Yellow Qiraji Battle Tank
}
local vashjirMaps = {
[204] = true, -- Abyssal Depths
[201] = true, -- Kelp'thar Forest
[205] = true, -- Shimmering Expanse
[203] = true, -- Vashj'ir
}
local mountIDs = C_MountJournal.GetMountIDs()
local randoms = {}
local function FillMountList(targetType)
-- print("Looking for:", targetType == SWIMMING and "SWIMMING" or targetType == FLYING and "FLYING" or "GROUND")
wipe(randoms)
local bestSpeed = 0
local mapID = C_Map.GetBestMapForUnit("player")
for i = 1, #mountIDs do
local mountID = mountIDs[i]
local name, spellID, _, _, isUsable, _, isFavorite = GetMountInfoByID(mountID)
if isUsable and (isFavorite or zoneMounts[mountID]) then
local _, _, sourceText, isSelfMount, mountType = GetMountInfoExtraByID(mountID)
local speed = mountTypeInfo[mountType][targetType]
if speed == 99 and flexMounts[mountID] then
speed = 100
elseif mountType == 254 and vashjirMaps[mapID] then -- Subdued Seahorse is faster in Vashj'ir
speed = 300
elseif mountType == 264 then -- Water Strider, prioritize in water, deprioritize on land
speed = IsSwimming() and 101 or 99
end
-- print("Checking:", name, mountType, "@", speed, "vs", bestSpeed)
if speed > 0 and speed >= bestSpeed then
if speed > bestSpeed then
bestSpeed = speed
wipe(randoms)
end
tinsert(randoms, spellID)
end
end
end
-- print("Found", #randoms, "possibilities")
return randoms
end
function GetMount()
-- TODO: Don't summon swimming mounts at water surface
local targetType = IsSubmerged() and SWIMMING or LibFlyable:IsFlyableArea() and FLYING or GROUND
FillMountList(targetType)
if #randoms == 0 and targetType == SWIMMING then
-- Fall back to non-swimming mounts
targetType = LibFlyable:IsFlyableArea() and FLYING or GROUND
FillMountList(targetType)
end
if #randoms > 0 then
local spellID = randoms[random(#randoms)]
return "/use " .. GetSpellInfo(spellID)
end
end
end
------------------------------------------------------------------------
------------------------------------------------------------------------
local GetAction
local function HasRidingSkill(flyingOnly)
local hasSkill = IsSpellKnown(90265) and 310 or IsSpellKnown(34091) and 280 or IsSpellKnown(34090) and 150
if flyingOnly then
return hasSkill
end
return hasSkill or IsSpellKnown(33391) and 100 or IsSpellKnown(33388) and 60
end
local function HasGlyph(id)
for i = 1, NUM_GLYPH_SLOTS do
local _, _, _, _, _, glyphID = GetGlyphSocketInfo(i)
if id == glyphID then
return true
end
end
end
------------------------------------------------------------------------
if PLAYER_CLASS == "DRUID" then
--[[
Travel Form
- outdoors,nocombat,flyable +310%
- outdoors,nocombat +100% (level 38, new in 7.1)
- outdoors +40%
--]]
local BLOCKING_FORMS
local orig_DISMOUNT = DISMOUNT
MOUNT_CONDITION = "[outdoors,nocombat,nomounted,noform,novehicleui,nomod:" .. MOD_TRAVEL_FORM .. "]"
DISMOUNT = DISMOUNT .. "\n/cancelform [form]"
function GetAction(force)
if force or not BLOCKING_FORMS then
BLOCKING_FORMS = "" -- in case of force
for i = 1, GetNumShapeshiftForms() do
local icon = strlower(GetShapeshiftFormInfo(i))
if not strmatch(icon, "spell_nature_forceofnature") then -- Moonkin Form OK
if BLOCKING_FORMS == "" then
BLOCKING_FORMS = ":" .. i
else
BLOCKING_FORMS = BLOCKING_FORMS .. "/" .. i
end
end
end
MOUNT_CONDITION = "[outdoors,nocombat,nomounted,noform" .. BLOCKING_FORMS .. ",novehicleui,nomod:" .. MOD_TRAVEL_FORM .. "]"
DISMOUNT = orig_DISMOUNT .. "\n/cancelform [form" .. BLOCKING_FORMS .. "]"
end
local mountOK, flightOK = SecureCmdOptionParse(MOUNT_CONDITION), LibFlyable:IsFlyableArea()
if mountOK and flightOK and IsPlayerSpell(SpellID["Travel Form"]) then
return "/cast " .. SpellName["Travel Form"]
end
local mount = mountOK and not IsPlayerMoving() and GetMount()
if mount then
return mount
elseif IsPlayerSpell(SpellID["Travel Form"]) and (IsOutdoors() or IsSubmerged()) then
return "/cast [nomounted,noform] " .. SpellName["Travel Form"]
elseif IsPlayerSpell(SpellID["Cat Form"]) then
return "/cast [nomounted,noform" .. BLOCKING_FORMS .. "] " .. SpellName["Cat Form"]
end
end
------------------------------------------------------------------------
elseif PLAYER_CLASS == "SHAMAN" then
MOUNT_CONDITION = "[outdoors,nocombat,nomounted,noform,novehicleui,nomod:" .. MOD_TRAVEL_FORM .. "]"
DISMOUNT = DISMOUNT .. "\n/cancelform [form]"
function GetAction()
local mount = SecureCmdOptionParse(MOUNT_CONDITION) and not IsPlayerMoving() and GetMount()
if mount then
return mount
elseif IsPlayerSpell(SpellID["Ghost Wolf"]) then
return "/cast [nomounted,noform] " .. SpellName["Ghost Wolf"]
end
end
------------------------------------------------------------------------
else
local ClassActionIDs = {
196555, -- Demon Hunter: Netherwalk (1.5m)
195072, -- Demon Hunter: Fel Rush (2x 10s)
125883, -- Monk: Zen Flight
115008, -- Monk: Chi Torpedo (2x 20s)
109132, -- Monk: Roll (2x 20s)
190784, -- Paladin: Divine Speed (45s)
202273, -- Paladin: Seal of Light
2983, -- Rogue: Sprint (1m)
111400, -- Warlock: Burning Rush
68992, -- Worgen: Darkflight (2m)
}
local ClassActionBlocked = {
[125883] = function(combat) return combat or IsIndoors() end, -- Zen Flight
}
function GetAction()
local combat = UnitAffectingCombat("player")
local classAction
for i = 1, #ClassActionIDs do
local id = ClassActionIDs[i]
if IsPlayerSpell(id) and not (ClassActionBlocked[id] and ClassActionBlocked[id](combat)) then
classAction = GetSpellInfo(id)
break
end
end
local moving = IsPlayerMoving()
if classAction and (moving or combat) then
return "/cast [nomounted,novehicleui] " .. classAction
elseif not moving then
local action
if SecureCmdOptionParse(MOUNT_CONDITION) then
action = GetMount()
end
if classAction and PLAYER_CLASS == "WARLOCK" then
-- TODO: why is /cancelform in here???
action = "/cancelaura " .. classAction .. (action and ("\n/cancelform [form]\n" .. action) or "")
end
return action
end
end
end
------------------------------------------------------------------------
local button = CreateFrame("Button", "MountMeButton", nil, "SecureActionButtonTemplate")
button:SetAttribute("type", "macro")
function button:Update()
if InCombatLockdown() then return end
self:SetAttribute("macrotext", strtrim(strjoin("\n",
GetOverrideMount() or GetAction() or "",
GetCVarBool("autoDismountFlying") and "" or SAFE_DISMOUNT,
DISMOUNT
)))
end
button:SetScript("PreClick", button.Update)
------------------------------------------------------------------------
button:RegisterEvent("PLAYER_LOGIN")
button:RegisterEvent("PLAYER_ENTERING_WORLD")
button:RegisterEvent("UPDATE_BINDINGS")
button:RegisterEvent("LEARNED_SPELL_IN_TAB")
button:RegisterEvent("PLAYER_REGEN_DISABLED")
button:RegisterEvent("PLAYER_REGEN_ENABLED")
button:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
button:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
button:RegisterEvent("ZONE_CHANGED_NEW_AREA") -- zone changed
button:RegisterEvent("ZONE_CHANGED") -- indoor/outdoor transition
button:SetScript("OnEvent", function(self, event)
if event == "PLAYER_LOGIN" then
if not MountJournalSummonRandomFavoriteButton then
CollectionsJournal_LoadUI()
end
elseif event == "UPDATE_BINDINGS" or event == "PLAYER_ENTERING_WORLD" then
ClearOverrideBindings(self)
local a, b = GetBindingKey("DISMOUNT")
if a then
SetOverrideBinding(self, false, a, "CLICK MountMeButton:LeftButton")
end
if b then
SetOverrideBinding(self, false, b, "CLICK MountMeButton:LeftButton")
end
else
self:Update(event == "UPDATE_SHAPESHIFT_FORMS") -- force extra update for druids
end
end)