-- Gatherer -- Written by Chandora GATHERER_VERSION="1.0.0"; -- -- Look, seriously a full half of this code is from MapNotes. -- The only reason I pinched it and put it in here is I couldn't -- work out how to extend MapNotes to do what I wanted it to do -- without actually editing the MapNotes files. -- -- Full credit to the MapNotes guys -- -- Global variables GATHERNOTE_UPDATE_INTERVAL = 0.25; GATHERNOTE_CHECK_INTERVAL = 5.0; GATHERER_MAXNUMNOTES = 25; GATHERER_LOADED = false; GATHERER_CLOSESTCHECK=0.4; Gatherer_RecordFlag=0; Gatherer_currentNode=""; Gatherer_currentAction=""; GatherMap_InCity = false; Gatherer_LoadCount = 0; Gatherer_MapOpen = false; Gatherer_UpdateWorldMap = -1; Gatherer_InWorld = false; GatherItems = { }; GatherSkills = { }; GatherZoneData = { }; -- Dict[ZoneName, Tuple[Continent, Zone]] GatherMainMapItem = { }; -- UI variables Gatherer_WorldMapDetailFrameWidth = 0; Gatherer_WorldMapDetailFrameHeight = 0; Gatherer_WorldMapPlayerFrameLevel = 0; Gather_Player = UnitName("player"); StaticPopupDialogs["GATHERER_VERSION_DIALOG"] = { text = TEXT(GATHERER_VERSION_WARNING), button1 = TEXT(OKAY), showAlert = 1, timeout = 0, }; --- ************************************************************************ -- Utilities function Gatherer_Round(x) if( x - math.floor(x) > 0.5) then x = x + 0.5; end return math.floor(x); end function Gatherer_GetMenuName(inputName) local name, info; if (inputName) then local firstLetter = string.sub(inputName, 1, 2); local carReplace = {["\195\160"] = "a", ["\195\161"] = "a", ["\195\162"] = "a", ["\195\163"] = "a", ["\195\164"] = "a", ["\195\168"] = "e", ["\195\169"] = "e", ["\195\170"] = "e", ["\195\171"] = "e", ["\195\180"] = "i", ["\195\173"] = "i", ["\195\174"] = "i", ["\195\175"] = "i", ["\195\179"] = "o", ["\195\180"] = "o", ["\195\181"] = "o", ["\195\182"] = "o", ["\195\185"] = "u", ["\195\186"] = "u", ["\195\187"] = "u", ["\195\188"] = "u"} local found; for code, repl in carReplace do firstLetter, found = string.gsub(firstLetter, code, repl); if (found > 0) then break; end end if (found > 0) then name = string.upper(firstLetter)..(string.sub(inputName, 3) or ""); else if (GetLocale()=="ruRU") then name = inputName; else name = string.upper(string.sub(inputName, 1, 1))..(string.sub(inputName, 2) or ""); end end local iconName, _ = Gatherer_GetDB_IconByGatherName(inputName); iconName = iconName or inputName; for _, rareMatch in Gather_RareMatch do if (iconName == rareMatch) then name = name.." ["..TYPE_RARE.."]"; break; end end if (Gather_SkillLevel[iconName]) then name = name.." ["..Gather_SkillLevel[iconName].."]"; end end return name, info; end -- ************************************************************************* -- Init and command line handler function Gatherer_OnLoad() this:RegisterEvent("WORLD_MAP_UPDATE"); this:RegisterEvent("CLOSE_WORLD_MAP"); -- never triggered apparently this:RegisterEvent("LEARNED_SPELL_IN_TAB"); -- follow current skills this:RegisterEvent("SPELLS_CHANGED"); -- follow current skills this:RegisterEvent("SKILL_LINES_CHANGED"); -- follow current skills this:RegisterEvent("SPELLCAST_START"); this:RegisterEvent("SPELLCAST_STOP"); this:RegisterEvent("SPELLCAST_FAILED"); this:RegisterEvent("CHAT_MSG_ADDON"); -- Events for off world non processing this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("PLAYER_LEAVING_WORLD"); -- Addon Loaded and player login/logout events this:RegisterEvent("ADDON_LOADED"); this:RegisterEvent("PLAYER_LOGIN"); this:RegisterEvent("PLAYER_LOGOUT"); Gatherer_LoadZoneData(); SLASH_GATHER1 = "/gather"; SLASH_GATHER2 = "/gatherer"; SlashCmdList["GATHER"] = function(msg) Gatherer_Command(msg); end end function Gatherer_Command(command) local SETTINGS = Gatherer_Settings; local i,j, cmd, param = string.find(command, "^([^ ]+) (.+)$"); if (not cmd) then cmd = command; end if (not cmd) then cmd = ""; end if (not param) then param = ""; end if ((cmd == "") or (cmd == "help")) then local useMinimap = "Off"; if (SETTINGS.useMinimap) then useMinimap = "On"; end local useMainmap = "Off"; if (SETTINGS.useMainmap) then useMainmap = "On"; end local mapMinder = "Off"; if (SETTINGS.mapMinder) then mapMinder = "On"; end local minderTime = "5s"; if (SETTINGS.minderTime) then minderTime = SETTINGS.minderTime.."s"; end Gatherer_ChatPrint("Usage:"); Gatherer_ChatPrint(" |cffffffff/gather (on|off|toggle)|r |cff2040ff["..useMinimap.."]|r - turns the gather minimap display on and off"); Gatherer_ChatPrint(" |cffffffff/gather mainmap (on|off|toggle)|r |cff2040ff["..useMainmap.."]|r - turns the gather mainmap display on and off"); Gatherer_ChatPrint(" |cffffffff/gather minder (on|off|toggle|)|r |cff2040ff["..mapMinder.."]|r - turns the gather map minder on and off (remembers and reopens your last open main map; within "..minderTime..")"); Gatherer_ChatPrint(" |cffffffff/gather dist |r |cff2040ff["..SETTINGS.maxDist.."]|r - sets the maximum search distance for display (0=infinite(default), typical=10)"); Gatherer_ChatPrint(" |cffffffff/gather num |r |cff2040ff["..SETTINGS.number.."]|r - sets the maximum number of items to display (default=10, up to 25)"); Gatherer_ChatPrint(" |cffffffff/gather fdist |r |cff2040ff["..SETTINGS.fadeDist.."]|r - sets a fade distance (in units) for the icons to fade out by (default = 20)"); Gatherer_ChatPrint(" |cffffffff/gather fperc |r |cff2040ff["..SETTINGS.fadePerc.."]|r - sets the percentage for fade at max fade distance (default = 80 [=80% faded])"); Gatherer_ChatPrint(" |cffffffff/gather theme |r |cff2040ff["..SETTINGS.iconSet.."]|r - sets the icon theme: original, shaded (default), iconic or iconshade"); Gatherer_ChatPrint(" |cffffffff/gather idist |r |cff2040ff["..SETTINGS.miniIconDist.."]|r - sets the minimap distance at which the gather icon will become iconic (0 = off, 1-60 = pixel radius on minimap, default = 40)"); Gatherer_ChatPrint(" |cffffffff/gather herbs (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("herbs").."]|r - select whether to show herb data on the minimap"); Gatherer_ChatPrint(" |cffffffff/gather mining (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("mining").."]|r - select whether to show mining data on the minimap"); Gatherer_ChatPrint(" |cffffffff/gather treasure (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("treasure").."]|r - select whether to show treasure data on the minimap"); Gatherer_ChatPrint(" |cffffffff/gather options|r - show/hide UI Options dialog."); Gatherer_ChatPrint(" |cffffffff/gather report|r - show/hide report dialog."); Gatherer_ChatPrint(" |cffffffff/gather search|r - show/hide search dialog."); Gatherer_ChatPrint(" |cffffffff/gather loginfo (on|off)|r - show/hide logon information."); Gatherer_ChatPrint(" |cffffffff/gather filterrec (herbs|mining|treasure)|r - link display filter to recording for selected gathering type"); Gatherer_ChatPrint(" |cffffffff/gather debug ([on]|off)|r |cff2040ff["..Gatherer_EBoolean[SETTINGS.debug].."]|r - show/hide debug messages"); Gatherer_ChatPrint(" |cffffffff/gather p2p ([on]|off)|r |cff2040ff["..Gatherer_EBoolean[SETTINGS.p2p].."]|r - enable/disable peer-to-peer functions"); elseif (cmd == "options" ) then if ( GathererUI_DialogFrame:IsVisible() ) then GathererUI_HideOptions(); else GathererUI_ShowOptions(); end elseif (cmd == "debug") then if (not param or param == "" or param == "on") then SETTINGS.debug = true; Gatherer_ChatPrint("Debug messages enabled"); elseif (param == "off") then SETTINGS.debug = false; Gatherer_ChatPrint("Debug messages disabled"); end elseif (cmd == "p2p") then if (not param or param == "" or param == "on") then SETTINGS.p2p = true; Gatherer_ChatPrint("Peer-to-peer functions enabled"); elseif (param == "off") then SETTINGS.p2p = false; Gatherer_ChatPrint("Peer-to-peer functions disabled"); end elseif (cmd == "report" ) then showGathererInfo(1); elseif (cmd == "search" ) then showGathererInfo(2); elseif (cmd == "loginfo" ) then local value; if (not param or param == "") then value = "on"; else value = param; end Gatherer_ChatPrint("Setting log information display to "..value); SETTINGS.logInfo = value; elseif ( cmd == "filterrec" ) then local value=-1; if (not param) then return; end; if ( param == "treasure" ) then value = 0; elseif ( param == "herbs" ) then value = 1; elseif ( param == "mining" ) then value = 2; end if ( value > -1 ) then if ( SETTINGS.filterRecording[value] ) then SETTINGS.filterRecording[value] = nil; Gatherer_ChatPrint("Turned filter/recording link for "..param.." off."); else SETTINGS.filterRecording[value] = 1; Gatherer_ChatPrint("Turned filter/recording link for "..param.." on."); end end elseif (cmd == "on") then SETTINGS.useMinimap = true; Gatherer_OnUpdate(0, true); SETTINGS.useMinimapText = "on"; Gatherer_ChatPrint("Turned gather minimap display on"); elseif (cmd == "off") then SETTINGS.useMinimap = false; SETTINGS.useMinimapText = "off"; Gatherer_OnUpdate(0, true); Gatherer_ChatPrint("Turned gather minimap display off (still collecting)"); elseif (cmd == "toggle") then SETTINGS.useMinimap = not SETTINGS.useMinimap; Gatherer_OnUpdate(0, true); if (SETTINGS.useMinimap) then Gatherer_ChatPrint("Turned gather minimap display on"); SETTINGS.useMinimapText = "on"; else Gatherer_ChatPrint("Turned gather minimap display off (still collecting)"); SETTINGS.useMinimapText = "off"; end elseif (cmd == "dist") then local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0.0; end if (value <= 0) then SETTINGS.maxDist = 0; else SETTINGS.maxDist = value + 0.0; end Gatherer_ChatPrint("Setting maximum note distance to "..SETTINGS.maxDist); Gatherer_OnUpdate(0, true); elseif (cmd == "fdist") then local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0.0; end if (value <= 0) then SETTINGS.fadeDist = 0; else SETTINGS.fadeDist = value + 0.0; end Gatherer_ChatPrint("Setting fade distance to "..SETTINGS.fadeDist); Gatherer_OnUpdate(0, true); elseif (cmd == "fperc") then local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0.0; end if (value <= 0) then SETTINGS.fadePerc = 0; else SETTINGS.fadePerc = value + 0.0; end Gatherer_ChatPrint("Setting fade percent at fade distance to "..SETTINGS.fadePerc); Gatherer_OnUpdate(0, true); elseif ((cmd == "idist") or (cmd == "icondist")) then local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0; end if (value <= 0) then SETTINGS.miniIconDist = 0; else SETTINGS.miniIconDist = value + 0; end Gatherer_ChatPrint("Setting iconic distance to "..SETTINGS.miniIconDist); Gatherer_OnUpdate(0, true); elseif (cmd == "theme") then if (Gather_IconSet[param]) then SETTINGS.iconSet = param; Gatherer_ChatPrint("Gatherer theme set to "..SETTINGS.iconSet); else Gatherer_ChatPrint("Unknown theme: "..param); end Gatherer_OnUpdate(0, true); elseif ((cmd == "num") or (cmd == "number")) then local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0; end if (value < 0) then SETTINGS.number = 10; elseif (value <= GATHERER_MAXNUMNOTES) then SETTINGS.number = math.floor(value + 0); else SETTINGS.number = GATHERER_MAXNUMNOTES; end if (SETTINGS.number == 0) then SETTINGS.useMinimap = false; SETTINGS.useMinimapText = "off"; Gatherer_OnUpdate(0, true); Gatherer_ChatPrint("Turned gather minimap display off (still collecting)"); else if ((SETTINGS.number > 0) and (SETTINGS.useMinimap == false)) then SETTINGS.useMinimap = true; SETTINGS.useMinimapText = "on"; Gatherer_ChatPrint("Turned gather minimap display on"); end Gatherer_ChatPrint("Displaying "..SETTINGS.number.." notes at once"); Gatherer_OnUpdate(0, true); end elseif (cmd == "mainmap") then if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then SETTINGS.useMainmap = false; elseif (param == "toggle") then SETTINGS.useMainmap = not SETTINGS.useMainmap; else SETTINGS.useMainmap = true; end if (SETTINGS.useMainmap) then Gatherer_ChatPrint("Displaying notes in main map"); Gatherer_WorldMapDisplay:SetText("Hide Items"); else Gatherer_ChatPrint("Not displaying notes in main map"); Gatherer_WorldMapDisplay:SetText("Show Items"); end if (SETTINGS.useMainmap and SETTINGS.showWorldMapFilters and SETTINGS.showWorldMapFilters == 1) then GathererWD_DropDownFilters:Show(); end elseif (cmd == "minder") then if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then SETTINGS.mapMinder = false; elseif (param == "toggle") then SETTINGS.mapMinder = not SETTINGS.mapMinder; elseif (param == "on") then SETTINGS.mapMinder = true; else local i,j, value = string.find(param, "(%d+)"); if (not value) then value = 0; else value = value + 0; end if (value <= 0) then SETTINGS.mapMinder = false; SETTINGS.minderTime = 0; else SETTINGS.mapMinder = true; SETTINGS.minderTime = value + 0; end Gatherer_ChatPrint("Setting map minder timeout to "..SETTINGS.minderTime); end if (SETTINGS.mapMinder) then Gatherer_ChatPrint("Map minder activated at "..SETTINGS.minderTime); else Gatherer_ChatPrint("Not minding your map"); end elseif ((cmd == "herbs") or (cmd == "mining") or (cmd == "treasure")) then if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then Gatherer_SetFilter(cmd, "off"); Gatherer_ChatPrint("Not displaying "..cmd.." notes in minimap"); elseif (param == "on" or param == "On" ) then Gatherer_SetFilter(cmd, "on"); Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap"); elseif (param == "toggle" or param == "") then local cur = Gatherer_GetFilterVal(cmd); if ((cur == "on") or (cur == "auto")) then cur = "off"; Gatherer_SetFilter(cmd, "off"); Gatherer_ChatPrint("Not displaying "..cmd.." notes in minimap"); else cur = "on"; Gatherer_SetFilter(cmd, "on"); Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap"); end else Gatherer_SetFilter(cmd, "auto"); Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap based on ability"); end Gatherer_OnUpdate(0, true); GatherMain_Draw(); end end -- ************************************************************************* -- Events Handler function Gatherer_OnEvent(event) if (not event) then return; end; -- Enable/Disable event processing for zoning if (event == "PLAYER_ENTERING_WORLD" ) then this:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF"); -- standard gathering event this:RegisterEvent("UI_ERROR_MESSAGE"); -- event added for impossible to gather item this:RegisterEvent("CHAT_MSG_LOOT"); -- event added for fishing node Gatherer_InWorld = true; elseif (event == "PLAYER_LEAVING_WORLD" ) then this:UnregisterEvent("CHAT_MSG_SPELL_SELF_BUFF"); -- standard gathering event this:UnregisterEvent("UI_ERROR_MESSAGE"); -- event added for impossible to gather item this:UnregisterEvent("CHAT_MSG_LOOT"); -- event added for fishing node Gatherer_InWorld = false; -- process loot received message for fishing node trigger elseif ( event == "CHAT_MSG_LOOT" ) then local _, _, fishItem = string.find(arg1, GATHERER_ReceivesLoot ); local gfishTooltip = Gatherer_ExtractItemFromTooltip() if ( fishItem and not UnitExists("mouseover") ) then Gatherer_ReadBuff(event, fishItem, gfishTooltip); end -- process event to record, normally not possible gather (low/inexistant skill) elseif ( event == "UI_ERROR_MESSAGE" ) then -- process gather error message -- need to be in standard gather range to get the correct message to process. if (arg1 and (strfind(arg1, GATHERER_REQUIRE.." "..OLD_TRADE_HERBALISM) or strfind(arg1, GATHERER_NOSKILL.." "..OLD_TRADE_HERBALISM) or strfind(arg1, OLD_TRADE_HERBALISM.." "..GATHERER_NOSKILL) or strfind(arg1, GATHERER_REQUIRE.." "..TRADE_MINING) or strfind(arg1, GATHERER_NOSKILL.." "..TRADE_MINING) or strfind(arg1, TRADE_MINING.." "..GATHERER_NOSKILL))) then Gatherer_ReadBuff(event); end -- process chatmessages elseif ( event == "CHAT_MSG_SPELL_SELF_BUFF" ) then Gatherer_ReadBuff(event); -- process AddOn communication elseif strfind(event, "CHAT_MSG_ADDON") then Gatherer_AddonMessageEvent(arg1, arg2, arg3); -- process tooltips text for 1.12 elseif ( event == "SPELLCAST_START" ) then if ( arg1 and (arg1 == GATHER_HERBALISM or arg1 == TRADE_MINING or arg1 == TRADE_OPENING or arg1 =="") ) then Gatherer_Debug("|cffffffffEvent :|r "..event.." => "..arg1); Gatherer_currentNode = GameTooltipTextLeft1:GetText(); Gatherer_currentAction = arg1; Gatherer_Debug("Current node: "..(Gatherer_currentNode or "nil")); Gatherer_RecordFlag=1; end elseif ( event == "SPELLCAST_STOP" and Gatherer_RecordFlag == 1 ) then Gatherer_ReadBuff(event); Gatherer_currentNode=nil; elseif ( event == "SPELLCAST_STOP" and Gatherer_RecordFlag == 0 ) then Gatherer_currentNode=nil; Gatherer_RecordFlag = 0; elseif ( event == "SPELLCAST_FAILED" ) then Gatherer_RecordFlag = 0; elseif (event == "WORLD_MAP_UPDATE") then if (WorldMapFrame:IsVisible()) then local serverTime = GetTime(); if (Gatherer_Settings.mapMinder == true and Gatherer_MapOpen == false) then -- Enhancement to open to last opened map if we were there less than a minute ago -- Otherwise, go to the player's current position local startContinent, startZone; if (Gatherer_CloseMap and (serverTime - Gatherer_CloseMap.time < Gatherer_Settings.minderTime)) then startContinent = Gatherer_CloseMap.continent; startZone = Gatherer_CloseMap.zone; else startContinent, startZone = Gatherer_GetCurrentZone(); end Gatherer_MapOpen = true; if ( GetCurrentMapContinent()>0 and startZone and startZone > 0 ) then SetMapZoom(startContinent, startZone); end end Gatherer_MapOpen = true; local mapContinent = GetCurrentMapContinent(); local mapZone = GetCurrentMapZone(); Gatherer_CloseMap = { continent = mapContinent, zone = mapZone, time = GetTime() }; GatherMain_Draw(); elseif (Gatherer_MapOpen) then Gatherer_MapOpen = false; Gatherer_ChangeMap(); GatherMain_Draw(); end elseif ( event == "CLOSE_WORLD_MAP") then -- never called apparently Gatherer_MapOpen = false; Gatherer_ChangeMap() GatherMain_Draw(); elseif( event == "ADDON_LOADED") then if ( myAddOnsFrame_Register ) then -- myAddons Support GathererDetails["name"] = "Gatherer"; GathererDetails["version"] = GATHERER_VERSION; GathererDetails["author"] = "Norganna"; GathererDetails["website"] = "http://gathereraddon.com"; GathererDetails["category"] = MYADDONS_CATEGORY_PROFESSIONS; GathererDetails["frame"] = "Gatherer"; GathererDetails["optionsframe"] = "GathererUI_DialogFrame"; -- Register the addon in myAddOns if(myAddOnsFrame_Register) then myAddOnsFrame_Register(GathererDetails, GathererHelp); end end if (arg1 and string.lower(arg1) == "gatherer") then Gatherer_Configuration.Load(); GATHERER_LOADED = true; Gatherer_sanitizeDatabase(GatherItems) Gatherer_OnUpdate(0, true); Gatherer_Print("Gatherer p2p v"..GATHERER_VERSION.." -- Loaded!"); if (Gatherer_Settings.useMainmap == true) then Gatherer_WorldMapDisplay:SetText("Hide Items"); else Gatherer_WorldMapDisplay:SetText("Show Items"); end -- Warning at logon on Gatherer Version number change to check for localization zone change (commented, not needed in 1.12) --if ( GetLocale() == "deDE" and (not Gatherer_Settings.Version or (Gatherer_Settings.Version and Gatherer_Settings.Version ~= GATHERER_VERSION ))) then -- StaticPopup_Show("GATHERER_VERSION_DIALOG"); --end -- record current version number in order to identify the data for backup utilities Gatherer_Settings.Version = GATHERER_VERSION; -- Get values for World Map once for all Gatherer_WorldMapDetailFrameWidth = WorldMapDetailFrame:GetWidth(); Gatherer_WorldMapDetailFrameHeight = WorldMapDetailFrame:GetHeight(); Gatherer_WorldMapPlayerFrameLevel = WorldMapPlayer:GetFrameLevel(); -- New backup format allows the user to permenantly save their data -- in an automatically restoring file "GatherBase.lua" -- All data in this file will be loaded "under" the saved data every -- time the addon loads so that in the event of a savedVariables wipe -- the next time they run WoW, their data will be back to the last -- backed up point. (This utility will appear in a later version of -- Gatherer as a Java applet) if (GatherItemBase ~= nil) then for c, cd in GatherItems do for z, zd in cd do for n,nd in zd do for i,id in nd do local matched = 0; local max = 0; for j,jd in GatherItemBase[c][z][n] do if (math.abs(id.x- jd.y) < 0.05) then matched = j; end max = j; end if (matched > 0) then GatherItemBase[c][z][n][matched] = id; else GatherItemBase[c][z][n][max+1] = id; end end end end end GatherItems = GatherItemBase; end end elseif ( event == "PLAYER_LOGIN" ) then local SETTINGS = Gatherer_Settings; local useMinimap = (SETTINGS.useMinimap) and "On" or "Off"; local useMainmap = (SETTINGS.useMainmap) and "On" or "Off"; local mapMinder = (SETTINGS.mapMinder) and "On" or "Off"; local minderTime = SETTINGS.minderTime.."s"; if ( SETTINGS.logInfo and SETTINGS.logInfo == "on" ) then Gatherer_Print("[Player: "..Gather_Player..", Theme: "..SETTINGS.iconSet..", Mainmap: "..useMainmap..", Minimap: "..useMinimap..", MaxDist: "..SETTINGS.maxDist.." units, NoteCount: "..SETTINGS.number..", Fade: "..SETTINGS.fadePerc.."% at "..SETTINGS.fadeDist.." units, IconDist: "..SETTINGS.miniIconDist.."px on minimap, MapMinder: "..mapMinder.." ("..minderTime.."), Filters: herbs="..Gatherer_GetFilterVal("herbs")..", mining="..Gatherer_GetFilterVal("mining")..", treasure="..Gatherer_GetFilterVal("treasure").."]"); end elseif ( event == "PLAYER_LOGOUT" ) then Gatherer_Configuration.Save(); elseif ( event == "LEARNED_SPELL_IN_TAB" or event == "SPELLS_CHANGED" ) then local numSkills = tonumber(GetNumSkillLines()); if ( GetNumSkillLines() > 0 ) then Gatherer_GetSkills(); end elseif ( event == "SKILL_LINES_CHANGED" ) then local numSkills = tonumber(GetNumSkillLines()); if ( GetNumSkillLines() > 0 ) then Gatherer_GetSkills(); end else Gatherer_ChatPrint("Gatherer Unknown event: "..event); end end -- ************************************************************************* -- Filter related functions local filterConversion = { ["herbs"] = 1, ["mining"] = 2, ["treasure"] = 0, [0] = 0, [1] = 1, [2] = 2, } function Gatherer_SetFilter(type, value) local type = filterConversion[type]; if ( type ) then Gatherer_Settings.filters[type] = value; end end function Gatherer_GetFilterVal(type) local type = filterConversion[type]; value = Gatherer_Settings.filters[type]; if (not value) then return "auto"; end return value; end function Gatherer_GetFilter(filter) local value = Gatherer_GetFilterVal(filter); local filterVal = false; if (value == "on") then filterVal = true; elseif (value == "off") then filterVal = false; elseif (value == "auto") then if (filter == "treasure") then filterVal = true; end if (not GatherSkills) then filterVal = true; end if ((GatherSkills[filter]) and (GatherSkills[filter] > 0)) then filterVal = true; end end return filterVal; end function Gatherer_GetSkills() local GatherExpandedHeaders = {}; local i, j; if ( not GatherSkills ) then GatherSkills = {}; end; Gatherer:UnregisterEvent("SKILL_LINES_CHANGED"); -- search the skill tree for gathering skills for i=0, GetNumSkillLines(), 1 do local skillName, header, isExpanded, skillRank, _, _, _, _, _, _, _, _ = GetSkillLineInfo(i); -- expand the header if necessary if ( header and not isExpanded ) then GatherExpandedHeaders[i] = skillName; end end ExpandSkillHeader(0); for i=1, GetNumSkillLines(), 1 do local skillName, header, _, skillRank, _, _, _, _, _, _, _, _ = GetSkillLineInfo(i); -- check for the skill name if (skillName and not header) then if (skillName == TRADE_HERBALISM) then GatherSkills.herbs = skillRank; elseif (skillName == TRADE_MINING) then GatherSkills.mining = skillRank; end end -- once we got both, no need to look the rest if ( GatherSkills.herbs and GatherSkills.mining ) then break; end end -- close headers expanded during search process for i=0, GetNumSkillLines() do local skillName, header, isExpanded, _, _, _, _, _, _, _, _, _ = GetSkillLineInfo(i); for j in GatherExpandedHeaders do if ( header and skillName == GatherExpandedHeaders[j] ) then CollapseSkillHeader(i); GatherExpandedHeaders[j] = nil; end end end end local function random_choice(t) if not t then return end local choiceI = nil; local choiceO = nil; local n = 0; for i, o in pairs(t) do n = n + 1 if math.random() < (1/n) then choiceI, choiceO = i, o end end return choiceI, choiceO end local function selectRandomGather() -- type: () -> Tuple[GatherName, Gatherer_EGatherType, Continent, Zone, float, float, IconName, EGatherEventType] -- returns the arguments set for the Gatherer_BroadcastGather function local randomContinent, continentData = random_choice(GatherItems); local randomZone, zoneData = random_choice(continentData); local randomGather, gatherNodes = random_choice(zoneData); local nodeIndex, randomNode = random_choice(gatherNodes); Gatherer_ChatNotify('randomly selected: '..table.concat( {randomContinent, randomZone, randomGather, nodeIndex}, ', ' ), Gatherer_ENotificationType.debug); if not nodeIndex then return nil end local eventType = 1; -- EGatherEventType.no_skill -- values don't matter, I just hate lua local FISHING_GATHERS = {['floating wreckage']=0, ['school']=''}; if FISHING_GATHERS[randomGather] then eventType = 2; -- EGatherEventType.fishing end return randomGather, randomNode.gtype, randomContinent, randomZone, randomNode.x, randomNode.y, randomNode.icon, eventType end -- ************************************************************************* -- Update related functions local Gatherer_UpdateTicker = 0.0; local Gatherer_AnnouncePeriod = 10; local Gatherer_CycleCount = 0; local Gatherer_SecondsToAnnounce = Gatherer_AnnouncePeriod; function Gatherer_TimeCheck(timeDelta) if (not GatherNotes) then GatherNotes = { timeDiff=0, checkDiff=0 }; else GatherNotes.checkDiff = GatherNotes.checkDiff + timeDelta; if (GatherNotes.checkDiff > GATHERNOTE_CHECK_INTERVAL) then GatherNotes.checkDiff = 0; Gatherer_OnUpdate(0,true); end end Gatherer_UpdateTicker = Gatherer_UpdateTicker + arg1; if( Gatherer_UpdateTicker < 1 ) then return end -- reset seconds counter Gatherer_UpdateTicker = 0.0; -- the code below will run not more frequently -- than once a second Gatherer_SecondsToAnnounce = Gatherer_SecondsToAnnounce - 1 -- Gatherer_Print('Every second '..Gatherer_SecondsToAnnounce..' seconds.') if Gatherer_SecondsToAnnounce > 0 then return end -- the code below will run not more frequently -- than once Gatherer_AnnouncePeriod seconds if Gatherer_Settings.p2p then local args = {selectRandomGather()}; -- if failed to get a random node skip cycle if not args[1] then return end Gatherer_CycleCount = Gatherer_CycleCount + 1 if Gatherer_Settings.debug then Gatherer_ChatPrint('Gatherer: Cycle #'..Gatherer_CycleCount); Gatherer_ChatNotify( 'Sending random node once in '..Gatherer_AnnouncePeriod..' seconds.', Gatherer_ENotificationType.sending ); Gatherer_ChatNotify( 'args to send: '..table.concat(args, ', '), Gatherer_ENotificationType.sending ); end Gatherer_BroadcastGather(unpack(args)) end Gatherer_SecondsToAnnounce = Gatherer_AnnouncePeriod end function Gatherer_OnUpdate(timeDelta, force) if (not GATHERER_LOADED) then Gatherer_Print("Gatherer not loaded"); return; end local SETTINGS = Gatherer_Settings; if (Gatherer_InWorld == false ) then return; end if (not SETTINGS.useMinimap) then Gatherer_HideAll(); return; end local recalculate = false; local needsUpdate = false; if (not GatherNotes) then GatherNotes = { timeDiff=0, checkDiff=0 }; needsUpdate = true; else GatherNotes.timeDiff = GatherNotes.timeDiff + timeDelta; if (GatherNotes.timeDiff > GATHERNOTE_UPDATE_INTERVAL) then needsUpdate = true; end end if (force) then needsUpdate = true; recalculate = true; end if (needsUpdate) then GatherNotes.timeDiff = 0; -- Find the closest gathers local continent, zone = Gatherer_GetCurrentZone(); if ((continent == 0) or (zone == 0)) then Gatherer_HideAll(); return; end local inCity = GatherMap_InCity; local zoomLevel = Minimap:GetZoom(); local px, py = Gatherer_PlayerPos(); if ((px == 0) and (py == 0)) then return; end local xMovement = 0; if (ClosestGathers and ClosestGathers.px) then xMovement = math.abs(ClosestGathers.px - px); end local yMovement = 0; if (ClosestGathers and ClosestGathers.py) then yMovement = math.abs(ClosestGathers.py - py); end if (not ClosestGathers or ClosestGathers.playerC ~= continent or ClosestGathers.playerZ ~= zone or xMovement + yMovement > 0.01) then recalculate = true; end local displayNumber = 10; if (SETTINGS.number and SETTINGS.number > 0) then displayNumber = SETTINGS.number; end local playerDeltaX = 0; local playerDeltaY = 0; if (recalculate == true) then ClosestGathers = {}; ClosestGathers = Gatherer_FindClosest(displayNumber); if (ClosestGathers.count > 0) then ClosestGathers.inCity = inCity; ClosestGathers.zoomLevel = zoomLevel; ClosestGathers.scaleX, ClosestGathers.scaleY = Gatherer_GetMapScale(continent, zone, inCity, zoomLevel); end else if ((inCity ~= ClosestGathers.inCity) or (zoomLevel ~= ClosestGathers.zoomLevel)) then ClosestGathers.inCity = inCity; ClosestGathers.zoomLevel = zoomLevel; ClosestGathers.scaleX, ClosestGathers.scaleY = Gatherer_GetMapScale(continent, zone, inCity, zoomLevel); end local absX, absY = Gatherer_AbsCoord(continent, zone, px, py); playerDeltaX = ClosestGathers.playerX - absX; playerDeltaY = ClosestGathers.playerY - absY; end local maxPos = 0; if (ClosestGathers and ClosestGathers.count > 0) then local closestPos, closestGather, closestID; local currentPos = 1; for closestPos, closestGather in ClosestGathers.items do local skip_node = 0; if ( currentPos > SETTINGS.number ) then skip_node =1; end if ( skip_node == 0 ) then -- need to position and label the corresponding button local gatherNote = getglobal("GatherNote"..currentPos); local gatherNoteTexture = getglobal("GatherNote"..currentPos.."Texture"); local itemDeltaX = closestGather.deltax+playerDeltaX; local itemDeltaY = closestGather.deltay+playerDeltaY; local offsX, offsY, gDist = Gatherer_MiniMapPos(itemDeltaX, itemDeltaY, ClosestGathers.scaleX, ClosestGathers.scaleY); gatherNote:SetPoint("CENTER", Minimap, "CENTER", offsX, -offsY); local iconSet = SETTINGS.iconSet; local iDist = SETTINGS.miniIconDist; if (not iconSet) then iconSet = "shaded"; end if (not iDist) then iDist = 38; end local _, _, sDist = Gatherer_MiniMapPos(iDist/10000, 0, ClosestGathers.scaleX, ClosestGathers.scaleY); if ((iDist > 0) and (gDist > (math.floor(sDist)-1))) then iconSet = "iconic"; end local fadeDist = SETTINGS.fadeDist / 1000; local fadePerc = SETTINGS.fadePerc / 100; local alpha = 1.0; local objDist = Gatherer_Pythag(itemDeltaX, itemDeltaY); if ((fadeDist > 0) and (fadePerc > 0)) then local distRatio = objDist / fadeDist ; alpha = 1.0 - (math.min(1.0, math.max(0.0, distRatio)) * fadePerc); end local textureType = closestGather.item.gtype; local textureIcon = closestGather.item.icon; if ( type(textureType) == "number" ) then textureType = Gatherer_EGatherType[textureType]; end if ( type(textureIcon) == "number" ) then textureIcon = Gatherer_GetDB_IconIndex(textureIcon, textureType); end if (not textureIcon) then textureIcon = "default"; end; if (SETTINGS.iconSet == "iconshade" ) then iconSet="iconic"; if ( gDist < (math.floor(sDist)-1) ) then alpha=0.4; end end if (not Gather_IconSet[iconSet]) then iconSet = "shaded"; end if (not Gather_IconSet[iconSet][textureType]) then textureType = "Default"; end local selectedTexture = Gather_IconSet[iconSet][textureType][textureIcon]; if (not selectedTexture) then selectedTexture = Gather_IconSet[iconSet][textureType]["default"]; end gatherNoteTexture:SetTexture(selectedTexture); gatherNote:SetFrameLevel(MiniMapTrackingFrame:GetFrameLevel()); gatherNote:SetAlpha(alpha); -- Added to allow hiding if under min distance if ( SETTINGS.NoIconOnMinDist ~= nil and SETTINGS.NoIconOnMinDist == 1 ) then if ( gDist < (math.floor(sDist)-1) ) then gatherNote:Hide(); else gatherNote:Show(); end elseif ( (not SETTINGS.NoIconOnMinDist or SETTINGS.NoIconOnMinDist == 0) and SETTINGS.alphaUnderMinIcon and gDist < (math.floor(sDist)-1) ) then if ( SETTINGS.iconSet and SETTINGS.iconSet ~= "iconshade" ) then gatherNote:SetAlpha(SETTINGS.alphaUnderMinIcon / 100); end gatherNote:Show(); else gatherNote:Show(); end if (currentPos > maxPos) then maxPos = currentPos; end currentPos = currentPos + 1; end skip_node = 0; end end while (maxPos < GATHERER_MAXNUMNOTES) do maxPos = maxPos+1; local gatherNote = getglobal("GatherNote"..maxPos); if ( gatherNote:IsShown() ) then gatherNote:Hide(); end end end end -- ************************************************************************* -- UI related functions function Gatherer_OnClick() -- function changed to be able to ping through the MiniNote (mostly direct copy) (only change: this -> Minimap) local x, y = GetCursorPosition(); if ( Minimap.GetEffectiveScale ~= nil ) then x = x / Minimap:GetEffectiveScale(); y = y / Minimap:GetEffectiveScale(); else x = x / Minimap:GetScale(); y = y / Minimap:GetScale(); end local cx, cy = Minimap:GetCenter(); x = x + CURSOR_OFFSET_X - cx; y = y + CURSOR_OFFSET_Y - cy; if ( sqrt(x * x + y * y) < (Minimap:GetWidth() / 2) ) then Minimap:PingLocation(x, y); end end -- ************************************************************************* -- Display functions function Gatherer_HideAll() local mmPos = 0; while (mmPos < GATHERER_MAXNUMNOTES) do mmPos = mmPos+1; local gatherNote = getglobal("GatherNote"..mmPos); gatherNote:Hide(); end end function Gatherer_CreateNoteObject(noteNumber) local button; local overlayFrameNumber = math.floor((noteNumber - 1000) / 100 + 1); if(getglobal("GatherMain"..noteNumber)) then button = getglobal("GatherMain"..noteNumber); else Gatherer_Debug("create id "..noteNumber.." frame ".. overlayFrameNumber); button = CreateFrame("Button" ,"GatherMain"..noteNumber, getglobal("GathererMapOverlayFrame"..overlayFrameNumber), "GatherMainTemplate"); button:SetID(noteNumber); end return button; end function GatherMain_Draw() local lastUnused = 1000; local maxNotes = 1600; local gatherName, gatherData; local SETTINGS = Gatherer_Settings; -- prevent the function from running twice at the same time. if (Gatherer_UpdateWorldMap == 0 ) then return; end; Gatherer_UpdateWorldMap = 0; if ((Gatherer_MapOpen) and (SETTINGS.useMainmap)) then local mapContinent = GetCurrentMapContinent(); local mapZone = GetCurrentMapZone(); if ((mapContinent > 0) and (mapZone > 0) and (GatherItems[mapContinent]) and (GatherItems[mapContinent][mapZone])) then for gatherName, gatherData in GatherItems[mapContinent][mapZone] do local gatherType = "Default"; local specificType = ""; local allowed = true; local minSetSkillLevel = 0; local idx_count=0; specificType = Gatherer_FindOreType(gatherName); if (specificType) then -- Ore gatherType = 2; allowed = Gatherer_GetFilter("mining"); else specificType = Gatherer_FindTreasureType(gatherName); if (specificType ) then -- Treasure gatherType = 0; allowed = Gatherer_GetFilter("treasure"); else specificType = Gatherer_FindFishType(gatherName); if ( specificType ) then -- Treasure Fish gatherType = 0; allowed = Gatherer_GetFilter("treasure"); else -- Herb specificType = gatherName; gatherType = 1; allowed = Gatherer_GetFilter("herbs"); end end end if (not specificType) then specificType = "default"; end -- extra filtering options if (SETTINGS) then if ( SETTINGS.interested and SETTINGS.interested[gatherType] ) then for interest_index in SETTINGS.interested[gatherType] do idx_count= idx_count +1; end end if( gatherType == 2 ) then -- Ore minSetSkillLevel = SETTINGS.minSetOreSkill; if ( not minSetSkillLevel ) then minSetSkillLevel = -1; end elseif ( gatherType == 1 ) then -- Herb minSetSkillLevel = SETTINGS.minSetHerbSkill; if ( not minSetSkillLevel ) then minSetSkillLevel = -1; end end if ( allowed == true and Gather_SkillLevel[specificType] and minSetSkillLevel > 0 and (minSetSkillLevel > Gather_SkillLevel[specificType] or (SETTINGS.rareOre == 1 and Gather_SkillLevel[Gather_RareMatch[specificType]] and Gather_SkillLevel[specificType] < Gather_SkillLevel[Gather_RareMatch[specificType]] and minSetSkillLevel > Gather_SkillLevel[Gather_RareMatch[specificType]] )) ) then allowed = false; end end if ((allowed == true) and ((SETTINGS == nil) or (SETTINGS.interested == nil) or (idx_count == 0) or SETTINGS.interested[gatherType] == nil or (SETTINGS.interested[gatherType][specificType] == true or (SETTINGS.rareOre == 1 and SETTINGS.interested[gatherType][Gather_RareMatch[specificType]]) ) ) ) then for hPos, gatherInfo in gatherData do local convertedGatherType=""; local numGatherType, numGatherIcon; if ( lastUnused > 1000 and lastUnused < maxNotes and mod(lastUnused, 100) == 0 ) then local overlayFrameNumber = math.floor((lastUnused - 1000 )/100 + 1); getglobal("GathererMapOverlayFrame"..overlayFrameNumber):Show(); elseif (lastUnused < 1100 and not GathererMapOverlayFrame1:IsShown() ) then GathererMapOverlayFrame1:Show(); end if ((gatherInfo.x) and (gatherInfo.y) and (gatherInfo.x>0) and (gatherInfo.y>0) and (lastUnused <= maxNotes)) then local mainNote = Gatherer_CreateNoteObject(lastUnused); local mnX,mnY; mnX = gatherInfo.x / 100 * Gatherer_WorldMapDetailFrameWidth; mnY = -gatherInfo.y / 100 * Gatherer_WorldMapDetailFrameHeight; if ( SETTINGS and SETTINGS.IconAlpha ~= nil ) then mainNote:SetAlpha(SETTINGS.IconAlpha / 100); else mainNote:SetAlpha(0.8); end mainNote:SetPoint("CENTER", "GathererMapOverlayFrame", "TOPLEFT", mnX, mnY); if ( SETTINGS and SETTINGS.ToggleWorldNotes and SETTINGS.ToggleWorldNotes == 1) then mainNote.toolTip = Gatherer_GetMenuName(gatherName); else mainNote.toolTip = Gatherer_GetMenuName(specificType); end if ( type(gatherType) == "number" ) then convertedGatherType = Gatherer_EGatherType[gatherType]; numGatherType = gatherType else convertedGatherType = gatherType; numGatherType = Gatherer_EGatherType[gatherType]; end if (not Gather_IconSet["iconic"][convertedGatherType]) then gatherType = "Default"; else gatherType = convertedGatherType; end if ( type(specificType) == "number" ) then specificType = Gatherer_GetDB_IconIndex(specificType, convertedGatherType); end local texture = Gather_IconSet["iconic"][convertedGatherType][specificType]; if (not texture) then texture = Gather_IconSet["iconic"][gatherType]["default"]; end if ( gatherInfo.gtype == "Default" or gatherInfo.gtype == 3 ) then texture = Gather_IconSet["iconic"]["Default"]["default"]; end local mainNoteTexture = getglobal("GatherMain"..lastUnused.."Texture"); mainNoteTexture:SetTexture(texture); if ( SETTINGS and SETTINGS.IconSize ~= nil ) then mainNote:SetWidth(SETTINGS.IconSize); mainNote:SetHeight(SETTINGS.IconSize); end -- setting value for editing if (type(gatherInfo.icon) == "string" ) then numGatherIcon = Gather_DB_IconIndex[numGatherType]; else numGatherIcon = gatherInfo.icon; end mainNote.continent = mapContinent; mainNote.zoneIndex = mapZone; mainNote.gatherName = gatherName; mainNote.localIndex = hPos; mainNote.gatherType = numGatherType; mainNote.gatherIcon = numGatherIcon; if ( not mainNote:IsShown() ) then mainNote:Show(); end lastUnused = lastUnused + 1; end end end end end end local i=1000; for i=lastUnused, maxNotes, 1 do -- Delay code to try to work around the delay display problem, pause for 50 ms each 100 items local overlayFrameNumber = math.floor((i - 1000) / 100 +1); if ( i < maxNotes and mod(i, 100) == 0 ) then getglobal("GathererMapOverlayFrame"..overlayFrameNumber):Hide(); end local mainNote = getglobal("GatherMain"..i); if ( mainNote and mainNote:IsShown() ) then mainNote:SetPoint("CENTER", "GathererMapOverlayFrame", "TOPLEFT", Gatherer_WorldMapDetailFrameWidth+16, Gatherer_WorldMapDetailFrameHeight+16); end end Gatherer_UpdateWorldMap = -1; end function Gatherer_FindClosest(num, interested) local SETTINGS = Gatherer_Settings; local gatherLocal = {playerC=0,playerZ=0,playerX=0,playerY=0,px=0,py=0,items={},count=0}; if (not GATHERER_LOADED) then return gatherLocal; end if (Gatherer_InWorld == false ) then return gatherLocal; end local continent, zone = Gatherer_GetCurrentZone(); if ((continent == 0) or (zone == 0)) then return gatherLocal; end local px,py = Gatherer_PlayerPos(); if ((px == 0) and (py == 0)) then return gatherLocal; end local absPx, absPy = Gatherer_AbsCoord(continent, zone, px, py); gatherLocal = { playerC=continent, playerZ=zone, playerX=absPx, playerY=absPy, px=px, py=py, items={}, count=0 }; local gatherCount = 0; local maxAllowable = 0; if ((SETTINGS.maxDist) and (SETTINGS.maxDist > 0)) then maxAllowable = SETTINGS.maxDist / 1000; end if (not GatherItems[continent] or (GatherItems[continent] and not GatherItems[continent][zone])) then return gatherLocal; end -- local gatherZone, gathersInZone; -- for gatherZone, gathersInZone in GatherItems[continent] do local gatherName, gatherData; for gatherName, gatherData in GatherItems[continent][zone] do -- for gatherName, gatherData in gathersInZone do local gatherType = "Default"; local specificType = ""; local allowed = true; specificType = Gatherer_FindOreType(gatherName); if (specificType) then -- Ore gatherType = 2; allowed = Gatherer_GetFilter("mining"); else specificType = Gatherer_FindTreasureType(gatherName); if (specificType) then -- Treasure gatherType = 0; allowed = Gatherer_GetFilter("treasure"); else -- Herb specificType = gatherName; gatherType = 1; allowed = Gatherer_GetFilter("herbs"); end end if (not specificType) then specificType = "default"; end local idx_count=0; if (SETTINGS) then if ( SETTINGS.interested and SETTINGS.interested[gatherType] ) then for interest_index in SETTINGS.interested[gatherType] do idx_count= idx_count +1; end end if( gatherType == 2 ) then -- Ore minSetSkillLevel = SETTINGS.minSetOreSkill; if ( not minSetSkillLevel ) then minSetSkillLevel = -1; end elseif ( gatherType == 1 ) then -- Herb minSetSkillLevel = SETTINGS.minSetHerbSkill; if ( not minSetSkillLevel ) then minSetSkillLevel = -1; end end if ( allowed == true and Gather_SkillLevel[specificType] and minSetSkillLevel > 0 and (minSetSkillLevel > Gather_SkillLevel[specificType] or (SETTINGS.rareOre == 1 and Gather_SkillLevel[Gather_RareMatch[specificType]] and minSetSkillLevel > Gather_SkillLevel[Gather_RareMatch[specificType]] )) ) then allowed = false; end end if ((allowed == true) and ((SETTINGS == nil) or (SETTINGS.interested == nil) or (idx_count == 0) or SETTINGS.interested[gatherType] == nil or (SETTINGS.interested[gatherType][specificType] == true or (SETTINGS.rareOre == 1 and SETTINGS.interested[gatherType][Gather_RareMatch[specificType]]) ) ) ) then local maxNum=100; local gatherInfo; for _, gatherInfo in gatherData do local absHx; local absHy; if ((not gatherInfo.gtype) or (gatherInfo.gtype == "")) then gatherInfo.gtype = gatherType; end if ((not gatherInfo.icon) or (gatherInfo.icon == "")) then gatherInfo.icon = Gatherer_GetDB_IconIndex(specificType, gatherType); end absHx, absHy = Gatherer_AbsCoord(continent, zone, gatherInfo.x/100, gatherInfo.y/100); -- absHx, absHy = Gatherer_AbsCoord(continent, gatherZone, gatherInfo.x/100, gatherInfo.y/100); absHx = math.floor(absHx * 100000)/100000; absHy = math.floor(absHy * 100000)/100000; if ((absHx ~= 0) and (absHy ~= 0)) then local maxLocalPos = 0; local replCandidate = 0; local dist, deltaX, deltaY = Gatherer_Distance(absPx, absPy, absHx, absHy); if ((maxAllowable == 0) or (dist < maxAllowable)) then local localPos, localInfo; for localPos, localInfo in gatherLocal.items do if ( localPos <= num ) then if (localPos > maxLocalPos) then maxLocalPos = localPos; end -- take node at the same approximate spot into consideration if ( (abs(gatherInfo.x - localInfo.item.x) <= GATHERER_CLOSESTCHECK or Gatherer_Round(gatherInfo.x * 10) == Gatherer_Round(localInfo.item.x * 10)) and (abs(gatherInfo.y - localInfo.item.y) <= GATHERER_CLOSESTCHECK or Gatherer_Round(gatherInfo.y * 10) == Gatherer_Round(localInfo.item.y * 10)) ) then replCandidate = maxNum; maxNum = maxNum + 1; -- shorter distance elseif (localInfo.dist > dist) then if ((replCandidate == 0) or (gatherLocal.items[replCandidate] and (localInfo.dist > gatherLocal.items[replCandidate].dist))) then replCandidate = localPos; end end end end -- select candidate if ( maxLocalPos < num ) then replCandidate = maxLocalPos+1; elseif ( maxLocalPos == num and gatherLocal.items[replCandidate] and dist > gatherLocal.items[replCandidate].dist ) then replCandidate = 0; end if (replCandidate > 0) then local setItem = {}; setItem.name = gatherName; setItem.item = gatherInfo; setItem.dist = dist; setItem.deltax = deltaX; setItem.deltay = deltaY; gatherLocal["items"][replCandidate] = setItem; gatherCount = gatherCount+1; end end end end end -- end end gatherLocal.count = gatherCount; return gatherLocal; end -- ************************************************************************* -- Coordinates computing related functions function isMinimapInCity() local tempzoom = 0; local inCity = false; if (GetCVar("minimapZoom") == GetCVar("minimapInsideZoom")) then if (GetCVar("minimapInsideZoom")+0 >= 3) then Minimap:SetZoom(Minimap:GetZoom() - 1); tempzoom = 1; else Minimap:SetZoom(Minimap:GetZoom() + 1); tempzoom = -1; end end if (GetCVar("minimapInsideZoom")+0 == Minimap:GetZoom()) then inCity = true; end Minimap:SetZoom(Minimap:GetZoom() + tempzoom); return inCity; end function Gatherer_AbsCoord(continent, zone, x, y) if ((continent == 0) or (zone == 0)) then return x, y; end local r = GatherRegionData[continent][zone]; local absX = x * r.scale + r.xoffset; local absY = y * r.scale + r.yoffset; return absX, absY; end function Gatherer_Distance(originX, originY, targetX, targetY) local dx = (targetX - originX); local dy = (targetY - originY); local d = Gatherer_Pythag(dx, dy); return d, dx, dy; end function Gatherer_Pythag(dx, dy) local d = math.sqrt(dx*dx + dy*dy); -- (a*a) is usually computationally faster than math.pow(a,2) if (d == 0) then d = 0.0000000001; end -- to avoid divide by zero errors. return d; end function Gatherer_GetMapScale(continent, zone, inCity, zoomLevel) if ((continent == nil) or (zoomLevel == nil)) then return 0,0; end if ((not GatherRegionData) or (not GatherRegionData[continent]) or (not GatherRegionData[continent].scales) or (not GatherRegionData[continent].scales[zoomLevel]) or (not GatherRegionData[continent].scales[zoomLevel].xscale) or (not GatherRegionData[continent].scales[zoomLevel].yscale)) then return 0,0; end local scaleX = GatherRegionData[continent].scales[zoomLevel].xscale; local scaleY = GatherRegionData[continent].scales[zoomLevel].yscale; if (inCity == true) then local cityScale = GatherRegionData.cityZoom[zoomLevel]; scaleX = scaleX * cityScale; scaleY = scaleY * cityScale; end return scaleX, scaleY; end function Gatherer_MiniMapPos(deltaX, deltaY, scaleX, scaleY) -- works out the distance on the minimap local mapX = deltaX * scaleX; local mapY = deltaY * scaleY; local mapDist = 0; mapDist = Gatherer_Pythag(mapX, mapY); local mapWidth = Minimap:GetWidth()/2; local mapHeight = Minimap:GetHeight()/2; if (Squeenix or (pfUI and pfUI_config["disabled"]["minimap"] ~= "1") or (simpleMinimap_Skins and simpleMinimap_Skins:GetShape() == "square")) then if (math.abs(mapX) > mapWidth) then mapX = (mapWidth)*((mapX<0 and -1) or 1); end if (math.abs(mapY) > mapHeight) then mapY = (mapHeight)*((mapY<0 and -1) or 1); end elseif (mapDist >= mapWidth) then -- Remap it to just inside the minimap, by:converting dx,dy to angle,distance -- then truncate distance to 58 and convert angle,58 to dx,dy local flipAxis = 1; if (mapX == 0) then mapX = 0.0000000001; elseif (mapX < 0) then flipAxis = -1; end local angle = math.atan(mapY / mapX); mapX = math.cos(angle) * mapWidth * flipAxis; mapY = math.sin(angle) * mapHeight * flipAxis; end return mapX, mapY, mapDist; end -- ************************************************************************* -- Recording related functions -- Test: /script arg1="You perform Herb Gathering on Mageroyal."; Gatherer_ReadBuff("event"); function Gatherer_ReadBuff(event, fishItem, fishTooltip) local record = false; local gather, chatline; local gatherType, gatherIcon, gatherCanonicalName; local gatherEventType = 0; if (not arg1 and not event == "SPELLCAST_STOP") then Gatherer_Print("Gatherer: error, no arg recording aborted."); Gatherer_RecordFlag = 0; return; elseif (arg1) then chatline = string.gsub(arg1, GATHERER_NOTEXT, ""); end if ( strfind(event, "CHAT_MSG_LOOT") ) then -- process loot received message for fishing node recording gather = Gatherer_FindFishType(fishItem, fishTooltip); if ( gather ) then gatherIcon = gather; gatherType = 0; -- considered as treasure gatherEventType = 2; record = true; end elseif( strfind(event, "CHAT_MSG") or strfind(event, "CHAT_MSG_SPELL_SELF_BUFF")) then if (string.find(chatline, HERB_GATHER_STRING)) then record = true; gather = string.lower(strsub(chatline, HERB_GATHER_LENGTH, HERB_GATHER_END)) gatherType = 1; gatherIcon = gather; elseif (string.find(chatline, ORE_GATHER_STRING)) then record = true; gather = string.lower(strsub(chatline, ORE_GATHER_LENGTH, ORE_GATHER_END)) gatherType = 2; gatherIcon = Gatherer_FindOreType(gather); if (not gatherIcon) then return; end; elseif (string.find(chatline, TREASURE_GATHER_STRING)) then record = true; gather = string.lower(strsub(chatline, TREASURE_GATHER_LENGTH, TREASURE_GATHER_END)); gatherType = 0; gatherIcon, gatherCanonicalName = Gatherer_FindTreasureType(gather); if (not gatherIcon) then return; end; if ( gatherCanonicalName ) then gather = gatherCanonicalName; end; end elseif (strfind(event, "UI_ERROR_MESSAGE") or (strfind(event, "SPELLCAST_STOP") and Gatherer_RecordFlag == 1)) then -- process event to record, normally not possible gather (low/inexistant skill) gather = string.lower(Gatherer_ExtractItemFromTooltip()); if ( Gatherer_currentNode and Gatherer_currentNode ~= gather and event == "SPELLCAST_STOP" and Gatherer_RecordFlag == 1) then gather = string.lower(Gatherer_currentNode); end; if (not arg1 and Gatherer_currentAction==nil and event == "SPELLCAST_STOP" and Gatherer_RecordFlag == 1) then chatline=""; elseif (event ~= "UI_ERROR_MESSAGE" ) then chatline = Gatherer_currentAction; end -- process non gatherable item because of low/lack of skill if( gather and not gather ~= "" and (strfind(chatline, TRADE_HERBALISM) or strfind(chatline, OLD_TRADE_HERBALISM) or strfind(chatline, GATHER_HERBALISM)) ) then -- Herb Gatherer_Debug("record Herb"); record = true; gatherType = 1; gatherIcon = Gatherer_FindHerbType(gather); gatherEventType = 1; elseif (gather and not gather ~= "" and strfind(chatline, TRADE_MINING)) then -- Ore Gatherer_Debug("record Ore"); record = true; gatherType = 2; gatherIcon = Gatherer_FindOreType(gather); gatherEventType = 1; elseif(gather and not gather ~= "" and (strfind(chatline, TRADE_OPENING) or chatline=="")) then -- Treasure Gatherer_Debug("record Treasure"); record = true gatherType = 0; gatherIcon, gatherCanonicalName = Gatherer_FindTreasureType(gather); if ( gatherCanonicalName ) then gather = gatherCanonicalName; end; gatherEventType = 1; end if (not gatherIcon) then Gatherer_Print("Gatherer: no icon identified, aborting record."); Gatherer_RecordFlag = 0; return; end if (event == "SPELLCAST_STOP" and Gatherer_RecordFlag == 1) then gatherEventType = 0; end Gatherer_RecordFlag = 0; else Gatherer_RecordFlag = 0; return; end if (record == true) then Gatherer_Debug("record type: "..gatherEventType); Gatherer_AddGatherHere(gather, gatherType, gatherIcon, gatherEventType); end end local function insertionGatherIndex(gatherList, maxCheckDist, gatherX, gatherY) -- type: (GatherList, float, float, float) -> Tuple(int, bool, float, float) -- gatherList - list of the same gather -- maxCheckDist - two nodes closer than this distance are considered the same node -- returns: index to insert new gather, whether new node was found, corrected gathering coordinates local resultIndex = 0; local lastGather = 0; local firstNumerationGap = 0; local count = 0; local closest = 0; local newNodeFound = true; for gatherIndex, gatherData in gatherList do count = count + 1; if ( firstNumerationGap == 0 and gatherIndex ~= count ) then firstNumerationGap = count; end local dist, deltaX, deltaY = Gatherer_Distance(gatherX,gatherY, gatherData.x,gatherData.y); if ((dist < maxCheckDist) and ((closest == 0) or (closest > dist))) then -- same gather gatherX = (gatherX + gatherData.x) / 2; gatherY = (gatherY + gatherData.y) / 2; closest = dist; resultIndex = gatherIndex; newNodeFound = false; end if (gatherIndex > lastGather) then lastGather = gatherIndex; end end local thereIsNumerationGap = firstNumerationGap ~= 0; if (newNodeFound) then -- need to create a new one (at gatherIndex+1) resultIndex = lastGather+1; -- no duplicate but there's a hole, let's fill it if thereIsNumerationGap then resultIndex = firstNumerationGap; end end return resultIndex, newNodeFound, gatherX, gatherY end function Gatherer_AddGatherToBase(gather, gatherType, gatherC, gatherZ, gatherX, gatherY, iconIndex, gatherEventType, updateCount) -- type: (GatherName, Gatherer_EGatherType, Continent, Zone, float, float, IconIndex, EGatherEventType, bool) -> bool -- comparing to the latest official version (2.99.0.0284) -- this function was brought out into the global space and extended with the updateCount argument. -- The latter denotes whether the gather count should be incremented. -- Also it has started to return whether new node was found. if (not GatherItems[gatherC]) then GatherItems[gatherC] = { }; end if (not GatherItems[gatherC][gatherZ]) then GatherItems[gatherC][gatherZ] = { }; end if (not GatherItems[gatherC][gatherZ][gather]) then GatherItems[gatherC][gatherZ][gather] = { }; end local maxCheckDist = 0.5; if ( gatherEventType and gatherEventType == 2 ) then maxCheckDist = 1; end local insertionIndex, newNodeFound; insertionIndex, newNodeFound, gatherX, gatherY = insertionGatherIndex( GatherItems[gatherC][gatherZ][gather], maxCheckDist, gatherX, gatherY ); local countIncrement = 1; -- when gathered without required skill or received via broacast if (not updateCount) or (gatherEventType and gatherEventType == 1) then countIncrement = 0 end local newCount; if (GatherItems[gatherC][gatherZ][gather][insertionIndex] == nil) then GatherItems[gatherC][gatherZ][gather][insertionIndex] = { }; newCount = countIncrement; else newCount = GatherItems[gatherC][gatherZ][gather][insertionIndex].count + countIncrement; end -- Round off those coordinates gatherX = math.floor(gatherX * 100)/100; gatherY = math.floor(gatherY * 100)/100; assert(type(iconIndex) == 'number') GatherItems[gatherC][gatherZ][gather][insertionIndex].x = gatherX; GatherItems[gatherC][gatherZ][gather][insertionIndex].y = gatherY; GatherItems[gatherC][gatherZ][gather][insertionIndex].gtype = gatherType; GatherItems[gatherC][gatherZ][gather][insertionIndex].count = newCount; GatherItems[gatherC][gatherZ][gather][insertionIndex].icon = iconIndex return newNodeFound; end -- this function can be used as an interface by other addons to record things -- in Gatherer's database, though display is still based only on what is defined -- in Gatherer items and icons tables. -- Parameters: -- gather (string): gather name (string printed in chat) -- gatherType (number): gather type (0 treasure, 1 herbs, 2 ore) -- gatherIcon (string): matching icon from Gatherer icon table (match gather name) -- gatherEventType (number): 0 normal gather, 1 gather without required skill, 2 fishing node function Gatherer_AddGatherHere(gather, gatherType, gatherIcon, gatherEventType) -- type: (GatherName, EGatherType, IconName, EGatherEventType) -> nil assert(type(gatherIcon) == 'string') if ( Gatherer_Settings.filterRecording[gatherType] and not Gatherer_Settings.interested[gatherType][gatherIcon] ) then return; end local px, py = Gatherer_PlayerPos(1); if (px == 0 and py == 0) then --Gatherer_Print("Gatherer: Cannot record item position as client is reporting position 0,0"); return; end local gatherContinent, gatherZone = Gatherer_GetCurrentZone(); if (gatherContinent == 0 or gatherZone == 0) then --Gatherer_Print("Gatherer: Cannot record item, invalid continent/zone."); return; end local gatherX = px * 100; local gatherY = py * 100; if (not GatherItems) then Gatherer_Print("Initializing empty gatherable database"); GatherItems = { }; end local iconIndex = Gatherer_GetDB_IconIndex(gatherIcon, gatherType); if Gatherer_Settings.p2p then -- Broadcast to guild Gatherer_BroadcastGather(gather, gatherType, gatherContinent, gatherZone, gatherX, gatherY, iconIndex, gatherEventType) end Gatherer_AddGatherToBase(gather, gatherType, gatherContinent, gatherZone, gatherX, gatherY, iconIndex, gatherEventType, true); Gatherer_OnUpdate(0,true); GatherMain_Draw(); end -- ************************************************************************* -- Miscellaneous functions (clearing DB, dumping DB content) function Gatherer_Clear() Gatherer_Print("Clearing your gather data"); GatherItems = { }; ClosestList = { }; ClosestSearchGather = ""; Gatherer_OnUpdate(0,true); GatherMain_Draw(); end function Gatherer_Show() local gatherCont, gatherZone, gatherName, contData, zoneData, nameData, gatherPos, gatherItem; for gatherCont, contData in GatherItems do for gatherZone, zoneData in contData do for gatherName, nameData in zoneData do for gatherPos, gatherItem in nameData do Gatherer_Print(Gatherer_EGatherType[gatherItem.gtype].." "..gatherName.." was found in zone "..gatherCont..":"..gatherZone.." at "..gatherItem.x..","..gatherItem.y.." ("..gatherItem.count.." times)"); end end end end end -- ************************************************************************* -- String display related functions function Gatherer_ChatPrint(str) -- usually DEFAULT_CHAT_FRAME is the default "General" chat window if ( DEFAULT_CHAT_FRAME ) then DEFAULT_CHAT_FRAME:AddMessage(str, 1.0, 0.5, 0.25); end end function Gatherer_Print(str, add) if ((Gather_LastPrinted) and (str == Gather_LastPrinted)) then return; end Gather_LastPrinted = str; if (add) then str = str..": "..add; end -- adds a message into the combat log if(ChatFrame2) then ChatFrame2:AddMessage(str, 1.0, 1.0, 0.0); end end function Gatherer_Debug(str, add) if not ( type(Gatherer_DebugFrame) == "table" and Gatherer_DebugFrame.AddMessage ) then return; end if (add) then str = str..": "..add; end Gatherer_DebugFrame:AddMessage("DEBUG: "..str, 1.0, 1.0, 0.0); end function Gatherer_TitleCase(str) if (GetLocale() == "frFR") then return str; end local function ucaseWord(first, rest) return string.upper(first)..string.lower(rest) end return string.gsub(str, "([a-zA-Z])([a-zA-Z']*)", ucaseWord); end function Gatherer_MakeName(frameID) local tmpClosest = ClosestGathers; local tmpItemIDtable = { } local tmpCount = 1; if ( GATHERER_LOADED ) then local gatherInfo = tmpClosest.items[frameID]; tmpItemIDtable[tmpCount] = {}; tmpItemIDtable[tmpCount].name = Gatherer_GetMenuName(gatherInfo.name); tmpItemIDtable[tmpCount].count = gatherInfo.item.count; tmpItemIDtable[tmpCount].dist = math.floor(gatherInfo.dist*10000)/10; tmpCount = tmpCount + 1; for id in tmpClosest.items do if (id ~= frameID and (abs(gatherInfo.item.x - tmpClosest.items[id].item.x) <= GATHERER_CLOSESTCHECK or Gatherer_Round(gatherInfo.item.x * 10) == Gatherer_Round(tmpClosest.items[id].item.x * 10)) and (abs(gatherInfo.item.y - tmpClosest.items[id].item.y) <= GATHERER_CLOSESTCHECK or Gatherer_Round(gatherInfo.item.y * 10) == Gatherer_Round(tmpClosest.items[id].item.y * 10))) then tmpItemIDtable[tmpCount] = {}; tmpItemIDtable[tmpCount].name = Gatherer_GetMenuName(tmpClosest.items[id].name); tmpItemIDtable[tmpCount].count = tmpClosest.items[id].item.count; tmpItemIDtable[tmpCount].dist = math.floor(tmpClosest.items[id].dist*10000)/10; tmpCount = tmpCount + 1; end end else tmpItemIDtable[1].name = "Unknown"; tmpItemIDtable[1].count = 0; tmpItemIDtable[1].dist = 0; end return tmpItemIDtable; end -- ************************************************************************* -- Position/Map related functions function Gatherer_LoadZoneData() local continentData = {GetMapContinents()}; for continentIndex, _ in continentData do local zoneData = {GetMapZones(continentIndex)}; for zoneIndex, zoneName in zoneData do GatherZoneData[zoneName] = { [1] = continentIndex, [2] = zoneIndex }; end end end function Gatherer_GetCurrentZone() -- type: () -> Tuple[Continent, Zone] local zoneData = GatherZoneData[GetRealZoneText()]; if ( zoneData ) then return zoneData[1], zoneData[2]; else return 0,0; end end Gatherer_LastZone = {}; function Gatherer_ChangeMap() local mapContinent = GetCurrentMapContinent(); local mapZone = GetCurrentMapZone(); local minderCurZone = false; if ((Gatherer_CloseMap ~= nil) and (Gatherer_CloseMap.continent == Gatherer_LastZone.continent) and (Gatherer_CloseMap.zone == Gatherer_LastZone.zone)) then minderCurZone = true; end local playerContinent, playerZone = Gatherer_GetCurrentZone(); Gatherer_LastZone = { continent = playerContinent, zone = playerZone }; if ((playerContinent == 0) or (playerZone == 0)) then return false; end if ((playerContinent == mapContinent) and (playerZone == mapZone)) then return true; end if ( GetCurrentMapContinent()>0 and playerZone and playerZone > 0 ) then if (not WorldMapFrame:IsVisible()) then SetMapZoom(playerContinent, playerZone); end end local lastTime = 0; if ((Gatherer_CloseMap ~= nil) and (Gatherer_CloseMap.time ~= nil)) then lastTime = Gatherer_CloseMap.time; end if (minderCurZone) then Gatherer_MapOpen = false; Gatherer_CloseMap = { continent = playerContinent, zone = playerZone, time = lastTime }; end return true; end function Gatherer_PlayerPos(forced) local currentZoneName = GetRealZoneText(); if (currentZoneName ~= Gather_LastZone) then forced = true; end Gather_LastZone = currentZoneName; if (forced) then Gatherer_ChangeMap(); end local px, py = GetPlayerMapPosition("player"); if ((px == 0) and (py == 0)) then if (not forced) then if (Gatherer_ChangeMap()) then px, py = GetPlayerMapPosition("player"); else return 0,0; end end end return px, py; end -- ************************************************************************* -- Special Item handling functions: Database manipulation from world map. -- following 2 functions have to take scope into account. function Gatherer_DeleteItem() local gathIdx = 0; local zoneIdx = 0; local cntIdx = 0; if ( GatherMainMapItem.scope == "Node" ) then GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex] = nil; elseif ( GatherMainMapItem.scope == "Zone" ) then GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName] = nil; elseif ( GatherMainMapItem.scope == "Continent" ) then local search_index; for search_index in GatherItems[GatherMainMapItem.continent] do if ( GatherItems[GatherMainMapItem.continent][search_index][GatherMainMapItem.gatherName] ) then GatherItems[GatherMainMapItem.continent][search_index][GatherMainMapItem.gatherName] = nil; end end elseif ( GatherMainMapItem.scope == "World" ) then local search_index; -- continent 1 if ( GatherItems[1] ) then for search_index in GatherItems[1] do if ( GatherItems[1][search_index][GatherMainMapItem.gatherName] ) then GatherItems[1][search_index][GatherMainMapItem.gatherName] = nil; end end end -- continent 2 if ( GatherItems[2] ) then for search_index in GatherItems[2] do if ( GatherItems[2][search_index][GatherMainMapItem.gatherName] ) then GatherItems[2][search_index][GatherMainMapItem.gatherName] = nil; end end end end GatherMain_Draw(); -- clean up for empty zones/continent for locIdx in GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName] do gathIdx = gathIdx + 1; end if ( gathIdx == 0 ) then GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName] = nil; for locIdx in GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex] do zoneIdx = zoneIdx + 1; end if ( zoneIdx == 0 ) then GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex] = nil; --DEFAULT_CHAT_FRAME:AddMessage("Removed zone no data left") for locIdx in GatherItems[GatherMainMapItem.continent] do cntIdx = cntIdx + 1; end if ( cntIdx == 0 ) then --DEFAULT_CHAT_FRAME:AddMessage("Removed continents no data left") GatherItems[GatherMainMapItem.continent] = nil; end end end GatherMain_Draw(); end -- Modify Gather Name and/or icon function Gatherer_ModifyItem() local newIcon, newGatherName, newType; local mod_continent = GatherMainMapItem.continent; local mod_zone = GatherMainMapItem.zoneIndex; local mod_name = GatherMainMapItem.gatherName; -- setting new names (NB: icons on world map are deduced from gather name) if ( GatherMainMapItem.newIcon and GatherMainMapItem.newIcon ~= GatherMainMapItem.gatherIcon ) then newIcon = GatherMainMapItem.newIcon; else newIcon = GatherMainMapItem.gatherIcon; end if ( GatherMainMapItem.newType and GatherMainMapItem.newType ~= GatherMainMapItem.gatherType ) then newType = GatherMainMapItem.newType; else newType = GatherMainMapItem.gatherType; end if ( GatherMainMapItem.newGatherName ) then newGatherName = GatherMainMapItem.newGatherName; else newGatherName = mod_name; end if ( GatherMainMapItem.scope == "Node" ) then -- remove old entries if gather name different if ( GatherMainMapItem.newGatherName and mod_name ~= GatherMainMapItem.newGatherName ) then if ( not GatherItems[mod_continent][mod_zone][newGatherName] ) then GatherItems[mod_continent][mod_zone][newGatherName] = {}; GatherItems[mod_continent][mod_zone][newGatherName][GatherMainMapItem.localIndex] = GatherItems[mod_continent][mod_zone][mod_name][GatherMainMapItem.localIndex]; GatherItems[mod_continent][mod_zone][mod_name][GatherMainMapItem.localIndex] = nil; end end -- replace icons if different GatherItems[mod_continent][mod_zone][newGatherName][GatherMainMapItem.localIndex].icon = newIcon; GatherItems[mod_continent][mod_zone][newGatherName][GatherMainMapItem.localIndex].gtype = newType; elseif ( GatherMainMapItem.scope == "Zone" ) then -- remove old entries if gather name different if ( GatherMainMapItem.newGatherName and mod_name ~= GatherMainMapItem.newGatherName ) then GatherItems[mod_continent][mod_zone][newGatherName] = {}; GatherItems[mod_continent][mod_zone][newGatherName] = GatherItems[mod_continent][mod_zone][mod_name]; GatherItems[mod_continent][mod_zone][mod_name] = nil; end -- replace icons if different if ( newIcon ~= GatherMainMapItem.gatherIcon ) then local index; for index in GatherItems[mod_continent][mod_zone][newGatherName] do GatherItems[mod_continent][mod_zone][newGatherName][index].icon = newIcon; end end -- replace type if different if ( newType ~= GatherMainMapItem.gatherType ) then local index; for index in GatherItems[mod_continent][mod_zone][newGatherName] do GatherItems[mod_continent][mod_zone][newGatherName][index].gtype = newType; end end elseif ( GatherMainMapItem.scope == "Continent" ) then local search_index; for search_index in GatherItems[mod_continent] do if ( GatherItems[mod_continent][search_index][mod_name] ) then -- remove old entries if gather name different if ( GatherMainMapItem.newGatherName and mod_name ~= GatherMainMapItem.newGatherName ) then GatherItems[mod_continent][search_index][newGatherName] = {}; GatherItems[mod_continent][search_index][newGatherName] = GatherItems[mod_continent][search_index][mod_name]; GatherItems[mod_continent][search_index][mod_name] = nil; end -- replace icons if different if ( newIcon ~= GatherMainMapItem.gatherIcon ) then local index; for index in GatherItems[mod_continent][search_index][newGatherName] do GatherItems[mod_continent][search_index][newGatherName][index].icon = newIcon; end end -- replace type if different if ( newType ~= GatherMainMapItem.gatherType ) then local index; for index in GatherItems[mod_continent][search_index][newGatherName] do GatherItems[mod_continent][search_index][newGatherName][index].gtype = newType; end end end end elseif ( GatherMainMapItem.scope == "World" ) then local search_index; -- continent 1 if ( GatherItems[1] ) then for search_index in GatherItems[1] do if ( GatherItems[1][search_index][mod_name] ) then -- remove old entries if gather name different if ( GatherMainMapItem.newGatherName and mod_name ~= GatherMainMapItem.newGatherName ) then GatherItems[1][search_index][newGatherName] = {}; GatherItems[1][search_index][newGatherName] = GatherItems[1][search_index][mod_name]; GatherItems[1][search_index][mod_name] = nil; end -- replace icons if different if ( newIcon ~= GatherMainMapItem.gatherIcon ) then local index; for index in GatherItems[1][search_index][newGatherName] do GatherItems[1][search_index][newGatherName][index].icon = newIcon; end end -- replace type if different if ( newType ~= GatherMainMapItem.gatherType ) then local index; for index in GatherItems[1][search_index][newGatherName] do GatherItems[1][search_index][newGatherName][index].gtype = newType; end end end end end -- continent 2 if ( GatherItems[2] ) then for search_index in GatherItems[2] do if ( GatherItems[2][search_index][mod_name] ) then -- remove old entries if gather name different if ( GatherMainMapItem.newGatherName and mod_name ~= GatherMainMapItem.newGatherName ) then GatherItems[2][search_index][newGatherName] = {}; GatherItems[2][search_index][newGatherName] = GatherItems[2][search_index][mod_name]; GatherItems[2][search_index][mod_name] = nil; end -- replace icons if different if ( newIcon ~= GatherMainMapItem.gatherIcon ) then local index; for index in GatherItems[2][search_index][newGatherName] do GatherItems[2][search_index][newGatherName][index].icon = newIcon; end end -- replace type if different if ( newType ~= GatherMainMapItem.gatherType ) then local index; for index in GatherItems[2][search_index][newGatherName] do GatherItems[2][search_index][newGatherName][index].gtype = newType; end end end end end end GatherMain_Draw(); GatherMainMapItem.newIcon = nil; GatherMainMapItem.newGatherName = nil; end -- dropdown menus function Gatherer_WMDropDownGType_Initialize() local index; local info = {}; local iconGtype = GatherMainMapItem.gatherType; if ( GatherMainMapItem.newType ) then iconGtype = GatherMainMapItem.newType; end for index, value in Gatherer_EGatherType do if ( type(value) == "number" and value ~= 3 ) then info.text = index; info.value = value; if ( value == iconGtype ) then info.checked = 1; else info.checked = nil; end info.func = Gatherer_WMDropDownGType_OnClick; UIDropDownMenu_AddButton(info); end end if ( type(GatherMainMapItem.gatherType) == "number" ) then UIDropDownMenu_SetText(Gatherer_EGatherType[GatherMainMapItem.gatherType], Gatherer_WMDropDownGType); else UIDropDownMenu_SetText(GatherMainMapItem.gatherType, Gatherer_WMDropDownGType); end end function Gatherer_WMDropDownGType_OnClick() UIDropDownMenu_SetSelectedID(Gatherer_WMDropDownGType, this:GetID()); GatherMainMapItem.newType = Gatherer_EGatherType[UIDropDownMenu_GetText(Gatherer_WMDropDownGType)]; UIDropDownMenu_ClearAll(Gatherer_WMDropDownIcons); UIDropDownMenu_Initialize(Gatherer_WMDropDownIcons, Gatherer_WMDropDownIcons_Initialize); end function Gatherer_WMDropDownIcons_Initialize() local index; local info = {}; local iconGtype = "Default"; if ( GatherMainMapItem.newType ) then iconGtype = GatherMainMapItem.newType; else iconGtype = GatherMainMapItem.gatherType; end for index, value in Gather_DB_IconIndex[iconGtype] do info.text = index; info.value = value; if ( value == GatherMainMapItem.gatherIcon and not GatherMainMapItem.newType) then info.checked = 1; else info.checked = nil; end info.func = Gatherer_WMDropDownIcons_OnClick; UIDropDownMenu_AddButton(info); end UIDropDownMenu_SetText(Gatherer_GetDB_IconIndex(GatherMainMapItem.gatherIcon, iconGtype), Gatherer_WMDropDownIcons); end function Gatherer_WMDropDownIcons_OnClick() UIDropDownMenu_SetSelectedID(Gatherer_WMDropDownIcons, this:GetID()); GatherMainMapItem.newIcon = Gatherer_GetDB_IconIndex(UIDropDownMenu_GetText(Gatherer_WMDropDownIcons), GatherMainMapItem.gatherType); end function Gatherer_WMDropDownScope_Initialize() local index; local scope_list = { "Node", "Zone", "Continent", "World" }; local info = {}; GatherMainMapItem.scope = "Node"; for index in scope_list do info.text = scope_list[index]; info.value = index; if ( scope_list[index] == "Node" ) then info.checked = 1; else info.checked = nil; end info.func = Gatherer_WMDropDownScope_OnClick; UIDropDownMenu_AddButton(info); end UIDropDownMenu_SetSelectedID(Gatherer_WMDropDownScope, 1); end function Gatherer_WMDropDownScope_OnClick() UIDropDownMenu_SetSelectedID(Gatherer_WMDropDownScope, this:GetID()); GatherMainMapItem.scope = UIDropDownMenu_GetText(Gatherer_WMDropDownScope); end -- Toggle item to bugged state -- this one always ignore scope, only done on origin item. function Gatherer_ToggleBuggedItem() Gatherer_ChatPrint("Toggling node to bugged state"); local gtype = GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].gtype; local icon = GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].icon; if ( GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].icon ~= "default" ) then GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldicon = icon; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldgtype = gtype; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].icon = "default"; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].gtype = "Default"; else GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].icon = GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldicon; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].gtype = GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldgtype; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldicon = nil; GatherItems[GatherMainMapItem.continent][GatherMainMapItem.zoneIndex][GatherMainMapItem.gatherName][GatherMainMapItem.localIndex].oldgtype = nil; end GatherMain_Draw(); end