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 quest_list
= setmetatable({}, {__mode
="k"})
90 local QuestCriteriaWarningBroadcast
92 local function GetQuestMetaobjective(questid
, lbcount
)
93 if not quest_list
[questid
] then
94 local q
= DB_GetItem("quest", questid
, true, true)
97 QuestHelper
: TextOut("Missing lbcount, guessing wildly")
98 if q
and q
.criteria
then
100 for k
, v
in ipairs(q
.criteria
) do
101 lbcount
= math
.max(lbcount
, k
)
108 -- just doublechecking here
109 if not QuestCriteriaWarningBroadcast
and q
and q
.criteria
then for k
, v
in pairs(q
.criteria
) do
110 if type(k
) == "number" and k
> lbcount
then
111 --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
112 QuestHelper_ErrorCatcher_ExplicitError(false, string.format("Too many stored objectives (%s %s %s)", questid
, lbcount
, k
))
113 QuestCriteriaWarningBroadcast
= true
117 ite
= {type_quest
= {__backlink
= ite
}} -- we don't want to mutate the existing quest data. backlink exists only for nasty GC reasons
118 ite
.desc
= string.format("Quest %s", q
and q
.name
or "(unknown)") -- this gets changed later anyway
120 for i
= 1, lbcount
do
122 --QuestHelper:TextOut(string.format("critty %d %d", k, c.loc and #c.loc or -1))
124 ttx
.tooltip_canned
= {}
126 if q
and q
.criteria
and q
.criteria
[i
] then
127 AppendObjlinks(ttx
, q
.criteria
[i
], ttx
.tooltip_canned
)
129 if debug_output
and q
.criteria
[i
].loc
and #q
.criteria
[i
] > 0 then
130 QuestHelper
:TextOut(string.format("Wackyquest %d/%d", questid
, i
))
135 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
138 for idx
, v
in ipairs(ttx
) do
139 v
.desc
= string.format("Criteria %d", i
)
142 v
.type_quest
= ite
.type_quest
145 for k
, v
in pairs(ttx
.tooltip_canned
) do
146 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
153 local ttx
= {type_quest_finish
= true}
154 --QuestHelper:TextOut(string.format("finny %d", q.finish.loc and #q.finish.loc or -1))
155 if q
and q
.finish
and q
.finish
.loc
then for m
, v
in ipairs(q
.finish
.loc
) do
157 --print(QuestHelper_IndexLookup[v.rc])
158 --print(QuestHelper_IndexLookup[v.rc][v.rz])
159 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
})
163 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
169 quest_list
[questid
] = ite
171 if q
then DB_ReleaseItem(q
) end
174 return quest_list
[questid
]
178 local function GetQuestType(link
)
179 return tonumber(string.match(link
,
180 "^|cff%x%x%x%x%x%x|Hquest:(%d+):[%d-]+|h%[[^%]]*%]|h|r$"
181 )), tonumber(string.match(link
,
182 "^|cff%x%x%x%x%x%x|Hquest:%d+:([%d-]+)|h%[[^%]]*%]|h|r$"
187 local function UpdateTrigger()
191 -- 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.
194 local objective_parse_table
= {
195 item
= function (txt
) return QuestHelper
:convertPattern(QUEST_OBJECTS_FOUND
)(txt
) end,
196 object
= function (txt
) return QuestHelper
:convertPattern(QUEST_OBJECTS_FOUND
)(txt
) end, -- why does this even exist
197 monster
= function (txt
) return QuestHelper
:convertPattern(QUEST_MONSTERS_KILLED
)(txt
) end,
198 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.
199 reputation
= function (txt
) return QuestHelper
:convertPattern(QUEST_FACTION_NEEDED
)(txt
) end, -- :ughh:
200 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.
203 local function objective_parse(typ
, txt
, done
)
204 local pt
, target
, have
, need
= typ
, objective_parse_table
[typ
](txt
, done
)
207 -- well, that didn't work
208 target
, have
, need
= string.match(txt
, "^%s*(.-)%s*:%s*(.-)%s*/%s*(.-)%s*$")
210 --QuestHelper:TextOut(string.format("%s rebecomes %s/%s/%s", tostring(title), tostring(target), tostring(have), tostring(need)))
214 target
, have
, need
= string.match(txt
, "^%s*(.-)%s*$"), (done
and 1 or 0), 1
215 --QuestHelper:TextOut(string.format("%s rerebecomes %s/%s/%s", tostring(title), tostring(target), tostring(have), tostring(need)))
218 QuestHelper
: Assert(target
) -- This will fail repeatedly. Come on. We all know it.
219 QuestHelper
: Assert(have
)
220 QuestHelper
: Assert(need
) -- As will these.
222 if tonumber(have
) then have
= tonumber(have
) end
223 if tonumber(need
) then need
= tonumber(need
) end
225 return pt
, target
, have
, need
228 local function clamp(v
)
229 if v
< 0 then return 0 elseif v
> 255 then return 255 else return v
end
232 local function colorlerp(position
, r1
, g1
, b1
, r2
, g2
, b2
)
233 local antip
= 1 - position
234 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))
237 -- We're just gonna do the same thing QH originally did - red->yellow->green.
238 local function difficulty_color(position
)
239 if position
< 0 then position
= 0 end
240 if position
> 1 then position
= 1 end
241 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)
244 local function MakeQuestTitle(title
, level
)
245 local plevel
= UnitLevel("player") -- meh, should probably cache this, buuuuut
250 elseif plevel
>= 40 then
251 grayd
= plevel
/ 5 + 1
253 grayd
= plevel
/ 10 + 5
256 local isgray
= (plevel
- floor(grayd
) >= level
)
258 local ccode
= isgray
and "|cffb0b0b0" or difficulty_color(1 - ((level
- plevel
) / grayd
+ 1) / 2)
259 local qlevel
= string.format("[%d] ", level
)
262 if QuestHelper_Pref
.track_level
then ret
= qlevel
.. ret
end
263 if QuestHelper_Pref
.track_qcolour
then ret
= ccode
.. ret
end
268 local function MakeQuestObjectiveTitle(progress
, target
)
269 if not progress
then return nil end
271 local player
= UnitName("player")
274 for _
, v
in pairs(progress
) do
276 if v
[3] == 1 then pd
= pd
+ 1 end
282 local party_show
= false
283 local party_compact
= false
285 if progress
[player
] then
286 local have
, need
= tonumber(progress
[player
][1]), tonumber(progress
[player
][2])
288 ccode
= difficulty_color(progress
[player
][3])
290 if have
and need
then
292 status
= string.format("%d/%d", have
, need
)
296 status
= string.format("%s/%s", progress
[player
][1], progress
[player
][2])
300 if pt
> 1 then party_show
= true end
302 ccode
= difficulty_color(1) -- probably just in the process of being removed from the tracker
305 ccode
= difficulty_color(pd
/ pt
)
311 if party_compact
then
312 party
= string.format("(P: %d/%d)", pd
, pt
)
314 party
= string.format("Party %d/%d", pd
, pt
)
318 if QuestHelper_Pref
.track_ocolour
then
319 target
= ccode
.. target
322 if status
or party
then
323 target
= target
.. ":"
327 target
= target
.. " " .. status
331 target
= target
.. " " .. party
337 local function Clicky(index
)
338 ShowUIPanel(QuestLogFrame
)
339 QuestLog_SetSelection(index
)
344 name
= "director_quest_unknown_objective",
347 friendly_reason
= QHText("UNKNOWN_OBJ"),
350 -- InsertedItem[item] = {"list", "of", "reasons"}
351 local InsertedItems
= {}
352 local TooltipType
= {}
353 local Unknowning
= {}
356 local function SetTooltip(item
, typ
)
357 --print("stt", item, typ, item.tooltip_defer_questobjective)
358 if TooltipType
[item
] == typ
and typ
~= "defer" and not item
.tooltip_defer_questobjective_last
then return end
359 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
361 if TooltipType
[item
] == "canned" then
362 QuestHelper
: Assert(item
.tooltip_canned
)
363 QH_Tooltip_Canned_Remove(item
.tooltip_canned
)
364 elseif TooltipType
[item
] == "defer" then
365 QuestHelper
: Assert(item
.tooltip_defer_questname
)
366 if item
.tooltip_defer_questobjective_last
then
367 QH_Tooltip_Defer_Remove(item
.tooltip_defer_questname
, item
.tooltip_defer_questobjective_last
)
369 QH_Tooltip_Defer_Remove(item
.tooltip_defer_questname
, item
.tooltip_defer_questobjective
)
371 elseif TooltipType
[item
] == nil then
373 QuestHelper
: Assert(false)
376 if typ
== "canned" then
377 QuestHelper
: Assert(item
.tooltip_canned
)
378 QH_Tooltip_Canned_Add(item
.tooltip_canned
)
379 elseif typ
== "defer" then
380 QuestHelper
: Assert(item
.tooltip_defer_questname
)
381 QH_Tooltip_Defer_Add(item
.tooltip_defer_questname
, item
.tooltip_defer_questobjective
, {{}, item
})
382 elseif typ
== nil then
384 QuestHelper
: Assert(false)
387 item
.tooltip_defer_questobjective_last
= nil -- if it was anything, it is not now
388 TooltipType
[item
] = typ
391 local function StartInsertionPass(id
)
392 QuestHelper
: Assert(not in_pass
)
394 for k
, v
in pairs(InsertedItems
) do
399 local desc
= MakeQuestObjectiveTitle(k
.progress
, k
.target
)
400 for _
, v
in ipairs(k
) do
401 v
.tracker_desc
= desc
or "(no description available)"
406 local function RefreshItem(id
, item
, required
)
407 --if not required and math.random() < 0.2 then return false end -- ha ha bzzzzt
409 QuestHelper
: Assert(in_pass
== id
)
411 if not InsertedItems
[item
] then
412 QH_Route_ClusterAdd(item
)
413 --QH_Route_SetClusterPriority(item, math.random(5))
415 InsertedItems
[item
] = {}
417 InsertedItems
[item
][id
] = true
419 if item
.tooltip_defer_questname
then
420 SetTooltip(item
, "defer")
421 elseif item
.tooltip_canned
then
422 SetTooltip(item
, "canned")
424 SetTooltip(item
, nil)
427 if item
.type_quest_unknown
then table.insert(Unknowning
, item
) end
429 local desc
= MakeQuestObjectiveTitle(item
.progress
, item
.target
)
430 for _
, v
in ipairs(item
) do
431 v
.tracker_desc
= desc
or "(no description available)"
436 local function EndInsertionPass(id
)
437 QuestHelper
: Assert(in_pass
== id
)
438 local rem
= QuestHelper
:CreateTable("ip rem")
439 for k
, v
in pairs(InsertedItems
) do
441 for _
, _
in pairs(v
) do
446 QH_Tracker_Unpin(k
[1])
447 QH_Route_ClusterRemove(k
)
454 for k
, _
in pairs(rem
) do
455 InsertedItems
[k
] = nil
457 QuestHelper
:ReleaseTable(rem
)
459 for _
, v
in ipairs(Unknowning
) do
460 QH_Route_IgnoreCluster(v
, dontknow
)
462 while table.remove(Unknowning
) do end
467 function QuestProcessor(user_id
, db
, title
, level
, group
, variety
, groupsize
, watched
, complete
, lbcount
, timed
)
469 db
.tracker_desc
= MakeQuestTitle(title
, level
)
471 db
.type_quest
.objectives
= lbcount
472 db
.type_quest
.level
= level
473 db
.type_quest
.done
= (complete
== 1)
474 db
.type_quest
.variety
= variety
475 db
.type_quest
.groupsize
= groupsize
476 db
.type_quest
.title
= title
481 -- This is our "quest turnin" objective, which is currently being handled separately for no particularly good reason.
482 if db
.finish
and #db
.finish
> 0 then
483 for _
, v
in ipairs(db
.finish
) do
484 v
.map_highlight
= (complete
== 1)
488 --print("turnin:", turnin.tooltip_defer_questname)
489 if RefreshItem(user_id
, turnin
, true) then
491 for k
, v
in ipairs(turnin
) do
492 v
.tracker_clicked
= function () Clicky(lindex
) end
494 v
.map_desc
= {QHFormat("OBJECTIVE_REASON_TURNIN", title
)}
497 if watched
~= "(ignore)" then QH_Tracker_SetPin(db
.finish
[1], watched
) end
500 -- These are the individual criteria of the quest. Remember that each criteria can be represented by multiple routing objectives.
501 for i
= 1, lbcount
do
503 local pt
, pd
, have
, need
= objective_parse(db
[i
].temp_typ
, db
[i
].temp_desc
, db
[i
].temp_done
)
505 if pt
== "item" or pt
== "object" then
506 dline
= QHFormat("OBJECTIVE_REASON", QHText("ACQUIRE_VERB"), pd
, title
)
507 elseif pt
== "monster" then
508 dline
= QHFormat("OBJECTIVE_REASON", QHText("SLAY_VERB"), pd
, title
)
510 dline
= QHFormat("OBJECTIVE_REASON_FALLBACK", pd
, title
)
513 if not db
[i
].progress
then
517 if type(have
) == "number" and type(need
) == "number" then
518 db
[i
].progress
[db
[i
].temp_person
] = {have
, need
, have
/ need
}
520 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
523 local _
, target
= objective_parse(db
[i
].temp_typ
, db
[i
].temp_desc
)
524 db
[i
].target
= target
526 db
[i
].desc
= QHFormat("TOOLTIP_QUEST", title
)
528 for k
, v
in ipairs(db
[i
]) do
529 v
.desc
= db
[i
].temp_desc
530 v
.tracker_clicked
= db
.tracker_clicked
532 v
.progress
= db
[i
].progress
535 v
.map_desc
= copy(v
.path_desc
)
536 v
.map_desc
[1] = dline
542 -- This is the snatch of code that actually adds it to routing.
543 if not db
[i
].temp_done
and #db
[i
] > 0 then
544 if RefreshItem(user_id
, db
[i
]) then
545 if turnin
then QH_Route_ClusterRequires(turnin
, db
[i
]) end
547 if watched
~= "(ignore)" then QH_Tracker_SetPin(db
[i
][1], watched
) end
550 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
= nil, nil, nil
554 if turnin_new
and timed
then
555 QH_Route_SetClusterPriority(turnin
, -1)
559 function SerItem(item
)
561 if type(item
) == "boolean" then
562 rtx
= "b" .. (item
and "t" or "f")
563 elseif type(item
) == "number" then
564 rtx
= "n" .. tostring(item
)
565 elseif type(item
) == "string" then
566 rtx
= "s" .. item
:gsub("\\", "\\\\"):gsub(":", "\\;")
567 elseif type(item
) == "nil" then
570 print(type(item
), item
)
571 QuestHelper
: Assert()
576 function DeSerItem(item
)
577 local t
= item
:sub(1, 1)
578 local d
= item
:sub(2)
584 return d
:gsub("\\;", ":"):gsub("\\\\", "\\")
588 QuestHelper
: Assert()
592 local function Serialize(...)
594 for i
= 1, select("#", ...) do
595 if sx
then sx
= sx
.. ":" else sx
= "" end
596 sx
= sx
.. SerItem(select(i
, ...))
598 QuestHelper
: Assert(sx
)
602 local function SAM(msg
, chattype
, target
)
603 --QuestHelper: TextOut(string.format("%s/%s: %s", chattype, tostring(target), msg))
607 if #msg
> thresh
then
608 for i
= 1, #msg
, msgsize
do
610 if i
== 1 then prefx
= "v:" elseif i
+ msgsize
> #msg
then prefx
= "X:" end
611 SAM(prefx
.. msg
:sub(i
, i
+ msgsize
- 1), chattype
, target
)
614 ChatThrottleLib
:SendAddonMessage("BULK", "QHpr", msg
, chattype
, target
, "QHpr")
619 function is_uncached(typ
, txt
, done
)
620 if not txt
then return true end
621 if txt
== "" then return true end
622 if txt
:match("^ : %d+/%d+$") then return true end
623 local _
, target
= objective_parse(typ
, txt
, done
)
624 if target
== "" or target
== " " then return true end
629 local current_chunks
= {}
631 -- Here's the core update function
632 function QH_UpdateQuests(force
)
633 if not DB_Ready() then return end
635 if update
or force
then -- Sometimes (usually) we don't actually update
638 local player
= UnitName("player")
639 StartInsertionPass(player
)
641 local next_chunks
= {}
643 -- This begins the main update loop that loops through all of the quests
645 local title
, level
, variety
, groupsize
, _
, _
, complete
= GetQuestLogTitle(index
)
646 if not title
then break end
648 title
= title
:match("%[.*%] (.*)") or title
650 local qlink
= GetQuestLink(index
)
651 if qlink
then -- If we don't have a quest link, it's not really a quest
652 local id
= GetQuestType(qlink
)
653 if id
then -- If we don't have a *valid* quest link, give up
654 local lbcount
= GetNumQuestLeaderBoards(index
)
655 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
657 QuestHelper
: Assert(db
)
659 local watched
= IsQuestWatched(index
)
661 -- 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)
663 db
.tracker_clicked
= function () Clicky(lindex
) end
665 db
.type_quest
.index
= index
669 local timer
= GetQuestIndexForTimer(timidx
)
670 if not timer
then timidx
= nil break end
671 if timer
== index
then break end
674 local timed
= not not timidx
676 --print(id, title, level, groupsize, variety, groupsize, complete, timed)
677 local chunk
= "q:" .. Serialize(id
, title
, level
, groupsize
, variety
, groupsize
, complete
, timed
)
678 for i
= 1, lbcount
do
679 QuestHelper
: Assert(db
[i
])
680 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
= GetQuestLogLeaderBoard(i
, index
)
681 if is_uncached(db
[i
].temp_typ
, db
[i
].temp_desc
, db
[i
].temp_done
) then
682 db
[i
].temp_desc
= string.format("(missing description %d)", i
)
684 db
[i
].temp_person
= player
686 if db
[i
].temp_desc
~= db
[i
].tooltip_defer_questobjective
then
687 db
[i
].tooltip_defer_questobjective_last
= db
[i
].tooltip_defer_questobjective
689 db
[i
].tooltip_defer_questname
= title
690 db
[i
].tooltip_defer_questobjective
= db
[i
].temp_desc
-- yoink
693 chunk
= chunk
.. ":" .. Serialize(db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
)
696 db
.finish
.tooltip_defer_questname
= title
-- we're using this as our fallback right now
698 next_chunks
[id
] = chunk
700 QuestProcessor(player
, db
, title
, level
, groupsize
, variety
, groupsize
, watched
, complete
, lbcount
, timed
)
706 EndInsertionPass(player
)
708 QH_Route_Filter_Rescan() -- 'cause filters may also change
710 if not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
then
711 for k
, v
in pairs(next_chunks
) do
712 if current_chunks
[k
] ~= v
then
717 for k
, v
in pairs(current_chunks
) do
718 if not next_chunks
[k
] then
719 SAM(string.format("q:n%d", k
), "PARTY")
724 current_chunks
= next_chunks
728 -- comm_packets[user][qid] = data
729 local comm_packets
= {}
731 local function RefreshUserComms(user
)
732 StartInsertionPass(user
)
734 if comm_packets
[user
] then for _
, dat
in pairs(comm_packets
[user
]) do
735 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]
740 if dat
[#obj
* 3 + objstart
] == nil and dat
[#obj
* 3 + objstart
+ 1] == nil and dat
[#obj
* 3 + objstart
+ 2] == nil then break end
741 table.insert(obj
, {dat
[#obj
* 3 + objstart
], dat
[#obj
* 3 + objstart
+ 1], dat
[#obj
* 3 + objstart
+ 2]})
745 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
747 QuestHelper
: Assert(db
)
749 for i
= 1, lbcount
do
750 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
753 QuestProcessor(user
, db
, title
, level
, group
, variety
, groupsize
, "(ignore)", complete
, lbcount
, false)
756 EndInsertionPass(user
)
758 QH_Route_Filter_Rescan() -- 'cause filters may also change
761 function QH_InsertCommPacket(user
, data
)
762 local q
, chunk
= data
:match("([^:]+):(.*)")
763 if q
~= "q" then return end
767 for item
in chunk
:gmatch("([^:]+)") do
768 dat
[idx
] = DeSerItem(item
)
772 if not comm_packets
[user
] then comm_packets
[user
] = {} end
774 comm_packets
[user
][dat
[1]]
= nil
776 comm_packets
[user
][dat
[1]]
= dat
780 RefreshUserComms(user
)
783 local function QH_DumpCommUser(user
)
784 comm_packets
[user
] = nil
785 RefreshUserComms(user
)
788 QH_Event("UNIT_QUEST_LOG_CHANGED", UpdateTrigger
)
789 QH_Event("QUEST_LOG_UPDATE", QH_UpdateQuests
)
791 -- 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.
792 QH_AddNotifier(GetTime() + 5, function ()
793 local aqw_orig
= AddQuestWatch
794 AddQuestWatch
= function(...)
796 QH_UpdateQuests(true)
798 local rqw_orig
= RemoveQuestWatch
799 RemoveQuestWatch
= function(...)
801 QH_UpdateQuests(true)
805 local old_playerlist
= {}
807 function QH_Questcomm_Sync()
808 if not (not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
) then
813 local playerlist
= {}
814 --[[if GetNumRaidMembers() > 0 then
816 local liv = UnitName(string.format("raid%d", i))
817 if liv then playerlist[liv] = true end
819 elseif]] if GetNumPartyMembers() > 0 then
822 local targ
= string.format("party%d", i
)
823 local liv
= UnitName(targ
)
824 if liv
and liv
~= UNKNOWNOBJECT
and UnitIsConnected(targ
) then playerlist
[liv
] = true end
827 playerlist
[UnitName("player")] = nil
830 for k
, v
in pairs(playerlist
) do
831 if not old_playerlist
[k
] then
832 --print("new player:", k)
833 table.insert(additions
, k
)
838 for k
, v
in pairs(old_playerlist
) do
839 if not playerlist
[k
] then
840 --print("lost player:", k)
841 table.insert(removals
, k
)
845 old_playerlist
= playerlist
847 for _
, v
in ipairs(removals
) do
851 if #additions
== 0 then return end
853 if #additions
== 1 then
854 SAM("syn:2", "WHISPER", additions
[1])
856 SAM("syn:2", "PARTY")
862 local newer_reported
= false
863 local older_reported
= false
864 function QH_Questcomm_Msg(data
, from
)
865 if data
:match("syn:0") then
866 QH_DumpCommUser(from
)
869 if QuestHelper_Pref
.solo
then return end
871 --print("received", from, data)
875 local key
, value
= data
:match("(.):(.*)")
878 elseif key
== "x" then
880 aku
[from
] = aku
[from
] .. value
882 elseif key
== "X" then
884 aku
[from
] = aku
[from
] .. value
893 if not cont
then return end
896 --print("packet received", from, data)
897 if data
:match("syn:.*") then
898 local synv
= data
:match("syn:([0-9]*)")
899 if synv
then synv
= tonumber(synv
) end
900 if synv
and synv
~= 2 then
901 if synv
> 2 and not newer_reported
then
902 QuestHelper
:TextOut(QHFormat("PEER_NEWER", from
))
903 newer_reported
= true
904 elseif synv
< 2 and not older_reported
then
905 QuestHelper
:TextOut(QHFormat("PEER_OLDER", from
))
906 older_reported
= true
910 if synv
and synv
>= 2 then
911 SAM("hello:2", "WHISPER", from
)
913 elseif data
== "hello:2" or data
== "retrieve:2" then
914 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
916 for k
, v
in pairs(current_chunks
) do
917 SAM(v
, "WHISPER", from
)
920 if old_playerlist
[from
] then
921 QH_InsertCommPacket(from
, data
)
926 function QuestHelper
:SetShare(flag
)
930 SAM("syn:0", "PARTY")
931 local cpb
= comm_packets
933 for k
in pairs(cpb
) do RefreshUserComms(k
) end