forked from LudiusMaximus/PersistentWorldMap
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.lua
More file actions
415 lines (306 loc) · 13.2 KB
/
Copy pathmain.lua
File metadata and controls
415 lines (306 loc) · 13.2 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
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
403
404
405
406
407
408
409
410
411
412
413
414
415
local folderName, Addon = ...
-- Locals for frequently used global frames and functions.
local C_Map_GetPlayerMapPosition = _G.C_Map.GetPlayerMapPosition
local Clamp = _G.Clamp
local GetTime = _G.GetTime
local WorldMapFrame = _G.WorldMapFrame
local IsShiftKeyDown = _G.IsShiftKeyDown
local GetScaledCursorPosition = _G.GetScaledCursorPosition
-- To prevent player pin pings when we don't need them.
local playerPin = nil
Addon.PlayerPingAnimation = function(start)
-- If we do not have the player pin yet, search it.
if not playerPin or not playerPin.ShouldShowUnit or not playerPin:ShouldShowUnit("player") then
playerPin = nil
for k, _ in pairs(WorldMapFrame.dataProviders) do
if type(k) == "table" and k.ShouldShowUnit then
-- print("Found GroupMembersDataProvider.")
if k:ShouldShowUnit("player") then
playerPin = k
break
end
end
end
end
if playerPin then
-- TOOD: Make PIN size an option.
-- TODO: Also continuous ping animation.
-- Default is 27.
-- playerPin:SetUnitPinSize("player", 100)
if start then
-- Arguments are duration and fade-out duration.
playerPin.pin:StartPlayerPing(2, .25)
else
playerPin.pin:StopPlayerPing(2, .25)
end
-- Got to call this to prevent pin size flicker.
playerPin.pin:SynchronizePinSizes()
end
end
local function EaseOutQuart(t)
local invT = 1 - t
local invT2 = invT * invT -- (1 - t)^2
local invT4 = invT2 * invT2 -- ((1 - t)^2)^2 = (1 - t)^4
return 1 - invT4
end
-- Current target zoom.
local zoomStartTime
local currentStartScale = nil
local currentTargetScale = nil
local currentStartX = nil
local currentTargetX = nil
local currentStartY = nil
local currentTargetY = nil
-- Using an OnUpdate frame did not work without flickering of quest blobs.
-- local smoothZoomFrame = CreateFrame("Frame")
-- local function SmoothZoomOnUpdateFunction(self, elapsed)
-- local zoomElapsed = Clamp(GetTime() - zoomStartTime, 0, PWM_config.zoomTimeSeconds)
-- if zoomElapsed >= PWM_config.zoomTimeSeconds then
-- WorldMapFrame.ScrollContainer:InstantPanAndZoom(currentTargetScale, currentTargetX, currentTargetY, true)
-- smoothZoomFrame:SetScript("OnUpdate", nil)
-- return
-- end
-- local zoomElapsedNormalized = zoomElapsed / PWM_config.zoomTimeSeconds
-- WorldMapFrame.ScrollContainer:InstantPanAndZoom(currentStartScale + zoomElapsedNormalized * (currentTargetScale - currentStartScale), currentTargetX, currentTargetY, true)
-- end
-- So instead I use a super-fast ticker.
local zoomTicker
local function ZoomAndPanStop()
if zoomTicker then
zoomTicker:Cancel()
zoomTicker = nil
zoomStartTime = nil
currentStartScale = nil
currentTargetScale = nil
currentStartX = nil
currentTargetX = nil
currentStartY = nil
currentTargetY = nil
end
end
local function ZoomTickerFunction(self)
local zoomElapsed = Clamp(GetTime() - zoomStartTime, 0, PWM_config.zoomTimeSeconds)
-- When we are done, make sure we have the final position.
if zoomElapsed >= PWM_config.zoomTimeSeconds or (
WorldMapFrame.ScrollContainer:GetCanvasScale() == currentTargetScale and
WorldMapFrame.ScrollContainer:GetCurrentScrollX() == currentTargetX and
WorldMapFrame.ScrollContainer:GetCurrentScrollY() == currentTargetY ) then
local xMin, xMax, yMin, yMax = WorldMapFrame.ScrollContainer:CalculateScrollExtentsAtScale(currentTargetScale)
local nextX = Clamp(currentTargetX, xMin, xMax)
local nextY = Clamp(currentTargetY, yMin, yMax)
WorldMapFrame.ScrollContainer:InstantPanAndZoom(currentTargetScale, currentTargetX, currentTargetY, true)
ZoomAndPanStop()
-- Otherwise, set the intermediate zoom and pan.
else
local zoomElapsedNormalized = EaseOutQuart(zoomElapsed / PWM_config.zoomTimeSeconds)
local nextScale = currentStartScale + zoomElapsedNormalized * (currentTargetScale - currentStartScale)
local xMin, xMax, yMin, yMax = WorldMapFrame.ScrollContainer:CalculateScrollExtentsAtScale(nextScale)
local nextX = Clamp(currentStartX + zoomElapsedNormalized * (currentTargetX - currentStartX), xMin, xMax)
local nextY = Clamp(currentStartY + zoomElapsedNormalized * (currentTargetY - currentStartY), yMin, yMax)
WorldMapFrame.ScrollContainer:InstantPanAndZoom(nextScale, nextX, nextY, true)
end
end
local function ZoomAndPan(currentScale, targetScale, currentX, targetX, currentY, targetY)
if PWM_config.zoomTimeSeconds == 0 then
WorldMapFrame.ScrollContainer:InstantPanAndZoom(targetScale, targetX, targetY, true)
else
-- Setup variables for the zoom/pan process.
zoomStartTime = GetTime()
currentStartScale = currentScale
currentTargetScale = targetScale
currentStartX = currentX
currentTargetX = targetX
currentStartY = currentY
currentTargetY = targetY
-- Using an OnUpdate frame did not work without flickering of quest blobs.
-- smoothZoomFrame:SetScript("OnUpdate", SmoothZoomOnUpdateFunction)
-- So instead we use a super-fast ticker.
if not zoomTicker then
zoomTicker = C_Timer.NewTicker(0.01, ZoomTickerFunction)
end
end
end
-- Disable default zoom.
WorldMapFrame.ScrollContainer:SetMouseWheelZoomMode(MAP_CANVAS_MOUSE_WHEEL_ZOOM_BEHAVIOR_NONE)
-- Override mouse wheel for custom zoom behavior.
WorldMapFrame.ScrollContainer:HookScript("OnMouseWheel", function(self, delta)
-- If we are currently zooming, we take currentTargetScale as the current scale.
local currentScale = currentTargetScale or self:GetCanvasScale()
-- Check if zooming is still possible.
if (delta < 0 and currentScale == self:GetScaleForMinZoom()) or (delta > 0 and currentScale == self:GetScaleForMaxZoom()) then
return
end
-- We could do it like MapCanvasScrollControllerMixin:OnMouseWheel()
-- but self.zoomAmountPerMouseWheelDelta behaves differently on different maps.
-- local targetScale = currentScale + self.zoomAmountPerMouseWheelDelta * delta
-- Using the predefined zoom levels of each map is better.
local currentZoomLevelIndex = self:GetZoomLevelIndexForScale(currentScale)
local targetScale = (self.zoomLevels[currentZoomLevelIndex + delta] or self.zoomLevels[currentZoomLevelIndex]).scale
local currentX = self:GetCurrentScrollX()
local currentY = self:GetCurrentScrollY()
local targetX = currentX
local targetY = currentY
-- TODO: Make zoom-panning towards cursor position optional.
-- The map boundaries are taken care of by the zoom function.
ZoomAndPan(currentScale, targetScale, currentX, targetX, currentY, targetY)
end)
-- Do a smooth pan before activating auto-centering.
local autoCenterStartTimer
local function AutoCenterStartTimerFunction()
autoCenterStartTimer = nil
end
Addon.EnableCenterOnPlayer = function()
if PWM_config.autoCentering then return end
PWM_config.autoCentering = true
Addon.UpdateAutoCenterLockButton()
-- Do a smooth pan before activating auto-centering.
-- But not when zoomed fully out, because that causes a slight map jerk.
local currentScale = WorldMapFrame.ScrollContainer:GetCanvasScale()
if currentScale > WorldMapFrame.ScrollContainer:GetScaleForMinZoom() then
local currentX = WorldMapFrame.ScrollContainer:GetCurrentScrollX()
local currentY = WorldMapFrame.ScrollContainer:GetCurrentScrollY()
local playerPos = C_Map_GetPlayerMapPosition(WorldMapFrame:GetMapID(), "player")
if playerPos then
local targetX, targetY = playerPos:GetXY()
targetX = Clamp(targetX, WorldMapFrame.ScrollContainer.scrollXExtentsMin, WorldMapFrame.ScrollContainer.scrollXExtentsMax)
targetY = Clamp(targetY, WorldMapFrame.ScrollContainer.scrollYExtentsMin, WorldMapFrame.ScrollContainer.scrollYExtentsMax)
Addon.PlayerPingAnimation(true)
ZoomAndPan(currentScale, currentScale, currentX, targetX, currentY, targetY)
end
autoCenterStartTimer = C_Timer.NewTimer(PWM_config.zoomTimeSeconds, AutoCenterStartTimerFunction)
end
end
Addon.DisableCenterOnPlayer = function()
if autoCenterStartTimer then
autoCenterStartTimer:Cancel()
autoCenterStartTimer = nil
end
ZoomAndPanStop()
PWM_config.autoCentering = false
Addon.UpdateAutoCenterLockButton()
end
-- Disable center on player when trying to drag the map.
-- Enable when double-clicking the map.
local isMouseDown = false
local lastCursorX = nil
local lastCursorY = nil
-- For double click detection
local lastClickTime = 0
-- Shift click the map to reset it!
local resetMap = false
WorldMapFrame.ScrollContainer:HookScript("OnMouseDown", function(self, button)
if button == "LeftButton" then
isMouseDown = true
lastCursorX, lastCursorY = GetScaledCursorPosition()
if self:CanPan() then
-- Stop any zoom animation.
ZoomAndPanStop()
-- If currently moving towards auto-centering, interrupt.
if autoCenterStartTimer then
Addon.DisableCenterOnPlayer()
end
end
if IsShiftKeyDown() then
resetMap = true
else
resetMap = false
end
end
end)
WorldMapFrame.ScrollContainer:HookScript("OnMouseUp", function(self, button)
if button == "LeftButton" then
isMouseDown = false
-- Check for double click
if PWM_config.autoCenterEnabled then
local currentTime = GetTime()
if currentTime - lastClickTime < PWM_config.doubleClickTime then
Addon.EnableCenterOnPlayer()
end
lastClickTime = currentTime
end
if resetMap then
if IsShiftKeyDown() then
Addon.ResetMap()
end
resetMap = false
end
end
end)
WorldMapFrame.ScrollContainer:HookScript("OnUpdate", function(self)
-- Disable auto-centering and stop zooming when player starts to drag.
if isMouseDown then
ZoomAndPanStop()
local cursorX, cursorY = GetScaledCursorPosition()
if cursorX ~= lastCursorX or cursorY ~= lastCursorY then
Addon.DisableCenterOnPlayer()
end
end
-- Auto-centering.
if PWM_config.autoCentering and not autoCenterStartTimer then
-- Ensure that targetScale exists. Not checking this sometimes caused an error
-- when changing into a zone with loading screen (e.g. Ringing Deeps to Dornogal).
if not self.targetScale then return end
-- Ensure scroll extents are initialized. Has not caused issues so far, but you never know.
if not self.scrollXExtentsMin or not self.scrollXExtentsMax or
not self.scrollYExtentsMin or not self.scrollYExtentsMax then
return
end
local playerPos = C_Map_GetPlayerMapPosition(self:GetParent():GetMapID(), "player")
if not playerPos then return end
local targetX, targetY = playerPos:GetXY()
targetX = Clamp(targetX, self.scrollXExtentsMin, self.scrollXExtentsMax)
targetY = Clamp(targetY, self.scrollYExtentsMin, self.scrollYExtentsMax)
-- Use instant pan to correct the position.
if self.currentScrollX ~= targetX then
self.currentScrollX = targetX
self.targetScrollX = targetX
self:SetNormalizedHorizontalScroll(targetX)
end
if self.currentScrollY ~= targetY then
self.currentScrollY = targetY
self.targetScrollY = targetY
self:SetNormalizedVerticalScroll(targetY)
end
end
end)
local updateMapFrame = CreateFrame("Frame")
-- Needed to remove tomb stone pin.
updateMapFrame:RegisterEvent("PLAYER_UNGHOST")
-- Sometimes accepting (world) quests does not remove the exlamation mark.
updateMapFrame:RegisterEvent("QUEST_ACCEPTED")
-- Just to be on the save side.
updateMapFrame:RegisterEvent("QUEST_REMOVED")
-- Needed to change flightpoint icon colour after learning a new flightpoint.
updateMapFrame:RegisterEvent("TAXI_NODE_STATUS_CHANGED")
-- Update map after killing dungeon/raid boss.
updateMapFrame:RegisterEvent("TREASURE_PICKER_CACHE_FLUSH")
-- To refresh map after trader's tenders chest reward collected.
updateMapFrame:RegisterEvent("CHEST_REWARDS_UPDATED_FROM_SERVER")
updateMapFrame:SetScript("OnEvent", function()
-- Sometimes does not work right away.
C_Timer.NewTimer(0.2, function()
if not WorldMapFrame:IsShown() then return end
WorldMapFrame:OnMapChanged()
Addon.PlayerPingAnimation(false)
end)
end)
-- -- In case you ever need this again.
-- function RedrawBlobs()
-- for provider, _ in pairs(WorldMapFrame.dataProviders) do
-- if type(provider) == "table" then
-- -- if provider.AddQuest then
-- -- print("Found QuestDataProvider")
-- -- provider:RefreshAllData() -- Working!
-- -- end
-- -- Minimal alternative to redraw blob. Unfortunately does not fix flicker.
-- if provider.IsShowingWorldQuests then
-- -- print("Found QuestBlobDataProvider")
-- -- provider:OnMapChanged()
-- -- provider.pin:OnMapChanged()
-- -- provider.pin:Refresh()
-- provider.pin:DrawNone()
-- provider.pin:DrawBlob(provider.pin.questID, true)
-- end
-- end
-- end
-- end