-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrestoreAndReset.lua
More file actions
340 lines (261 loc) · 11 KB
/
Copy pathrestoreAndReset.lua
File metadata and controls
340 lines (261 loc) · 11 KB
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
local folderName, Addon = ...
-- Locals for frequently used global frames and functions.
local MapUtil_GetDisplayableMapForPlayer = _G.MapUtil.GetDisplayableMapForPlayer
local C_Map_GetPlayerMapPosition = _G.C_Map.GetPlayerMapPosition
local C_Map_GetMapArtID = _G.C_Map.GetMapArtID
local Clamp = _G.Clamp
local GetTime = _G.GetTime
local InCombatLockdown = _G.InCombatLockdown
local WorldMapFrame = _G.WorldMapFrame
-- #######################################################################
-- ### - Storing and restoring the map when closing and re-opening it. ###
-- ### - Automatically switching to current map when changing zones ###
-- #######################################################################
-- To store map status when closing it.
local lastMapID = nil
local lastScale = nil
local lastScrollX = nil
local lastScrollY = nil
-- Flag to prevent saving before map was first shown.
local firstShownAfterLogin = false
-- Only store the last map for PWM_config.resetMapAfter seconds.
local lastMapCloseTime = GetTime()
-- If the last viewed map was the map in which the player was,
-- we want the map to automatically change to the new map;
-- both if the map is visible or not.
local lastViewedMapWasCurrentMap = false
local function CheckMap()
local currentMap = MapUtil_GetDisplayableMapForPlayer()
if WorldMapFrame:GetMapID() ~= currentMap then
if lastViewedMapWasCurrentMap then
-- If auto-centering is active, we want to preserve the zoom level.
Addon.ResetMap(PWM_config.autoCentering)
else
Addon.RecenterButtonSetEnabled(true)
end
else
Addon.RecenterButtonSetEnabled(false)
end
if C_Map_GetPlayerMapPosition(currentMap, "player") then
Addon.AutoCenterLockButtonSetEnabled(true)
else
Addon.AutoCenterLockButtonSetEnabled(false)
end
end
local function SaveMapState()
if not firstShownAfterLogin then return end
local mapID = WorldMapFrame:GetMapID()
if not mapID then
lastMapID = nil
lastScale = nil
lastScrollX = nil
lastScrollY = nil
return
end
lastMapID = WorldMapFrame:GetMapID()
lastScale = WorldMapFrame.ScrollContainer.currentScale
lastScrollX = WorldMapFrame.ScrollContainer.currentScrollX
lastScrollY = WorldMapFrame.ScrollContainer.currentScrollY
-- print("saving", lastMapID, lastScale, lastScrollX, lastScrollY)
-- These values are the same as obtained by these functions.
-- see \Interface\AddOns\Blizzard_MapCanvas\MapCanvas_ScrollContainerMixin.lua
-- WorldMapFrame.ScrollContainer.currentScale == WorldMapFrame.ScrollContainer:GetCanvasScale()
-- WorldMapFrame.ScrollContainer.currentScrollX == WorldMapFrame.ScrollContainer:GetNormalizedHorizontalScroll()
-- WorldMapFrame.ScrollContainer.currentScrollY == WorldMapFrame.ScrollContainer:GetNormalizedVerticalScroll()
end
-- Called when reopening the map.
-- During combat, pin instances have patched SetPassThroughButtons and
-- SetPropagateMouseClicks (see taintPrevent.lua) so pins are created
-- without errors. reloadAfterCombat refreshes passthrough after combat.
local function RestoreMapState()
if lastMapID and lastScale and lastScrollX and lastScrollY then
if InCombatLockdown() then
Addon.reloadAfterCombat = true
end
-- print("restoring", lastMapID, lastScale, lastScrollX, lastScrollY)
-- Selected content of WorldMapFrame:SetMapID(lastMapID):
local mapArtID = C_Map_GetMapArtID(lastMapID)
if WorldMapFrame.mapID ~= lastMapID or WorldMapFrame.mapArtID ~= mapArtID then
WorldMapFrame.areDetailLayersDirty = true
WorldMapFrame.mapID = lastMapID
WorldMapFrame.mapArtID = mapArtID
WorldMapFrame.expandedMapInsetsByMapID = {}
WorldMapFrame.ScrollContainer:SetMapID(lastMapID)
if WorldMapFrame:IsShown() then
WorldMapFrame:RefreshDetailLayers()
end
end
WorldMapFrame.ScrollContainer:InstantPanAndZoom(lastScale, lastScrollX, lastScrollY, true)
WorldMapFrame:OnMapChanged()
-- Force third-party addon pins to resize for the restored zoom.
-- Blizzard's own pins re-scale automatically because they inherit
-- MapCanvasPinMixin:OnCanvasScaleChanged, which InstantPanAndZoom fires
-- via MapCanvasMixin:OnCanvasScaleChanged. But many third-party map
-- addons (RareScanner is the prompting example; the pattern is common)
-- attach their pins as children/grandchildren of
-- WorldMapFrame.ScrollContainer.Child and expose a `UpdateScale`
-- method that is only run once at acquire time -- they don't subscribe
-- to OnCanvasScaleChanged. Without intervention their pins stay at
-- whatever size they computed at acquire-time, which is wrong when the
-- restored zoom differs from the canvas scale at that moment.
--
-- Walk the canvas descendant tree and call :UpdateScale() on every
-- frame that exposes it. Wrapped in pcall so a buggy addon's
-- UpdateScale can't break our restore. The depth cap is just a safety
-- limit -- RareScanner's pins are at depth 2, leave room for nested
-- containers.
do
local function Walk(frame, depth)
if not frame or depth > 4 then return end
if type(frame.UpdateScale) == "function" then
pcall(frame.UpdateScale, frame)
end
if frame.GetChildren then
for _, child in ipairs({ frame:GetChildren() }) do
Walk(child, depth + 1)
end
end
end
if WorldMapFrame.ScrollContainer and WorldMapFrame.ScrollContainer.Child then
Walk(WorldMapFrame.ScrollContainer.Child, 0)
end
end
lastViewedMapWasCurrentMap = (lastMapID == MapUtil_GetDisplayableMapForPlayer())
CheckMap()
end
end
-- Post hook for ToggleWorldMap to restore map after it is shown.
-- (Cannot do this with WorldMapFrame.ScrollContainer:HookScript("OnShow"),
-- because it gets called too early.)
local function RestoreMap()
if WorldMapFrame:IsShown() then
if not firstShownAfterLogin then
firstShownAfterLogin = true
end
-- print("Post Hook after showing", GetTime(), lastMapCloseTime, GetTime() - lastMapCloseTime, PWM_config.resetMapAfter)
if GetTime() - lastMapCloseTime > PWM_config.resetMapAfter then
lastMapID = nil
else
RestoreMapState()
end
-- To refresh buttons.
CheckMap()
end
end
hooksecurefunc("ToggleWorldMap", RestoreMap)
hooksecurefunc("ToggleQuestLog", RestoreMap)
-- Pre hook for WorldMapFrame.ScrollContainer OnHide to store map before it is hidden.
-- (Cannot do this with HookScript, because then it is called too late.
-- Also cannot do this in hooksecurefunc of ToggleWorldMap, because then it is not called when
-- closing the map manually.)
local OtherWorldMapFrameOnHideScripts = WorldMapFrame.ScrollContainer:GetScript("OnHide")
WorldMapFrame.ScrollContainer:SetScript("OnHide", function(self, ...)
-- print("Prehook before Hiding")
lastMapCloseTime = GetTime()
SaveMapState()
OtherWorldMapFrameOnHideScripts(self, ...)
end)
-- Also got to store and restore when the SidePanelToggle is shown or hidden.
local OtherCloseButtonScripts = WorldMapFrame.SidePanelToggle.CloseButton:GetScript("OnClick")
WorldMapFrame.SidePanelToggle.CloseButton:SetScript("OnClick", function(...)
SaveMapState()
OtherCloseButtonScripts(...)
RestoreMapState()
Addon.PlayerPingAnimation(false)
end)
local OtherOpenButtonScripts = WorldMapFrame.SidePanelToggle.OpenButton:GetScript("OnClick")
WorldMapFrame.SidePanelToggle.OpenButton:SetScript("OnClick", function(...)
SaveMapState()
OtherOpenButtonScripts(...)
RestoreMapState()
Addon.PlayerPingAnimation(false)
end)
Addon.ResetMap = function(preserveZoom)
if InCombatLockdown() then
Addon.reloadAfterCombat = true
end
local previousScale = WorldMapFrame.ScrollContainer.currentScale
-- print("ResetMap", MapUtil_GetDisplayableMapForPlayer())
WorldMapFrame:SetMapID(MapUtil_GetDisplayableMapForPlayer())
if preserveZoom and previousScale then
-- print("restoring", previousScale)
previousScale = Clamp(previousScale, WorldMapFrame.ScrollContainer:GetScaleForMinZoom(), WorldMapFrame.ScrollContainer:GetScaleForMaxZoom())
WorldMapFrame.ScrollContainer.currentScale = previousScale
WorldMapFrame.ScrollContainer.targetScale = previousScale
WorldMapFrame:OnMapChanged()
-- TODO: Find out if the player pin has changed its absolute position and only ping then.
-- Addon.PlayerPingAnimation(false)
else
WorldMapFrame:OnMapChanged()
end
SaveMapState()
end
hooksecurefunc(WorldMapFrame, "SetMapID",
function(self, mapID)
-- print("SetMapID", mapID, MapUtil_GetDisplayableMapForPlayer())
if WorldMapFrame:IsShown() then
lastViewedMapWasCurrentMap = (mapID == MapUtil_GetDisplayableMapForPlayer())
if not lastViewedMapWasCurrentMap then
Addon.RecenterButtonSetEnabled(true)
else
Addon.RecenterButtonSetEnabled(false)
end
end
Addon.HookPins()
end
)
-- If the dungeon pins were not activated when the map was first changed,
-- we need to execute the hooks again, when the user activates them.
hooksecurefunc(WorldMapFrame, "RefreshAllDataProviders",
function(...)
Addon.HookPins()
end
)
-- Frame to listen to zone change events such that the map gets reset when opened after having
-- changed into an area with a different map.
local zoneChangeFrame = CreateFrame ("Frame")
-- But only do this for areas, not for sub-zone changes.
zoneChangeFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
zoneChangeFrame:RegisterEvent("ZONE_CHANGED")
zoneChangeFrame:RegisterEvent("ZONE_CHANGED_INDOORS")
zoneChangeFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
-- Needed for map changes in dungeons.
zoneChangeFrame:RegisterEvent("AREA_POIS_UPDATED")
zoneChangeFrame:SetScript("OnEvent",
function(_, event, ...)
if WorldMapFrame:IsShown() then
-- print(event, ":", GetZoneText(), GetSubZoneText(), WorldMapFrame:GetMapID(), MapUtil_GetDisplayableMapForPlayer(), lastViewedMapWasCurrentMap)
CheckMap()
end
end
)
-- Some map changes (especially when moving out of any zone, so that the map shows the whole continent)
-- are not accompanied by any event. So we just check repeatedly.
local function TimedUpdate()
if WorldMapFrame:IsShown() then
CheckMap()
end
end
local updateTimer
local function StartMapUpdates()
if updateTimer then return end
updateTimer = C_Timer.NewTicker(1, TimedUpdate)
end
local function StopMapUpdates()
if updateTimer then
updateTimer:Cancel()
updateTimer = nil
end
end
-- Use in OnShow/OnHide.
-- OnShow goes through taintPrevent.lua section (2)'s RegisterWorldMapOnShow
-- so the callback still fires when section (2) SetScripts OnShow while in
-- an instance -- see the section (2) "OWN OnShow HOOKS" note.
Addon.RegisterWorldMapOnShow(StartMapUpdates)
WorldMapFrame:HookScript("OnHide", StopMapUpdates)
local startupFrame = CreateFrame("Frame")
startupFrame:RegisterEvent("LOADING_SCREEN_DISABLED")
startupFrame:SetScript("OnEvent", function()
-- After each loading screen, reset the map memory.
lastMapID = nil
end)