TourGuide
[WoW-TourGuide.git] / TourGuide.lua
blob17c3cccb7f777ef882acce18c3835c5b0089c3ea
2 local OptionHouse = DongleStub("OptionHouse-1.0")
5 local myfaction = UnitFactionGroup("player")
8 TourGuide = DongleStub("Dongle-1.0"):New("TourGuide")
9 TourGuide.guides = {}
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\\Icons\\Spell_Holy_ElunesGrace",
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 ITEM = "Interface\\Icons\\INV_Misc_Bag_08",
27 BUY = "Interface\\Icons\\INV_Misc_Coin_01",
28 }, {__index = function() return "Interface\\Icons\\INV_Misc_QuestionMark" end})
31 local actiontypes = {
32 A = "ACCEPT",
33 C = "COMPLETE",
34 T = "TURNIN",
35 R = "RUN",
36 t = "TRAIN",
37 H = "HEARTH",
38 h = "SETHEARTH",
39 G = "GRIND",
40 I = "ITEM",
41 F = "FLY",
42 N = "NOTE",
43 B = "BUY",
47 function TourGuide:Initialize()
48 self.db = self:InitializeDB("TourGuideAlphaDB", {
49 char = {
50 currentguide = self.guidelist[1],
51 turnedin = {},
52 cachedturnins = {},
55 self.turnedin = self.db.char.turnedin
56 self.cachedturnins = self.db.char.cachedturnins
58 self:LoadGuide(self.db.char.currentguide)
59 end
62 function TourGuide:Enable()
63 local _, title = GetAddOnInfo("TourGuide")
64 local author, version = GetAddOnMetadata("TourGuide", "Author"), GetAddOnMetadata("TourGuide", "Version")
65 local oh = OptionHouse:RegisterAddOn("Tour Guide", title, author, version)
66 oh:RegisterCategory("Guides", TourGuide, "CreateGuidesPanel")
67 oh:RegisterCategory("Objectives", TourGuide, "CreateObjectivePanel")
69 for _,event in pairs(self.TrackEvents) do self:RegisterEvent(event) end
70 self.TrackEvents = nil
71 self:UpdateStatusFrame()
72 end
75 function TourGuide:RegisterGuide(name, nextzone, faction, sequencefunc)
76 if faction ~= myfaction then return end
77 self.guides[name] = sequencefunc
78 self.nextzones[name] = nextzone
79 table.insert(self.guidelist, name)
80 end
83 function TourGuide:LoadGuide(name)
84 name = name or self.nextzones[self.db.char.currentguide]
85 if not name then return end
87 -- Clean out old completed objectives, to avoid conflicts
88 if name ~= self.db.char.currentguide then
89 for i,quest in ipairs(self.quests) do self.turnedin[quest] = nil end
90 end
92 self.db.char.currentguide = name
93 self:ParseObjectives(self.guides[name](), showdebug)
94 end
97 function TourGuide:GetQuestLogIndexByName(name)
98 name = name or self.quests[self.current]:gsub("%s%(Part %d+%)", "")
99 for i=1,GetNumQuestLogEntries() do
100 if GetQuestLogTitle(i) == name then return i end
104 function TourGuide:GetQuestDetails(name)
105 if not name then return end
106 name = name:gsub("%s%(Part %d+%)", "")
108 local i = self:GetQuestLogIndexByName(name)
109 local complete = i and select(7, GetQuestLogTitle(i)) == 1
110 return i, complete
114 local function FindBagSlot(itemid)
115 for bag=0,4 do
116 for slot=1,GetContainerNumSlots(bag) do
117 local item = GetContainerItemLink(bag, slot)
118 if item and string.find(item, "item:"..itemid) then return bag, slot end
124 function TourGuide:GetCurrentObjectiveInfo()
125 return self:GetObjectiveInfo(self.current)
129 function TourGuide:GetObjectiveInfo(i)
130 local action, quest, note = self.actions[i], self.quests[i], self.notes[i]
131 if not action then return end
133 local logi, complete = self:GetQuestDetails(quest)
134 local hasitem = action == "ITEM" and self.questitems[i] and FindBagSlot(self.questitems[i])
136 return action, quest:gsub("@.*@", ""), note, logi, complete, hasitem, self.turnedin[quest], quest
140 local isdebugging = false
141 local myclass = UnitClass("player")
142 local titlematches = {"For", "A", "The", "Or", "In", "Then", "From", "Our"}
143 local accepts, turnins, completes = {}, {}, {}
144 local function ParseQuests(...)
145 local uniqueid = 1
146 local actions, notes, quests, items = {}, {}, {}, {}
147 local i = 1
149 for j=1,select("#", ...) do
150 local text = select(j, ...)
151 local _, _, class = text:find("|C|([^|]+)|")
153 if text ~= "" and (not class or class == myclass) then
154 local _, _, action, quest = text:find("^(%a) ([^|]*)")
155 quest = quest:trim()
156 if not (action == "I" or action == "A" or action =="C" or action =="T") then
157 quest = quest.."@"..uniqueid.."@"
158 uniqueid = uniqueid + 1
161 actions[i], quests[i] = actiontypes[action], quest
163 local _, _, note = string.find(text, "|N|([^|]+)|")
164 if note then notes[i] = note end
166 local _, _, item = string.find(text, "|I|(%d+)|")
167 if item then items[i] = item end
169 i = i + 1
171 -- Debuggery
172 if isdebugging and action == "A" then accepts[quest] = true
173 elseif isdebugging and action == "T" then turnins[quest] = true
174 elseif isdebugging and action == "C" then completes[quest] = true end
176 if isdebugging and (action == "A" or action == "C" or action == "T") then
177 -- Catch bad Title Case
178 for _,word in pairs(titlematches) do
179 if quest:find("[^:]%s"..word.."%s") or quest:find("[^:]%s"..word.."$") or quest:find("[^:]%s"..word.."@") then
180 TourGuide:PrintF("%s %s -- Contains bad title case", action, quest)
184 local _, _, comment = string.find(text, "(|.|[^|]+)$")
185 if comment then TourGuide:Print("Unclosed comment: ".. comment) end
189 -- More debug
190 if isdebugging then
191 for quest in pairs(accepts) do if not turnins[quest] then TourGuide:PrintF("Quest has no 'turnin' objective: %s", quest) end end
192 for quest in pairs(turnins) do if not accepts[quest] then TourGuide:PrintF("Quest has no 'accept' objective: %s", quest) end end
193 for quest in pairs(completes) do if not accepts[quest] and not turnins[quest] then TourGuide:PrintF("Quest has no 'accept' and 'turnin' objectives: %s", quest) end end
196 return actions, notes, quests, items
200 function TourGuide:ParseObjectives(text, showdebug)
201 isdebugging = showdebug
202 self.actions, self.notes, self.quests, self.questitems = ParseQuests(string.split("\n", text))
206 function TourGuide:SetTurnedIn(i, value)
207 if not i then
208 i = self.current
209 value = true
212 self.turnedin[self.quests[i]] = value
213 self:UpdateStatusFrame()