TourGuide - Finished off Ghostlands, fix to sunstrider
[WoW-TourGuide.git] / TourGuide.lua
blob1f84107def39fed2d3d23d756f2cbf562b6e78cd
2 local OptionHouse = DongleStub("OptionHouse-1.0")
5 local myfaction = UnitFactionGroup("player")
6 local debugframe = TourGuideOHDebugFrame
7 TourGuideOHDebugFrame = nil
9 TourGuide = DongleStub("Dongle-1.0"):New("TourGuide")
10 TourGuide:EnableDebug(1, debugframe)
11 TourGuide.guides = {}
12 TourGuide.guidelist = {}
13 TourGuide.nextzones = {}
16 TourGuide.icons = setmetatable({
17 ACCEPT = "Interface\\GossipFrame\\AvailableQuestIcon",
18 COMPLETE = "Interface\\Icons\\Ability_DualWield",
19 TURNIN = "Interface\\GossipFrame\\ActiveQuestIcon",
20 RUN = "Interface\\Icons\\Ability_Tracking",
21 MAP = "Interface\\Icons\\Ability_Spy",
22 FLY = "Interface\\Icons\\Ability_Druid_FlightForm",
23 TRAIN = "Interface\\GossipFrame\\trainerGossipIcon",
24 SETHEARTH = "Interface\\Icons\\Spell_Holy_ElunesGrace",
25 HEARTH = "Interface\\Icons\\INV_Misc_Rune_01",
26 NOTE = "Interface\\Icons\\INV_Misc_Note_01",
27 GRIND = "Interface\\Icons\\INV_Stone_GrindingStone_05",
28 ITEM = "Interface\\Icons\\INV_Misc_Bag_08",
29 BUY = "Interface\\Icons\\INV_Misc_Coin_01",
30 BOAT = "Interface\\Icons\\Spell_Frost_SummonWaterElemental",
31 GETFLIGHTPOINT = "Interface\\Icons\\Spell_Nature_GiftoftheWaterSpirit",
32 }, {__index = function() return "Interface\\Icons\\INV_Misc_QuestionMark" end})
35 local actiontypes = {
36 A = "ACCEPT",
37 C = "COMPLETE",
38 T = "TURNIN",
39 R = "RUN",
40 t = "TRAIN",
41 H = "HEARTH",
42 h = "SETHEARTH",
43 G = "GRIND",
44 I = "ITEM",
45 F = "FLY",
46 f = "GETFLIGHTPOINT",
47 N = "NOTE",
48 B = "BUY",
49 b = "BOAT",
53 function TourGuide:Initialize()
54 self.db = self:InitializeDB("TourGuideAlphaDB", {
55 char = {
56 turnedin = {},
57 cachedturnins = {},
60 self.turnedin = self.db.char.turnedin
61 self.cachedturnins = self.db.char.cachedturnins
63 self.db.char.currentguide = self.db.char.currentguide or self.guidelist[1]
64 self:LoadGuide(self.db.char.currentguide)
65 end
68 function TourGuide:Enable()
69 local _, title = GetAddOnInfo("TourGuide")
70 local author, version = GetAddOnMetadata("TourGuide", "Author"), GetAddOnMetadata("TourGuide", "Version")
71 local oh = OptionHouse:RegisterAddOn("Tour Guide", title, author, version)
72 oh:RegisterCategory("Guides", TourGuide, "CreateGuidesPanel")
73 oh:RegisterCategory("Objectives", TourGuide, "CreateObjectivePanel")
74 oh:RegisterCategory("Debug", function() return debugframe end)
76 for _,event in pairs(self.TrackEvents) do self:RegisterEvent(event) end
77 self.TrackEvents = nil
78 self:UpdateStatusFrame()
79 end
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)
87 end
90 function TourGuide:LoadGuide(name)
91 if not name then return end
93 self.db.char.currentguide = name
94 if not self.guides[name] then self.db.char.currentguide = self.guidelist[1] end
95 self:DebugF(1, "Loading guide: %s", name)
96 self:ParseObjectives(self.guides[self.db.char.currentguide]())
97 end
100 function TourGuide:LoadNextGuide()
101 local name = self.nextzones[self.db.char.currentguide]
102 if not name then return end
104 for i,quest in ipairs(self.quests) do self.turnedin[quest] = nil end -- Clean out old completed objectives, to avoid conflicts
106 self:LoadGuide(name)
107 self:UpdateGuidesPanel()
108 return true
112 function TourGuide:GetQuestLogIndexByName(name)
113 name = name or self.quests[self.current]
114 name = name:gsub("%s%(Part %d+%)", "")
115 for i=1,GetNumQuestLogEntries() do
116 if GetQuestLogTitle(i) == name then return i end
120 function TourGuide:GetQuestDetails(name)
121 if not name then return end
123 local i = self:GetQuestLogIndexByName(name)
124 local complete = i and select(7, GetQuestLogTitle(i)) == 1
125 return i, complete
129 local function FindBagSlot(itemid)
130 for bag=0,4 do
131 for slot=1,GetContainerNumSlots(bag) do
132 local item = GetContainerItemLink(bag, slot)
133 if item and string.find(item, "item:"..itemid) then return bag, slot end
139 function TourGuide:GetCurrentObjectiveInfo()
140 return self:GetObjectiveInfo(self.current)
144 function TourGuide:GetObjectiveInfo(i)
145 local action, quest, note = self.actions[i], self.quests[i], self.notes[i]
146 if not action then return end
148 local logi, complete = self:GetQuestDetails(quest)
149 local hasitem = action == "ITEM" and self.questitems[i] and FindBagSlot(self.questitems[i])
151 return action, quest:gsub("@.*@", ""), note, logi, complete, hasitem, self.turnedin[quest], quest, self.useitems[i], self.optional[i]
155 local myclass = UnitClass("player")
156 local titlematches = {"For", "A", "The", "Or", "In", "Then", "From", "To"}
157 local function ParseQuests(...)
158 local accepts, turnins, completes = {}, {}, {}
159 local uniqueid = 1
160 local actions, notes, quests, items, useitems, optionals = {}, {}, {}, {}, {}, {}
161 local i = 1
163 for j=1,select("#", ...) do
164 local text = select(j, ...)
165 local _, _, class = text:find("|C|([^|]+)|")
167 if text ~= "" and (not class or class == myclass) then
168 local _, _, action, quest = text:find("^(%a) ([^|]*)")
169 assert(actiontypes[action], "Unknown action: "..text)
170 quest = quest:trim()
171 if not (action == "I" or action == "A" or action =="C" or action =="T") then
172 quest = quest.."@"..uniqueid.."@"
173 uniqueid = uniqueid + 1
176 actions[i], quests[i] = actiontypes[action], quest
178 local _, _, note = string.find(text, "|N|([^|]+)|")
179 if note then notes[i] = note end
181 local _, _, item = string.find(text, "|I|(%d+)|")
182 if item then items[i] = item end
184 local _, _, useitem = string.find(text, "|U|(%d+)|")
185 if useitem then useitems[i] = useitem end
187 if string.find(text, "|O|") then optionals[i] = true end
189 i = i + 1
191 -- Debuggery
192 if action == "A" then accepts[quest] = true
193 elseif action == "T" then turnins[quest] = true
194 elseif action == "C" then completes[quest] = true end
196 if action == "A" or action == "C" or action == "T" then
197 -- Catch bad Title Case
198 for _,word in pairs(titlematches) do
199 if quest:find("[^:]%s"..word.."%s") or quest:find("[^:]%s"..word.."$") or quest:find("[^:]%s"..word.."@") then
200 TourGuide:DebugF(1, "%s %s -- Contains bad title case", action, quest)
204 local _, _, comment = string.find(text, "(|.|[^|]+)$")
205 if comment then TourGuide:Debug(1, "Unclosed comment: ".. comment) end
209 -- More debug
210 for quest in pairs(accepts) do if not turnins[quest] then TourGuide:DebugF(1, "Quest has no 'turnin' objective: %s", quest) end end
211 for quest in pairs(turnins) do if not accepts[quest] then TourGuide:DebugF(1, "Quest has no 'accept' objective: %s", quest) end end
212 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
214 return actions, notes, quests, items, useitems, optionals
218 function TourGuide:ParseObjectives(text)
219 self.actions, self.notes, self.quests, self.questitems, self.useitems, self.optional = ParseQuests(string.split("\n", text))
223 function TourGuide:SetTurnedIn(i, value)
224 if not i then
225 i = self.current
226 value = true
229 if value then value = true else value = nil end -- Cleanup to minimize savedvar data
231 self.turnedin[self.quests[i]] = value
232 self:DebugF(1, "Set turned in %q = %s", self.quests[i], tostring(value))
233 self:UpdateStatusFrame()