vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | ------------------------------------------------------ |
2 | -- FeedOMatic.lua |
||
3 | ------------------------------------------------------ |
||
4 | FOM_VERSION = "11200.1"; |
||
5 | ------------------------------------------------------ |
||
6 | |||
7 | -- TODO: if you don't auto-feed, have an option to make the need to feed more noticeable (e.g., pulsing halo around the pet-happiness icon). |
||
8 | |||
9 | -- constants |
||
10 | FOM_WARNING_INTERVAL = 10; -- don't warn more than once per this many seconds |
||
11 | MAX_QUALITY = 35 * 60 + 1; -- We store a notion of a food's "quality": its best happiness-per-tick multiplied by the pet's level as of when that tick occurred. We use "best" because a pet that's closer to "sated" (maximum happiness) will receive less happiness per tick than he would from the same food if he were hungrier. (So, a food that gives 35 happiness per tick to a level 60 pet is "better" than a food that's worth 35 happiness per tick to a level 30 pet.) Foods whose quality hasn't been observed yet are given this value when sorting, so we can prioritize the discovery of new foods' quality ratings. |
||
12 | MAX_KEEPOPEN_SLOTS = 150; |
||
13 | |||
14 | -- Configuration |
||
15 | FOM_Config_Default = { |
||
16 | Enabled = false; |
||
17 | Alert = "emote"; |
||
18 | Level = "content"; |
||
19 | KeepOpenSlots = 8; |
||
20 | AvoidUsefulFood = true; |
||
21 | AvoidQuestFood = true; |
||
22 | AvoidBonusFood = true; |
||
23 | Fallback = false; |
||
24 | SaveForCookingLevel = 1; |
||
25 | PreferHigherQuality = true; |
||
26 | Tooltip = true; |
||
27 | }; |
||
28 | FOM_Config = FOM_Config_Default; |
||
29 | |||
30 | -- FOM_Cooking = { }; |
||
31 | -- Has the following internal structure: |
||
32 | -- REALM_PLAYER = { |
||
33 | -- FOODNAME = SKILL_DIFFICULTY, |
||
34 | -- } |
||
35 | |||
36 | -- FOM_QuestFood = { }; |
||
37 | -- Has the following internal structure: |
||
38 | -- REALM_PLAYER = { |
||
39 | -- FOODNAME = QUANTITY_REQUIRED, |
||
40 | -- } |
||
41 | |||
42 | -- FOM_FoodQuality = { }; |
||
43 | -- Has the following internal structure: |
||
44 | -- REALM_PLAYER = { |
||
45 | -- PETNAME = { |
||
46 | -- FOODNAME = HAPPINESS, |
||
47 | -- } |
||
48 | -- } |
||
49 | |||
50 | -- Variables |
||
51 | FOM_State = { }; |
||
52 | FOM_State.InCombat = false; |
||
53 | FOM_State.IsAFK = false; |
||
54 | FOM_State.ShouldFeed = false; |
||
55 | FOM_LastWarning = 0; |
||
56 | |||
57 | FOM_LastFood = nil; |
||
58 | FOM_RealmPlayer = nil; |
||
59 | FOM_LastPetName = nil; |
||
60 | |||
61 | -- Anti-freeze code borrowed from ReagentInfo (in turn, from Quest-I-On): |
||
62 | -- keeps WoW from locking up if we try to scan the tradeskill window too fast. |
||
63 | FOM_TradeSkillLock = { }; |
||
64 | FOM_TradeSkillLock.Locked = false; |
||
65 | FOM_TradeSkillLock.EventTimer = 0; |
||
66 | FOM_TradeSkillLock.EventCooldown = 0; |
||
67 | FOM_TradeSkillLock.EventCooldownTime = 1; |
||
68 | |||
69 | |||
70 | -- State variable used to track required quantities of quest food when it's in more than one stack |
||
71 | FOM_Quantity = { }; |
||
72 | |||
73 | -- Remember how item IDs map to food names at runtime, but don't bloat long-term memory with it... |
||
74 | FOM_FoodIDsToNames = {}; |
||
75 | |||
76 | function FOM_FeedButton_OnClick() |
||
77 | if (arg1 == "RightButton") then |
||
78 | if FOM_OptionsFrame:IsVisible() then |
||
79 | HideUIPanel(FOM_OptionsFrame); |
||
80 | else |
||
81 | ShowUIPanel(FOM_OptionsFrame); |
||
82 | end |
||
83 | else |
||
84 | FOM_Feed(); |
||
85 | end |
||
86 | end |
||
87 | |||
88 | function FOM_FeedButton_OnEnter() |
||
89 | if ( PetFrameHappiness.tooltip ) then |
||
90 | GameTooltip:SetOwner(PetFrameHappiness, "ANCHOR_RIGHT"); |
||
91 | GameTooltip:SetText(PetFrameHappiness.tooltip); |
||
92 | if ( PetFrameHappiness.tooltipDamage ) then |
||
93 | GameTooltip:AddLine(PetFrameHappiness.tooltipDamage, "", 1, 1, 1); |
||
94 | end |
||
95 | if ( PetFrameHappiness.tooltipLoyalty ) then |
||
96 | GameTooltip:AddLine(PetFrameHappiness.tooltipLoyalty, "", 1, 1, 1); |
||
97 | end |
||
98 | GameTooltip:Show(); |
||
99 | end |
||
100 | end |
||
101 | |||
102 | function FOM_FeedButton_OnLeave() |
||
103 | GameTooltip:Hide(); |
||
104 | end |
||
105 | |||
106 | function FOM_OnLoad() |
||
107 | |||
108 | -- Register for Events |
||
109 | this:RegisterEvent("VARIABLES_LOADED"); |
||
110 | |||
111 | -- Register Slash Commands |
||
112 | SLASH_FEEDOMATIC1 = "/feedomatic"; |
||
113 | SLASH_FEEDOMATIC2 = "/fom"; |
||
114 | SLASH_FEEDOMATIC3 = "/feed"; |
||
115 | SLASH_FEEDOMATIC4 = "/petfeed"; -- Rauen's PetFeed compatibility |
||
116 | SLASH_FEEDOMATIC5 = "/pf"; |
||
117 | SlashCmdList["FEEDOMATIC"] = function(msg) |
||
118 | FOM_ChatCommandHandler(msg); |
||
119 | end |
||
120 | |||
121 | -- hook functions so we can manage per-pet saved food quality data |
||
122 | FOM_Original_PetRename = PetRename; |
||
123 | PetRename = FOM_PetRename; |
||
124 | FOM_Original_PetAbandon = PetAbandon; |
||
125 | PetAbandon = FOM_PetAbandon; |
||
126 | |||
127 | --GFWUtils.Debug = true; |
||
128 | |||
129 | GFWUtils.Print("Fizzwidget Feed-O-Matic "..FOM_VERSION.." initialized!"); |
||
130 | |||
131 | end |
||
132 | |||
133 | function FOM_CheckSetup() |
||
134 | |||
135 | _, realClass = UnitClass("player"); |
||
136 | if (realClass ~= "HUNTER") then return; end |
||
137 | |||
138 | if (FOM_RealmPlayer == nil) then |
||
139 | FOM_RealmPlayer = GetCVar("realmName") .. "." .. UnitName("player"); |
||
140 | end |
||
141 | local currentPetName = UnitName("pet"); |
||
142 | if (currentPetName and currentPetName ~= "" and currentPetName ~= UNKNOWNOBJECT) then |
||
143 | FOM_LastPetName = currentPetName; |
||
144 | end |
||
145 | |||
146 | if (FOM_FoodQuality == nil) then |
||
147 | FOM_FoodQuality = { }; |
||
148 | end |
||
149 | if (FOM_FoodQuality[FOM_RealmPlayer] == nil) then |
||
150 | FOM_FoodQuality[FOM_RealmPlayer] = { }; |
||
151 | end |
||
152 | if (FOM_LastPetName) then |
||
153 | if (FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] == nil) then |
||
154 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = { }; |
||
155 | end |
||
156 | end |
||
157 | |||
158 | end |
||
159 | |||
160 | function FOM_Tooltip(frame, name, link, source) |
||
161 | if (FOM_Config.Tooltip and name ~= nil and UnitExists("pet")) then |
||
162 | FOM_CheckSetup(); |
||
163 | |||
164 | local itemID = FOM_IDFromLink(link); |
||
165 | if (not FOM_IsInDiet(itemID)) then |
||
166 | return false; |
||
167 | end |
||
168 | |||
169 | local color; |
||
170 | local absoluteQuality = FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID]; |
||
171 | if (absoluteQuality == nil) then |
||
172 | color = HIGHLIGHT_FONT_COLOR; |
||
173 | frame:AddLine(string.format(FOM_QUALITY_UNKNOWN, FOM_LastPetName), color.r, color.g, color.b); |
||
174 | return true; |
||
175 | else |
||
176 | local currentQuality = absoluteQuality / UnitLevel("pet"); |
||
177 | if (currentQuality < 0) then |
||
178 | color = QuestDifficultyColor["trivial"]; |
||
179 | frame:AddLine(string.format(FOM_QUALITY_UNDER, FOM_LastPetName), color.r, color.g, color.b); |
||
180 | return true; |
||
181 | elseif (currentQuality == 0) then |
||
182 | color = QuestDifficultyColor["trivial"]; |
||
183 | frame:AddLine(string.format(FOM_QUALITY_MIGHT, FOM_LastPetName), color.r, color.g, color.b); |
||
184 | return true; |
||
185 | elseif (currentQuality <= 8) then |
||
186 | color = QuestDifficultyColor["standard"]; |
||
187 | frame:AddLine(string.format(FOM_QUALITY_WILL, FOM_LastPetName), color.r, color.g, color.b); |
||
188 | return true; |
||
189 | elseif (currentQuality <= 17) then |
||
190 | color = QuestDifficultyColor["difficult"]; |
||
191 | frame:AddLine(string.format(FOM_QUALITY_LIKE, FOM_LastPetName), color.r, color.g, color.b); |
||
192 | return true; |
||
193 | elseif (currentQuality <= 35) then |
||
194 | color = QuestDifficultyColor["verydifficult"]; |
||
195 | frame:AddLine(string.format(FOM_QUALITY_LOVE, FOM_LastPetName), color.r, color.g, color.b); |
||
196 | return true; |
||
197 | else |
||
198 | GFWUtils.DebugLog("Unexpected food quality level "..currentQuality); |
||
199 | return false; |
||
200 | end |
||
201 | end |
||
202 | end |
||
203 | end |
||
204 | |||
205 | function FOM_OnEvent(event, arg1) |
||
206 | |||
207 | -- Save Variables |
||
208 | if ( event == "VARIABLES_LOADED" ) then |
||
209 | |||
210 | _, realClass = UnitClass("player"); |
||
211 | if (realClass == "HUNTER") then |
||
212 | -- monitor status for whether we're able to feed |
||
213 | this:RegisterEvent("PET_ATTACK_START"); |
||
214 | this:RegisterEvent("PET_ATTACK_STOP"); |
||
215 | -- this:RegisterEvent("CHAT_MSG_SYSTEM"); |
||
216 | |||
217 | -- check your pet roster when at the stables so we don't bloat SavedVariables |
||
218 | this:RegisterEvent("PET_STABLE_SHOW"); |
||
219 | this:RegisterEvent("PET_STABLE_UPDATE"); |
||
220 | |||
221 | -- track whether foods are useful for Cooking |
||
222 | this:RegisterEvent("TRADE_SKILL_SHOW"); |
||
223 | this:RegisterEvent("TRADE_SKILL_UPDATE"); |
||
224 | |||
225 | -- figure out what happens when we try to feed pet (gain happiness, didn't like, etc) |
||
226 | this:RegisterEvent("CHAT_MSG_SPELL_TRADESKILLS"); |
||
227 | this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"); |
||
228 | this:RegisterEvent("UI_ERROR_MESSAGE"); |
||
229 | |||
230 | -- Events for trying to catch when the pet needs feeding |
||
231 | this:RegisterEvent("PET_BAR_SHOWGRID"); |
||
232 | this:RegisterEvent("PET_BAR_UPDATE"); |
||
233 | this:RegisterEvent("PET_UI_UPDATE"); |
||
234 | this:RegisterEvent("UNIT_HAPPINESS"); |
||
235 | |||
236 | FOM_FeedButton = CreateFrame("Button", "FOM_FeedButton", PetFrameHappiness); |
||
237 | FOM_FeedButton:SetAllPoints(PetFrameHappiness); |
||
238 | FOM_FeedButton:RegisterForClicks("LeftButtonUp", "RightButtonUp"); |
||
239 | FOM_FeedButton:SetScript("OnClick", FOM_FeedButton_OnClick); |
||
240 | FOM_FeedButton:SetScript("OnEnter", FOM_FeedButton_OnEnter); |
||
241 | FOM_FeedButton:SetScript("OnLeave", FOM_FeedButton_OnLeave); |
||
242 | |||
243 | table.insert(UISpecialFrames,"FOM_OptionsFrame"); |
||
244 | |||
245 | if (FOM_Config.Level == "happy") then |
||
246 | -- we've redefined the Level option and this setting is no loger available |
||
247 | FOM_Config.Level = "content"; |
||
248 | end |
||
249 | |||
250 | if (FOM_Config.Tooltip) then |
||
251 | GFWTooltip_AddCallback("GFW_FeedOMatic", FOM_Tooltip); |
||
252 | end |
||
253 | end |
||
254 | return; |
||
255 | |||
256 | elseif ( event == "PET_ATTACK_START" ) then |
||
257 | |||
258 | -- Set Flag |
||
259 | FOM_State.InCombat = true; |
||
260 | return; |
||
261 | |||
262 | elseif ( event == "PET_ATTACK_STOP" ) then |
||
263 | |||
264 | -- Remove Flag |
||
265 | FOM_State.InCombat = false; |
||
266 | |||
267 | elseif ( event == "CHAT_MSG_SPELL_TRADESKILLS" ) then |
||
268 | |||
269 | if (FOM_FEEDPET_LOG_FIRSTPERSON == nil) then |
||
270 | FOM_FEEDPET_LOG_FIRSTPERSON = GFWUtils.FormatToPattern(FEEDPET_LOG_FIRSTPERSON); |
||
271 | end |
||
272 | _, _, foodName = string.find(arg1, FOM_FEEDPET_LOG_FIRSTPERSON); |
||
273 | if (foodName and foodName ~= "") then |
||
274 | local foodID = GFWTable.KeyOf(FOM_FoodIDsToNames, foodName); |
||
275 | if (foodID == nil) then |
||
276 | local bag, slot = FOM_FindSpecificFood(foodName); |
||
277 | local foodLink = GetContainerItemLink(bag, slot); |
||
278 | foodID = FOM_IDFromLink(foodLink); |
||
279 | end |
||
280 | if (foodID) then |
||
281 | FOM_LastFood = GFWUtils.ItemLink(foodID); |
||
282 | GFWUtils.DebugLog("Manually fed "..FOM_LastFood); |
||
283 | end |
||
284 | end |
||
285 | return; |
||
286 | |||
287 | elseif ( event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" ) then |
||
288 | |||
289 | if (arg1 and FOM_HasFeedEffect()) then |
||
290 | if (FOM_POWERGAIN_OTHER == nil and POWERGAINSELFOTHER) then |
||
291 | FOM_POWERGAIN_OTHER = GFWUtils.FormatToPattern(POWERGAINSELFOTHER); |
||
292 | end |
||
293 | if (FOM_POWERGAIN_OTHER == nil and POWERGAIN_OTHER) then |
||
294 | FOM_POWERGAIN_OTHER = GFWUtils.FormatToPattern(POWERGAIN_OTHER); |
||
295 | end |
||
296 | if (FOM_POWERGAIN_OTHER == nil) then |
||
297 | GFWUtils.PrintOnce(GFWUtils.Red("Feed-O-Matic Error: ").. "Can't find parse pattern for pet happiness."); |
||
298 | return; |
||
299 | end |
||
300 | _, _, name, amount, powerType = string.find(arg1, FOM_POWERGAIN_OTHER); |
||
301 | local happiness; |
||
302 | if (name == UnitName("pet") and powerType == HAPPINESS_POINTS) then |
||
303 | happiness = tonumber(amount); |
||
304 | else |
||
305 | return; |
||
306 | end |
||
307 | |||
308 | if (FOM_LastFood and happiness > 0) then |
||
309 | FOM_CheckSetup(); |
||
310 | local itemID = FOM_IDFromLink(FOM_LastFood); |
||
311 | local knownQuality = FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID]; |
||
312 | local quality = happiness * UnitLevel("pet"); |
||
313 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID] = math.max((knownQuality or 0), quality); |
||
314 | FOM_LastFood = nil; |
||
315 | end |
||
316 | end |
||
317 | return; |
||
318 | |||
319 | elseif ( event == "UI_ERROR_MESSAGE" ) then |
||
320 | |||
321 | if (arg1 and string.find(arg1, SPELL_FAILED_FOOD_LOWLEVEL)) then |
||
322 | |||
323 | FOM_CheckSetup(); |
||
324 | |||
325 | if not (FOM_LastFood == nil) then |
||
326 | local itemID = FOM_IDFromLink(FOM_LastFood); |
||
327 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID] = -1; |
||
328 | FOM_LastFood = nil; |
||
329 | if ( FOM_Config.Alert == "chat") then |
||
330 | GFWUtils.Print(string.format(FOM_FEEDING_EAT_ANOTHER, UnitName("pet"))); |
||
331 | elseif ( FOM_Config.Alert == "emote") then |
||
332 | SendChatMessage(string.format(FOM_FEEDING_FEED_ANOTHER, UnitName("pet")), "EMOTE"); |
||
333 | end |
||
334 | return; |
||
335 | end |
||
336 | |||
337 | elseif (arg1 and string.find(arg1, SPELL_FAILED_WRONG_PET_FOOD)) then |
||
338 | |||
339 | FOM_CheckSetup(); |
||
340 | |||
341 | if (FOM_LastFood) then |
||
342 | |||
343 | if ( FOM_Config.Alert == "chat") then |
||
344 | GFWUtils.Print(string.format(FOM_FEEDING_EAT_ANOTHER, UnitName("pet"))); |
||
345 | elseif ( FOM_Config.Alert == "emote") then |
||
346 | SendChatMessage(string.format(FOM_FEEDING_FEED_ANOTHER, UnitName("pet")), "EMOTE"); |
||
347 | end |
||
348 | -- remove from quality tracking |
||
349 | local itemID = FOM_IDFromLink(FOM_LastFood); |
||
350 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID] = nil; |
||
351 | |||
352 | -- remove from diet |
||
353 | local dietList = {GetPetFoodTypes()}; |
||
354 | for _, diet in dietList do |
||
355 | if ( FOM_RemoveFood(string.lower(diet), itemID) ) then |
||
356 | local capDiet = string.upper(string.sub(diet, 1, 1)) .. string.sub(diet, 2); -- print a nicely capitalized version |
||
357 | GFWUtils.Print("Removed "..FOM_LastFood.." from "..GFWUtils.Hilite(capDiet).." list."); |
||
358 | end |
||
359 | end |
||
360 | FOM_LastFood = nil; |
||
361 | return; |
||
362 | end |
||
363 | end |
||
364 | return; |
||
365 | |||
366 | elseif (event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_UPDATE") then |
||
367 | if (GetTradeSkillLine() ~= nil and GetTradeSkillLine() == FOM_CookingSpellName()) then |
||
368 | if (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3) then |
||
369 | -- Update Cooking reagents list so we can avoid consuming food we could skillup from. |
||
370 | if (FOM_RealmPlayer == nil) then |
||
371 | FOM_RealmPlayer = GetCVar("realmName") .. "." .. UnitName("player"); |
||
372 | end |
||
373 | if (FOM_Cooking == nil) then |
||
374 | FOM_Cooking = { }; |
||
375 | end |
||
376 | if (FOM_Cooking[FOM_RealmPlayer] == nil) then |
||
377 | FOM_Cooking[FOM_RealmPlayer] = { }; |
||
378 | end |
||
379 | if (FOM_Cooking ~= nil and FOM_Cooking[FOM_RealmPlayer] ~= nil and TradeSkillFrame and TradeSkillFrame:IsVisible() and not FOM_TradeSkillLock.Locked) then |
||
380 | -- This prevents further update events from being handled if we're already processing one. |
||
381 | -- This is done to prevent the game from freezing under certain conditions. |
||
382 | FOM_TradeSkillLock.Locked = true; |
||
383 | |||
384 | for i=1, GetNumTradeSkills() do |
||
385 | local itemName, type, _, _ = GetTradeSkillInfo(i); |
||
386 | if (type ~= "header") then |
||
387 | for j=1, GetTradeSkillNumReagents(i) do |
||
388 | local reagentLink = GetTradeSkillReagentItemLink(i, j); |
||
389 | local itemID = FOM_IDFromLink(reagentLink); |
||
390 | |||
391 | if (itemID and FOM_IsKnownFood(itemID)) then |
||
392 | if (FOM_Cooking[FOM_RealmPlayer][itemID] == nil) then |
||
393 | FOM_Cooking[FOM_RealmPlayer][itemID] = FOM_DifficultyToNum(type); |
||
394 | else |
||
395 | FOM_Cooking[FOM_RealmPlayer][itemID] = max(FOM_Cooking[FOM_RealmPlayer][itemID], FOM_DifficultyToNum(type)); |
||
396 | end |
||
397 | end |
||
398 | end |
||
399 | end |
||
400 | end |
||
401 | end |
||
402 | end |
||
403 | end |
||
404 | return; |
||
405 | |||
406 | elseif (event == "PET_STABLE_SHOW" or event == "PET_STABLE_UPDATE") then |
||
407 | |||
408 | -- clean up the FOM_FoodQuality sub-tables in case we missed you abandoning a pet |
||
409 | FOM_CheckSetup(); |
||
410 | local stabledPetNames = {}; |
||
411 | for petIndex = 0, 2 do |
||
412 | local _, petName, _, _, _ = GetStablePetInfo(petIndex); |
||
413 | if (petName) then |
||
414 | table.insert(stabledPetNames, petName); |
||
415 | end |
||
416 | end |
||
417 | local orphanedPetNames = {}; |
||
418 | for savedPetName in FOM_FoodQuality[FOM_RealmPlayer] do |
||
419 | if (stabledPetNames == nil) then |
||
420 | GFWUtils.DebugLog("stabledPetNames == nil"); |
||
421 | end |
||
422 | if (stabledPetNames ~= nil and GFWTable.IndexOf(stabledPetNames, savedPetName) == 0) then |
||
423 | table.insert(orphanedPetNames, savedPetName); |
||
424 | end |
||
425 | end |
||
426 | for _, orphanedPet in orphanedPetNames do |
||
427 | FOM_FoodQuality[FOM_RealmPlayer][orphanedPet] = nil; |
||
428 | end |
||
429 | return; |
||
430 | |||
431 | elseif (FOM_Config.Level) then |
||
432 | FOM_CheckHappiness(); |
||
433 | end |
||
434 | |||
435 | end |
||
436 | |||
437 | -- Update our list of quest objectives so we can avoid consuming food we want to accumulate for a quest. |
||
438 | function FOM_ScanQuests() |
||
439 | FOM_QuestFood = nil; |
||
440 | for questNum=1, GetNumQuestLogEntries() do |
||
441 | local QText, level, questTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(questNum); |
||
442 | if (not isHeader) then |
||
443 | for objectiveNum=1, GetNumQuestLeaderBoards(questNum) do |
||
444 | local text, type, finished = GetQuestLogLeaderBoard(objectiveNum, questNum); |
||
445 | if (text ~= nil and strlen(text) > 0) then |
||
446 | local _, _, objectiveName, numCurrent, numRequired = string.find(text, "(.*): (%d+)/(%d+)"); |
||
447 | if (FOM_IsKnownFood(objectiveName)) then |
||
448 | if (FOM_QuestFood == nil) then |
||
449 | FOM_QuestFood = { }; |
||
450 | end |
||
451 | if (FOM_QuestFood[FOM_RealmPlayer] == nil) then |
||
452 | FOM_QuestFood[FOM_RealmPlayer] = { }; |
||
453 | end |
||
454 | |||
455 | if (FOM_QuestFood[FOM_RealmPlayer][objectiveName] == nil) then |
||
456 | FOM_QuestFood[FOM_RealmPlayer][objectiveName] = tonumber(numRequired); |
||
457 | else |
||
458 | FOM_QuestFood[FOM_RealmPlayer][objectiveName] = max(FOM_QuestFood[FOM_RealmPlayer][objectiveName], tonumber(numRequired)); |
||
459 | end |
||
460 | end |
||
461 | end |
||
462 | end |
||
463 | end |
||
464 | end |
||
465 | end |
||
466 | |||
467 | function FOM_DifficultyToNum(level) |
||
468 | if (level == "optimal" or level == "orange") then |
||
469 | return 3; |
||
470 | elseif (level == "medium" or level == "yellow") then |
||
471 | return 2; |
||
472 | elseif (level == "easy" or level == "green") then |
||
473 | return 1; |
||
474 | elseif (level == "trivial" or level == "gray" or level == "grey") then |
||
475 | return 1; |
||
476 | else -- bad input |
||
477 | return nil; |
||
478 | end |
||
479 | end |
||
480 | |||
481 | function FOM_OnUpdate(elapsed) |
||
482 | |||
483 | _, realClass = UnitClass("player"); |
||
484 | if (realClass ~= "HUNTER") then return; end |
||
485 | |||
486 | -- If it's been more than a second since our last tradeskill update, |
||
487 | -- we can allow the event to process again. |
||
488 | FOM_TradeSkillLock.EventTimer = FOM_TradeSkillLock.EventTimer + elapsed; |
||
489 | if (FOM_TradeSkillLock.Locked) then |
||
490 | FOM_TradeSkillLock.EventCooldown = FOM_TradeSkillLock.EventCooldown + elapsed; |
||
491 | if (FOM_TradeSkillLock.EventCooldown > FOM_TradeSkillLock.EventCooldownTime) then |
||
492 | |||
493 | FOM_TradeSkillLock.EventCooldown = 0; |
||
494 | FOM_TradeSkillLock.Locked = false; |
||
495 | end |
||
496 | end |
||
497 | |||
498 | --GFWUtils.Debug = true; |
||
499 | |||
500 | if (FOM_State.ShouldFeed and FOM_Config.IconWarning and PetFrameHappiness) then |
||
501 | if (PetFrameHappiness:IsVisible() and PetFrameHappiness:GetAlpha() == 1) then |
||
502 | FOM_FadeOut(); |
||
503 | end |
||
504 | end |
||
505 | end |
||
506 | |||
507 | function FOM_FadeOut() |
||
508 | local fadeInfo = {}; |
||
509 | fadeInfo.mode = "OUT"; |
||
510 | fadeInfo.timeToFade = 0.5; |
||
511 | fadeInfo.finishedFunc = FOM_FadeIn; |
||
512 | UIFrameFade(PetFrameHappiness, fadeInfo); |
||
513 | end |
||
514 | |||
515 | --hack since a frame can't have a reference to itself in it |
||
516 | function FOM_FadeIn() |
||
517 | UIFrameFadeIn(PetFrameHappiness, 0.5); |
||
518 | end |
||
519 | |||
520 | function FOM_CanFeed() |
||
521 | if ( not UnitExists("pet") ) then |
||
522 | GFWUtils.DebugLog("Can't feed; pet doesn't exist."); |
||
523 | return false; |
||
524 | end |
||
525 | if ( UnitHealth("pet") <= 0 ) then |
||
526 | GFWUtils.DebugLog("Can't feed; pet is dead."); |
||
527 | return false; |
||
528 | end |
||
529 | if ( UnitHealth("player") <= 0 ) then |
||
530 | GFWUtils.DebugLog("Can't feed; I'm dead."); |
||
531 | return false; |
||
532 | end |
||
533 | if ( CastingBarFrameStatusBar:IsVisible() ) then |
||
534 | GFWUtils.DebugLog("Can't feed; casting a spell / tradeksill."); |
||
535 | return false; |
||
536 | end |
||
537 | if ( UnitOnTaxi("player") ) then |
||
538 | GFWUtils.DebugLog("Can't feed; flying."); |
||
539 | return false; |
||
540 | end |
||
541 | if ( FOM_State.InCombat ) or ( PlayerFrame.inCombat ) then |
||
542 | GFWUtils.DebugLog("Can't feed; in combat."); |
||
543 | return false; |
||
544 | end |
||
545 | if ( LootFrame:IsVisible() ) then |
||
546 | GFWUtils.DebugLog("Shouldn't feed; loot window is open."); |
||
547 | return false; |
||
548 | end |
||
549 | |||
550 | |||
551 | local buff, buffIndex; |
||
552 | local dontFeedBuffTextures = { |
||
553 | "Interface\\Icons\\Ability_Ambush", -- NE Shadowmeld (maybe not unique buff icon?) |
||
554 | "Interface\\Icons\\Ability_Rogue_FeignDeath", -- Feign Death |
||
555 | "Interface\\Icons\\INV_Drink_07", -- drinking |
||
556 | "Interface\\Icons\\INV_Misc_Fork&Knife", -- eating |
||
557 | }; |
||
558 | local mountTextureSubStrings = { |
||
559 | "Ability_Mount", |
||
560 | "INV_Misc_Foot_Kodo", |
||
561 | }; |
||
562 | for buffIndex=0, 15 do |
||
563 | local buff = GetPlayerBuffTexture(buffIndex); |
||
564 | if ( buff ~= nil) then |
||
565 | for _, buffTexture in dontFeedBuffTextures do |
||
566 | if ( buff == buffTexture ) then |
||
567 | GFWUtils.DebugLog("Can't feed; currently, eating, drinking, or feigning death."); |
||
568 | return false; |
||
569 | end |
||
570 | end |
||
571 | if ( UnitLevel("player") >= 40 ) then |
||
572 | for _, buffTexture in mountTextureSubStrings do |
||
573 | if ( string.find(buff, buffTexture) ) then |
||
574 | FOMTooltip:SetUnitBuff("player", buffIndex+1); |
||
575 | local msg = FOMTooltipTextLeft1:GetText(); |
||
576 | if (msg ~= nil) then |
||
577 | msg = string.lower(msg); |
||
578 | for _, mountName in FOM_MOUNT_NAME_SUBSTRINGS do |
||
579 | if (string.find(msg, mountName)) then |
||
580 | GFWUtils.DebugLog("Can't feed; mounted."); |
||
581 | return false; |
||
582 | end |
||
583 | end |
||
584 | end |
||
585 | end |
||
586 | end |
||
587 | end |
||
588 | end |
||
589 | end |
||
590 | |||
591 | return true; |
||
592 | end |
||
593 | |||
594 | function FOM_ChatCommandHandler(msg) |
||
595 | |||
596 | if ( msg == "" ) then |
||
597 | if FOM_OptionsFrame:IsVisible() then |
||
598 | HideUIPanel(FOM_OptionsFrame); |
||
599 | else |
||
600 | ShowUIPanel(FOM_OptionsFrame); |
||
601 | end |
||
602 | return; |
||
603 | end |
||
604 | |||
605 | -- Check for Pet (we don't really need one for most of our chat commands, but we conveniently use its name.) |
||
606 | if ( UnitExists("pet") ) then |
||
607 | petName = UnitName("pet"); |
||
608 | if (GetLocale() ~= "enUS") then |
||
609 | if (FOM_LocaleInfo == nil) then |
||
610 | FOM_LocaleInfo = {}; |
||
611 | end |
||
612 | FOM_LocaleInfo[UnitCreatureFamily("pet")] = {GetPetFoodTypes()}; |
||
613 | end |
||
614 | else |
||
615 | petName = "Your pet"; |
||
616 | end |
||
617 | |||
618 | -- Print Help |
||
619 | if ( msg == "help" ) or ( msg == "" ) then |
||
620 | GFWUtils.Print("Fizzwidget Feed-O-Matic "..FOM_VERSION..":"); |
||
621 | GFWUtils.Print("/feedomatic /fom <command>"); |
||
622 | GFWUtils.Print("- "..GFWUtils.Hilite("help").." - Print this helplist."); |
||
623 | GFWUtils.Print("- "..GFWUtils.Hilite("status").." - Check current settings."); |
||
624 | GFWUtils.Print("- "..GFWUtils.Hilite("reset").." - Reset to default settings."); |
||
625 | GFWUtils.Print("- "..GFWUtils.Hilite("alert chat").." | "..GFWUtils.Hilite("emote").." | "..GFWUtils.Hilite("off").." - Alert via chat window or emote channel when feeding."); |
||
626 | GFWUtils.Print("- "..GFWUtils.Hilite("level content").." | "..GFWUtils.Hilite("happy").." | "..GFWUtils.Hilite("off").." - Provide an extra reminder to feed your pet when happiness is below this level."); |
||
627 | GFWUtils.Print("- "..GFWUtils.Hilite("saveforcook orange").." | "..GFWUtils.Hilite("yellow").." | "..GFWUtils.Hilite("green").." | "..GFWUtils.Hilite("gray").." | "..GFWUtils.Hilite("off").." - Avoid foods used in cooking recipes (based on their difficulty)."); |
||
628 | GFWUtils.Print("- "..GFWUtils.Hilite("savequest on").." | "..GFWUtils.Hilite("off").." - Avoid foods you need to collect for a quest."); |
||
629 | GFWUtils.Print("- "..GFWUtils.Hilite("savebonus on").." | "..GFWUtils.Hilite("off").." - Avoid foods which have bonus effects."); |
||
630 | GFWUtils.Print("- "..GFWUtils.Hilite("fallback on").." | "..GFWUtils.Hilite("off").." - Fall back to foods we'd normally avoid if no other food is available."); |
||
631 | GFWUtils.Print("- "..GFWUtils.Hilite("keepopen <number>").." - Set when to prefer smaller stacks of food versus evaluating food based on quality. Specify "..GFWUtils.Hilite("off").." instead of a number to always select foods by quality, or "..GFWUtils.Hilite("max").." to always prefer smaller stacks."); |
||
632 | GFWUtils.Print("- "..GFWUtils.Hilite("quality high").." | "..GFWUtils.Hilite("low").." - Set whether to prefer foods that give your pet more happiness faster or less happiness more slowly."); |
||
633 | GFWUtils.Print("- "..GFWUtils.Hilite("tooltip on").." | "..GFWUtils.Hilite("off").." - Identifies and rates pet foods in their tooltips."); |
||
634 | GFWUtils.Print("- "..GFWUtils.Hilite("feed").." - Feed your pet (automatically finds an appropriate food)."); |
||
635 | GFWUtils.Print("- "..GFWUtils.Hilite("feed <name>").." - Feed your pet a specific food."); |
||
636 | GFWUtils.Print("- "..GFWUtils.Hilite("add <diet> <name>").." - Add food to list."); |
||
637 | GFWUtils.Print("- "..GFWUtils.Hilite("remove <diet> <name>").." - Remove food from list."); |
||
638 | GFWUtils.Print("- "..GFWUtils.Hilite("show <diet>").." - Show food list."); |
||
639 | return; |
||
640 | end |
||
641 | |||
642 | if ( msg == "version" ) then |
||
643 | GFWUtils.Print("Fizzwidget Feed-O-Matic "..FOM_VERSION..":"); |
||
644 | return; |
||
645 | end |
||
646 | |||
647 | -- Check Status |
||
648 | if ( msg == "status" ) then |
||
649 | if (FOM_Config.Level) then |
||
650 | GFWUtils.Print("Feed-O-Matic will help remind you to feed your pet when he's "..GFWUtils.Hilite(FOM_Config.Level).."."); |
||
651 | else |
||
652 | GFWUtils.Print("Feed-O-Matic will "..GFWUtils.Hilite("not").." help remind you when to feed your pet."); |
||
653 | end |
||
654 | |||
655 | if (FOM_Config.KeepOpenSlots < MAX_KEEPOPEN_SLOTS) then |
||
656 | |||
657 | if (FOM_Config.PreferHigherQuality) then |
||
658 | GFWUtils.Print("Feed-O-Matic will prefer to use higher quality foods first."); |
||
659 | else |
||
660 | GFWUtils.Print("Feed-O-Matic will prefer to use lower quality foods first."); |
||
661 | end |
||
662 | |||
663 | if (FOM_Config.KeepOpenSlots == 0) then |
||
664 | GFWUtils.Print("Feed-O-Matic will look first at food quality when determining what to feed to your pet."); |
||
665 | else |
||
666 | GFWUtils.Print("If fewer than "..GFWUtils.Hilite(FOM_Config.KeepOpenSlots).." spaces are open in your inventory, Feed-O-Matic will prefer smaller stacks of food regardless of quality."); |
||
667 | end |
||
668 | |||
669 | else |
||
670 | GFWUtils.Print("Feed-O-Matic will always prefer smaller stacks of food regardless of quality."); |
||
671 | end |
||
672 | |||
673 | if (FOM_Config.Alert == "emote") then |
||
674 | GFWUtils.Print("You will automatically emote when feeding "..petName.."."); |
||
675 | elseif (FOM_Config.Alert == "chat") then |
||
676 | GFWUtils.Print("Feed-O-Matic will notify you in chat when feeding "..petName.."."); |
||
677 | else |
||
678 | GFWUtils.Print("There will be no alert when feeding "..petName.."."); |
||
679 | end |
||
680 | |||
681 | if (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3) then |
||
682 | if (FOM_Config.SaveForCookingLevel == 3) then |
||
683 | level = "orange"; |
||
684 | elseif (FOM_Config.SaveForCookingLevel == 2) then |
||
685 | level = "yellow"; |
||
686 | elseif (FOM_Config.SaveForCookingLevel == 1) then |
||
687 | level = "green"; |
||
688 | elseif (FOM_Config.SaveForCookingLevel == 0) then |
||
689 | level = "gray"; |
||
690 | end |
||
691 | GFWUtils.Print("Feed-O-Matic will avoid foods used in "..GFWUtils.Hilite(level).." or higher Cooking recipes."); |
||
692 | else |
||
693 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're used in Cooking."); |
||
694 | end |
||
695 | |||
696 | if (FOM_Config.AvoidQuestFood) then |
||
697 | GFWUtils.Print("Feed-O-Matic will avoid foods you need to collect for quests."); |
||
698 | else |
||
699 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're needed for quests."); |
||
700 | end |
||
701 | if (FOM_Config.AvoidBonusFood) then |
||
702 | GFWUtils.Print("Feed-O-Matic will avoid foods that have an additional bonus effect when eaten by a player."); |
||
703 | else |
||
704 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they have bonus effects."); |
||
705 | end |
||
706 | if (FOM_Config.Fallback) then |
||
707 | GFWUtils.Print("Feed-O-Matic will fall back to food it would otherwise avoid if no other food is available."); |
||
708 | else |
||
709 | GFWUtils.Print("Feed-O-Matic will not feed your pet if the only foods available are foods you'd prefer to avoid feeding."); |
||
710 | end |
||
711 | if (FOM_Config.Tooltip) then |
||
712 | GFWUtils.Print("Adding food quality information to tooltips for foods your current pet can eat."); |
||
713 | else |
||
714 | GFWUtils.Print("Not adding information to item tooltips."); |
||
715 | end |
||
716 | return; |
||
717 | end |
||
718 | |||
719 | -- Reset Variables |
||
720 | if ( msg == "reset" ) then |
||
721 | FOM_Config = FOM_Config_Default; |
||
722 | FOM_Cooking = nil; |
||
723 | FOM_FoodQuality = nil; |
||
724 | FOM_AddedFoods = nil; |
||
725 | FOM_RemovedFoods = nil; |
||
726 | FOM_QuestFood = nil; |
||
727 | GFWUtils.Print("Feed-O-Matic configuration reset."); |
||
728 | FOM_ChatCommandHandler("status"); |
||
729 | return; |
||
730 | end |
||
731 | |||
732 | -- Turn automatic feeding On |
||
733 | if ( msg == "on" ) then |
||
734 | GFWUtils.Print("Automatic feeding is no longer available due to changes in the WoW client as of Patch 1.10."); |
||
735 | return; |
||
736 | end |
||
737 | |||
738 | local _, _, cmd, option = string.find(msg, "(%w+) (%w+)"); |
||
739 | |||
740 | -- Toggle Alert |
||
741 | if ( cmd == "alert" ) then |
||
742 | |||
743 | if (option == "emote") then |
||
744 | FOM_Config.Alert = "emote"; |
||
745 | GFWUtils.Print("You will automatically emote when feeding "..petName.."."); |
||
746 | elseif (option == "chat") then |
||
747 | FOM_Config.Alert = "chat"; |
||
748 | GFWUtils.Print("Feed-O-Matic will notify you in chat when feeding "..petName.."."); |
||
749 | elseif (option == "off") then |
||
750 | FOM_Config.Alert = nil; |
||
751 | GFWUtils.Print("There will be no alert when feeding "..petName.."."); |
||
752 | else |
||
753 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic alert chat").." | "..GFWUtils.Hilite("emote").." | "..GFWUtils.Hilite("off")); |
||
754 | end |
||
755 | return; |
||
756 | end |
||
757 | |||
758 | -- Set Happiness Level |
||
759 | if ( cmd == "level" ) then |
||
760 | if ( option == "content" ) then |
||
761 | FOM_Config.Level = "content"; |
||
762 | elseif ( option == "happy" ) then |
||
763 | FOM_Config.Level = "happy"; |
||
764 | elseif ( option == "debug" ) then |
||
765 | FOM_Config.Level = "debug"; |
||
766 | else |
||
767 | FOM_Config.Level = nil; |
||
768 | end |
||
769 | if (FOM_Config.Level) then |
||
770 | GFWUtils.Print("Feed-O-Matic will help remind you to feed your pet when he's less than "..GFWUtils.Hilite(FOM_Config.Level).."."); |
||
771 | FOM_CheckHappiness(); |
||
772 | else |
||
773 | GFWUtils.Print("Feed-O-Matic will "..GFWUtils.Hilite("not").." help remind you when to feed your pet."); |
||
774 | FOM_Status.ShouldFeed = nil; |
||
775 | end |
||
776 | return; |
||
777 | end |
||
778 | |||
779 | -- Set Cooking recipe level |
||
780 | if ( cmd == "saveforcook" ) then |
||
781 | local level = option; |
||
782 | if (level ~= nil) then |
||
783 | local levelNum = FOM_DifficultyToNum(level); |
||
784 | if (levelNum ~= nil) then |
||
785 | FOM_Config.SaveForCookingLevel = levelNum; |
||
786 | FOM_Config.AvoidUsefulFood = true; |
||
787 | GFWUtils.Print("Feed-O-Matic will avoid foods used in "..GFWUtils.Hilite(level).." or higher Cooking recipes. You'll need to open your Cooking window for Feed-O-Matic to cache information about what recipes you know."); |
||
788 | return; |
||
789 | elseif (level == "off") then |
||
790 | FOM_Config.SaveForCookingLevel = 4; |
||
791 | if (not FOM_Config.AvoidQuestFood and not FOM_Config.Avoid9) then |
||
792 | FOM_Config.AvoidUsefulFood = false; |
||
793 | end |
||
794 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're used in Cooking."); |
||
795 | return; |
||
796 | end |
||
797 | end |
||
798 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic saveforcook orange").." | "..GFWUtils.Hilite("yellow").." | "..GFWUtils.Hilite("green").." | "..GFWUtils.Hilite("gray").." | "..GFWUtils.Hilite("off")); |
||
799 | return; |
||
800 | end |
||
801 | |||
802 | -- Set avoiding food with bonuses |
||
803 | if ( cmd == "savequest" ) then |
||
804 | if (option == "on") then |
||
805 | FOM_Config.AvoidQuestFood = true; |
||
806 | FOM_Config.AvoidUsefulFood = true; |
||
807 | FOM_ScanQuests(); |
||
808 | GFWUtils.Print("Feed-O-Matic will avoid foods you need to collect for quests."); |
||
809 | elseif (option == "off") then |
||
810 | FOM_Config.AvoidQuestFood = true; |
||
811 | if not (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3 and not FOM_Config.AvoidBonusFood) then |
||
812 | FOM_Config.AvoidUsefulFood = false; |
||
813 | end |
||
814 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're needed for quests."); |
||
815 | else |
||
816 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic savequest on").." | "..GFWUtils.Hilite("off")); |
||
817 | end |
||
818 | return; |
||
819 | end |
||
820 | |||
821 | -- Set avoiding quest-objective food |
||
822 | if ( cmd == "savebonus" ) then |
||
823 | if (option == "on") then |
||
824 | FOM_Config.AvoidBonusFood = true; |
||
825 | FOM_Config.AvoidUsefulFood = true; |
||
826 | GFWUtils.Print("Feed-O-Matic will avoid foods that have an additional bonus effect when eaten by a player."); |
||
827 | elseif (option == "off") then |
||
828 | FOM_Config.AvoidBonusFood = true; |
||
829 | if not (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3 and not FOM_Config.AvoidQuestFood) then |
||
830 | FOM_Config.AvoidUsefulFood = false; |
||
831 | end |
||
832 | GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they have bonus effects."); |
||
833 | else |
||
834 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic savebonus on").." | "..GFWUtils.Hilite("off")); |
||
835 | end |
||
836 | return; |
||
837 | end |
||
838 | |||
839 | if ( cmd == "fallback" ) then |
||
840 | if (option == "on") then |
||
841 | FOM_Config.Fallback = true; |
||
842 | GFWUtils.Print("Feed-O-Matic will fall back to food it would otherwise avoid if no other food is available."); |
||
843 | elseif (option == "off") then |
||
844 | FOM_Config.Fallback = false; |
||
845 | GFWUtils.Print("Feed-O-Matic will not feed your pet if the only foods available are foods you'd prefer to avoid feeding."); |
||
846 | else |
||
847 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic fallback on").." | "..GFWUtils.Hilite("off")); |
||
848 | end |
||
849 | return; |
||
850 | end |
||
851 | |||
852 | if ( cmd == "tooltip" ) then |
||
853 | if (option == "on") then |
||
854 | FOM_Config.Tooltip = true; |
||
855 | GFWTooltip_AddCallback("GFW_FeedOMatic", FOM_Tooltip); |
||
856 | GFWUtils.Print("Adding food quality information to tooltips for foods your current pet can eat."); |
||
857 | elseif (option == "off") then |
||
858 | FOM_Config.Tooltip = false; |
||
859 | GFWUtils.Print("Not adding information to item tooltips."); |
||
860 | else |
||
861 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic tooltip on").." | "..GFWUtils.Hilite("off")); |
||
862 | end |
||
863 | return; |
||
864 | end |
||
865 | |||
866 | -- Set quality sorting direction |
||
867 | if ( cmd == "quality" ) then |
||
868 | if (option == "high") then |
||
869 | FOM_Config.PreferHigherQuality = true; |
||
870 | GFWUtils.Print("Feed-O-Matic will prefer to use higher quality foods first."); |
||
871 | elseif (option == "low") then |
||
872 | FOM_Config.PreferHigherQuality = false; |
||
873 | GFWUtils.Print("Feed-O-Matic will prefer to use lower quality foods first."); |
||
874 | else |
||
875 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic quality high").." | "..GFWUtils.Hilite("low")); |
||
876 | end |
||
877 | return; |
||
878 | end |
||
879 | |||
880 | -- Set inventory management threshold |
||
881 | if ( cmd == "keepopen" ) then |
||
882 | if (option == "off" or option == "none") then |
||
883 | newNum = 0; |
||
884 | elseif (option == "max") then |
||
885 | newNum = MAX_KEEPOPEN_SLOTS; |
||
886 | else |
||
887 | newNum = tonumber(option); |
||
888 | end |
||
889 | if (newNum == nil) then |
||
890 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic keepopen <number>")); |
||
891 | return; |
||
892 | end |
||
893 | FOM_Config.KeepOpenSlots = newNum; |
||
894 | GFWUtils.Print("Feed-O-Matic will try to keep at least "..GFWUtils.Hilite(FOM_Config.KeepOpenSlots).." spaces open in your inventory when looking for food."); |
||
895 | return; |
||
896 | end |
||
897 | |||
898 | -- Feed Pet |
||
899 | local _, _, cmd, foodString = string.find(msg, "(%w+) *(.*)"); |
||
900 | if ( cmd == "feed" ) then |
||
901 | if (foodString == "") then |
||
902 | FOM_Feed(nil); -- automatically find a food and feed it |
||
903 | else |
||
904 | local inputFoods = { }; |
||
905 | for itemLink in string.gfind(foodString, "%[[%w%s:()\"'-]+%]") do |
||
906 | local _, _, foodName = string.find(itemLink, "^%[([%w%s:()\"'-]+)%]$"); |
||
907 | table.insert(inputFoods, foodName); |
||
908 | end |
||
909 | if (table.getn(inputFoods) == 0) then |
||
910 | table.insert(inputFoods, foodString); -- if no item links, treat whole input line as one food's name |
||
911 | end |
||
912 | |||
913 | for _, food in inputFoods do |
||
914 | FOM_Feed(food); |
||
915 | end |
||
916 | end |
||
917 | return; |
||
918 | end |
||
919 | |||
920 | local _, _, cmd, diet, foodString = string.find(msg, "(%w+) (%w+) *(.*)"); |
||
921 | if ( cmd == "add" or cmd == "remove" or cmd == "show" or cmd == "list" ) then |
||
922 | |||
923 | diet = string.lower(diet); -- let's be case insensitive |
||
924 | if ( FOM_Foods[diet] == nil and diet ~= FOM_DIET_ALL) then |
||
925 | local usageString = "Usage: "..GFWUtils.Hilite("/feedomatic "..cmd..FOM_DIET_MEAT).." | "..GFWUtils.Hilite(FOM_DIET_FISH).." | "..GFWUtils.Hilite(FOM_DIET_BREAD).." | "..GFWUtils.Hilite(FOM_DIET_CHEESE).." | "..GFWUtils.Hilite(FOM_DIET_FRUIT).." | "..GFWUtils.Hilite(FOM_DIET_FUNGUS).." | "..GFWUtils.Hilite(FOM_DIET_BONUS) |
||
926 | if (cmd ~= "show" and cmd ~= "list") then |
||
927 | usageString = usageString.." <item link>."; |
||
928 | end |
||
929 | GFWUtils.Print(usageString); |
||
930 | return; |
||
931 | end |
||
932 | |||
933 | if (cmd == "show" or cmd == "list") then |
||
934 | if ( diet == FOM_DIET_ALL ) then |
||
935 | diets = {FOM_DIET_MEAT, FOM_DIET_FISH, FOM_DIET_BREAD, FOM_DIET_CHEESE, FOM_DIET_FRUIT, FOM_DIET_FUNGUS, FOM_DIET_BONUS}; |
||
936 | else |
||
937 | diets = {diet}; |
||
938 | end |
||
939 | |||
940 | for _, aDiet in diets do |
||
941 | local capDiet = string.upper(string.sub(aDiet, 1, 1)) .. string.sub(aDiet, 2); -- print a nicely capitalized version |
||
942 | GFWUtils.Print("Feed-O-Matic "..GFWUtils.Hilite(capDiet).." List:"); |
||
943 | local dietFoods = FOM_Foods[aDiet]; |
||
944 | if (FOM_AddedFoods ~= nil and FOM_AddedFoods[aDiet] ~= nil) then |
||
945 | dietFoods = GFWTable.Merge(dietFoods, FOM_AddedFoods[aDiet]); |
||
946 | end |
||
947 | if (FOM_RemovedFoods ~= nil and FOM_RemovedFoods[aDiet] ~= nil) then |
||
948 | dietFoods = GFWTable.Subtract(dietFoods, FOM_RemovedFoods[aDiet]); |
||
949 | end |
||
950 | table.sort(dietFoods); |
||
951 | for _, food in dietFoods do |
||
952 | local foodName = GetItemInfo(food); |
||
953 | if (foodName) then |
||
954 | if (FOM_FoodIDsToNames == nil) then |
||
955 | FOM_FoodIDsToNames = {}; |
||
956 | end |
||
957 | FOM_FoodIDsToNames[food] = foodName; |
||
958 | GFWUtils.Print(GFWUtils.Hilite(" - ")..foodName); |
||
959 | else |
||
960 | GFWUtils.Print(GFWUtils.Hilite(" - ").."item id "..food.." (name not available)"); |
||
961 | end |
||
962 | end |
||
963 | end |
||
964 | return; |
||
965 | else |
||
966 | |||
967 | local inputFoods = { }; |
||
968 | for itemLink in string.gfind(foodString, "|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[.-%]|h|r") do |
||
969 | table.insert(inputFoods, itemLink); |
||
970 | local foodID = FOM_IDFromLink(itemLink); |
||
971 | if (foodID) then |
||
972 | local foodName = FOM_NameFromLink(itemLink); |
||
973 | if (FOM_FoodIDsToNames == nil) then |
||
974 | FOM_FoodIDsToNames = {}; |
||
975 | end |
||
976 | FOM_FoodIDsToNames[foodID] = foodName; |
||
977 | end |
||
978 | end |
||
979 | if (table.getn(inputFoods) == 0) then |
||
980 | GFWUtils.Print("The "..GFWUtils.Hilite("/fom "..cmd).." command requires an item link; shift-click an item to insert a link."); |
||
981 | return; |
||
982 | end |
||
983 | |||
984 | local capDiet = string.upper(string.sub(diet, 1, 1)) .. string.sub(diet, 2); -- print a nicely capitalized version |
||
985 | if ( cmd == "add" ) then |
||
986 | for _, food in inputFoods do |
||
987 | local foodID = FOM_IDFromLink(food); |
||
988 | if ( FOM_AddFood(diet, tonumber(foodID)) ) then |
||
989 | GFWUtils.Print("Added "..food.." to "..GFWUtils.Hilite(capDiet).." list."); |
||
990 | else |
||
991 | GFWUtils.Print(food.." already in "..GFWUtils.Hilite(capDiet).." list."); |
||
992 | end |
||
993 | end |
||
994 | if (FOM_Config.AvoidQuestFood) then |
||
995 | FOM_ScanQuests(); -- in case any of the newly added foods are quest objectives |
||
996 | end |
||
997 | return; |
||
998 | elseif (cmd == "remove" ) then |
||
999 | for _, food in inputFoods do |
||
1000 | local foodID = FOM_IDFromLink(food); |
||
1001 | if ( FOM_RemoveFood(diet, tonumber(foodID)) ) then |
||
1002 | GFWUtils.Print("Removed "..food.." from "..GFWUtils.Hilite(capDiet).." list."); |
||
1003 | else |
||
1004 | GFWUtils.Print("Could not find "..food.." in "..GFWUtils.Hilite(capDiet).." list."); |
||
1005 | end |
||
1006 | end |
||
1007 | return; |
||
1008 | end |
||
1009 | end |
||
1010 | end |
||
1011 | |||
1012 | -- if we got down to here, we got bad input |
||
1013 | FOM_ChatCommandHandler("help"); |
||
1014 | end |
||
1015 | |||
1016 | -- Add a food to a list |
||
1017 | function FOM_AddFood(diet, food) |
||
1018 | |||
1019 | if (FOM_Foods[diet] == nil) then |
||
1020 | GFWUtils.DebugLog("FOM_Foods[diet] == nil"); |
||
1021 | end |
||
1022 | if (FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil) then |
||
1023 | GFWUtils.DebugLog("FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil"); |
||
1024 | end |
||
1025 | if (FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil) then |
||
1026 | GFWUtils.DebugLog("FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil"); |
||
1027 | end |
||
1028 | if ( GFWTable.IndexOf(FOM_Foods[diet], food) == 0 ) then |
||
1029 | if (FOM_AddedFoods == nil) then |
||
1030 | FOM_AddedFoods = {}; |
||
1031 | end |
||
1032 | if (FOM_AddedFoods[diet] == nil) then |
||
1033 | FOM_AddedFoods[diet] = {}; |
||
1034 | end |
||
1035 | if ( GFWTable.IndexOf(FOM_AddedFoods[diet], food) == 0 ) then |
||
1036 | table.insert( FOM_AddedFoods[diet], food ); |
||
1037 | table.sort( FOM_AddedFoods[diet] ); |
||
1038 | if (FOM_RemovedFoods and FOM_RemovedFoods[diet] and GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ~= 0) then |
||
1039 | table.remove( FOM_RemovedFoods[diet], GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ); |
||
1040 | table.sort( FOM_RemovedFoods[diet] ); |
||
1041 | end |
||
1042 | return true; |
||
1043 | else |
||
1044 | return false; |
||
1045 | end |
||
1046 | else |
||
1047 | return false; |
||
1048 | end |
||
1049 | |||
1050 | end |
||
1051 | |||
1052 | -- Remove a food from a list |
||
1053 | function FOM_RemoveFood(diet, food) |
||
1054 | |||
1055 | if (FOM_Foods[diet] == nil) then |
||
1056 | GFWUtils.DebugLog("FOM_Foods[diet] == nil"); |
||
1057 | end |
||
1058 | if (FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil) then |
||
1059 | GFWUtils.DebugLog("FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil"); |
||
1060 | end |
||
1061 | if (FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil) then |
||
1062 | GFWUtils.DebugLog("FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil"); |
||
1063 | end |
||
1064 | if ( GFWTable.IndexOf(FOM_Foods[diet], food) ~= 0 ) then |
||
1065 | if (FOM_RemovedFoods == nil) then |
||
1066 | FOM_RemovedFoods = {}; |
||
1067 | end |
||
1068 | if (FOM_RemovedFoods[diet] == nil) then |
||
1069 | FOM_RemovedFoods[diet] = {}; |
||
1070 | end |
||
1071 | if ( GFWTable.IndexOf(FOM_RemovedFoods[diet], food) == 0 ) then |
||
1072 | table.insert( FOM_RemovedFoods[diet], food ); |
||
1073 | table.sort( FOM_RemovedFoods[diet] ); |
||
1074 | if (FOM_AddedFoods and FOM_AddedFoods[diet] and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then |
||
1075 | table.remove( FOM_AddedFoods[diet], GFWTable.IndexOf(FOM_AddedFoods[diet], food) ); |
||
1076 | table.sort( FOM_AddedFoods[diet] ); |
||
1077 | end |
||
1078 | return true; |
||
1079 | else |
||
1080 | return false; |
||
1081 | end |
||
1082 | else |
||
1083 | if (FOM_AddedFoods and FOM_AddedFoods[diet] and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then |
||
1084 | table.remove( FOM_AddedFoods[diet], GFWTable.IndexOf(FOM_AddedFoods[diet], food) ); |
||
1085 | table.sort( FOM_AddedFoods[diet] ); |
||
1086 | return true; |
||
1087 | end |
||
1088 | return false; |
||
1089 | end |
||
1090 | |||
1091 | end |
||
1092 | |||
1093 | function FOM_IsBGActive() |
||
1094 | local bgNum = 1; |
||
1095 | local status; |
||
1096 | repeat |
||
1097 | status = GetBattlefieldStatus(bgNum); |
||
1098 | if (status == "active") then |
||
1099 | return true; |
||
1100 | end |
||
1101 | bgNum = bgNum + 1; |
||
1102 | until (status == nil) |
||
1103 | return false; |
||
1104 | end |
||
1105 | -- Check Happiness |
||
1106 | function FOM_CheckHappiness() |
||
1107 | |||
1108 | -- Check for pet |
||
1109 | if not ( UnitExists("pet") ) then |
||
1110 | FOM_State.ShouldFeed = nil; |
||
1111 | return; |
||
1112 | end |
||
1113 | |||
1114 | -- Get Pet Info |
||
1115 | local pet = UnitName("pet"); |
||
1116 | local happiness, damage, loyalty = GetPetHappiness(); |
||
1117 | |||
1118 | -- Check No Happiness |
||
1119 | if ( happiness == 0 ) or ( happiness == nil ) then return; end |
||
1120 | |||
1121 | local level; |
||
1122 | if ( FOM_Config.Level == "unhappy" ) then |
||
1123 | level = 1; |
||
1124 | elseif ( FOM_Config.Level == "content" ) then |
||
1125 | level = 2; |
||
1126 | elseif ( FOM_Config.Level == "happy" ) then |
||
1127 | level = 3; |
||
1128 | elseif ( FOM_Config.Level == "debug" ) then |
||
1129 | level = 4; |
||
1130 | else |
||
1131 | level = 0; |
||
1132 | end |
||
1133 | |||
1134 | -- Check if Need Feeding |
||
1135 | if ( happiness < level + 1 ) then |
||
1136 | FOM_State.ShouldFeed = true; |
||
1137 | if (not FOM_HasFeedEffect() and GetTime() - FOM_LastWarning > FOM_WARNING_INTERVAL) then |
||
1138 | if (FOM_Config.TextWarning) then |
||
1139 | local msg; |
||
1140 | if (level - happiness == 0) then |
||
1141 | msg = FOM_PET_HUNGRY; |
||
1142 | else |
||
1143 | msg = FOM_PET_VERY_HUNGRY; |
||
1144 | end |
||
1145 | GFWUtils.Print(string.format(msg, pet)); |
||
1146 | GFWUtils.Note(string.format(msg, pet)); |
||
1147 | end |
||
1148 | FOM_PlayHungrySound(); |
||
1149 | FOM_LastWarning = GetTime(); |
||
1150 | end |
||
1151 | else |
||
1152 | FOM_State.ShouldFeed = nil; |
||
1153 | end |
||
1154 | |||
1155 | end |
||
1156 | |||
1157 | FOM_HungrySounds = { |
||
1158 | [BAT] = "Sound\\Creature\\FelBat\\FelBatDeath.wav", |
||
1159 | [BEAR] = "Sound\\Creature\\Bear\\mBearDeathA.wav", |
||
1160 | [BOAR] = "Sound\\Creature\\Boar\\mWildBoarAggro2.wav", |
||
1161 | [CAT] = "Sound\\Creature\\Tiger\\mTigerStand2A.wav", |
||
1162 | [CARRION_BIRD] = "Sound\\Creature\\Carrion\\mCarrionWoundCriticalA.wav", |
||
1163 | [CRAB] = "Sound\\Creature\\Crab\\CrabDeathA.wav", |
||
1164 | [CROCOLISK] = "Sound\\Creature\\Basilisk\\mBasiliskSpellCastA.wav", |
||
1165 | [GORILLA] = "Sound\\Creature\\Gorilla\\GorillaDeathA.wav", |
||
1166 | [HYENA] = "Sound\\Creature\\Hyena\\HyenaPreAggroA.wav", |
||
1167 | [OWL] = "Sound\\Creature\\OWl\\OwlPreAggro.wav", |
||
1168 | [RAPTOR] = "Sound\\Creature\\Raptor\\mRaptorWoundCriticalA.wav", |
||
1169 | [SCORPID] = "Sound\\Creature\\SilithidWasp\\mSilithidWaspStand2A.wav", |
||
1170 | [SPIDER] = "Sound\\Creature\\Tarantula\\mTarantulaFidget2a.wav", |
||
1171 | [TALLSTRIDER] = "Sound\\Creature\\TallStrider\\tallStriderPreAggroA.wav", |
||
1172 | [TURTLE] = "Sound\\Creature\\SeaTurtle\\SeaTurtleWoundCritA.wav", |
||
1173 | [WIND_SERPENT] = "Sound\\Creature\\WindSerpant\\mWindSerpantDeathA.wav", |
||
1174 | [WOLF] = "Sound\\Creature\\Wolf\\mWolfFidget2c.wav", |
||
1175 | }; |
||
1176 | function FOM_PlayHungrySound() |
||
1177 | if (FOM_Config.AudioWarning) then |
||
1178 | local type = UnitCreatureFamily("pet"); |
||
1179 | local sound = FOM_HungrySounds[type]; |
||
1180 | if (sound == nil or FOM_Config.AudioWarning == "bell") then |
||
1181 | PlaySoundFile("Sound\\Doodad\\BellTollNightElf.wav"); |
||
1182 | else |
||
1183 | PlaySoundFile(sound); |
||
1184 | end |
||
1185 | end |
||
1186 | end |
||
1187 | |||
1188 | -- Check Feed Effect |
||
1189 | function FOM_HasFeedEffect() |
||
1190 | |||
1191 | local i = 1; |
||
1192 | local buff; |
||
1193 | buff = UnitBuff("pet", i); |
||
1194 | while buff do |
||
1195 | if ( string.find(buff, "Ability_Hunter_BeastTraining") ) then |
||
1196 | return true; |
||
1197 | end |
||
1198 | i = i + 1; |
||
1199 | buff = UnitBuff("pet", i); |
||
1200 | end |
||
1201 | return false; |
||
1202 | |||
1203 | end |
||
1204 | |||
1205 | -- Feed Pet |
||
1206 | function FOM_Feed(aFood) |
||
1207 | |||
1208 | FOM_State.ShouldFeed = false; |
||
1209 | |||
1210 | -- Make sure we have a feedable pet |
||
1211 | if not (UnitExists("pet")) then |
||
1212 | GFWUtils.Note(FOM_ERROR_NO_PET); |
||
1213 | return; |
||
1214 | end |
||
1215 | if (UnitIsDead("pet")) then |
||
1216 | GFWUtils.Note(FOM_ERROR_PET_DEAD); |
||
1217 | return; |
||
1218 | end |
||
1219 | if (GetPetFoodTypes() == nil) then |
||
1220 | GFWUtils.Note(FOM_ERROR_NO_FEEDABLE_PET); |
||
1221 | return; |
||
1222 | end |
||
1223 | |||
1224 | -- Assign Variable |
||
1225 | local pet = UnitName("pet"); |
||
1226 | |||
1227 | FOM_CheckSetup(); |
||
1228 | if (FOM_LastPetName == nil or FOM_LastPetName == "") then |
||
1229 | GFWUtils.DebugLog("Can't get pet info."); |
||
1230 | return; |
||
1231 | end |
||
1232 | |||
1233 | if (GetLocale() ~= "enUS") then |
||
1234 | if (FOM_LocaleInfo == nil) then |
||
1235 | FOM_LocaleInfo = {}; |
||
1236 | end |
||
1237 | FOM_LocaleInfo[UnitCreatureFamily("pet")] = {GetPetFoodTypes()}; |
||
1238 | end |
||
1239 | |||
1240 | -- Look for Food |
||
1241 | local foodBag, foodItem; |
||
1242 | if (aFood ~= nil) then |
||
1243 | -- if told to feed a specific food, do so |
||
1244 | foodBag, foodItem = FOM_FindSpecificFood(aFood); |
||
1245 | if ( foodBag == nil) then |
||
1246 | -- No Food Could be Found |
||
1247 | GFWUtils.Print(string.format(FOM_ERROR_FOOD_NOT_FOUND, pet, aFood)); |
||
1248 | return; |
||
1249 | end |
||
1250 | else |
||
1251 | foodBag, foodItem = FOM_NewFindFood(); |
||
1252 | end |
||
1253 | |||
1254 | if ( foodBag == nil) then |
||
1255 | -- No Food Could be Found |
||
1256 | GFWUtils.Print(string.format(FOM_ERROR_NO_FOOD, pet)); |
||
1257 | return; |
||
1258 | end |
||
1259 | |||
1260 | FOM_LastFood = GetContainerItemLink(foodBag, foodItem); |
||
1261 | |||
1262 | GFWUtils.DebugLog("Picked "..FOM_LastFood.." (bag "..foodBag..", slot "..foodItem..") for feeding."); |
||
1263 | if (FOM_Config.Debug) then |
||
1264 | -- don't actually feed anything, just show what we would choose |
||
1265 | return; |
||
1266 | end |
||
1267 | |||
1268 | -- Actually feed the item to the pet |
||
1269 | PickupContainerItem(foodBag, foodItem); |
||
1270 | if ( CursorHasItem() ) then |
||
1271 | DropItemOnUnit("pet"); |
||
1272 | end |
||
1273 | if ( CursorHasItem() ) then |
||
1274 | PickupContainerItem(foodBag, foodItem); |
||
1275 | else |
||
1276 | FOM_State.ShouldFeed = nil; |
||
1277 | -- Alert |
||
1278 | if ( FOM_Config.Alert == "chat") then |
||
1279 | GFWUtils.Print(string.format(FOM_FEEDING_EAT, pet, GFWUtils.Hilite(FOM_LastFood))); |
||
1280 | elseif ( FOM_Config.Alert == "emote") then |
||
1281 | SendChatMessage(string.format(FOM_FEEDING_FEED, pet, FOM_LastFood).. FOM_RandomEmote(), "EMOTE"); |
||
1282 | end |
||
1283 | end |
||
1284 | |||
1285 | end |
||
1286 | |||
1287 | function FOM_RandomEmote() |
||
1288 | |||
1289 | local randomEmotes = {}; |
||
1290 | if (UnitSex("pet") == 2) then |
||
1291 | randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["male"]); |
||
1292 | elseif (UnitSex("pet") == 3) then |
||
1293 | randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["female"]); |
||
1294 | end |
||
1295 | |||
1296 | randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes[UnitCreatureFamily("pet")]); |
||
1297 | randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes[FOM_NameFromLink(FOM_LastFood)]); |
||
1298 | randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["any"]); |
||
1299 | |||
1300 | return randomEmotes[math.random(table.getn(randomEmotes))]; |
||
1301 | |||
1302 | end |
||
1303 | |||
1304 | function FOM_FindSpecificFood(foodName) |
||
1305 | for bagNum = 0, 4 do |
||
1306 | if (not FOM_BagIsQuiver(bagNum) ) then |
||
1307 | -- skip bags that can't contain food |
||
1308 | |||
1309 | local bagSize = GetContainerNumSlots(bagNum); |
||
1310 | for itemNum = 1, bagSize do |
||
1311 | |||
1312 | itemName = FOM_GetItemName(bagNum, itemNum); |
||
1313 | if ( itemName == foodName ) then |
||
1314 | return bagNum, itemNum; |
||
1315 | end |
||
1316 | |||
1317 | end |
||
1318 | end |
||
1319 | end |
||
1320 | return nil; |
||
1321 | end |
||
1322 | |||
1323 | function FOM_IsTemporaryFood(itemLink) |
||
1324 | |||
1325 | local _, _, link = string.find(itemLink, "(item:%d+:%d+:%d+:%d+)"); |
||
1326 | if (link == nil or link == "") then |
||
1327 | return false; |
||
1328 | end |
||
1329 | FOMTooltip:ClearLines(); |
||
1330 | FOMTooltip:SetHyperlink(link); |
||
1331 | if (FOMTooltipTextLeft2:GetText() == ITEM_CONJURED) then |
||
1332 | return true; |
||
1333 | else |
||
1334 | return false; |
||
1335 | end |
||
1336 | |||
1337 | end |
||
1338 | |||
1339 | function FOM_FlatFoodList() |
||
1340 | local foodList = {}; |
||
1341 | FOM_Quantity = { }; |
||
1342 | for bagNum = 0, 4 do |
||
1343 | if (not FOM_BagIsQuiver(bagNum) ) then |
||
1344 | -- skip bags that can't contain food |
||
1345 | for itemNum = 1, GetContainerNumSlots(bagNum) do |
||
1346 | local itemLink = GetContainerItemLink(bagNum, itemNum); |
||
1347 | if (itemLink) then |
||
1348 | local itemID = FOM_IDFromLink(itemLink); |
||
1349 | local _, itemCount = GetContainerItemInfo(bagNum, itemNum); |
||
1350 | if ( FOM_IsInDiet(itemID) ) then |
||
1351 | if (FOM_FoodIDsToNames == nil) then |
||
1352 | FOM_FoodIDsToNames = {}; |
||
1353 | end |
||
1354 | local name = FOM_NameFromLink(itemLink); |
||
1355 | FOM_FoodIDsToNames[itemID] = name; |
||
1356 | local isUseful = FOM_IsUsefulFood(itemID, itemCount); |
||
1357 | foodQuality = (FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName][itemID] or MAX_QUALITY); |
||
1358 | if (foodQuality > 0) then |
||
1359 | table.insert(foodList, {bag=bagNum, slot=itemNum, link=itemLink, count=itemCount, quality=foodQuality, useful=isUseful, temp=FOM_IsTemporaryFood(itemLink)}); |
||
1360 | end |
||
1361 | end |
||
1362 | end |
||
1363 | end |
||
1364 | end |
||
1365 | end |
||
1366 | return foodList; |
||
1367 | end |
||
1368 | |||
1369 | function FOM_NewFindFood() |
||
1370 | FlatFoodList = FOM_FlatFoodList(); |
||
1371 | |||
1372 | table.sort(FlatFoodList, FOM_SortCount); -- small stacks first |
||
1373 | if (FOM_NumOpenBagSlots() > FOM_Config.KeepOpenSlots) then |
||
1374 | if (FOM_Config.PreferHigherQuality) then |
||
1375 | table.sort(FlatFoodList, FOM_SortQualityDescending); -- higher quality first |
||
1376 | else |
||
1377 | table.sort(FlatFoodList, FOM_SortQualityAscending); -- lower quality first |
||
1378 | end |
||
1379 | end |
||
1380 | table.sort(FlatFoodList, FOM_SortTemporary); -- temporary foods first |
||
1381 | if (FOM_Config.AvoidUsefulFood) then |
||
1382 | table.sort(FlatFoodList, FOM_SortUseful); -- non-useful foods first |
||
1383 | end |
||
1384 | |||
1385 | for _, foodInfo in FlatFoodList do |
||
1386 | if (foodInfo.useful and FOM_Config.AvoidUsefulFood and not FOM_Config.Fallback) then |
||
1387 | GFWUtils.DebugLog("Skipping "..foodInfo.count.."x "..foodInfo.link.."; no falling back to avoided foods."); |
||
1388 | else |
||
1389 | return foodInfo.bag, foodInfo.slot; |
||
1390 | end |
||
1391 | end |
||
1392 | |||
1393 | return nil; |
||
1394 | end |
||
1395 | |||
1396 | function FOM_SortTemporary(a, b) |
||
1397 | if (a.temp) then |
||
1398 | aTemp = 1; |
||
1399 | else |
||
1400 | aTemp = 0; |
||
1401 | end |
||
1402 | if (b.temp) then |
||
1403 | bTemp = 1; |
||
1404 | else |
||
1405 | bTemp = 0; |
||
1406 | end |
||
1407 | return aTemp > bTemp; |
||
1408 | end |
||
1409 | |||
1410 | function FOM_SortCount(a, b) |
||
1411 | return a.count < b.count; |
||
1412 | end |
||
1413 | |||
1414 | function FOM_SortUseful(a, b) |
||
1415 | if (a.useful) then |
||
1416 | aUseful = 1; |
||
1417 | else |
||
1418 | aUseful = 0; |
||
1419 | end |
||
1420 | if (b.useful) then |
||
1421 | bUseful = 1; |
||
1422 | else |
||
1423 | bUseful = 0; |
||
1424 | end |
||
1425 | return aUseful < bUseful; |
||
1426 | end |
||
1427 | |||
1428 | function FOM_SortQualityDescending(a, b) |
||
1429 | return a.quality > b.quality; |
||
1430 | end |
||
1431 | |||
1432 | function FOM_SortQualityAscending(a, b) |
||
1433 | return a.quality < b.quality; |
||
1434 | end |
||
1435 | |||
1436 | function FOM_IsUsefulFood(itemID, quantity) |
||
1437 | local foodName = GetItemInfo(itemID); |
||
1438 | if (foodName == nil) then |
||
1439 | GFWUtils.DebugLog("Can't get info for item ID "..itemID..", assuming it's OK to eat."); |
||
1440 | return false; |
||
1441 | end |
||
1442 | if (FOM_Cooking and FOM_Cooking[FOM_RealmPlayer] and FOM_Cooking[FOM_RealmPlayer][itemID]) then |
||
1443 | if (FOM_Cooking[FOM_RealmPlayer][itemID] >= FOM_Config.SaveForCookingLevel) then |
||
1444 | GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; is good for cooking."); |
||
1445 | return true; |
||
1446 | end |
||
1447 | end |
||
1448 | if (FOM_Config.AvoidQuestFood) then |
||
1449 | FOM_ScanQuests(); |
||
1450 | if (FOM_QuestFood ~= nil and FOM_QuestFood[FOM_RealmPlayer] ~= nil and FOM_QuestFood[FOM_RealmPlayer][foodName]) then |
||
1451 | if (FOM_Quantity[foodName] == nil) then |
||
1452 | FOM_Quantity[foodName] = quantity; |
||
1453 | else |
||
1454 | FOM_Quantity[foodName] = FOM_Quantity[foodName] + quantity; |
||
1455 | end |
||
1456 | if (FOM_Quantity[foodName] > FOM_QuestFood[FOM_RealmPlayer][foodName]) then |
||
1457 | GFWUtils.DebugLog("Not skipping "..quantity.."x "..foodName.."; is needed for quest, but we have more than enough."); |
||
1458 | return false; |
||
1459 | else |
||
1460 | GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; is needed for quest."); |
||
1461 | return true; |
||
1462 | end |
||
1463 | end |
||
1464 | end |
||
1465 | if (FOM_Config.AvoidBonusFood and FOM_IsInDiet(itemID, FOM_DIET_BONUS)) then |
||
1466 | GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; has bonus effect when eaten by player."); |
||
1467 | return true; |
||
1468 | end |
||
1469 | --GFWUtils.DebugLog("Not skipping "..quantity.."x "..foodName.."; doesn't have other uses."); |
||
1470 | return false; |
||
1471 | end |
||
1472 | |||
1473 | function FOM_NumOpenBagSlots() |
||
1474 | local openSlots = 0; |
||
1475 | for bagNum = 0, 4 do |
||
1476 | if (not FOM_BagIsQuiver(bagNum) ) then |
||
1477 | -- skip bags that can't contain food |
||
1478 | |||
1479 | local bagSize = GetContainerNumSlots(bagNum); |
||
1480 | for itemNum = 1, bagSize do |
||
1481 | if (GetContainerItemInfo(bagNum, itemNum) == nil) then |
||
1482 | openSlots = openSlots + 1; |
||
1483 | end |
||
1484 | end |
||
1485 | end |
||
1486 | end |
||
1487 | return openSlots; |
||
1488 | end |
||
1489 | |||
1490 | function FOM_IsInDiet(food, dietList) |
||
1491 | |||
1492 | if ( dietList == nil ) then |
||
1493 | dietList = {GetPetFoodTypes()}; |
||
1494 | end |
||
1495 | if ( dietList == nil ) then |
||
1496 | return false; |
||
1497 | end |
||
1498 | if (type(dietList) ~= "table") then |
||
1499 | dietList = {dietList}; |
||
1500 | end |
||
1501 | for _, diet in dietList do |
||
1502 | diet = string.lower(diet); -- let's be case insensitive |
||
1503 | if (FOM_Foods[diet] == nil) then |
||
1504 | GFWUtils.DebugLog("FOM_Foods[diet] == nil"); |
||
1505 | end |
||
1506 | if (FOM_RemovedFoods ~= nil and FOM_RemovedFoods[diet] ~= nil and GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ~= 0) then |
||
1507 | return false; |
||
1508 | end |
||
1509 | if (FOM_AddedFoods ~= nil and FOM_AddedFoods[diet] ~= nil and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then |
||
1510 | return true; |
||
1511 | end |
||
1512 | if (GFWTable.IndexOf(FOM_Foods[diet], food) ~= 0) then |
||
1513 | return true; |
||
1514 | end |
||
1515 | end |
||
1516 | |||
1517 | return false; |
||
1518 | |||
1519 | end |
||
1520 | |||
1521 | function FOM_IsKnownFood(food) |
||
1522 | return FOM_IsInDiet(food, {FOM_DIET_MEAT, FOM_DIET_FISH, FOM_DIET_BREAD, FOM_DIET_CHEESE, FOM_DIET_FUNGUS, FOM_DIET_FRUIT}); |
||
1523 | end |
||
1524 | |||
1525 | -- Get Item Name |
||
1526 | function FOM_GetItemName(bag, slot) |
||
1527 | |||
1528 | local itemLink = GetContainerItemLink(bag, slot); |
||
1529 | if (itemLink) then |
||
1530 | return FOM_NameFromLink(itemLink); |
||
1531 | else |
||
1532 | return ""; |
||
1533 | end |
||
1534 | end |
||
1535 | |||
1536 | function FOM_PetRename(newName) |
||
1537 | |||
1538 | FOM_CheckSetup(); |
||
1539 | |||
1540 | -- move our saved food quality data to be indexed under the new name |
||
1541 | FOM_FoodQuality[FOM_RealmPlayer][newName] = FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName]; |
||
1542 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = nil; |
||
1543 | |||
1544 | FOM_Original_PetRename(newName); |
||
1545 | |||
1546 | end |
||
1547 | |||
1548 | function FOM_PetAbandon() |
||
1549 | |||
1550 | FOM_CheckSetup(); |
||
1551 | |||
1552 | -- delete saved food-quality data for this pet so we don't bloat SavedVariables |
||
1553 | FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = nil; |
||
1554 | |||
1555 | FOM_Original_PetAbandon(); |
||
1556 | |||
1557 | end |
||
1558 | |||
1559 | -- The icon for the cooking spell is unique and the same in all languages; use that to determine the localized name. |
||
1560 | function FOM_CookingSpellName() |
||
1561 | FOM_COOKING_ICON = "Interface\\Icons\\INV_Misc_Food_15"; |
||
1562 | if (FOM_COOKING_NAME == nil) then |
||
1563 | local spellName; |
||
1564 | local i = 0; |
||
1565 | repeat |
||
1566 | i = i + 1; |
||
1567 | spellName = GetSpellName(i, BOOKTYPE_SPELL); |
||
1568 | if (spellName ~= nil and GetSpellTexture(i, BOOKTYPE_SPELL) == FOM_COOKING_ICON) then |
||
1569 | FOM_COOKING_NAME = spellName; |
||
1570 | return FOM_COOKING_NAME; |
||
1571 | end |
||
1572 | until (spellName == nil); |
||
1573 | end |
||
1574 | return FOM_COOKING_NAME; |
||
1575 | end |
||
1576 | |||
1577 | function FOM_BagIsQuiver(bagNum) |
||
1578 | local invSlotID = ContainerIDToInventoryID(bagNum); |
||
1579 | local bagLink = GetInventoryItemLink("player", invSlotID); |
||
1580 | if (bagLink == nil) then |
||
1581 | return false; |
||
1582 | end |
||
1583 | local _, _, itemID = string.find(bagLink, "item:(%d+):%d+:%d+:%d+"); |
||
1584 | if (tonumber(itemID)) then |
||
1585 | itemID = tonumber(itemID); |
||
1586 | local name, link, rarity, minLevel, type, subType, stackCount, equipLoc = GetItemInfo(itemID); |
||
1587 | if (type == "Ammo Pouch" or type == "Quiver" or subType == "Ammo Pouch" or subType == "Quiver") then |
||
1588 | return true; |
||
1589 | end |
||
1590 | if (type == FOM_AMMO_POUCH or type == FOM_QUIVER or subType == FOM_AMMO_POUCH or subType == FOM_QUIVER) then |
||
1591 | return true; |
||
1592 | end |
||
1593 | end |
||
1594 | return false; |
||
1595 | end |
||
1596 | |||
1597 | function FOM_IDFromLink(itemLink) |
||
1598 | if (itemLink == nil) then return nil; end |
||
1599 | local _, _, itemID = string.find(itemLink, "item:(%d+):%d+:%d+:%d+"); |
||
1600 | if (tonumber(itemID)) then |
||
1601 | return tonumber(itemID); |
||
1602 | else |
||
1603 | return nil; |
||
1604 | end |
||
1605 | end |
||
1606 | |||
1607 | function FOM_NameFromLink(itemLink) |
||
1608 | if (itemLink == nil) then return nil; end |
||
1609 | local _, _, name = string.find(itemLink, "%[(.+)%]"); |
||
1610 | return name; |
||
1611 | end |
||
1612 | |||
1613 | function FOM_OptionsShow() |
||
1614 | FOM_VersionText:SetText("v. "..FOM_VERSION); |
||
1615 | FOM_KeepOpenSlots:SetText(FOM_Config.KeepOpenSlots); |
||
1616 | for option, text in FOM_OptionsButtonText do |
||
1617 | local button = getglobal("FOM_OptionsButton_"..option); |
||
1618 | local buttonText = getglobal("FOM_OptionsButton_"..option.."Text"); |
||
1619 | if (button and buttonText) then |
||
1620 | if (FOM_Config[option]) then |
||
1621 | button:SetChecked(true); |
||
1622 | elseif (option == "SaveForCook_All" and FOM_Config.SaveForCookingLevel <= 0) then |
||
1623 | button:SetChecked(true); |
||
1624 | elseif (option == "SaveForCook_Green" and FOM_Config.SaveForCookingLevel == 1) then |
||
1625 | button:SetChecked(true); |
||
1626 | elseif (option == "SaveForCook_Yellow" and FOM_Config.SaveForCookingLevel == 2) then |
||
1627 | button:SetChecked(true); |
||
1628 | elseif (option == "SaveForCook_Orange" and FOM_Config.SaveForCookingLevel == 3) then |
||
1629 | button:SetChecked(true); |
||
1630 | elseif (option == "SaveForCook_None" and FOM_Config.SaveForCookingLevel >= 4) then |
||
1631 | button:SetChecked(true); |
||
1632 | elseif (option == "AudioWarningBell" and FOM_Config.AudioWarning == "bell") then |
||
1633 | button:SetChecked(true); |
||
1634 | elseif (option == "AlertEmote" and FOM_Config.Alert == "emote") then |
||
1635 | button:SetChecked(true); |
||
1636 | elseif (option == "AlertChat" and FOM_Config.Alert == "chat") then |
||
1637 | button:SetChecked(true); |
||
1638 | elseif (option == "AlertNone" and not FOM_Config.Alert) then |
||
1639 | button:SetChecked(true); |
||
1640 | elseif (option == "LevelContent" and FOM_Config.Level == "content") then |
||
1641 | button:SetChecked(true); |
||
1642 | elseif (option == "LevelUnhappy" and FOM_Config.Level == "unhappy") then |
||
1643 | button:SetChecked(true); |
||
1644 | elseif (option == "LevelOff" and not FOM_Config.Level) then |
||
1645 | button:SetChecked(true); |
||
1646 | else |
||
1647 | button:SetChecked(false); |
||
1648 | end |
||
1649 | buttonText:SetText(text); |
||
1650 | end |
||
1651 | end |
||
1652 | end |
||
1653 | |||
1654 | function FOM_OptionsClick() |
||
1655 | local button = this:GetName(); |
||
1656 | local option = string.gsub(button, "FOM_OptionsButton_", ""); |
||
1657 | |||
1658 | if (option == "SaveForCook_All" and this:GetChecked()) then |
||
1659 | FOM_Config.SaveForCookingLevel = 0; |
||
1660 | elseif (option == "SaveForCook_Green" and this:GetChecked()) then |
||
1661 | FOM_Config.SaveForCookingLevel = 1; |
||
1662 | elseif (option == "SaveForCook_Yellow" and this:GetChecked()) then |
||
1663 | FOM_Config.SaveForCookingLevel = 2; |
||
1664 | elseif (option == "SaveForCook_Orange" and this:GetChecked()) then |
||
1665 | FOM_Config.SaveForCookingLevel = 3; |
||
1666 | elseif (option == "SaveForCook_None" and this:GetChecked()) then |
||
1667 | FOM_Config.SaveForCookingLevel = 4; |
||
1668 | elseif (option == "AudioWarningBell") then |
||
1669 | if (this:GetChecked()) then |
||
1670 | FOM_Config.AudioWarning = "bell"; |
||
1671 | else |
||
1672 | FOM_Config.AudioWarning = 1; |
||
1673 | end |
||
1674 | elseif (option == "AlertEmote" and this:GetChecked()) then |
||
1675 | FOM_Config.Alert = "emote"; |
||
1676 | elseif (option == "AlertChat" and this:GetChecked()) then |
||
1677 | FOM_Config.Alert = "chat"; |
||
1678 | elseif (option == "AlertNone" and this:GetChecked()) then |
||
1679 | FOM_Config.Alert = nil; |
||
1680 | elseif (option == "LevelContent" and this:GetChecked()) then |
||
1681 | FOM_Config.Level = "content"; |
||
1682 | elseif (option == "LevelUnhappy" and this:GetChecked()) then |
||
1683 | FOM_Config.Level = "unhappy"; |
||
1684 | elseif (option == "LevelOff" and this:GetChecked()) then |
||
1685 | FOM_Config.Level = nil; |
||
1686 | else |
||
1687 | FOM_Config[option] = this:GetChecked(); |
||
1688 | end |
||
1689 | FOM_OptionsShow(); |
||
1690 | end |
||
1691 | |||
1692 | function FOM_KeepOpenSlots_TextChanged() |
||
1693 | FOM_Config.KeepOpenSlots = tonumber(this:GetText()) or 0; |
||
1694 | end |
||
1695 | |||
1696 |