2 local OptionHouse
= DongleStub("OptionHouse-1.0")
5 local myfaction
= UnitFactionGroup("player")
8 TourGuide
= DongleStub("Dongle-1.0"):New("TourGuide")
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})
47 function TourGuide
:Initialize()
48 self
.db
= self
:InitializeDB("TourGuideAlphaDB", {
50 currentguide
= self
.guidelist
[1],
55 self
.turnedin
= self
.db
.char
.turnedin
56 self
.cachedturnins
= self
.db
.char
.cachedturnins
58 self
:LoadGuide(self
.db
.char
.currentguide
)
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()
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
)
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
92 self
.db
.char
.currentguide
= name
93 self
:ParseObjectives(self
.guides
[name
](), showdebug
)
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
114 local function FindBagSlot(itemid
)
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(...)
146 local actions
, notes
, quests
, items
= {}, {}, {}, {}
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) ([^|]*)")
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
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
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
)
212 self
.turnedin
[self
.quests
[i]]
= value
213 self
:UpdateStatusFrame()