Aaron@4: --[[--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--)) Aaron@4: Aaron@4: RecipeProfit by -[@project-author@]- Aaron@4: Aaron@4: Rev: @project-revision@ Aaron@4: Updated: @file-date-iso@ Aaron@4: Aaron@4: --))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--)) Aaron@4: Aaron@22: URL: Aaron@22: http://www.wrathguides.com/ Aaron@22: Aaron@22: License: Aaron@22: This file is a part of "RecipeProfit for GatherMate." Aaron@22: Aaron@22: This program is free software; you can redistribute it and/or Aaron@22: modify it under the terms of the GNU General Public License Aaron@22: as published by the Free Software Foundation, either version 3 Aaron@22: of the License, or (at your option) any later version. Aaron@22: Aaron@22: This program is distributed in the hope that it will be useful, Aaron@22: but WITHOUT ANY WARRANTY; without even the implied warranty of Aaron@22: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Aaron@22: GNU General Public License for more details. Aaron@22: Aaron@22: You should have received a copy of the GNU General Public License killermonkey99@36: along with this program. If not, see . Aaron@22: Aaron@22: Note: Aaron@22: This program's source code is specifically designed to work with Aaron@22: World of Warcraft's interpreted AddOn system. Aaron@22: Aaron@22: You have an implicit license to use this program with these facilities Aaron@22: since that is it's designated purpose as per: Aaron@22: http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat Aaron@4: Aaron@4: --]]--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--))--)) Aaron@4: Aaron@22: killermonkey99@36: local RecipeProfit = LibStub("AceAddon-3.0"):NewAddon("RecipeProfit", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0") killermonkey99@36: killermonkey99@36: local GatherMate = LibStub("AceAddon-3.0"):GetAddon("GatherMate2") killermonkey99@36: Aaron@0: local tabletest = {} Aaron@16: local db = {} Aaron@1: local safeRecipes = {} Aaron@16: local ids = {} Aaron@24: local nodeLookup = {} Aaron@16: Aaron@17: local profile Aaron@17: Aaron@0: RecipeProfit.db = db; Aaron@0: Aaron@17: --[[ Forward Definitions (for local functions) ]] Aaron@16: local get_faction_db, Aaron@16: add_note, Aaron@16: button_update, Aaron@16: find_good_id, Aaron@16: safe_cache_vendor, Aaron@17: get_note_title, Aaron@17: get_next_texture_id, Aaron@17: set_node_constants, Aaron@24: get_colored_note_name, Aaron@17: inject_options; Aaron@16: Aaron@17: --[[ Deep table inspection for debugging ]] Aaron@0: function debugprint(val, indent) Aaron@0: indent = indent or ""; Aaron@0: if not(type(val) == "table") then Aaron@0: print("Not table: " .. val) Aaron@0: return Aaron@0: end Aaron@0: Aaron@0: for k,v in pairs(val) do Aaron@0: if(type(k) == "table" and type(v) == "table") then Aaron@0: print(indent .. "table key: {") Aaron@0: debugprint(k, indent .. " ") Aaron@0: print(indent .. "} = {") Aaron@0: debugprint(v, indent .. " ") Aaron@0: print(indent .. "}") Aaron@12: elseif(type(v) == "table") then Aaron@0: print(indent .. k .. " = {") Aaron@0: debugprint(v, indent .. " ") Aaron@0: print(indent .. "}") Aaron@0: else Aaron@12: if(type(v) ~= "boolean") then Aaron@12: print(indent .. k .. " = " .. v) Aaron@12: else Aaron@12: print(indent .. k .. " = " .. (v and "true" or "false")) Aaron@12: end Aaron@0: end Aaron@0: end Aaron@0: end Aaron@8: Aaron@0: local options = { Aaron@0: type = "group", Aaron@0: name = "RecipeProfit", -- addon name to import from, don't localize Aaron@0: handler = {}, Aaron@0: disabled = false, Aaron@0: args = { killermonkey99@36: heading = { killermonkey99@36: order = 0, killermonkey99@36: type = "description", killermonkey99@36: name = "Thank you for using RecipeProfit for GatherMate!\n\n".. killermonkey99@36: "RecipeProfit was recently updated to work with patch 4.0.x and GatherMate 2. If you find any bugs you can report them on curse.com or curseforge.com by contacting the author 'Yuffles'. ".. killermonkey99@36: "You can also email the author at killermonkey99".. --[[anti-spambot]] "@".."gmail.com (please try to put \"RecipeProfit\" in the subject!)\n\n".. killermonkey99@36: "You can toggle the display of RecipeProfit nodes on the map and minimap by changing the options in the general GatherMate 2 menu.", killermonkey99@36: width = "full", killermonkey99@36: }, Aaron@0: } Aaron@0: } Aaron@0: killermonkey99@36: --maps wowhead IDs to localization independant map ids O_O killermonkey99@36: local zidmap={[1]=27,[3]=17,[4]=19,[8]=38,[10]=34,[11]=40,[12]=30,[14]=4, killermonkey99@36: [15]=141,[16]=181,[17]=11,[28]=22,[33]=37,[38]=35,[40]=39,[41]=32,[44]=36, killermonkey99@36: [45]=16,[46]=29,[47]=26,[51]=28,[65]=488,[66]=496,[67]=495,[85]=20, killermonkey99@36: [130]=21,[139]=23,[141]=41,[148]=42,[210]=492,[215]=9,[267]=24,[331]=43, killermonkey99@36: [357]=121,[361]=182,[394]=490,[400]=61,[405]=101,[406]=81,[440]=161, killermonkey99@36: [490]=201,[493]=241,[495]=491,[616]=606,[618]=281,[1377]=261,[1497]=382, killermonkey99@36: [1519]=301,[1537]=341,[1637]=321,[1638]=362,[1657]=381,[3430]=462, killermonkey99@36: [3433]=463,[3483]=465,[3487]=480,[3518]=477,[3519]=478,[3520]=473, killermonkey99@36: [3521]=467,[3522]=475,[3523]=479,[3524]=464,[3525]=476,[3537]=486, killermonkey99@36: [3557]=471,[3703]=481,[3711]=493,[4080]=499,[4197]=501,[4395]=504, killermonkey99@36: [4709]=607,[4714]=545,[4720]=544,[4737]=605,[4755]=611,[4815]=610, killermonkey99@36: [4922]=700,[5034]=720,[5042]=640,[5095]=708,[5144]=615,[5145]=614, killermonkey99@36: [5146]=613,[5287]=673,[5339]=689,[5416]=737,[5630]=737,[5695]=772 killermonkey99@36: } killermonkey99@36: Aaron@0: local defaults = { Aaron@16: --submitting cached data not yet implemented Aaron@16: location_cache = {}, killermonkey99@51: debugvars = {}, killermonkey99@51: blacklist = {}, Aaron@0: } Aaron@0: Aaron@0: function RecipeProfit:OnInitialize() Aaron@17: profile = RECIPEPROFIT_profile or defaults Aaron@7: Aaron@1: for k, v in pairs(defaults) do Aaron@1: profile[k] = profile[k] or v; Aaron@1: end Aaron@1: Aaron@12: self:RegisterChatCommand("recipeprofit", "ShowOptions") Aaron@12: self:RegisterChatCommand("rp", "ShowOptions") Aaron@12: self:RegisterChatCommand("profit", "ShowOptions") Aaron@12: Aaron@0: db.profile = profile Aaron@0: db.storage = {} Aaron@0: Aaron@0: GatherMate:GetModule("Config"):RegisterModule("RecipeProfit", options) Aaron@0: GatherMate:RegisterDBType("RecipeProfit", db.storage) Aaron@8: GatherMate.db.profile.show["RecipeProfit"] = GatherMate.db.profile.show["RecipeProfit"] or "always" Aaron@0: Aaron@17: set_node_constants() Aaron@17: inject_options() Aaron@8: Aaron@0: GatherMate:GetModule("Config"):UpdateConfig() killermonkey99@36: GatherMate:GetModule("Config"):SendMessage("GatherMate2ConfigChanged") Aaron@24: Aaron@24: --[[ hook GetNameForNode for custom highlighting ]] Aaron@24: local oldFunction = GatherMate.GetNameForNode Aaron@24: Aaron@24: GatherMate.GetNameForNode = function(lself, type, nodeID) Aaron@24: if(type == "RecipeProfit") then Aaron@24: return get_colored_note_name(lself, nodeID) Aaron@24: else Aaron@24: return oldFunction(lself, type, nodeID) Aaron@24: end Aaron@24: end Aaron@24: killermonkey99@36: --[[hook OnProfileChanged to fix cleanup database ]] Aaron@26: local oldFunction2 = GatherMate.OnProfileChanged Aaron@26: Aaron@26: GatherMate.OnProfileChanged = function(lself, ...) Aaron@26: lself.db.profile.cleanupRange["RecipeProfit"] = lself.db.profile.cleanupRange["RecipeProfit"] or 15 killermonkey99@36: lself.db.profile.show["RecipeProfit"] = lself.db.profile.show["RecipeProfit"] == "never" and "never" or "always"; Aaron@26: oldFunction2(lself, ...) Aaron@26: end Aaron@26: Aaron@0: end Aaron@0: Aaron@17: Aaron@17: function RecipeProfit:OnEnable() Aaron@17: Aaron@17: _G["MerchantPrevPageButton"]:HookScript("OnClick", self.UpdateButtons) Aaron@17: _G["MerchantNextPageButton"]:HookScript("OnClick", self.UpdateButtons) Aaron@17: Aaron@17: self:RegisterEvent("MERCHANT_SHOW", "UpdateButtons") Aaron@17: self:RegisterEvent("MERCHANT_UPDATE", "UpdateButtons") Aaron@17: self:RegisterEvent("BAG_UPDATE", "UpdateButtons") Aaron@28: Aaron@17: RecipeProfit:DoMerge() killermonkey99@45: if(GatherMate.db.profile.show["RecipeProfit"] ~= "always" and GatherMate.db.profile.show["RecipeProfit"] ~= "never") then killermonkey99@45: GatherMate.db.profile.show["RecipeProfit"] = "always" killermonkey99@45: GatherMate:GetModule("Config"):SendMessage("GatherMate2ConfigChanged") killermonkey99@45: end Aaron@17: end Aaron@17: killermonkey99@51: --Forward Functions for command listing killermonkey99@51: local set_var, killermonkey99@51: get_var, killermonkey99@51: show_help, killermonkey99@51: blacklist_add, killermonkey99@51: blacklist_show, killermonkey99@51: blacklist_query killermonkey99@51: killermonkey99@51: local commandList --Forward declaration of command list killermonkey99@51: killermonkey99@51: function RecipeProfit:ParseCommands(input, level, list) killermonkey99@51: args = {self:GetArgs(input, level)} killermonkey99@51: arg0 = args[#args - 1] killermonkey99@51: killermonkey99@51: if(not list[arg0]) then killermonkey99@51: print("Error parsing command. Type /rp help for help with this command.") killermonkey99@51: return killermonkey99@51: end killermonkey99@51: killermonkey99@51: if(type(list[arg0]) == "table") then killermonkey99@51: self:ParseCommands(input, level + 1, list[arg0]) killermonkey99@51: else killermonkey99@51: list[arg0](input) killermonkey99@51: end killermonkey99@51: end killermonkey99@51: killermonkey99@51: function RecipeProfit:ShowOptions(input) killermonkey99@51: if (input ~= "") then killermonkey99@51: self:ParseCommands(input, 1, commandList) killermonkey99@51: else killermonkey99@51: InterfaceOptionsFrame_OpenToCategory("GatherMate 2") killermonkey99@51: LibStub("AceConfigDialog-3.0"):SelectGroup("GatherMate 2", "RecipeProfit") killermonkey99@51: end Aaron@12: end Aaron@12: Aaron@16: function RecipeProfit:UpdateButtons(event, ...) Aaron@16: --print("UpdateButtons", event) Aaron@28: if(WorldMapFrame:IsShown()) then Aaron@28: local continent, zone = GetCurrentMapContinent(), GetCurrentMapZone() Aaron@28: SetMapZoom(continent) Aaron@28: SetMapZoom(continent, zone) Aaron@28: else Aaron@28: SetMapZoom(-1) Aaron@28: end Aaron@28: Aaron@28: GatherMate:GetModule("Display"):UpdateMaps() Aaron@28: Aaron@16: if(not MerchantFrame:IsVisible()) then Aaron@16: --print("UpdateButtons - (Event: ", event, ") - MerchantFrame not visible."); Aaron@16: return; Aaron@16: end Aaron@16: Aaron@16: for i=1, MERCHANT_ITEMS_PER_PAGE, 1 do Aaron@16: local buttonframe = _G["MerchantItem"..i]; Aaron@16: local index = (((MerchantFrame.page - 1) * MERCHANT_ITEMS_PER_PAGE) + i); Aaron@16: --print(index) Aaron@16: if index <= GetMerchantNumItems() then Aaron@16: button_update(buttonframe) Aaron@16: end Aaron@16: end Aaron@17: end Aaron@16: Aaron@16: function RecipeProfit:DoMerge() Aaron@16: ids = {} killermonkey99@36: local alliance = get_faction_db(); Aaron@16: Aaron@16: GatherMate:ClearDB("RecipeProfit") killermonkey99@36: for id, note in pairs(RECIPEPROFIT_database) do killermonkey99@51: local blacklisted = false; killermonkey99@51: killermonkey99@51: for _, v in pairs(RECIPEPROFIT_blacklist) do killermonkey99@51: if(note.stock == -1 and not note.item:find("Recipe:")) then killermonkey99@51: blacklisted = true; killermonkey99@51: end killermonkey99@51: killermonkey99@51: if(tonumber(v) == note.entry) then killermonkey99@51: blacklisted = true; killermonkey99@51: end killermonkey99@51: end killermonkey99@51: killermonkey99@51: if(not blacklisted) then killermonkey99@51: if((note.a and alliance) or (note.h and not alliance)) then killermonkey99@51: x, y = find_good_id(note.x, note.y) killermonkey99@51: add_note(x, y, note) killermonkey99@51: end killermonkey99@36: end Aaron@16: end Aaron@16: Aaron@16: GatherMate:SendMessage("GatherMateDataImport") killermonkey99@36: GatherMate:GetModule("Config"):SendMessage("GatherMate2ConfigChanged") Aaron@16: end Aaron@16: Aaron@16: function get_note_title(note, factionTag) Aaron@16: if(not factionTag) then Aaron@16: _, factionTag = get_faction_db(); Aaron@16: end Aaron@16: Aaron@16: return note.item.." - ("..note.vendor.." ".. factionTag ..")"; Aaron@16: end Aaron@16: Aaron@16: function add_note(x, y, note) killermonkey99@36: local coords = GatherMate.mapData:EncodeLoc(x/100, y/100, 0) killermonkey99@36: local zoneID = zidmap[note.map] killermonkey99@51: killermonkey99@51: if(note["realmap"]) then killermonkey99@51: zoneID = note.realmap killermonkey99@51: end killermonkey99@51: killermonkey99@36: if(not zoneID) then killermonkey99@36: return killermonkey99@36: end Aaron@33: killermonkey99@36: local nodeID = GatherMate.nodeIDs["RecipeProfit"][get_note_title(note, "")] killermonkey99@36: GatherMate:InjectNode(zoneID, coords, "RecipeProfit", nodeID) Aaron@16: end Aaron@16: Aaron@16: function get_faction_db() Aaron@16: local factionAlliance = db.profile.faction == "Alliance" or Aaron@16: db.profile.faction == "default" and UnitFactionGroup("player") == "Alliance"; killermonkey99@36: Aaron@16: if(factionAlliance) then killermonkey99@36: return true, "" Aaron@16: else killermonkey99@36: return false, "" Aaron@16: end Aaron@16: end Aaron@16: Aaron@16: function safe_cache_vendor() Aaron@16: if(not profile.location_cache[UnitName("NPC")]) then Aaron@16: SetMapToCurrentZone() Aaron@16: local pos = {} Aaron@16: pos.x, pos.y = GetPlayerMapPosition("player") killermonkey99@51: pos.map = GetCurrentMapAreaID(); Aaron@16: profile.location_cache[UnitName("NPC")] = pos Aaron@16: end Aaron@16: end Aaron@15: Aaron@9: function button_update(self) Aaron@9: local buttonName = _G[self:GetName().."Name"]; Aaron@9: local link = GetMerchantItemLink(_G[self:GetName().."ItemButton"]:GetID()); Aaron@9: if(not link) then Aaron@9: return; Aaron@9: end Aaron@9: Aaron@9: local sName, sLink, iRarity, iLevel, iMinLevel, sType, sSubType, iStackCount = GetItemInfo(link) Aaron@9: Aaron@16: if(sType == "Recipe" and safeRecipes[sName]) then Aaron@16: safe_cache_vendor(); Aaron@9: SetItemButtonNameFrameVertexColor(self, 0, 0, 1.0); Aaron@9: SetItemButtonSlotVertexColor(self, 0, 0, 0.5); Aaron@9: buttonName:SetText("* " .. sName) Aaron@28: Aaron@9: if(GetItemCount(link, true) == 0) then Aaron@9: buttonName:SetTextColor(0,1,1); Aaron@9: elseif(GetItemCount(link, true) < 5) then Aaron@9: buttonName:SetTextColor(1,0,1); Aaron@9: else Aaron@9: buttonName:SetTextColor(1,0,0); Aaron@9: end Aaron@28: Aaron@9: else Aaron@9: buttonName:SetTextColor(GameFontNormalSmall:GetTextColor()); Aaron@9: end Aaron@9: end Aaron@9: Aaron@16: function find_good_id(x, y) Aaron@0: if ids[x.." "..y] then Aaron@16: return find_good_id(x + .01, y) Aaron@0: end Aaron@0: Aaron@0: ids[x.." "..y] = true Aaron@0: return x, y Aaron@17: end Aaron@17: Aaron@17: local lastNodeTextureId = 0; Aaron@17: Aaron@17: function get_next_texture_id() Aaron@17: lastNodeTextureId = lastNodeTextureId + 1; Aaron@17: return lastNodeTextureId; Aaron@17: end Aaron@17: Aaron@17: function set_node_constants() Aaron@17: GatherMate.nodeIDs["RecipeProfit"] = {} Aaron@17: GatherMate.nodeTextures["RecipeProfit"] = {} Aaron@17: GatherMate.nodeMinHarvest["RecipeProfit"] = {} Aaron@17: Aaron@17: local nodes = GatherMate.nodeIDs["RecipeProfit"] killermonkey99@36: for id, note in pairs(RECIPEPROFIT_database) do Aaron@17: safeRecipes[note.item] = true; Aaron@24: local id = get_next_texture_id(); killermonkey99@36: killermonkey99@36: nodes[get_note_title(note, "")] = id; Aaron@24: nodeLookup[id] = note; Aaron@17: end Aaron@17: Aaron@17: for i = 1, lastNodeTextureId, 1 do Aaron@17: GatherMate.nodeTextures["RecipeProfit"][i] = "Interface\\Icons\\INV_Scroll_05" Aaron@17: end Aaron@17: Aaron@17: GatherMate.reverseNodeIDs["RecipeProfit"] = GatherMate:CreateReversedTable(nodes) Aaron@17: end Aaron@17: Aaron@17: function inject_options() killermonkey99@36: local acr = LibStub("AceConfigRegistry-3.0") killermonkey99@36: acr:GetOptionsTable("GatherMate 2", "dialog", "RecipeProfit-1.0").args["showRecipeProfit"] = { killermonkey99@36: order = 7, Aaron@17: name = "Show RecipeProfit nodes.", Aaron@17: desc = "Toggle showing nodes added by RecipeProfit.", Aaron@17: type = "select", Aaron@17: values = { killermonkey99@36: always = "Always show", killermonkey99@36: never = "Never show", Aaron@17: }, Aaron@17: arg = "RecipeProfit", Aaron@17: } Aaron@17: killermonkey99@36: GatherMate:GetModule("Config"):SendMessage("GatherMate2ConfigChanged") Aaron@17: end Aaron@24: Aaron@24: function get_colored_note_name(self, nodeID) Aaron@24: local text = self.reverseNodeIDs["RecipeProfit"][nodeID] Aaron@24: local sName, sLink, iRarity, iLevel, iMinLevel, sType, sSubType, iStackCount = GetItemInfo(nodeLookup[nodeID].itementry) Aaron@24: Aaron@24: if(not sLink) then killermonkey99@36: RecipeProfit:ScheduleTimer("UpdateButtons", 3) killermonkey99@36: return "|cFF000000(??) |cFF66DD66" .. text .. "|cFFFF0000 Please Wait (Querying Server)..." Aaron@24: end Aaron@24: Aaron@24: local count = GetItemCount(sLink, true) Aaron@24: local prefix = "|cFF888888(" .. count .. ") |cFF66DD66" Aaron@24: Aaron@24: if(count == 0) then Aaron@24: prefix = "|cFF00FFFF(" .. count .. ") |cFF66DD66" Aaron@24: elseif(count >= 5) then Aaron@24: prefix = "|cFFFF0000(" .. count .. ") |cFF66DD66" Aaron@24: end Aaron@24: Aaron@24: return prefix .. text Aaron@24: end killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: killermonkey99@51: --[[ Debug Commands ]] killermonkey99@51: --[[ killermonkey99@51: set_var, killermonkey99@51: get_var, killermonkey99@51: show_help, killermonkey99@51: blacklist_add, killermonkey99@51: blacklist_show killermonkey99@51: --]] killermonkey99@51: killermonkey99@51: function set_var(input) killermonkey99@51: print("set_var") killermonkey99@51: end killermonkey99@51: killermonkey99@51: function get_var(input) killermonkey99@51: print("get_var") killermonkey99@51: end killermonkey99@51: killermonkey99@51: function show_help(input) killermonkey99@51: print("show_help") killermonkey99@51: end killermonkey99@51: killermonkey99@51: function blacklist_add(input) killermonkey99@51: if(not profile["blacklist"]) then killermonkey99@51: profile["blacklist"] = {} killermonkey99@51: end killermonkey99@51: _, _, val = RecipeProfit:GetArgs(input,3) killermonkey99@51: table.insert(profile.blacklist, val) killermonkey99@51: print("Added npc id \""..val.."\"to NPC blacklist."); killermonkey99@51: end killermonkey99@51: killermonkey99@51: function blacklist_show(input) killermonkey99@51: if(not profile["blacklist"]) then killermonkey99@51: profile["blacklist"] = {} killermonkey99@51: end killermonkey99@51: killermonkey99@51: for k,v in ipairs(profile.blacklist) do killermonkey99@51: print(k..": "..v) killermonkey99@51: end killermonkey99@51: end killermonkey99@51: killermonkey99@51: function blacklist_query(input) killermonkey99@51: _, _, val = RecipeProfit:GetArgs(input,3) killermonkey99@51: if(not val) then killermonkey99@51: print("oops") killermonkey99@51: return killermonkey99@51: end killermonkey99@51: killermonkey99@51: entries = {}; killermonkey99@51: for _,note in pairs(RECIPEPROFIT_database) do killermonkey99@51: if(note.vendor:lower():find(val:lower()) and not entries[note.entry]) then killermonkey99@51: entries[note.entry] = true; killermonkey99@51: print(note.vendor.." (ID "..note.entry.."): "..note.x..", "..note.y) killermonkey99@51: end killermonkey99@51: end killermonkey99@51: end killermonkey99@51: killermonkey99@51: commandList = { killermonkey99@51: set = set_var, killermonkey99@51: get = get_var, killermonkey99@51: help = show_help, killermonkey99@51: blacklist = { killermonkey99@51: query = blacklist_query, killermonkey99@51: add = blacklist_add, killermonkey99@51: show = blacklist_show killermonkey99@51: } killermonkey99@51: } killermonkey99@51: