2 local OptionHouse
= LibStub("OptionHouse-1.1")
5 local myfaction
= UnitFactionGroup("player")
7 TourGuide
= DongleStub("Dongle-1.0"):New("TourGuide")
8 if tekDebug
then TourGuide
:EnableDebug(10, tekDebug
:GetFrame("TourGuide")) end
10 TourGuide
.guidelist
= {}
11 TourGuide
.nextzones
= {}
14 TourGuide
.icons
= setmetatable({
15 ACCEPT
= "Interface\\GossipFrame\\AvailableQuestIcon",
16 COMPLETE
= "Interface\\Icons\\Ability_DualWield",
17 TURNIN
= "Interface\\GossipFrame\\ActiveQuestIcon",
18 RUN
= "Interface\\Icons\\Ability_Tracking",
19 MAP
= "Interface\\Icons\\Ability_Spy",
20 FLY
= "Interface\\Icons\\Ability_Druid_FlightForm",
21 TRAIN
= "Interface\\GossipFrame\\trainerGossipIcon",
22 SETHEARTH
= "Interface\\AddOns\\TourGuide\\resting.tga",
23 HEARTH
= "Interface\\Icons\\INV_Misc_Rune_01",
24 NOTE
= "Interface\\Icons\\INV_Misc_Note_01",
25 GRIND
= "Interface\\Icons\\INV_Stone_GrindingStone_05",
26 USE
= "Interface\\Icons\\INV_Misc_Bag_08",
27 BUY
= "Interface\\Icons\\INV_Misc_Coin_01",
28 BOAT
= "Interface\\Icons\\Spell_Frost_SummonWaterElemental",
29 GETFLIGHTPOINT
= "Interface\\Icons\\Ability_Hunter_EagleEye",
30 }, {__index
= function() return "Interface\\Icons\\INV_Misc_QuestionMark" end})
51 function TourGuide
:Initialize()
52 self
.db
= self
:InitializeDB("TourGuideAlphaDB", {
58 self
.turnedin
= self
.db
.char
.turnedin
59 self
.cachedturnins
= self
.db
.char
.cachedturnins
61 self
.db
.char
.currentguide
= self
.db
.char
.currentguide
or self
.guidelist
[1]
62 self
:LoadGuide(self
.db
.char
.currentguide
)
63 self
:PositionStatusFrame()
67 function TourGuide
:Enable()
68 local _
, title
= GetAddOnInfo("TourGuide")
69 local author
, version
= GetAddOnMetadata("TourGuide", "Author"), GetAddOnMetadata("TourGuide", "Version")
70 local oh
= OptionHouse
:RegisterAddOn("Tour Guide", title
, author
, version
)
71 oh
:RegisterCategory("Guides", TourGuide
, "CreateGuidesPanel")
72 oh
:RegisterCategory("Objectives", TourGuide
, "CreateObjectivePanel")
74 for _
,event
in pairs(self
.TrackEvents
) do self
:RegisterEvent(event
) end
75 self
:RegisterEvent("QUEST_COMPLETE", "UpdateStatusFrame")
76 self
:RegisterEvent("QUEST_DETAIL", "UpdateStatusFrame")
77 self
.TrackEvents
= nil
78 self
:UpdateStatusFrame()
82 function TourGuide
:RegisterGuide(name
, nextzone
, faction
, sequencefunc
)
83 if faction
~= myfaction
then return end
84 self
.guides
[name
] = sequencefunc
85 self
.nextzones
[name
] = nextzone
86 table.insert(self
.guidelist
, name
)
90 function TourGuide
:LoadNextGuide()
91 local name
= self
.nextzones
[self
.db
.char
.currentguide
]
92 if not name
then return end
94 for i
,quest
in ipairs(self
.quests
) do self
.turnedin
[quest
] = nil end -- Clean out old completed objectives, to avoid conflicts
97 self
:UpdateGuidesPanel()
102 function TourGuide
:GetQuestLogIndexByName(name
)
103 name
= name
or self
.quests
[self
.current
]
104 name
= name
:gsub("%s%(Part %d+%)", "")
105 for i
=1,GetNumQuestLogEntries() do
106 if GetQuestLogTitle(i
) == name
then return i
end
110 function TourGuide
:GetQuestDetails(name
)
111 if not name
then return end
113 local i
= self
:GetQuestLogIndexByName(name
)
114 local complete
= i
and select(7, GetQuestLogTitle(i
)) == 1
119 function TourGuide
:FindBagSlot(itemid
)
121 for slot
=1,GetContainerNumSlots(bag
) do
122 local item
= GetContainerItemLink(bag
, slot
)
123 if item
and string.find(item
, "item:"..itemid
) then return bag
, slot
end
129 function TourGuide
:GetObjectiveInfo(i
)
130 i
= i
or self
.current
131 if not self
.actions
[i
] then return end
133 return self
.actions
[i
], self
.quests
[i
]:gsub("@.*@", ""), self
.quests
[i
] -- Action, display name, full name
137 function TourGuide
:GetObjectiveStatus(i
)
138 i
= i
or self
.current
139 if not self
.actions
[i
] then return end
141 return self
.turnedin
[self
.quests
[i]]
, self
:GetQuestDetails(self
.quests
[i
]) -- turnedin, logi, complete
145 function TourGuide
:GetObjectiveTag(tag, i
)
146 self
:Debug(11, "GetObjectiveTag", tag, i
)
147 i
= i
or self
.current
148 if tag == "LV" then return self
.levels
[i
]
149 elseif tag == "N" then return self
.notes
[i
]
150 elseif tag == "U" then return self
.useitems
[i
]
151 elseif tag == "O" then return self
.optional
[i
]
152 elseif tag == "Z" then return self
.zones
[i
]
153 elseif tag == "L" then
154 local _
, _
, lootitem
, lootqty
= string.find(self
.lootitems
[i
] or "", "(%d+)%s?(%d*)")
155 lootqty
= tonumber(lootqty
) or 1
157 return lootitem
, lootqty
162 local myclass
= UnitClass("player")
163 local titlematches
= {"For", "A", "The", "Or", "In", "Then", "From", "To"}
164 local function ParseQuests(...)
165 local accepts
, turnins
, completes
= {}, {}, {}
167 local actions
, notes
, quests
, useitems
, optionals
, lootitems
, levels
, zones
= {}, {}, {}, {}, {}, {}, {}, {}
168 local i
, haserrors
= 1, false
170 for j
=1,select("#", ...) do
171 local text
= select(j
, ...)
172 local _
, _
, class
= text
:find("|C|([^|]+)|")
174 if text
~= "" and (not class
or class
== myclass
) then
175 local _
, _
, action
, quest
= text
:find("^(%a) ([^|]*)")
176 assert(actiontypes
[action
], "Unknown action: "..text
)
178 if not (action
== "A" or action
=="C" or action
=="T") then
179 quest
= quest
.."@"..uniqueid
.."@"
180 uniqueid
= uniqueid
+ 1
183 actions
[i
], quests
[i
] = actiontypes
[action
], quest
185 local _
, _
, note
= string.find(text
, "|N|([^|]+)|")
186 if note
then notes
[i
] = note
end
188 local _
, _
, level
= string.find(text
, "|LV|(%d+)|")
189 if level
then levels
[i
] = tonumber(level
) end
191 local _
, _
, useitem
= string.find(text
, "|U|(%d+)|")
192 if useitem
then useitems
[i
] = useitem
end
194 local _
, _
, zone
= string.find(text
, "|Z|([^|]+)|")
195 if zone
then zones
[i
] = zone
end
197 local _
, _
, lootitem
= string.find(text
, "|L|(%d+%s?%d*)|")
198 if lootitem
then lootitems
[i
] = lootitem
end
200 if string.find(text
, "|O|") then optionals
[i
] = true end
205 if not string.find(text
, "|NODEBUG|") then
206 if action
== "A" then accepts
[quest
] = true
207 elseif action
== "T" then turnins
[quest
] = true
208 elseif action
== "C" then completes
[quest
] = true end
210 if action
== "A" or action
== "C" or action
== "T" then
211 -- Catch bad Title Case
212 for _
,word
in pairs(titlematches
) do
213 if quest
:find("[^:]%s"..word
.."%s") or quest
:find("[^:]%s"..word
.."$") or quest
:find("[^:]%s"..word
.."@") then
214 TourGuide
:DebugF(1, "%s %s -- Contains bad title case", action
, quest
)
219 if quest
:find("[“”’]") then
220 TourGuide
:DebugF(1, "%s %s -- Contains bad char", action
, quest
)
225 local _
, _
, comment
= string.find(text
, "(|[NLUC]V?|[^|]+)$") or string.find(text
, "(|[NLUC]V?|[^|]+) |[NLUC]V?|")
227 TourGuide
:Debug(1, "Unclosed comment: ".. comment
)
235 for quest
in pairs(accepts
) do if not turnins
[quest
] then TourGuide
:DebugF(1, "Quest has no 'turnin' objective: %s", quest
) end end
236 for quest
in pairs(turnins
) do if not accepts
[quest
] then TourGuide
:DebugF(1, "Quest has no 'accept' objective: %s", quest
) end end
237 for quest
in pairs(completes
) do if not accepts
[quest
] and not turnins
[quest
] then TourGuide
:DebugF(1, "Quest has no 'accept' and 'turnin' objectives: %s", quest
) end end
239 if haserrors
and TourGuide
:IsDebugEnabled() then TourGuide
:Print("This guide contains errors") end
241 return actions
, notes
, quests
, useitems
, optionals
, lootitems
, levels
, zones
245 function TourGuide
:LoadGuide(name
)
246 if not name
then return end
248 self
.db
.char
.currentguide
= name
249 if not self
.guides
[name
] then self
.db
.char
.currentguide
= self
.guidelist
[1] end
250 self
:DebugF(1, "Loading guide: %s", name
)
251 self
.guidechanged
= true
252 local _
, _
, zonename
= name
:find("^(.*) %(.*%)$")
253 self
.zonename
= zonename
254 self
.actions
, self
.notes
, self
.quests
, self
.useitems
, self
.optional
, self
.lootitems
, self
.levels
, self
.zones
= ParseQuests(string.split("\n", self
.guides
[self
.db
.char
.currentguide
]()))
258 function TourGuide
:SetTurnedIn(i
, value
, noupdate
)
264 if value
then value
= true else value
= nil end -- Cleanup to minimize savedvar data
266 self
.turnedin
[self
.quests
[i]]
= value
267 self
:DebugF(1, "Set turned in %q = %s", self
.quests
[i
], tostring(value
))
268 if not noupdate
then self
:UpdateStatusFrame()
269 else self
.updatedelay
= true end
273 function TourGuide
:CompleteQuest(name
, noupdate
)
274 local i
= self
.current
277 action
, quest
= self
:GetObjectiveInfo(i
)
278 if action
== "TURNIN" and not self
:GetObjectiveStatus(i
) and name
== quest
:gsub("%s%(Part %d+%)", "") then
279 self
:DebugF(1, "Saving quest turnin %q", quest
)
280 return self
:SetTurnedIn(i
, true, noupdate
)
284 self
:DebugF(1, "Quest %q not found!", name
)