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 local current_chunks
= {}
621 -- Here's the core update function
622 function QH_UpdateQuests(force
)
623 if not DB_Ready() then return end
625 if update
or force
then -- Sometimes (usually) we don't actually update
628 local player
= UnitName("player")
629 StartInsertionPass(player
)
631 local next_chunks
= {}
633 -- This begins the main update loop that loops through all of the quests
635 local title
, level
, variety
, groupsize
, _
, _
, complete
= GetQuestLogTitle(index
)
636 if not title
then break end
638 title
= title
:match("%[.*%] (.*)") or title
640 local qlink
= GetQuestLink(index
)
641 if qlink
then -- If we don't have a quest link, it's not really a quest
642 local id
= GetQuestType(qlink
)
643 if id
then -- If we don't have a *valid* quest link, give up
644 local lbcount
= GetNumQuestLeaderBoards(index
)
645 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
647 QuestHelper
: Assert(db
)
649 local watched
= IsQuestWatched(index
)
651 -- 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)
653 db
.tracker_clicked
= function () Clicky(lindex
) end
655 db
.type_quest
.index
= index
659 local timer
= GetQuestIndexForTimer(timidx
)
660 if not timer
then timidx
= nil break end
661 if timer
== index
then break end
664 local timed
= not not timidx
666 --print(id, title, level, groupsize, variety, groupsize, complete, timed)
667 local chunk
= "q:" .. Serialize(id
, title
, level
, groupsize
, variety
, groupsize
, complete
, timed
)
668 for i
= 1, lbcount
do
669 QuestHelper
: Assert(db
[i
])
670 db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
= GetQuestLogLeaderBoard(i
, index
)
671 db
[i
].temp_person
= player
673 if db
[i
].temp_desc
~= db
[i
].tooltip_defer_questobjective
then
674 db
[i
].tooltip_defer_questobjective_last
= db
[i
].tooltip_defer_questobjective
676 db
[i
].tooltip_defer_questname
= title
677 db
[i
].tooltip_defer_questobjective
= db
[i
].temp_desc
-- yoink
680 chunk
= chunk
.. ":" .. Serialize(db
[i
].temp_desc
, db
[i
].temp_typ
, db
[i
].temp_done
)
683 db
.finish
.tooltip_defer_questname
= title
-- we're using this as our fallback right now
685 next_chunks
[id
] = chunk
687 QuestProcessor(player
, db
, title
, level
, groupsize
, variety
, groupsize
, watched
, complete
, lbcount
, timed
)
693 EndInsertionPass(player
)
695 QH_Route_Filter_Rescan() -- 'cause filters may also change
697 if not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
then
698 for k
, v
in pairs(next_chunks
) do
699 if current_chunks
[k
] ~= v
then
704 for k
, v
in pairs(current_chunks
) do
705 if not next_chunks
[k
] then
706 SAM(string.format("q:n%d", k
), "PARTY")
711 current_chunks
= next_chunks
715 -- comm_packets[user][qid] = data
716 local comm_packets
= {}
718 local function RefreshUserComms(user
)
719 StartInsertionPass(user
)
721 if comm_packets
[user
] then for _
, dat
in pairs(comm_packets
[user
]) do
722 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]
727 if dat
[#obj
* 3 + objstart
] == nil and dat
[#obj
* 3 + objstart
+ 1] == nil and dat
[#obj
* 3 + objstart
+ 2] == nil then break end
728 table.insert(obj
, {dat
[#obj
* 3 + objstart
], dat
[#obj
* 3 + objstart
+ 1], dat
[#obj
* 3 + objstart
+ 2]})
732 local db
= GetQuestMetaobjective(id
, lbcount
) -- This generates the above-mentioned metaobjective, including doing the database lookup.
734 QuestHelper
: Assert(db
)
736 for i
= 1, lbcount
do
737 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
740 QuestProcessor(user
, db
, title
, level
, group
, variety
, groupsize
, "(ignore)", complete
, lbcount
, false)
743 EndInsertionPass(user
)
745 QH_Route_Filter_Rescan() -- 'cause filters may also change
748 function QH_InsertCommPacket(user
, data
)
749 local q
, chunk
= data
:match("([^:]+):(.*)")
750 if q
~= "q" then return end
754 for item
in chunk
:gmatch("([^:]+)") do
755 dat
[idx
] = DeSerItem(item
)
759 if not comm_packets
[user
] then comm_packets
[user
] = {} end
761 comm_packets
[user
][dat
[1]]
= nil
763 comm_packets
[user
][dat
[1]]
= dat
767 RefreshUserComms(user
)
770 local function QH_DumpCommUser(user
)
771 comm_packets
[user
] = nil
772 RefreshUserComms(user
)
775 QH_Event("UNIT_QUEST_LOG_CHANGED", UpdateTrigger
)
776 QH_Event("QUEST_LOG_UPDATE", QH_UpdateQuests
)
778 -- 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.
779 QH_AddNotifier(GetTime() + 5, function ()
780 local aqw_orig
= AddQuestWatch
781 AddQuestWatch
= function(...)
783 QH_UpdateQuests(true)
785 local rqw_orig
= RemoveQuestWatch
786 RemoveQuestWatch
= function(...)
788 QH_UpdateQuests(true)
792 local old_playerlist
= {}
794 function QH_Questcomm_Sync()
795 if not (not QuestHelper_Pref
.solo
and QuestHelper_Pref
.share
) then
800 local playerlist
= {}
801 --[[if GetNumRaidMembers() > 0 then
803 local liv = UnitName(string.format("raid%d", i))
804 if liv then playerlist[liv] = true end
806 elseif]] if GetNumPartyMembers() > 0 then
809 local targ
= string.format("party%d", i
)
810 local liv
= UnitName(targ
)
811 if liv
and liv
~= UNKNOWNOBJECT
and UnitIsConnected(targ
) then playerlist
[liv
] = true end
814 playerlist
[UnitName("player")] = nil
817 for k
, v
in pairs(playerlist
) do
818 if not old_playerlist
[k
] then
819 --print("new player:", k)
820 table.insert(additions
, k
)
825 for k
, v
in pairs(old_playerlist
) do
826 if not playerlist
[k
] then
827 --print("lost player:", k)
828 table.insert(removals
, k
)
832 old_playerlist
= playerlist
834 for _
, v
in ipairs(removals
) do
838 if #additions
== 0 then return end
840 if #additions
== 1 then
841 SAM("syn:2", "WHISPER", additions
[1])
843 SAM("syn:2", "PARTY")
849 local newer_reported
= false
850 local older_reported
= false
851 function QH_Questcomm_Msg(data
, from
)
852 if data
:match("syn:0") then
853 QH_DumpCommUser(from
)
856 if QuestHelper_Pref
.solo
then return end
858 --print("received", from, data)
862 local key
, value
= data
:match("(.):(.*)")
865 elseif key
== "x" then
867 aku
[from
] = aku
[from
] .. value
869 elseif key
== "X" then
871 aku
[from
] = aku
[from
] .. value
880 if not cont
then return end
883 --print("packet received", from, data)
884 if data
:match("syn:.*") then
885 local synv
= data
:match("syn:([0-9]*)")
886 if synv
then synv
= tonumber(synv
) end
887 if synv
and synv
~= 2 then
888 if synv
> 2 and not newer_reported
then
889 QuestHelper
:TextOut(QHFormat("PEER_NEWER", from
))
890 newer_reported
= true
891 elseif synv
< 2 and not older_reported
then
892 QuestHelper
:TextOut(QHFormat("PEER_OLDER", from
))
893 older_reported
= true
897 if synv
and synv
>= 2 then
898 SAM("hello:2", "WHISPER", from
)
900 elseif data
== "hello:2" or data
== "retrieve:2" then
901 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
903 for k
, v
in pairs(current_chunks
) do
904 SAM(v
, "WHISPER", from
)
907 if old_playerlist
[from
] then
908 QH_InsertCommPacket(from
, data
)
913 function QuestHelper
:SetShare(flag
)
917 SAM("syn:0", "PARTY")
918 local cpb
= comm_packets
920 for k
in pairs(cpb
) do RefreshUserComms(k
) end