You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
16 KiB
529 lines
16 KiB
--[[
|
|
This addon designed to be as lightweight as possible.
|
|
It will only track, Mine, Herb, Fish, Gas and some Treasure nodes.
|
|
This mods whole purpose is to be lean, simple and feature complete.
|
|
]]
|
|
-- Mixin AceEvent
|
|
local GatherMate = LibStub("AceAddon-3.0"):NewAddon("GatherMate2","AceConsole-3.0","AceEvent-3.0")
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("GatherMate2",false)
|
|
_G["GatherMate2"] = GatherMate
|
|
|
|
GatherMate.HBD = LibStub("HereBeDragons-2.0")
|
|
local HBDMigrate = LibStub("HereBeDragons-Migrate")
|
|
|
|
-- locals
|
|
local db, gmdbs, filter
|
|
local reverseTables = {}
|
|
-- defaults for storage
|
|
local defaults = {
|
|
profile = {
|
|
scale = 1.0,
|
|
miniscale = 0.75,
|
|
alpha = 1,
|
|
show = {
|
|
["Treasure"] = "always",
|
|
["Logging"] = "active",
|
|
["*"] = "with_profession"
|
|
},
|
|
showMinimap = true,
|
|
showWorldMap = true,
|
|
worldMapIconsInteractive = true,
|
|
minimapTooltips = true,
|
|
filter = {
|
|
["*"] = {
|
|
["*"] = true,
|
|
},
|
|
},
|
|
trackColors = {
|
|
["Herb Gathering"] = {Red = 0, Green = 1, Blue = 0, Alpha = 1},
|
|
["Fishing"] = {Red = 1, Green = 1, Blue = 0, Alpha = 1},
|
|
["Mining"] = {Red = 1, Green = 0, Blue = 0, Alpha = 1},
|
|
["Extract Gas"] = {Red = 0, Green = 1, Blue = 1, Alpha = 1},
|
|
["Treasure"] = {Red = 1, Green = 0, Blue = 1, Alpha = 1},
|
|
["Archaeology"] = {Red = 1, Green = 1, Blue = 0.5, Alpha = 1},
|
|
["Logging"] = {Red = 0, Green = 0.8, Blue = 1, Alpha = 1},
|
|
["*"] = {Red = 1, Green = 0, Blue = 1, Alpha = 1},
|
|
},
|
|
trackDistance = 100,
|
|
trackShow = "always",
|
|
nodeRange = true,
|
|
cleanupRange = {
|
|
["Herb Gathering"] = 15,
|
|
["Fishing"] = 15,
|
|
["Mining"] = 15,
|
|
["Extract Gas"] = 50,
|
|
["Treasure"] = 15,
|
|
["Archaeology"] = 10,
|
|
["Logging"] = 20,
|
|
},
|
|
dbLocks = {
|
|
["Herb Gathering"] = false,
|
|
["Fishing"] = false,
|
|
["Mining"] = false,
|
|
["Extract Gas"] = false,
|
|
["Treasure"] = false,
|
|
["Archaeology"] = false,
|
|
["Logging"] = false,
|
|
},
|
|
importers = {
|
|
["*"] = {
|
|
["Style"] = "Merge",
|
|
["Databases"] = {},
|
|
["lastImport"] = 0,
|
|
["autoImport"] = false,
|
|
["bcOnly"] = false,
|
|
},
|
|
}
|
|
},
|
|
}
|
|
local floor = floor
|
|
local next = next
|
|
|
|
--[[
|
|
Setup a few databases, we sub divide namespaces for resetting/importing
|
|
:OnInitialize() is called at ADDON_LOADED so savedvariables are loaded
|
|
]]
|
|
function GatherMate:OnInitialize()
|
|
self.db = LibStub("AceDB-3.0"):New("GatherMate2DB", defaults, "Default")
|
|
self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
|
|
self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
|
|
self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
|
|
|
|
-- Setup our saved vars, we dont use AceDB, cause it over kills
|
|
-- These 4 savedvars are global and doesnt need char specific stuff in it
|
|
GatherMate2HerbDB = GatherMate2HerbDB or {}
|
|
GatherMate2MineDB = GatherMate2MineDB or {}
|
|
GatherMate2FishDB = GatherMate2FishDB or {}
|
|
GatherMate2TreasureDB = GatherMate2TreasureDB or {}
|
|
GatherMate2GasDB = GatherMate2GasDB or {}
|
|
GatherMate2ArchaeologyDB = GatherMate2ArchaeologyDB or {}
|
|
GatherMate2LoggingDB = GatherMate2LoggingDB or {}
|
|
self.gmdbs = {}
|
|
self.db_types = {}
|
|
gmdbs = self.gmdbs
|
|
self:RegisterDBType("Herb Gathering", GatherMate2HerbDB)
|
|
self:RegisterDBType("Mining", GatherMate2MineDB)
|
|
self:RegisterDBType("Fishing", GatherMate2FishDB)
|
|
self:RegisterDBType("Treasure", GatherMate2TreasureDB)
|
|
self:RegisterDBType("Extract Gas", GatherMate2GasDB)
|
|
self:RegisterDBType("Archaeology", GatherMate2ArchaeologyDB)
|
|
self:RegisterDBType("Logging", GatherMate2LoggingDB)
|
|
db = self.db.profile
|
|
filter = db.filter
|
|
-- depractaion scan
|
|
if (self.db.global.data_version or 0) == 1 then
|
|
self:RemoveDepracatedNodes()
|
|
self.db.global.data_version = 2
|
|
end
|
|
if (self.db.global.data_version or 0) < 4 then
|
|
self:RemoveGarrisonNodes()
|
|
self.db.global.data_version = 4
|
|
end
|
|
if (self.db.global.data_version or 0) < 5 then
|
|
self:MigrateData80()
|
|
self.db.global.data_version = 5
|
|
end
|
|
if (self.db.global.data_version or 0) < 6 then
|
|
self:RemoveDepracatedNodes()
|
|
self.db.global.data_version = 6
|
|
end
|
|
end
|
|
|
|
function GatherMate:RemoveGarrisonNodes()
|
|
for _, database in pairs({"Herb Gathering", "Mining"}) do
|
|
gmdbs[database][971] = {}
|
|
gmdbs[database][976] = {}
|
|
end
|
|
end
|
|
|
|
function GatherMate:RemoveDepracatedNodes()
|
|
for database,storage in pairs(self.gmdbs) do
|
|
for zone,data in pairs(storage) do
|
|
for coord,value in pairs(data) do
|
|
local name = self:GetNameForNode(database,value)
|
|
if not name then
|
|
data[coord] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function GatherMate:MigrateData80()
|
|
for database,storage in pairs(self.gmdbs) do
|
|
local migrated_storage = {}
|
|
for zone,data in pairs(storage) do
|
|
for coord,value in pairs(data) do
|
|
local level = coord % 100
|
|
local newzone = HBDMigrate:GetUIMapIDFromMapAreaId(zone, level)
|
|
if newzone then
|
|
newzone = self.phasing[newzone] or newzone
|
|
if not migrated_storage[newzone] then
|
|
migrated_storage[newzone] = {}
|
|
end
|
|
migrated_storage[newzone][coord] = value
|
|
end
|
|
end
|
|
storage[zone] = nil
|
|
end
|
|
for zone,data in pairs(migrated_storage) do
|
|
storage[zone] = migrated_storage[zone]
|
|
migrated_storage[zone] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Register a new node DB for usage in GatherMate
|
|
]]
|
|
function GatherMate:RegisterDBType(name, db)
|
|
tinsert(self.db_types, name)
|
|
self.gmdbs[name] = db
|
|
end
|
|
|
|
function GatherMate:OnProfileChanged(db,name)
|
|
db = self.db.profile
|
|
filter = db.filter
|
|
GatherMate:SendMessage("GatherMate2ConfigChanged")
|
|
end
|
|
--[[
|
|
create a reverse lookup table for input table (we use it for english names of nodes)
|
|
]]
|
|
function GatherMate:CreateReversedTable(tbl)
|
|
if reverseTables[tbl] then
|
|
return reverseTables[tbl]
|
|
end
|
|
local reverse = {}
|
|
for k, v in pairs(tbl) do
|
|
reverse[v] = k
|
|
end
|
|
reverseTables[tbl] = reverse
|
|
return setmetatable(reverse, getmetatable(tbl))
|
|
end
|
|
--[[
|
|
Clearing function
|
|
]]
|
|
function GatherMate:ClearDB(dbx)
|
|
-- for our own DBs we just discard the table and be happy
|
|
-- db lock check
|
|
if GatherMate.db.profile.dbLocks[dbx] then
|
|
return
|
|
end
|
|
if dbx == "Herb Gathering" then GatherMate2HerbDB = {}; gmdbs[dbx] = GatherMate2HerbDB
|
|
elseif dbx == "Fishing" then GatherMate2FishDB = {}; gmdbs[dbx] = GatherMate2FishDB
|
|
elseif dbx == "Mining" then GatherMate2MineDB = {}; gmdbs[dbx] = GatherMate2MineDB
|
|
elseif dbx == "Treasure" then GatherMate2TreasureDB = {}; gmdbs[dbx] = GatherMate2TreasureDB
|
|
elseif dbx == "Extract Gas" then GatherMate2GasDB = {}; gmdbs[dbx] = GatherMate2GasDB
|
|
elseif dbx == "Archaeology" then GatherMate2ArchaeologyDB = {}; gmdbs[dbx] = GatherMate2ArchaeologyDB
|
|
elseif dbx == "Logging" then GatherMate2LoggingDB = {}; gmdbs[dbx] = GatherMate2LoggingDB
|
|
else -- for custom DBs we dont know the global name, so we clear it old-fashion style
|
|
local db = gmdbs[dbx]
|
|
if not db then error("Trying to clear unknown database: "..dbx) end
|
|
for k in pairs(db) do
|
|
db[k] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Add an item to the DB
|
|
]]
|
|
function GatherMate:AddNode(zone, x, y, nodeType, name)
|
|
local db = gmdbs[nodeType]
|
|
if not db then return end
|
|
local id = self:EncodeLoc(x,y)
|
|
-- db lock check
|
|
if GatherMate.db.profile.dbLocks[nodeType] then
|
|
return
|
|
end
|
|
db[zone] = db[zone] or {}
|
|
db[zone][id] = self.nodeIDs[nodeType][name]
|
|
self:SendMessage("GatherMate2NodeAdded", zone, nodeType, id, name)
|
|
end
|
|
|
|
--[[
|
|
Add an item to the DB, performing duplicate checks and cleanup
|
|
]]
|
|
function GatherMate:AddNodeChecked(zone, x, y, nodeType, name)
|
|
local db = gmdbs[nodeType]
|
|
if not db then return end
|
|
|
|
-- get the node id for what we're adding
|
|
local nid = GatherMate:GetIDForNode(nodeType, name)
|
|
if not nid then return end
|
|
|
|
-- cleanup range
|
|
local range = GatherMate.db.profile.cleanupRange[nodeType]
|
|
|
|
-- check for existing nodes
|
|
local skip = false
|
|
local rares = self.rareNodes
|
|
for coord, nodeID in GatherMate:FindNearbyNode(zone, x, y, nodeType, range, true) do
|
|
if (nodeID == nid or rares[nodeID] and rares[nodeID][nid]) then
|
|
GatherMate:RemoveNodeByID(zone, nodeType, coord)
|
|
-- we're trying to add a rare node, but there is already a normal node present, skip the adding
|
|
elseif rares[nid] and rares[nid][nodeID] then
|
|
skip = true
|
|
end
|
|
end
|
|
|
|
if not skip then
|
|
GatherMate:AddNode(zone, x, y, nodeType, name)
|
|
end
|
|
|
|
return not skip
|
|
end
|
|
|
|
--[[
|
|
These 2 functions are only called by the importer/sharing. These
|
|
do NOT fire GatherMateNodeAdded or GatherMateNodeDeleted messages.
|
|
|
|
Renamed to InjectNode2/DeleteNode2 to ensure data addon compatibility with 8.0 zone IDs
|
|
]]
|
|
function GatherMate:InjectNode2(zone, coords, nodeType, nodeID)
|
|
local db = gmdbs[nodeType]
|
|
if not db then return end
|
|
-- db lock check
|
|
if GatherMate.db.profile.dbLocks[nodeType] then
|
|
return
|
|
end
|
|
if (nodeType == "Mining" or nodeType == "Herb Gathering") and GatherMate.mapBlacklist[zone] then return end
|
|
db[zone] = db[zone] or {}
|
|
db[zone][coords] = nodeID
|
|
end
|
|
function GatherMate:DeleteNode2(zone, coords, nodeType)
|
|
if not gmdbs[nodeType] then return end
|
|
-- db lock check
|
|
if GatherMate.db.profile.dbLocks[nodeType] then
|
|
return
|
|
end
|
|
local db = gmdbs[nodeType][zone]
|
|
if db then
|
|
db[coords] = nil
|
|
end
|
|
end
|
|
|
|
-- Do-end block for iterator
|
|
do
|
|
local emptyTbl = {}
|
|
local tablestack = setmetatable({}, {__mode = 'k'})
|
|
|
|
local function dbCoordIterNearby(t, prestate)
|
|
if not t then return nil end
|
|
local data = t.data
|
|
local state, value = next(data, prestate)
|
|
local xLocal, yLocal, yw, yh = t.xLocal, t.yLocal, t.yw, t.yh
|
|
local radiusSquared, filterTable, ignoreFilter = t.radiusSquared, t.filterTable, t.ignoreFilter
|
|
while state do
|
|
if filterTable[value] or ignoreFilter then
|
|
-- inline the :getXY() here in critical minimap update loop
|
|
local x2, y2 = floor(state/1000000)/10000, floor(state % 1000000 / 100)/10000
|
|
local x = (x2 - xLocal) * yw
|
|
local y = (y2 - yLocal) * yh
|
|
if x*x + y*y <= radiusSquared then
|
|
return state, value
|
|
end
|
|
end
|
|
state, value = next(data, state)
|
|
end
|
|
tablestack[t] = true
|
|
return nil, nil
|
|
end
|
|
|
|
--[[
|
|
Find all nearby nodes within the radius of the given (x,y) for a nodeType and zone
|
|
this function returns an iterator
|
|
]]
|
|
function GatherMate:FindNearbyNode(zone, x, y, nodeType, radius, ignoreFilter)
|
|
local tbl = next(tablestack) or {}
|
|
tablestack[tbl] = nil
|
|
tbl.data = gmdbs[nodeType][zone] or emptyTbl
|
|
tbl.yw, tbl.yh = self.HBD:GetZoneSize(zone)
|
|
tbl.radiusSquared = radius * radius
|
|
tbl.xLocal, tbl.yLocal = x, y
|
|
tbl.filterTable = filter[nodeType]
|
|
tbl.ignoreFilter = ignoreFilter
|
|
return dbCoordIterNearby, tbl, nil
|
|
end
|
|
|
|
local function dbCoordIter(t, prestate)
|
|
if not t then return nil end
|
|
local data = t.data
|
|
local state, value = next(data, prestate)
|
|
local filterTable = t.filterTable
|
|
while state do
|
|
if filterTable[value] then
|
|
return state, value
|
|
end
|
|
state, value = next(data, state)
|
|
end
|
|
tablestack[t] = true
|
|
return nil, nil
|
|
end
|
|
|
|
--[[
|
|
This function returns an iterator for the given zone and nodeType
|
|
]]
|
|
function GatherMate:GetNodesForZone(zone, nodeType, ignoreFilter)
|
|
local t = gmdbs[nodeType][zone] or emptyTbl
|
|
if ignoreFilter then
|
|
return pairs(t)
|
|
else
|
|
local tbl = next(tablestack) or {}
|
|
tablestack[tbl] = nil
|
|
tbl.data = t
|
|
tbl.filterTable = filter[nodeType]
|
|
return dbCoordIter, tbl, nil
|
|
end
|
|
end
|
|
end
|
|
--[[
|
|
Node id function forward and reverse
|
|
]]
|
|
function GatherMate:GetIDForNode(type, name)
|
|
return self.nodeIDs[type][name]
|
|
end
|
|
--[[
|
|
Get the name for a nodeID
|
|
]]
|
|
function GatherMate:GetNameForNode(type, nodeID)
|
|
return self.reverseNodeIDs[type][nodeID]
|
|
end
|
|
--[[
|
|
Remove an item from the DB
|
|
]]
|
|
function GatherMate:RemoveNode(zone, x, y, nodeType)
|
|
if not gmdbs[nodeType] then return end
|
|
local db = gmdbs[nodeType][zone]
|
|
local coord = self:EncodeLoc(x,y)
|
|
if db[coord] then
|
|
local t = self.reverseNodeIDs[nodeType][db[coord]]
|
|
db[coord] = nil
|
|
self:SendMessage("GatherMate2NodeDeleted", zone, nodeType, coord, t)
|
|
end
|
|
end
|
|
--[[
|
|
Remove an item from the DB by node ID and type
|
|
]]
|
|
function GatherMate:RemoveNodeByID(zone, nodeType, coord)
|
|
if not gmdbs[nodeType] then return end
|
|
-- db lock check
|
|
if GatherMate.db.profile.dbLocks[nodeType] then
|
|
return
|
|
end
|
|
local db = gmdbs[nodeType][zone]
|
|
if db[coord] then
|
|
local t = self.reverseNodeIDs[nodeType][db[coord]]
|
|
db[coord] = nil
|
|
self:SendMessage("GatherMate2NodeDeleted", zone, nodeType, coord, t)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function to cleanup the databases by removing nearby nodes of similar types
|
|
As of 02/17/2013 will be converted to become a coroutine
|
|
--]]
|
|
|
|
local CleanerUpdateFrame = CreateFrame("Frame")
|
|
CleanerUpdateFrame.running = false
|
|
|
|
function CleanerUpdateFrame:OnUpdate(elapsed)
|
|
local finished = coroutine.resume(self.cleanup)
|
|
if finished then
|
|
if coroutine.status(self.cleanup) == "dead" then
|
|
self:SetScript("OnUpdate",nil)
|
|
self.running = false
|
|
self.cleanup = nil
|
|
GatherMate:Print(L["Cleanup Complete."])
|
|
end
|
|
else
|
|
self:SetScript("OnUpdate",nil)
|
|
self.runing = false
|
|
self.cleanup = nil
|
|
GatherMate:Print(L["Cleanup Failed."])
|
|
end
|
|
end
|
|
|
|
function GatherMate:IsCleanupRunning()
|
|
return CleanerUpdateFrame.running
|
|
end
|
|
|
|
function GatherMate:SweepDatabase()
|
|
local rares = self.rareNodes
|
|
for v,zone in pairs(GatherMate.HBD:GetAllMapIDs()) do
|
|
--self:Print(L["Processing "]..zone)
|
|
coroutine.yield()
|
|
for profession in pairs(gmdbs) do
|
|
local range = db.cleanupRange[profession]
|
|
for coord, nodeID in self:GetNodesForZone(zone, profession, true) do
|
|
local x,y = self:DecodeLoc(coord)
|
|
for _coord, _nodeID in self:FindNearbyNode(zone, x, y, profession, range, true) do
|
|
if coord ~= _coord and (nodeID == _nodeID or (rares[_nodeID] and rares[_nodeID][nodeID])) then
|
|
self:RemoveNodeByID(zone, profession, _coord)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self:RemoveDepracatedNodes()
|
|
self:SendMessage("GatherMate2Cleanup")
|
|
end
|
|
|
|
function GatherMate:CleanupDB()
|
|
if not CleanerUpdateFrame.running then
|
|
CleanerUpdateFrame.cleanup = coroutine.create(GatherMate.SweepDatabase)
|
|
CleanerUpdateFrame:SetScript("OnUpdate",CleanerUpdateFrame.OnUpdate)
|
|
CleanerUpdateFrame.running = true
|
|
local status = coroutine.resume(CleanerUpdateFrame.cleanup,GatherMate)
|
|
if not status then
|
|
CleanerUpdateFrame.running = false
|
|
CleanerUpdateFrame:SetScript("OnUpdate",nil)
|
|
CleanerUpdateFrame.cleanup = nil
|
|
self:Print(L["Cleanup Failed."])
|
|
else
|
|
self:Print(L["Cleanup Started."])
|
|
end
|
|
else
|
|
self:Print(L["Cleanup in progress."])
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Function to delete all of a specified node from a specific zone
|
|
]]
|
|
function GatherMate:DeleteNodeFromZone(nodeType, nodeID, zone)
|
|
if not gmdbs[nodeType] then return end
|
|
local db = gmdbs[nodeType][zone]
|
|
if db then
|
|
for coord, node in pairs(db) do
|
|
if node == nodeID then
|
|
self:RemoveNodeByID(zone, nodeType, coord)
|
|
end
|
|
end
|
|
self:SendMessage("GatherMate2Cleanup")
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Encode location
|
|
]]
|
|
function GatherMate:EncodeLoc(x, y)
|
|
if x > 0.9999 then
|
|
x = 0.9999
|
|
end
|
|
if y > 0.9999 then
|
|
y = 0.9999
|
|
end
|
|
return floor( x * 10000 + 0.5 ) * 1000000 + floor( y * 10000 + 0.5 ) * 100
|
|
end
|
|
|
|
--[[
|
|
Decode location
|
|
]]
|
|
function GatherMate:DecodeLoc(id)
|
|
return floor(id/1000000)/10000, floor(id % 1000000 / 100)/10000
|
|
end
|
|
|
|
function GatherMate:MapLocalize(map)
|
|
return self.HBD:GetLocalizedMap(map)
|
|
end
|
|
|