1 QuestHelper_File
["director_quest.lua"] = "Development Version"
2 QuestHelper_Loadtime
["director_quest.lua"] = GetTime()
4 local debug_output
= false
5 if QuestHelper_File
["director_quest.lua"] == "Development Version" then debug_output
= true end
9 Little bit of explanation here.
11 The db layer dumps things out in DB format. This isn't immediately usable for our routing engine. We convert this to an intermediate "metaobjective" format that the routing engine can use, as well as copying anything that needs to be copied. This also allows us to modify our metaobjective tables as we see fit, rather than doing nasty stuff to keep the original objectives intact.
13 It's worth mentioning that, completely accidentally, everything it requests from the DB is deallocated rapidly - it doesn't keep any references to the original DB objects around. This is unintentional, but kind of neat. It's not worth preserving, but it doesn't really have to be "fixed" either.
17 local function copy(tab
)
19 for _
, v
in ipairs(tab
) do
25 local function copy_without_last(tab
)
27 for _
, v
in ipairs(tab
) do
34 local function AppendObjlinks(target
, source
, tooltips
, icon
, last_name
, map_lines
, tooltip_lines
, seen
)
35 if not seen
then seen
= {} end
36 if not map_lines
then map_lines
= {} end
37 if not tooltip_lines
then tooltip_lines
= {} end
39 QuestHelper
: Assert(not seen
[source
])
41 if seen
[source
] then return end
46 for m
, v
in ipairs(source
.loc
) do
47 QuestHelper
: Assert(target
)
48 QuestHelper
: Assert(QuestHelper_ParentLookup
)
49 QuestHelper
: Assert(QuestHelper_ParentLookup
[v
.p
], v
.p
)
50 table.insert(target
, {loc
= {x
= v
.x
, y
= v
.y
, c
= QuestHelper_ParentLookup
[v
.p
], p
= v
.p
}, path_desc
= copy(map_lines
), icon_id
= icon
or 6})
54 target
= nil -- if we have a "source" as well, then we want to plow through it for tooltip data, but we don't want to add targets for it
57 for _
, v
in ipairs(source
) do
58 local dbgi
= DB_GetItem(v
.sourcetype
, v
.sourceid
, nil, true)
61 if v
.sourcetype
== "monster" then
62 table.insert(map_lines
, QHFormat("OBJECTIVE_SLAY", dbgi
.name
or QHText("OBJECTIVE_UNKNOWN_MONSTER")))
63 table.insert(tooltip_lines
, 1, QHFormat("TOOLTIP_SLAY", source
.name
or "nothing"))
65 elseif v
.sourcetype
== "item" then
66 table.insert(map_lines
, QHFormat("OBJECTIVE_ACQUIRE", dbgi
.name
or QHText("OBJECTIVE_ITEM_UNKNOWN")))
67 table.insert(tooltip_lines
, 1, QHFormat("TOOLTIP_LOOT", source
.name
or "nothing"))
70 table.insert(map_lines
, string.format("unknown %s (%s/%s)", tostring(dbgi
.name
), tostring(v
.sourcetype
), tostring(v
.sourceid
)))
71 table.insert(tooltip_lines
, 1, string.format("unknown %s (%s/%s)", tostring(last_name
), tostring(v
.sourcetype
), tostring(v
.sourceid
)))
75 tooltips
[string.format("%s@@%s", v
.sourcetype
, v
.sourceid
)] = copy_without_last(tooltip_lines
)
77 AppendObjlinks(target
, dbgi
, tooltips
, icon
or licon
, source
.name
, map_lines
, tooltip_lines
, seen
)
78 table.remove(tooltip_lines
, 1)
79 table.remove(map_lines
)
88 local function horribledupe(from
)
89 if not from
then return nil end
92 for k
, v
in pairs(from
) do
93 if k
== "__owner" then
94 elseif type(v
) == "table" then
95 rv
[k
] = horribledupe(v
)
104 local quest_list
= setmetatable({}, {__mode
="k"})
106 local QuestCriteriaWarningBroadcast
108 local function GetQuestMetaobjective(questid
, lbcount
)
109 if not quest_list
[questid
] then
110 local q
= DB_GetItem("quest", questid
, true, true)
113 QuestHelper
: TextOut("Missing lbcount, guessing wildly")
114 if q
and q
.criteria
then
116 for k
, v
in ipairs(q
.criteria
) do
117 lbcount
= math
.max(lbcount
, k
)
124 -- just doublechecking here
125 if not QuestCriteriaWarningBroadcast
and q
and q
.criteria
then for k
, v
in pairs(q
.criteria
) do
126 if type(k
) == "number" and k
> lbcount
then
127 --QuestHelper:TextOut(string.format("Too many stored objectives for this quest, please report on the Questhelper homepage (%s %s %s)", questid, lbcount, k)) -- we're just going to hide this for now
128 QuestHelper_ErrorCatcher_ExplicitError(false, string.format("Too many stored objectives (%s %s %s)", questid
, lbcount
, k
))
129 QuestCriteriaWarningBroadcast
= true
133 ite
= {type_quest
= {__backlink
= ite
}} -- we don't want to mutate the existing quest data. backlink exists only for nasty GC reasons
134 ite
.desc
= string.format("Quest %s", q
and q
.name
or "(unknown)") -- this gets changed later anyway
136 for i
= 1, lbcount
do
138 --QuestHelper:TextOut(string.format("critty %d %d", k, c.loc and #c.loc or -1))
140 ttx
.tooltip_canned
= {}
142 if q
and q
.criteria
and q
.criteria
[i
] then
143 AppendObjlinks(ttx
, q
.criteria
[i
], ttx
.tooltip_canned
)
145 if debug_output
and q
.criteria
[i
].loc
and #q
.criteria
[i
] > 0 then
146 QuestHelper
:TextOut(string.format("Wackyquest %d/%d", questid
, i
))
149 ttx
.solid
= horribledupe(q
.criteria
[i
].solid
)
153 table.insert(ttx
, {loc
= {x
= 5000, y
= 5000, c
= 0, p
= 2}, icon_id
= 7, type_quest_unknown
= true, map_desc
= {"Unknown"}}) -- this is Ashenvale, for no particularly good reason
156 for idx
, v
in ipairs(ttx
) do
157 v
.desc
= string.format("Criteria %d", i
)
160 v
.type_quest
= ite
.type_quest
163 for k
, v
in pairs(ttx
.tooltip_canned
) do
164 ttx
.tooltip_canned
[k
] = {ttx
.tooltip_canned
[k
], ttx
} -- we're gonna be handing out this table to other modules, so this isn't as dumb as it looks
171 local ttx
= {type_quest_finish
= true}
172 --QuestHelper:TextOut(string.format("finny %d", q.finish.loc and #q.finish.loc or -1))
173 if q
and q
.finish
and q
.finish
.loc
then
174 ttx
.solid
= horribledupe(q
.finish
.solid
)
175 for m
, v
in ipairs(q
.finish
.loc
) do
177 --print(QuestHelper_IndexLookup[v.rc])
178 --print(QuestHelper_IndexLookup[v.rc][v.rz])
179 table.insert(ttx
, {desc
= "Turn in quest", why
= ite
, loc
= {x
= v
.x
, y
= v
.y
, c
= QuestHelper_ParentLookup
[v
.p
], p
= v
.p
}, tracker_hidden
= true, cluster
= ttx
, icon_id
= 7, type_quest
= ite
.type_quest
})
184 table.insert(ttx
, {desc
= "Turn in quest", why
= ite
, loc
= {x
= 5000, y
= 5000, c
= 0, p
= 2}, tracker_hidden
= true, cluster
= ttx
, icon_id
= 7, type_quest
= ite
.type_quest
, type_quest_unknown
= true}) -- this is Ashenvale, for no particularly good reason
190 quest_list
[questid
] = ite
192 if q
then DB_ReleaseItem(q
) end
195 return quest_list
[questid
]
199 local function GetQuestType(link
)
200 return tonumber(string.match(link
,
201 "^|cff%x%x%x%x%x%x|Hquest:(%d+):[%d-]+|h%[[^%]]*%]|h|r$"
202 )), tonumber(string.match(link
,
203 "^|cff%x%x%x%x%x%x|Hquest:%d+:([%d-]+)|h%[[^%]]*%]|h|r$"
208 local function UpdateTrigger()
212 -- It's possible that things end up garbage-collected and we end up with different tables than we expect. This is something that the entire system is kind of prone to. The solution's pretty easy - we just have to store them ourselves while we're using them.
215 local objective_parse_table
= {
216 item
= function (txt
) return QuestHelper
:convertPattern(QUEST_OBJECTS_FOUND
)(txt
) end,
217 object
= function (txt
) return QuestHelper
:convertPattern(QUEST_OBJECTS_FOUND
)(txt
) end, -- why does this even exist
218 monster
= function (txt
) return QuestHelper
:convertPattern(QUEST_MONSTERS_KILLED
)(txt
) end,
219 event
= function (txt
, done
) return txt
, (done
and 1 or 0), 1 end, -- It appears that events are only used for things which can only happen once.
220 reputation
= function (txt
) return QuestHelper
:convertPattern(QUEST_FACTION_NEEDED
)(txt
) end, -- :ughh:
221 player
= function (txt
) return QuestHelper
:convertPattern(QUEST_MONSTERS_KILLED
)(txt
) end, -- We're using monsters here in the hopes that it follows the same pattern. I'd rather not try to find the exact right version of "player" in the locale files, though PLAYER might be it.
224 local function objective_parse(typ
, txt
, done
)
225 local pt
, target
, have
, need
= typ
, objective_parse_table
[typ
](txt
, done
)
228 -- well, that didn't work
229 target
, have
, need
= string.match(txt
, "^%s*(.-)%s*:%s*(.-)%s*/%s*(.-)%s*$")
231 --QuestHelper:TextOut(string.format("%s rebecomes %s/%s/%s", tostring(title), tostring(target), tostring(have), tostring(need)))
235 target
, have
, need
= string.match(txt
, "^%s*(.-)%s*$"), (done
and 1 or 0), 1
236 --QuestHelper:TextOut(string.format("%s rerebecomes %s/%s/%s", tostring(title), tostring(target), tostring(have), tostring(need)))
239 QuestHelper
: Assert(target
) -- This will fail repeatedly. Come on. We all know it.
240 QuestHelper
: Assert(have
)
241 QuestHelper
: Assert(need
) -- As will these.
243 if tonumber(have
) then have
= tonumber(have
) end
244 if tonumber(need
) then need
= tonumber(need
) end
246 return pt
, target
, have
, need
249 local function clamp(v
)
250 if v
< 0 then return 0 elseif v
> 255 then return 255 else return v
end
253 local function colorlerp(position
, r1
, g1
, b1
, r2
, g2
, b2
)
254 local antip
= 1 - position
255 return string.format("|cff%02x%02x%02x", clamp((r1
* antip
+ r2
* position
) * 255), clamp((g1
* antip
+ g2
* position
) * 255), clamp((b1
* antip
+ b2
* position
) * 255))
258 -- We're just gonna do the same thing QH originally did - red->yellow->green.
259 local function difficulty_color(position
)
260 if position
< 0 then position
= 0 end
261 if position
> 1 then position
= 1 end
262 return (position
< 0.5) and colorlerp(position
* 2, 1, 0, 0, 1, 1, 0) or colorlerp(position
* 2 - 1, 1, 1, 0, 0, 1, 0)
265 local function MakeQuestTitle(title
, level
)
266 local plevel
= UnitLevel("player") -- meh, should probably cache this, buuuuut
271 elseif plevel
>= 40 then
272 grayd
= plevel
/ 5 + 1
274 grayd
= plevel
/ 10 + 5
277 local isgray
= (plevel
- floor(grayd
) >= level
)
279 local ccode
= isgray
and "|cffb0b0b0" or difficulty_color(1 - ((level
- plevel
) / grayd
+ 1) / 2)
280 local qlevel
= string.format("[%d] ", level
)
283 if QuestHelper_Pref
.track_level
then ret
= qlevel
.. ret
end
284 if QuestHelper_Pref
.track_qcolour
then ret
= ccode
.. ret
end
289 local function MakeQuestObjectiveTitle(progress
, target
)
290 if not progress
then return nil end
292 local player
= UnitName("player")
295 for _
, v
in pairs(progress
) do
297 if v
[3] == 1 then pd
= pd
+ 1 end
303 local party_show
= false
304 local party_compact
= false
306 if progress
[player
] then
307 local have
, need
= tonumber(progress
[player
][1]), tonumber(progress
[player
][2])
309 ccode
= difficulty_color(progress
[player
][3])
311 if have
and need
then
313 status
= string.format("%d/%d", have
, need
)
317 status
= string.format("%s/%s", progress
[player
][1], progress
[player
][2])
321 if pt
> 1 then party_show
= true end
323 ccode
= difficulty_color(1) -- probably just in the process of being removed from the tracker
326 ccode
= difficulty_color(pd
/ pt
)
332 if party_compact
then
333 party
= string.format("(P: %d/%d)", pd
, pt
)
335 party
= string.format("Party %d/%d", pd
, pt
)
339 if QuestHelper_Pref
.track_ocolour
then
340 target
= ccode
.. target
343 if status
or party
then
344 target
= target
.. ":"
348 target
= target
.. " " .. status
352 target
= target
.. " " .. party
358 local function Clicky(index
)
359 ShowUIPanel(QuestLogFrame
)
360 QuestLog_SetSelection(index
)
365 name
= "director_quest_unknown_objective",
368 friendly_reason
= QHText("UNKNOWN_OBJ"),
371 -- InsertedItem[item] = {"list", "of", "reasons"}
372 local InsertedItems
= {}
373 local TooltipType
= {}
374 local Unknowning
= {}
377 local function SetTooltip(item
, typ
)
378 --print("stt", item, typ, item.tooltip_defer_questobjective)
379 if TooltipType
[item
] == typ
and typ
~= "defer" and not item
.tooltip_defer_questobjective_last
then return end
380 if TooltipType
[item
] == "defer" and typ
== "defer" and (not item
.tooltip_defer_questobjective_last
or item
.tooltip_defer_questobjective_last
== item
.tooltip_defer_questobjective
) then return end -- sigh
382 if TooltipType
[item
] == "canned" then
383 QuestHelper
: Assert(item
.tooltip_canned
)
384 QH_Tooltip_Canned_Remove(item
.tooltip_canned
)
385 elseif TooltipType
[item
] == "defer" then
386 QuestHelper
: Assert(item
.tooltip_defer_questname_last
)
387 --print("remove", item.tooltip_defer_questname_last, item.tooltip_defer_questobjective_last, item.tooltip_defer_questobjective)
388 if item
.tooltip_defer_questobjective_last
then
389 QH_Tooltip_Defer_Remove(item
.tooltip_defer_questname_last
, item
.tooltip_defer_questobjective_last
, item
.tooltip_defer_token_last
)
391 QH_Tooltip_Defer_Remove(item
.tooltip_defer_questname_last
, item
.tooltip_defer_questobjective
, item
.tooltip_defer_token_last
)
393 elseif TooltipType
[item
] == nil then
395 QuestHelper
: Assert(false)
398 item
.tooltip_defer_questobjective_last
= nil
399 item
.tooltip_defer_questname_last
= nil -- if it was anything, it is not now
400 item
.tooltip_defer_token_last
= nil
402 if typ
== "canned" then
403 QuestHelper
: Assert(item
.tooltip_canned
)
404 QH_Tooltip_Canned_Add(item
.tooltip_canned
)
405 elseif typ
== "defer" then
406 QuestHelper
: Assert(not not item
.tooltip_defer_questobjective
== not item
.type_quest_finish
) -- hmmm
407 --print("add", item.tooltip_defer_questname, item.tooltip_defer_questobjective)
408 QuestHelper
: Assert(item
.tooltip_defer_questname
)
409 item
.tooltip_defer_token_last
= {{}, item
}
410 QH_Tooltip_Defer_Add(item
.tooltip_defer_questname
, item
.tooltip_defer_questobjective
, item
.tooltip_defer_token_last
)
411 item
.tooltip_defer_questname_last
= item
.tooltip_defer_questname
412 item
.tooltip_defer_questobjective_last
= item
.tooltip_defer_questobjective
413 elseif typ
== nil then
415 QuestHelper
: Assert(false)
417 TooltipType
[item
] = typ
420 local function StartInsertionPass(id
)
421 QuestHelper
: Assert(not in_pass
)
423 for k
, v
in pairs(InsertedItems
) do
428 local desc
= MakeQuestObjectiveTitle(k
.progress
, k
.target
)
429 for _
, v
in ipairs(k
) do
430 v
.tracker_desc
= desc
or "(no description available)"
434 -- if these are needed to remove, they'll be stored in last, and this way they'll be obliterated if the user doesn't have that actual quest
435 if id
== UnitName("player") then
436 k
.tooltip_defer_questname
= nil
437 k
.tooltip_defer_questobjective
= nil
441 local function RefreshItem(id
, item
, required
)
442 --if not required and math.random() < 0.2 then return false end -- ha ha bzzzzt
444 QuestHelper
: Assert(in_pass
== id
)
446 if not InsertedItems
[item
] then
447 QH_Route_ClusterAdd(item
)
448 --QH_Route_SetClusterPriority(item, math.random(5))
450 InsertedItems
[item
] = {}
452 InsertedItems
[item
][id
] = true
454 if item
.tooltip_defer_questname
then
455 SetTooltip(item
, "defer")
456 elseif item
.tooltip_canned
then
457 SetTooltip(item
, "canned")
459 SetTooltip(item
, nil)
462 if item
.type_quest_unknown
then table.insert(Unknowning
, item
) end
464 local desc
= MakeQuestObjectiveTitle(item
.progress
, item
.target
)
465 for _
, v
in ipairs(item
) do
466 v
.tracker_desc
= desc
or "(no description available)"
471 local function EndInsertionPass(id
)
472 QuestHelper
: Assert(in_pass
== id
)
473 local rem
= QuestHelper
:CreateTable("ip rem")
474 for k
, v
in pairs(InsertedItems
) do
476 for _
, _
in pairs(v
) do
481 QH_Tracker_Unpin(k
[1])
482 QH_Route_ClusterRemove(k
)
489 for k
, _
in pairs(rem
) do
490 InsertedItems
[k
] = nil
492 QuestHelper
:ReleaseTable(rem
)
494 for _
, v
in ipairs(Unknowning
) do
495 QH_Route_IgnoreCluster(v
, dontknow
)
497 while table.remove(Unknowning
) do end
501 --QH_Tooltip_Defer_Dump()
504 function QuestProcessor(user_id
, db
, title
, level
, group
, variety
, groupsize
, watched
, complete
, lbcount
, timed
)
506 db
.tracker_desc
= MakeQuestTitle(title
, level
)
508 db
.type_quest
.objectives
= lbcount
509 db
.type_quest
.level
= level
510 db
.type_quest
.done
= (complete
== 1)
511 db
.type_quest
.variety
= variety
512 db
.type_quest
.groupsize
= groupsize
513 db
.type_quest
.title
= title
518 -- This is our "quest turnin" objective, which is currently being handled separately for no particularly good reason.
519 if db
.finish
and #db
.finish
> 0 then
520 for _
, v
in ipairs(db
.finish
) do
521 v
.map_highlight
= (complete
== 1)
525 --print("turnin:", turnin.tooltip_defer_questname)
526 if RefreshItem(user_id
, turnin
, true) then
528 for k
, v
in ipairs(turnin
) do
529 v
.tracker_clicked
= function () Clicky(lindex
) end
531 v
.map_desc
= {QHFormat("OBJECTIVE_REASON_TURNIN", title
)}
534 if watched
~= "(ignore)" then QH_Tracker_SetPin(db
.finish
[1], watched
) end
537 -- These are the individual criteria of the quest. Remember that each criteria can be represented by multiple routing objectives.
538 for i
= 1, lbcount
do
540 local pt
, pd
, have
, need
= objective_parse(db
[i
].temp_typ
, db
[i
].temp_desc
, db
[i
].temp_done
)
542 if pt
== "item" or pt
== "object" then
543 dline
= QHFormat("OBJECTIVE_REASON", QHText("ACQUIRE_VERB"), pd
, title
)
544 elseif pt
== "monster" then
545 dline
= QHFormat("OBJECTIVE_REASON", QHText("SLAY_VERB"), pd
, title
)
547 dline
= QHFormat("OBJECTIVE_REASON_FALLBACK", pd
, title
)
550 if not db
[i
].progress
then
554 if type(have
) == "number" and type(need
) == "number" then
555 db
[i
].progress
[db
[i
].temp_person
] = {have
, need
, have
/ need
}
557 db
[i
].progress
[db
[i
].temp_person
] = {have
, need
, db
[i
].temp_done
and 1 or 0} -- it's only used for the coloring anyway
560 local _
, target
= objective_parse(db
[i
].temp_typ
, db
[i
].temp_desc
)
561 db
[i
].target
= target
563 db
[i
].desc
= QHFormat("TOOLTIP_QUEST", title
)
565 for k
, v
in ipairs(db
[i
]) do
566 v
.desc
= db
[i
].temp_desc
567 v
.tracker_clicked
= db
.tracker_clicked
569 v
.progress
= db
[i
].progress
572 v
.map_desc
= copy(v
.path_desc
)
573 v
.map_desc
[1] = dline
579 -- This is the snatch of code that actually adds it to routing.
580 if not db
[i
].temp_done
and #db
[i
] > 0 then
581 if RefreshItem(user_id
, db
[i
]) then
582 if turnin
then QH_Route_ClusterRequires(turnin
, db
[i
]) end
584 if watched
~= "(ignore)" then QH_Tracker_SetPin(db
[i
][1], watched
) end
587 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
= nil, nil, nil
591 if turnin_new
and timed
then
592 QH_Route_SetClusterPriority(turnin
, -1)
596 function SerItem(item
)
598 if type(item
) == "boolean" then
599 rtx
= "b" .. (item
and "t" or "f")
600 elseif type(item
) == "number" then
601 rtx
= "n" .. tostring(item
)
602 elseif type(item
) == "string" then
603 rtx
= "s" .. item
:gsub("\\", "\\\\"):gsub(":", "\\;")
604 elseif type(item
) == "nil" then
607 print(type(item
), item
)
608 QuestHelper
: Assert()
613 function DeSerItem(item
)
614 local t
= item
:sub(1, 1)
615 local d
= item
:sub(2)
621 return d
:gsub("\\;", ":"):gsub("\\\\", "\\")
625 QuestHelper
: Assert()
629 local function Serialize(...)
631 for i
= 1, select("#", ...) do
632 if sx
then sx
= sx
.. ":" else sx
= "" end
633 sx
= sx
.. SerItem(select(i
, ...))
635 QuestHelper
: Assert(sx
)
639 local function SAM(msg
, chattype
, target
)
640 --QuestHelper: TextOut(string.format("%s/%s: %s", chattype, tostring(target), msg))
644 if #msg
> thresh
then
645 for i
= 1, #msg
, msgsize
do
647 if i
== 1 then prefx
= "v:" elseif i
+ msgsize
> #msg
then prefx
= "X:" end
648 SAM(prefx
.. msg
:sub(i
, i
+ msgsize
- 1), chattype
, target
)
651 ChatThrottleLib
:SendAddonMessage("BULK", "QHpr", msg
, chattype
, target
, "QHpr")
656 function is_uncached(typ
, txt
, done
)
657 if not txt
then return true end
658 if txt
== "" then return true end
659 if txt
:match("^ : %d+/%d+$") then return true end
660 local _
, target
= objective_parse(typ
, txt
, done
)
661 if target
== "" or target
== " " then return true end
666 local current_chunks
= {}
668 -- Here's the core update function
669 function QH_UpdateQuests(force
)
670 if not DB_Ready() then return end
672 if update
or force
then -- Sometimes (usually) we don't actually update
675 local player
= UnitName("player")
676 StartInsertionPass(player
)
678 local next_chunks
= {}
680 -- This begins the main update loop that loops through all of the quests
682 local title
, level
, variety
, groupsize
, _
, _
, complete
= GetQuestLogTitle(index
)
683 if not title
then break end
685 title
= title
:match("%[.*%] (.*)") or title
687 local qlink
= GetQuestLink(index
)
688 if qlink
then -- If we don't have a quest link, it's not really a quest
689 local id
= GetQuestType(qlink
)
690 if id
then -- If we don't have a *valid* quest link, give up
691 local lbcount
= GetNumQuestLeaderBoards(index
)
692 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
694 QuestHelper
: Assert(db
)
696 local watched
= IsQuestWatched(index
)
698 -- The section in here, in other words, is: we have a metaobjective (which may be a new one, or may not be), and we're trying to attach things to our routing engine. Now is where the real work begins! (many conditionals deep)
700 db
.tracker_clicked
= function () Clicky(lindex
) end
702 db
.type_quest
.index
= index
706 local timer
= GetQuestIndexForTimer(timidx
)
707 if not timer
then timidx
= nil break end
708 if timer
== index
then break end
711 local timed
= not not timidx
713 --print(id, title, level, groupsize, variety, groupsize, complete, timed)
714 local chunk
= "q:" .. Serialize(id
, title
, level
, groupsize
, variety
, groupsize
, complete
, timed
)
715 for i
= 1, lbcount
do
716 QuestHelper
: Assert(db
[i
])
717 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
= GetQuestLogLeaderBoard(i
, index
)
718 --[[if not db[i].temp_desc or is_uncached(db[i].temp_typ, db[i].temp_desc, db[i].temp_done) then
719 db[i].temp_desc = string.format("(missing description %d)", i)
721 db
[i
].temp_person
= player
723 db
[i
].tooltip_defer_questname
= title
724 db
[i
].tooltip_defer_questobjective
= db
[i
].temp_desc
-- yoink
725 QuestHelper
: Assert(db
[i
].tooltip_defer_questobjective
) -- hmmm
727 chunk
= chunk
.. ":" .. Serialize(db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
)
730 db
.finish
.tooltip_defer_questname
= title
-- we're using this as our fallback right now
732 next_chunks
[id
] = chunk
734 QuestProcessor(player
, db
, title
, level
, groupsize
, variety
, groupsize
, watched
, complete
, lbcount
, timed
)
740 EndInsertionPass(player
)
742 QH_Route_Filter_Rescan() -- 'cause filters may also change
744 if not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
then
745 for k
, v
in pairs(next_chunks
) do
746 if current_chunks
[k
] ~= v
then
751 for k
, v
in pairs(current_chunks
) do
752 if not next_chunks
[k
] then
753 SAM(string.format("q:n%d", k
), "PARTY")
758 current_chunks
= next_chunks
762 -- comm_packets[user][qid] = data
763 local comm_packets
= {}
765 local function RefreshUserComms(user
)
766 StartInsertionPass(user
)
768 if comm_packets
[user
] then for _
, dat
in pairs(comm_packets
[user
]) do
769 local id
, title
, level
, group
, variety
, groupsize
, complete
, timed
= dat
[1], dat
[2], dat
[3], dat
[4], dat
[5], dat
[6], dat
[7], dat
[8]
774 if dat
[#obj
* 3 + objstart
] == nil and dat
[#obj
* 3 + objstart
+ 1] == nil and dat
[#obj
* 3 + objstart
+ 2] == nil then break end
775 table.insert(obj
, {dat
[#obj
* 3 + objstart
], dat
[#obj
* 3 + objstart
+ 1], dat
[#obj
* 3 + objstart
+ 2]})
779 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
781 QuestHelper
: Assert(db
)
783 for i
= 1, lbcount
do
784 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
, db
[i
].temp_person
= obj
[i
][1], obj
[i
][2], obj
[i
][3], user
787 QuestProcessor(user
, db
, title
, level
, group
, variety
, groupsize
, "(ignore)", complete
, lbcount
, false)
790 EndInsertionPass(user
)
792 QH_Route_Filter_Rescan() -- 'cause filters may also change
795 function QH_InsertCommPacket(user
, data
)
796 local q
, chunk
= data
:match("([^:]+):(.*)")
797 if q
~= "q" then return end
801 for item
in chunk
:gmatch("([^:]+)") do
802 dat
[idx
] = DeSerItem(item
)
806 if not comm_packets
[user
] then comm_packets
[user
] = {} end
808 comm_packets
[user
][dat
[1]]
= nil
810 comm_packets
[user
][dat
[1]]
= dat
814 RefreshUserComms(user
)
817 local function QH_DumpCommUser(user
)
818 comm_packets
[user
] = nil
819 RefreshUserComms(user
)
822 QH_Event("UNIT_QUEST_LOG_CHANGED", UpdateTrigger
)
823 QH_Event("QUEST_LOG_UPDATE", QH_UpdateQuests
)
825 -- We don't return anything here, but I don't think that's actually an issue - those functions don't return anything anyway. Someday I'll regret writing this. Delay because of beql which is a bitch.
826 QH_AddNotifier(GetTime() + 5, function ()
827 local aqw_orig
= AddQuestWatch
828 AddQuestWatch
= function(...)
830 QH_UpdateQuests(true)
832 local rqw_orig
= RemoveQuestWatch
833 RemoveQuestWatch
= function(...)
835 QH_UpdateQuests(true)
839 local old_playerlist
= {}
841 function QH_Questcomm_Sync()
842 if not (not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
) then
847 local playerlist
= {}
848 --[[if GetNumRaidMembers() > 0 then
850 local liv = UnitName(string.format("raid%d", i))
851 if liv then playerlist[liv] = true end
853 elseif]] if GetNumPartyMembers() > 0 then
856 local targ
= string.format("party%d", i
)
857 local liv
, relm
= UnitName(targ
)
858 if liv
and not relm
and liv
~= UNKNOWNOBJECT
and UnitIsConnected(targ
) then playerlist
[liv
] = true end
861 playerlist
[UnitName("player")] = nil
864 for k
, v
in pairs(playerlist
) do
865 if not old_playerlist
[k
] then
866 --print("new player:", k)
867 table.insert(additions
, k
)
872 for k
, v
in pairs(old_playerlist
) do
873 if not playerlist
[k
] then
874 --print("lost player:", k)
875 table.insert(removals
, k
)
879 old_playerlist
= playerlist
881 for _
, v
in ipairs(removals
) do
885 if #additions
== 0 then return end
887 if #additions
== 1 then
888 SAM("syn:2", "WHISPER", additions
[1])
890 SAM("syn:2", "PARTY")
896 local newer_reported
= false
897 local older_reported
= false
898 function QH_Questcomm_Msg(data
, from
)
899 if data
:match("syn:0") then
900 QH_DumpCommUser(from
)
903 if QuestHelper_Pref
.solo
then return end
905 --print("received", from, data)
909 local key
, value
= data
:match("(.):(.*)")
912 elseif key
== "x" then
914 aku
[from
] = aku
[from
] .. value
916 elseif key
== "X" then
918 aku
[from
] = aku
[from
] .. value
927 if not cont
then return end
930 --print("packet received", from, data)
931 if data
:match("syn:.*") then
932 local synv
= data
:match("syn:([0-9]*)")
933 if synv
then synv
= tonumber(synv
) end
934 if synv
and synv
~= 2 then
935 if synv
> 2 and not newer_reported
then
936 QuestHelper
:TextOut(QHFormat("PEER_NEWER", from
))
937 newer_reported
= true
938 elseif synv
< 2 and not older_reported
then
939 QuestHelper
:TextOut(QHFormat("PEER_OLDER", from
))
940 older_reported
= true
944 if synv
and synv
>= 2 then
945 SAM("hello:2", "WHISPER", from
)
947 elseif data
== "hello:2" or data
== "retrieve:2" then
948 if data
== "hello:2" then SAM("retrieve:2", "WHISPER", from
) end -- requests their info as well, needed to deal with UI reloading/logon/logoff properly
950 for k
, v
in pairs(current_chunks
) do
951 SAM(v
, "WHISPER", from
)
954 if old_playerlist
[from
] then
955 QH_InsertCommPacket(from
, data
)
960 function QuestHelper
:SetShare(flag
)
964 SAM("syn:0", "PARTY")
965 local cpb
= comm_packets
967 for k
in pairs(cpb
) do RefreshUserComms(k
) end