-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathGuide.lua
415 lines (335 loc) · 10.9 KB
/
Guide.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
403
404
405
406
407
408
409
410
411
412
413
414
415
-----------------------------------------
-- LOCALIZED GLOBAL VARIABLES
-----------------------------------------
local ZGV = _G.ZGV
local GuideProto = {}
local Guide = ZGV.Class:New("Guide")
local Guide_mt = { __index=Guide }
local tinsert,_,_,min,max,floor,type,_,ipairs,_ = table.insert,table.remove,table.sort,math.min,math.max,math.floor,type,pairs,ipairs,_G.class
local L = ZGV.L
local Utils = ZGV.Utils
-----------------------------------------
-- SAVED REFERENCES
-----------------------------------------
ZGV.GuideProto = GuideProto
GuideProto.Types = {
LEVELING = 1,
TEST = 99,
}
GuideProto.SubTypes = {
TRI = 0,
TAM = 1,
MOR = 2,
}
GuideProto.Sides = {
AD = 1,
DC = 2,
EP = 3,
}
-----------------------------------------
-- GUIDEPROTO FUNCTIONS
-----------------------------------------
function GuideProto:New(title,data,extra)
local path,tit = title:match("^(.*)/+(.-)$")
local guidetype = path:match("^(.-)/") or path
local guide = {
title = title,
title_short = tit or title,
rawdata = data,
extra = extra,
num = #ZGV.registeredguides+1,
parsed = nil,
fully_parsed = nil,
type = guidetype,
subtype = 0 or ZGV.GuideMenuTier
}
guide.completionmode = guide.completionmode or (guide.type == "LEVELING" and "level") or "steps"
if ZGV:NeedsAnimatedPopup(guide) then
ZGV.AnimatePopup = true
return nil
end
setmetatable(guide,Guide_mt)
return guide
end
-----------------------------------------
-- GUIDE CLASS FUNCTIONS
-----------------------------------------
function Guide:Parse(fully)
if not self.rawdata or (self.parsed and self.fully_parsed) then return end
if self.parse_failed then return end
if fully then
GuideProto:Debug("Parsing guide: "..self.title)
end
local lastparsed = { linenum =- 2,linedata = "-?-" }
local success,parsed,err,linenum,stepnum,linedata = pcall(ZGV.Parser.ParseEntry,ZGV.Parser,self,fully,lastparsed)
if not success then
linenum,stepnum,linedata = lastparsed.linenum or -1, -1, lastparsed.linedata or "--"
end
if ZGV.sv and ZGV.sv.profile and ZGV.sv.profile.ignoreErrors then -- Allow loading still, but make sure the message gets spat out.
if not parsed then
if err then
ZGV:Error(L["message_errorloading_full"],self.title,linenum or 0,stepnum or "?",linedata or "???",err)
else
ZGV:Error(L["message_errorloading_brief"],self.title)
end
end
parsed = true
end
if not success then -- crashed!
local err = parsed
local errortext = L["message_errorloading_critical"]:format(self.title or "no title?",err or "no err?",linenum or -1,linedata or -1)
ZGV:Error(("%s"):format(errortext))
self.parse_failed = true
self.parse_error = errortext
return nil
elseif not parsed then -- just failed
local errortext
local line = _G.line
if err then
errortext = L["message_errorloading_full"]:format(self.title,line or 0,stepnum or "?",linedata or "???",err)
else
errortext = L["message_errorloading_brief"]:format(self.title)
end
ZGV:Error(("%s"):format(errortext))
self.parse_failed = true
self.parse_error = errortext
return nil
else
self.parsed = true
if self.steps and (#self.steps>0) then
self.fully_parsed = true
-- cap it with a finisher step
local step = ZGV.StepProto:New()
step.finish = true
local goal = ZGV.GoalProto:New()
goal.action = "text"
goal.text = "This guide is now complete."
step:AddGoal(goal)
self:AddStep(step)
self.steplabels = {}
for si,steps in ipairs(self.steps) do
local label = steps.label
if label then
if not self.steplabels[label] then
self.steplabels[label] = {}
end
tinsert(self.steplabels[label],si)
end
end
end
end
end
function Guide:DoCond(which,...)
if which == "valid" and not self.condition_valid then
if self.startlevel then
return Utils.GetPlayerPreciseLevel()>=self.startlevel,L['guide_level_req']:format(Utils.FormatLevel(self.startlevel))
end
return true -- If above is ok
end
if which == "suggested" and not self.condition_suggested and self.startlevel then
return Utils.GetPlayerPreciseLevel()>=self.startlevel
end
if which == "end" and not self.condition_end and self.endlevel then
return Utils.GetPlayerPreciseLevel()>=self.endlevel,L['guide_level_reached']:format(Utils.FormatLevel(self.startlevel))
end
if which and self['condition_'..which] then
local isOK,err = pcall(self['condition_'..which],self,...)
if isOK then
return err,self['condition_'..which..'_msg']
else
ZGV:Print("ERROR parsing condition for guide:\n"..self.title.."\n"..self['condition_'..which.."_raw"].."\nError: "..err)
return false,"ERROR: "..(self['condition_'..which..'_msg'] or "")
end
end
end
function Guide:GetStatus()
local ret,msg
ret,msg = self:DoCond("invalid")
if ret then
return "INVALID",msg
end
ret,msg = self:DoCond("valid")
if ret then
ret,msg = self:DoCond("end")
if ret then
return "COMPLETE",msg
end
msg = "" -- TODO it's a bug, we ask the end condition and we're reusing its value even if the guide isnt complete
if self.condition_suggested_raw or self.type=="LEVELING" then
ret,msg = self:DoCond("suggested")
if ret then
return "SUGGESTED"
end
end
return "VALID",msg
else
return "INVALID",msg
end
end
function Guide:Load(step)
ZGV:SetGuide(self,step)
end
-- TODO allow unloading of guides. It so few guides atm don't need to save on memory
function Guide:Unload()
--self.steps = nil
--self.fully_parsed = nil
--collectgarbage("step",100)
end
function Guide:SetAsCurrent()
ZGV.CurrentGuide = self
ZGV.CurrentGuideName = self.title
ZGV.sv.char.guidename = self.title
-- Clear this stuff because it gets set after
ZGV.CurrentStep = nil
ZGV.CurrentStepNum = nil
end
function Guide:GetCurStep()
return self:GetStep(ZGV.CurrentStepNum)
end
function Guide:GetStep(num)
if not num then return end
return self.steps[num]
end
function Guide:AddStep(step)
-- TODO type(step) ~= Step
step.parentGuide = self
step.num = #self.steps+1
tinsert(self.steps,step)
end
--TODO make sure this isn't redundant a bit inside
function Guide:GetFirstValidStep(start)
if not self.fully_parsed then return end
start = start or 1
local startstep = self:GetStep(start)
if start ~= 1 and not startstep then
return self:GetFirstValidStep(1)
end
assert(startstep,"GetFirstValidStep: no starting step at 1?? what the hell??")
-- starting step is good?
if startstep:AreRequirementsMet() then
return startstep
end
-- no? let's go forward...
local nextvalid = startstep:GetNextValidStep()
if nextvalid then
return nextvalid
end
if start ~= 1 then
return self:GetFirstValidStep(1)
end -- restart at 1, maybe that helps
return self.steps[1] -- always returns something, or breaks.
end
function Guide:GetCompletion(mode)
mode = mode or self.completionmode
if self.parse_failed then
return "error","parsing failed"
end
if mode == "steps" then
-- request full parsing for those
if not self.fully_parsed then
ZGV:Debug("Guide:GetCompletion : '"..self.title.."' needs parsing for completion type '"..mode.."'")
return "loading"
end
end
if mode == "none" then
return "none"
elseif mode == "level" then
if not self.startlevel or not self.endlevel then
return "error","no starting/ending level set"
end
return min( 1, max( 0, ( Utils.GetPlayerPreciseLevel() - self.startlevel ) / ( self.endlevel - self.startlevel ) ) )
elseif mode == "steps" then
local count,comp = 0,0
local prevStepComp = true -- Start as true, there is no previous step to the first step.
local curStepComp = self.steps[1]:IsComplete(1)
local nextStepComp
for si = 1, #self.steps do
local step = self.steps[si]
local nextstep = self.steps[si+1]
if not nextstep then -- If no next step just true it.
nextStepComp = true
else
nextStepComp = nextstep:IsComplete(1)
end
if step:AreRequirementsMet() -- Valid step?
and not step.finish -- Not last step
then
count = count + 1
if curStepComp then -- If this step is complete, easy
comp = comp + 1
elseif (prevStepComp and nextStepComp) then -- If the steps on both sides of this step are complete, lets say this step is complete. Rough, but for progress bar.
comp = comp + 1
end
end
-- going to next step, adjust variables.
prevStepComp = curStepComp
curStepComp = nextStepComp
end
return count > 0 and comp / count or 0, comp,count
end
-- other completions might not need a full parse.
return "error","we don't know if this guide completes or not"
end
-- Returns percentage in text form #%, a description, and a numeric percent for ease of use.
function Guide:GetCompletionText(mode)
mode = mode or self.completionmode
local comp,a,b,_,_ = self:GetCompletion(mode)
assert(comp) -- Sanity!
if comp == "loading" then
return "...","(loading)", 0
end
if comp == "error" or type(comp) ~= "number" then
return "?",("(an error occurred: %s)"):format(a or "black magic"), 0
end
comp = floor(comp*100)
if mode == "none" then
return "-", "This guide does not complete.", comp
elseif mode == "level" then
return comp.."%", ("Level %2d reached: %d%%"):format(self.endlevel,comp), comp
elseif mode == "steps" then
return comp.."%", ("Steps completed: %d/%d"):format(a,b), comp
else
return comp.."%", "", comp
end
end
function Guide:AdvertiseWithPopup(nodelay)
if not (ZGV.Frame and ZGV.Frame:IsShown()) then return end -- If ZGV is hidden then we don't need to be suggesting guides.
local delay = not nodelay
-- Don't suggest a guide while in combat. Wait until combat is done.
if delay and Utils.IsPlayerInCombat() then
ZGV.call_after_combat = function()
self:AdvertiseWithPopup(true)
end
ZGV:Print(L['guide_next_ready'])
return
end
local popup = ZGV.AdvertisePopup
if not popup then
-- This text hides unless needed
popup = ZGV.Popup:New("Zygor_AdvertiseGuide_Popup","sis")
popup.OnAccept = function(me)
ZGV:SetGuide(me.guide,me.guide.CurrentStepNum)
end
popup.OnDecline = function(me)
ZGV.db.char.ignoredguides[me.guide.title] = true
end
ZGV.AdvertisePopup = popup
end
popup:SetText(L['static_nextguide'],self.title_short,L['static_nextguide_anyzone'])
popup:SetDimensionConstraints(225,nil,625,nil)
popup.guide = self
ZGV.pause = true -- to avoid a loop of "step complete" clicks.
if not ZGV.AdvertisePopup:IsShown() then
ZGV.AdvertisePopup:Show()
end
end
function Guide:tostring()
return self.title_short
end
-----------------------------------------
-- DEBUG
-----------------------------------------
function GuideProto:Debug(...)
local str = ...
ZGV:Debug("&guide "..str, select(2,...) )
end