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@1: faction = "default", Aaron@1: safebuy = "on", Aaron@16: Aaron@16: --submitting cached data not yet implemented Aaron@16: enable_cache = false, Aaron@16: location_cache = {}, 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: Aaron@12: function RecipeProfit:ShowOptions() killermonkey99@36: InterfaceOptionsFrame_OpenToCategory("GatherMate 2") killermonkey99@36: LibStub("AceConfigDialog-3.0"):SelectGroup("GatherMate 2", "RecipeProfit") 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@36: if((note.a and alliance) or (note.h and not alliance)) then killermonkey99@36: x, y = find_good_id(note.x, note.y) killermonkey99@36: add_note(x, y, note) 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@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.enable_cache) then Aaron@16: return Aaron@16: end Aaron@16: 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") 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