--[[ Name: LibFishing-1.0 Maintainers: Sutorix <sutorix@hotmail.com> Description: A library with fishing support routines used by Fishing Buddy, Fishing Ace and FB_Broker. Copyright (c) by Bob Schumaker Licensed under a Creative Commons "Attribution Non-Commercial Share Alike" License --]] --[[Modded and name altered for purpose of S&L's profession module.]] local MAJOR_VERSION = "LibFishing-1.0-SLE" local MINOR_VERSION = 90000 + tonumber(("$Rev: 971 $"):match("%d+")) if not LibStub then error(MAJOR_VERSION .. " requires LibStub") end local FishLib, oldLib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION) if not FishLib then return end -- keep the old stuff, in case we want some of the settings. if oldLib then oldLib = {} for k, v in pairs(FishLib) do FishLib[k] = nil oldLib[k] = v end end -- 5.0.4 has a problem with a global "_" (see some for loops below) local _ local LT = LibStub("LibTourist-3.0"); -- These are now provided by LibTourist local BZ = LibStub("LibTourist-3.0"):GetLookupTable() local BZR = LibStub("LibTourist-3.0"):GetReverseLookupTable(); local BSL = LibStub("LibBabble-SubZone-3.0"):GetBaseLookupTable(); local BSZ = LibStub("LibBabble-SubZone-3.0"):GetLookupTable(); local BSZR = LibStub("LibBabble-SubZone-3.0"):GetReverseLookupTable(); FishLib.UNKNOWN = "UNKNOWN"; local WOW = {}; function FishLib:WOWVersion() return WOW.major, WOW.minor, WOW.dot; end if ( GetBuildInfo ) then local v, b, d = GetBuildInfo(); WOW.build = b; WOW.date = d; local s,e,maj,min,dot = string.find(v, "(%d+).(%d+).(%d+)"); WOW.major = tonumber(maj); WOW.minor = tonumber(min); WOW.dot = tonumber(dot); else WOW.major = 1; WOW.minor = 9; WOW.dot = 0; end function FishLib:GetFishingSkillInfo() local _, _, _, fishing, _, _ = GetProfessions(); if ( fishing ) then local name, _, _, _, _, _, _ = GetProfessionInfo(fishing); -- is this always the same as PROFESSIONS_FISHING? return true, name; end return false, PROFESSIONS_FISHING; end -- get our current fishing skill level function FishLib:GetCurrentSkill() local _, _, _, fishing, _, _ = GetProfessions(); if (fishing) then local name, _, rank, skillmax, _, _, _, mods = GetProfessionInfo(fishing); local _, lure = self:GetPoleBonus(); return rank, mods, skillmax, lure; end return 0, 0, 0; end -- Lure library local FISHINGLURES = { { ["id"] = 88710, ["n"] = "Nat's Hat", -- 150 for 10 mins ["b"] = 150, ["s"] = 100, ["d"] = 10, ["w"] = true, }, { ["id"] = 117405, ["n"] = "Nat's Drinking Hat", -- 150 for 10 mins ["b"] = 150, ["s"] = 100, ["d"] = 10, ["w"] = true, }, { ["id"] = 33820, ["n"] = "Weather-Beaten Fishing Hat", -- 75 for 10 minutes ["b"] = 75, ["s"] = 1, ["d"] = 10, ["w"] = true, }, { ["id"] = 116826, ["n"] = "Draenic Fishing Pole", -- 200 for 10 minutes ["b"] = 200, ["s"] = 1, ["d"] = 20, -- 20 minute cooldown ["w"] = true, }, { ["id"] = 116825, ["n"] = "Savage Fishing Pole", -- 200 for 10 minutes ["b"] = 200, ["s"] = 1, ["d"] = 20, -- 20 minute cooldown ["w"] = true, }, { ["id"] = 34832, ["n"] = "Captain Rumsey's Lager", -- 10 for 3 mins ["b"] = 10, ["s"] = 1, ["d"] = 3, ["u"] = 1, }, { ["id"] = 67404, ["n"] = "Glass Fishing Bobber", -- ??? ["b"] = 15, ["s"] = 1, ["d"] = 10, }, { ["id"] = 6529, ["n"] = "Shiny Bauble", -- 25 for 10 mins ["b"] = 25, ["s"] = 1, ["d"] = 10, }, { ["id"] = 6811, ["n"] = "Aquadynamic Fish Lens", -- 50 for 10 mins ["b"] = 50, ["s"] = 50, ["d"] = 10, }, { ["id"] = 6530, ["n"] = "Nightcrawlers", -- 50 for 10 mins ["b"] = 50, ["s"] = 50, ["d"] = 10, }, { ["id"] = 7307, ["n"] = "Flesh Eating Worm", -- 75 for 10 mins ["b"] = 75, ["s"] = 100, ["d"] = 10, }, { ["id"] = 6532, ["n"] = "Bright Baubles", -- 75 for 10 mins ["b"] = 75, ["s"] = 100, ["d"] = 10, }, { ["id"] = 34861, ["n"] = "Sharpened Fish Hook", -- 100 for 10 minutes ["b"] = 100, ["s"] = 100, ["d"] = 10, }, { ["id"] = 6533, ["n"] = "Aquadynamic Fish Attractor", -- 100 for 10 minutes ["b"] = 100, ["s"] = 100, ["d"] = 10, }, { ["id"] = 62673, ["n"] = "Feathered Lure", -- 100 for 10 minutes ["b"] = 100, ["s"] = 100, ["d"] = 10, }, { ["id"] = 46006, ["n"] = "Glow Worm", -- 100 for 60 minutes ["b"] = 100, ["s"] = 100, ["d"] = 60, ["l"] = 1, }, { ["id"] = 68049, ["n"] = "Heat-Treated Spinning Lure", -- 150 for 5 minutes ["b"] = 150, ["s"] = 250, ["d"] = 5, }, { ["id"] = 118391, ["n"] = "Worm Supreme", -- 200 for 10 mins ["b"] = 200, ["s"] = 100, ["d"] = 10, }, } -- sort ascending bonus and ascending time -- we may have to treat "Heat-Treated Spinning Lure" differently someday table.sort(FISHINGLURES, function(a,b) if ( a.b == b.b ) then return a.d < b.d; else return a.b < b.b; end end); function FishLib:GetLureTable() return FISHINGLURES; end function FishLib:IsWorn(itemid) for slot=1,19 do local link = GetInventoryItemLink("player", slot); if ( link ) then local _, id, _ = self:SplitFishLink(link); if ( itemid == id ) then return true; end end end -- return nil end function FishLib:IsItemOneHanded(item) if ( item ) then local _,_,_,_,_,_,_,_,bodyslot,_ = GetItemInfo(item); if ( bodyslot == "INVTYPE_2HWEAPON" or bodyslot == INVTYPE_2HWEAPON ) then return false; end end return true; end local useinventory = {}; local lureinventory = {}; function FishLib:UpdateLureInventory() local rawskill, _, _, _ = self:GetCurrentSkill(); useinventory = {}; lureinventory = {}; local b = 0; for _,lure in ipairs(FISHINGLURES) do local id = lure.id; local count = GetItemCount(id); -- does this lure have to be "worn" if ( count > 0 ) then local startTime, _, _ = GetItemCooldown(id); if (startTime == 0) then -- get the name so we can check enchants lure.n,_,_,_,_,_,_,_,_,_ = GetItemInfo(id); if ( lure.b > b or lure.w ) then b = lure.b; if ( lure.u ) then tinsert(useinventory, lure); elseif ( lure.s <= rawskill ) then if ( not lure.w or FishLib:IsWorn(id)) then tinsert(lureinventory, lure); end end end end end end return lureinventory, useinventory; end function FishLib:GetLureInventory() return lureinventory, useinventory; end -- Deal with lures function FishLib:HasBuff(buffName) if ( buffName ) then local name, _, _, _, _, _, _, _, _ = UnitBuff("player", buffName); return name ~= nil; end -- return nil end local function UseThisLure(lure, b, enchant, skill, level) if ( lure ) then local startTime, _, _ = GetItemCooldown(lure.id); -- already check for skill being nil, so that will skip the whole check with level -- skill = skill or 0; level = level or 0; local bonus = lure.b or 0; if ( startTime == 0 and (skill and level <= (skill + bonus)) and (bonus > enchant) ) then if ( not b or bonus > b ) then return true, bonus; end end return false, bonus; end return false, 0; end function FishLib:FindNextLure(b, state) local n = table.getn(lureinventory); for s=state+1,n,1 do if ( lureinventory[s] ) then local id = lureinventory[s].id; local startTime, _, _ = GetItemCooldown(id); if ( startTime == 0 ) then if ( not b or lureinventory[s].b > b ) then return s, lureinventory[s]; end end end end -- return nil; end function FishLib:FindBestLure(b, state, usedrinks) local zone, subzone = self:GetZoneInfo(); local level = self:GetFishingLevel(zone, subzone); if ( level and level > 1 ) then local rank, modifier, skillmax, enchant = self:GetCurrentSkill(); local skill = rank + modifier; -- don't need this now, LT has the full values -- level = level + 95; -- for no lost fish if ( skill <= level ) then self:UpdateLureInventory(); -- if drinking will work, then we're done if ( usedrinks and #useinventory > 0 ) then if ( not LastUsed or not self:HasBuff(LastUsed.n) ) then local id = useinventory[1].id; if ( not self:HasBuff(useinventory[1].n) ) then if ( level <= (skill + useinventory[1].b) ) then return nil, useinventory[1]; end end end end skill = skill - enchant; state = state or 0; local checklure; local useit, b = 0; -- Look for lures we're wearing, first for s=state+1,#lureinventory,1 do checklure = lureinventory[s]; if (checklure.w) then useit, b = UseThisLure(checklure, b, enchant, skill, level); if ( useit and b and b > 0 ) then return s, checklure; end end end b = 0; for s=state+1,#lureinventory,1 do checklure = lureinventory[s]; useit, b = UseThisLure(checklure, b, enchant, skill, level); if ( useit and b and b > 0 ) then return s, checklure; end end -- if we ran off the end of the table and we had a valid lure, let's use that one if ( (not enchant or enchant == 0) and b and (b > 0) and checklure ) then return #lureinventory, checklure; end end end -- return nil; end if oldlib then FishLib.caughtSoFar = oldlib.caughtSoFar or 0; FishLib.gearcheck = oldlib.gearcheck; FishLib.hasgear = oldlib.hasgear; FishLib.FindFishID = oldlib.FindFishID; FishLib.BOBBER_NAME = oldlib.BOBBER_NAME; FishLib.watchBobber = oldlib.watchBobber; FishLib.ActionBarID = oldlib.ActionBarID; else FishLib.caughtSoFar = 0; FishLib.gearcheck = true; FishLib.hasgear = false; end -- Handle events we care about local canCreateFrame = false; local FISHLIBFRAMENAME="FishLibFrame"; local fishlibframe = getglobal(FISHLIBFRAMENAME); if ( not fishlibframe) then fishlibframe = CreateFrame("Frame", FISHLIBFRAMENAME); fishlibframe:RegisterEvent("PLAYER_ENTERING_WORLD"); fishlibframe:RegisterEvent("PLAYER_LEAVING_WORLD"); fishlibframe:RegisterEvent("UPDATE_CHAT_WINDOWS"); fishlibframe:RegisterEvent("LOOT_OPENED"); fishlibframe:RegisterEvent("CHAT_MSG_SKILL"); fishlibframe:RegisterEvent("SKILL_LINES_CHANGED"); fishlibframe:RegisterEvent("UNIT_INVENTORY_CHANGED"); fishlibframe:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START"); fishlibframe:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP"); fishlibframe:RegisterEvent("EQUIPMENT_SWAP_FINISHED"); fishlibframe:RegisterEvent("ITEM_LOCK_CHANGED"); end fishlibframe.fl = FishLib; fishlibframe:SetScript("OnEvent", function(self, event, ...) local arg1 = select(1, ...); if ( event == "UPDATE_CHAT_WINDOWS" ) then canCreateFrame = true; self:UnregisterEvent(event); elseif ( event == "UNIT_INVENTORY_CHANGED" and arg1 == "player" ) then self.fl:UpdateLureInventory(); -- we can't actually rely on EQUIPMENT_SWAP_FINISHED, it appears self.fl:ForceGearCheck(); elseif ( event == "SKILL_LINES_CHANGED" or event == "ITEM_LOCK_CHANGED" or event == "EQUIPMENT_SWAP_FINISHED" ) then -- Did something we're wearing change? self.fl:ForceGearCheck(); elseif ( event == "CHAT_MSG_SKILL" ) then self.fl.caughtSoFar = 0; elseif ( event == "LOOT_OPENED" ) then if (IsFishingLoot()) then self.fl.caughtSoFar = self.fl.caughtSoFar + 1; end elseif ( event == "UNIT_SPELLCAST_CHANNEL_START" or event == "UNIT_SPELLCAST_CHANNEL_STOP" ) then if (arg1 == "player" ) then self.fl:UpdateLureInventory(); end elseif ( event == "PLAYER_ENTERING_WORLD" ) then self:RegisterEvent("ITEM_LOCK_CHANGED") self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") self:RegisterEvent("SPELLS_CHANGED") elseif ( event == "PLAYER_LEAVING_WORLD" ) then self:UnregisterEvent("ITEM_LOCK_CHANGED") self:UnregisterEvent("PLAYER_EQUIPMENT_CHANGED") self:UnregisterEvent("SPELLS_CHANGED") end end); fishlibframe:Show(); -- set up a table of slot mappings for looking up item information local slotinfo = { [1] = { name = "HeadSlot", tooltip = HEADSLOT, id = INVSLOT_HEAD }, [2] = { name = "NeckSlot", tooltip = NECKSLOT, id = INVSLOT_NECK }, [3] = { name = "ShoulderSlot", tooltip = SHOULDERSLOT, id = INVSLOT_SHOULDER }, [4] = { name = "BackSlot", tooltip = BACKSLOT, id = INVSLOT_BACK }, [5] = { name = "ChestSlot", tooltip = CHESTSLOT, id = INVSLOT_CHEST }, [6] = { name = "ShirtSlot", tooltip = SHIRTSLOT, id = INVSLOT_BODY }, [7] = { name = "TabardSlot", tooltip = TABARDSLOT, id = INVSLOT_TABARD }, [8] = { name = "WristSlot", tooltip = WRISTSLOT, id = INVSLOT_WRIST }, [9] = { name = "HandsSlot", tooltip = HANDSSLOT, id = INVSLOT_HAND }, [10] = { name = "WaistSlot", tooltip = WAISTSLOT, id = INVSLOT_WAIST }, [11] = { name = "LegsSlot", tooltip = LEGSSLOT, id = INVSLOT_LEGS }, [12] = { name = "FeetSlot", tooltip = FEETSLOT, id = INVSLOT_FEET }, [13] = { name = "Finger0Slot", tooltip = FINGER0SLOT, id = INVSLOT_FINGER1 }, [14] = { name = "Finger1Slot", tooltip = FINGER1SLOT, id = INVSLOT_FINGER2 }, [15] = { name = "Trinket0Slot", tooltip = TRINKET0SLOT, id = INVSLOT_TRINKET1 }, [16] = { name = "Trinket1Slot", tooltip = TRINKET1SLOT, id = INVSLOT_TRINKET2 }, [17] = { name = "MainHandSlot", tooltip = MAINHANDSLOT, id = INVSLOT_MAINHAND }, [18] = { name = "SecondaryHandSlot", tooltip = SECONDARYHANDSLOT, id = INVSLOT_OFFHAND }, } -- A map of item types to locations local slotmap = { ["INVTYPE_AMMO"] = { INVSLOT_AMMO }, ["INVTYPE_HEAD"] = { INVSLOT_HEAD }, ["INVTYPE_NECK"] = { INVSLOT_NECK }, ["INVTYPE_SHOULDER"] = { INVSLOT_SHOULDER }, ["INVTYPE_BODY"] = { INVSLOT_BODY }, ["INVTYPE_CHEST"] = { INVSLOT_CHEST }, ["INVTYPE_ROBE"] = { INVSLOT_CHEST }, ["INVTYPE_CLOAK"] = { INVSLOT_CHEST }, ["INVTYPE_WAIST"] = { INVSLOT_WAIST }, ["INVTYPE_LEGS"] = { INVSLOT_LEGS }, ["INVTYPE_FEET"] = { INVSLOT_FEET }, ["INVTYPE_WRIST"] = { INVSLOT_WRIST }, ["INVTYPE_HAND"] = { INVSLOT_HAND }, ["INVTYPE_FINGER"] = { INVSLOT_FINGER1,INVSLOT_FINGER2 }, ["INVTYPE_TRINKET"] = { INVSLOT_TRINKET1,INVSLOT_TRINKET2 }, ["INVTYPE_WEAPON"] = { INVSLOT_MAINHAND,INVSLOT_OFFHAND }, ["INVTYPE_SHIELD"] = { INVSLOT_OFFHAND }, ["INVTYPE_2HWEAPON"] = { INVSLOT_MAINHAND }, ["INVTYPE_WEAPONMAINHAND"] = { INVSLOT_MAINHAND }, ["INVTYPE_WEAPONOFFHAND"] = { INVSLOT_OFFHAND }, ["INVTYPE_HOLDABLE"] = { INVSLOT_OFFHAND }, ["INVTYPE_RANGED"] = { INVSLOT_RANGED }, ["INVTYPE_THROWN"] = { INVSLOT_RANGED }, ["INVTYPE_RANGEDRIGHT"] = { INVSLOT_RANGED }, ["INVTYPE_RELIC"] = { INVSLOT_RANGED }, ["INVTYPE_TABARD"] = { INVSLOT_TABARD }, ["INVTYPE_BAG"] = { 20,21,22,23 }, ["INVTYPE_QUIVER"] = { 20,21,22,23 }, [""] = { }, }; function FishLib:GetSlotInfo() return INVSLOT_MAINHAND, INVSLOT_OFFHAND, slotinfo; end function FishLib:GetSlotMap() return slotmap; end function FishLib:copytable(tab, level) local t = {}; if (tab) then level = level or 10000; for k,v in pairs(tab) do if ( type(v) == "table" and level > 0 ) then level = level - 1; t[k] = self:copytable(v, level); else t[k] = v; end end end return t; end -- count tables that don't have monotonic integer indexes function FishLib:tablecount(tab) local n = 0; for k,v in pairs(tab) do n = n + 1; end return n; end -- return a printable representation of a value function FishLib:printable(val) if ( val == nil ) then return "nil"; elseif (type(val) == "boolean") then return val and "true" or "false"; elseif (type(val) == "table") then return "table"; else return ""..val; end end -- this changes all the damn time -- "|c(%x+)|Hitem:(%d+)(:%d+):%d+:%d+:%d+:%d+:[-]?%d+:[-]?%d+:[-]?%d+:[-]?%d+|h%[(.*)%]|h|r" -- go with a fixed pattern, since sometimes the hyperlink trick appears not to work local _itempattern = "|c(%x+)|Hitem:([^:]+):([^:]+)[-:%d]+|h%[(.*)%]|h|r" function FishLib:GetItemPattern() if ( not _itempattern ) then -- This should work all the time self:GetPoleType(); -- force the default pole into the cache local _, pat, _, _, _, _, _, _ = GetItemInfo(6256); pat = string.gsub(pat, "|c(%x+)|Hitem:(%d+)(:%d+)", "|c(%%x+)|Hitem:(%%d+)(:%%d+)"); pat = string.gsub(pat, ":[-]?%d+", ":[-]?%%d+"); _itempattern = string.gsub(pat, "|h%[(.*)%]|h|r", "|h%%[(.*)%%]|h|r"); end return _itempattern; end function FishLib:SplitLink(link) if ( link ) then local _,_, color, id, enchant, name = string.find(link, self:GetItemPattern()); if ( name ) then return color, id..":"..enchant, name, enchant; end end end function FishLib:SplitFishLink(link) if ( link ) then local _,_, color, id, enchant, name = string.find(link, self:GetItemPattern()); return color, tonumber(id), name, enchant; end end function FishLib:GetItemInfo(link) -- name, link, rarity, itemlevel, minlevel, itemtype -- subtype, stackcount, equiploc, texture local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture, itemSellPrice = GetItemInfo(link); return itemName, itemLink, itemRarity, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture, itemLevel, itemSellPrice; end function FishLib:IsLinkableItem(link) local n,l,_,_,_,_,_,_ = self:GetItemInfo(link); return ( n and l ); end -- code taken from examples on wowwiki function FishLib:GetFishTooltip(force) local tooltip = FishLibTooltip; if ( force or not tooltip ) then tooltip = CreateFrame("GameTooltip", "FishLibTooltip", nil, "GameTooltipTemplate"); tooltip:SetOwner(WorldFrame, "ANCHOR_NONE"); -- Allow tooltip SetX() methods to dynamically add new lines based on these -- I don't think we need it if we use GameTooltipTemplate... -- tooltip:AddFontStrings( -- tooltip:CreateFontString( "$parentTextLeft9", nil, "GameTooltipText" ), -- tooltip:CreateFontString( "$parentTextRight9", nil, "GameTooltipText" ) ) end -- the owner gets unset sometimes, not sure why local owner, anchor = tooltip:GetOwner(); if (not owner or not anchor) then tooltip:SetOwner(WorldFrame, "ANCHOR_NONE"); end return FishLibTooltip; end local fp_itemtype = nil; local fp_subtype = nil; function FishLib:GetPoleType() if ( not fp_itemtype ) then _,_,_,_,fp_itemtype,fp_subtype,_,_,_,_ = self:GetItemInfo(6256); if ( not fp_itemtype ) then -- make sure it's in our cache local tooltip = self:GetFishTooltip(); tooltip:ClearLines(); tooltip:SetHyperlink("item:6256"); _,_,_,_,fp_itemtype,fp_subtype,_,_,_,_ = self:GetItemInfo(6256); end end return fp_itemtype, fp_subtype; end function FishLib:IsFishingPool(text) if ( not text ) then text = self:GetTooltipText(); end if ( text ) then local check = string.lower(text); for _,info in pairs(self.SCHOOLS) do local name = string.lower(info.name); if ( string.find(check, name) ) then return info; end end if ( string.find(check, self.SCHOOL) ) then return { name = text, kind = self.SCHOOL_FISH } ; end end -- return nil; end function FishLib:AddSchoolName(name) tinsert(self.SCHOOLS, { name = name, kind = SCHOOL_FISH }); end function FishLib:GetMainHandItem(id) local itemLink = GetInventoryItemLink("player", INVSLOT_MAINHAND); if ( not id ) then return itemLink; end _, id, _ = self:SplitFishLink(itemLink); return id; end function FishLib:IsFishingPole(itemLink) if (not itemLink) then -- Get the main hand item texture itemLink = self:GetMainHandItem(); end if ( itemLink ) then local _,_,_,_,itemtype,subtype,_,_,itemTexture,_ = self:GetItemInfo(itemLink); local _, id, _ = self:SplitFishLink(itemLink); self:GetPoleType(); if ( not fp_itemtype and itemTexture ) then -- If there is infact an item in the main hand, and it's texture -- that matches the fishing pole texture, then we have a fishing pole itemTexture = string.lower(itemTexture); if ( string.find(itemTexture, "inv_fishingpole") or string.find(itemTexture, "fishing_journeymanfisher") ) then -- Make sure it's not "Nat Pagle's Fish Terminator" if ( id ~= 19944 ) then fp_itemtype = itemtype; fp_subtype = subtype; return true; end end elseif ( fp_itemtype and fp_subtype ) then return (itemtype == fp_itemtype) and (subtype == fp_subtype); end end return false; end function FishLib:ForceGearCheck() self.gearcheck = true; self.hasgear = false; end function FishLib:IsFishingGear() if ( self.gearcheck ) then if (self:IsFishingPole()) then self.hasgear = true; end for i=1,16,1 do if ( not self.hasgear ) then if (self:FishingBonusPoints(slotinfo[i].id, 1) > 0) then self.hasgear = true; end end end self.gearcheck = false; end return self.hasgear; end function FishLib:IsFishingReady(partial) if ( partial ) then -- return self:IsFishingGear(); return true else return self:IsFishingPole(); end end -- fish tracking skill function FishLib:GetTrackingID(tex) if ( tex ) then for id=1,GetNumTrackingTypes() do local _, texture, _, _ = GetTrackingInfo(id); if ( texture == tex) then return id; end end end -- return nil; end local FINDFISHTEXTURE = "Interface\\Icons\\INV_Misc_Fish_02"; function FishLib:GetFindFishID() if ( not self.FindFishID ) then self.FindFishID = self:GetTrackingID(FINDFISHTEXTURE); end return self.FindFishID; end local bobber = {}; bobber["enUS"] = "Fishing Bobber"; bobber["esES"] = "Anzuelo"; bobber["esMX"] = "Anzuelo"; bobber["deDE"] = "Schwimmer"; bobber["frFR"] = "Flotteur"; bobber["ptBR"] = "Isca de Pesca"; bobber["ruRU"] = "Поплавок"; bobber["zhTW"] = "釣魚浮標"; bobber["zhCN"] = "垂钓水花"; -- in case the addon is smarter than us function FishLib:SetBobberName(name) self.BOBBER_NAME = name; end function FishLib:GetBobberName() if ( not self.BOBBER_NAME ) then local locale = GetLocale(); if ( bobber[locale] ) then self.BOBBER_NAME = bobber[locale]; else self.BOBBER_NAME = bobber["enUS"]; end end return self.BOBBER_NAME; end function FishLib:GetTooltipText() if ( GameTooltip:IsVisible() ) then local text = getglobal("GameTooltipTextLeft1"); if ( text ) then return text:GetText(); end end -- return nil; end function FishLib:SaveTooltipText() self.lastTooltipText = self:GetTooltipText(); return self.lastTooltipText; end function FishLib:GetLastTooltipText() return self.lastTooltipText; end function FishLib:ClearLastTooltipText() self.lastTooltipText = nil; end function FishLib:OnFishingBobber() if ( GameTooltip:IsVisible() and GameTooltip:GetAlpha() == 1 ) then local text = self:GetTooltipText() or self:GetLastTooltipText(); -- let a partial match work (for translations) return ( text and string.find(text, self:GetBobberName() ) ); end end local ACTIONDOUBLEWAIT = 0.4; local MINACTIONDOUBLECLICK = 0.05; function FishLib:WatchBobber(flag) self.watchBobber = flag; end -- look for double clicks function FishLib:CheckForDoubleClick(button) if (button and button ~= self:GetSAMouseButton()) then return false; end if ( not LootFrame:IsShown() and self.lastClickTime ) then local pressTime = GetTime(); local doubleTime = pressTime - self.lastClickTime; if ( (doubleTime < ACTIONDOUBLEWAIT) and (doubleTime > MINACTIONDOUBLECLICK) ) then if ( not self.watchBobber or not self:OnFishingBobber() ) then self.lastClickTime = nil; return true; end end end self.lastClickTime = GetTime(); if ( self:OnFishingBobber() ) then GameTooltip:Hide(); end return false; end function FishLib:ExtendDoubleClick() if ( self.lastClickTime ) then self.lastClickTime = self.lastClickTime + ACTIONDOUBLEWAIT/2; end end function FishLib:GetZoneInfo() local zone = GetRealZoneText(); local subzone = GetSubZoneText(); if ( not zone or zone == "" ) then zone = UNKNOWN; end if ( not subzone or subzone == "" ) then subzone = zone; end -- Hack to fix issues with 4.1 and LibBabbleZone and LibTourist if (zone == "City of Ironforge" ) then zone = "Ironforge"; end local continent = GetCurrentMapContinent(); zone = LT:GetUniqueEnglishZoneNameForLookup(zone, continent) return zone, subzone; end function FishLib:GetBaseSubZone(sname) if ( sname == FishLib.UNKNOWN or sname == UNKNOWN ) then return FishLib.UNKNOWN; end if (sname and not BSL[sname] and BSZR[sname]) then sname = BSZR[sname]; end if (not sname) then sname = FishLib.UNKNOWN; end return sname; end function FishLib:GetLocSubZone(sname) if ( sname == FishLib.UNKNOWN or sname == UNKNOWN ) then return UNKNOWN; end if (sname and BSL[sname] ) then sname = BSZ[sname]; end if (not sname) then sname = FishLib.UNKNOWN; end return sname; end local subzoneskills = { ["Jademir Lake"] = 425, ["Verdantis River"] = 300, ["The Forbidding Sea"] = 225, ["Ruins of Arkkoran"] = 300, ["The Tainted Forest"] = 25, ["Ruins of Gilneas"] = 75, ["The Throne of Flame"] = 1, ["Forge Camp: Hate"] = 375, -- Nagrand ["Lake Sunspring"] = 490, -- Nagrand ["Skysong Lake"] = 490, -- Nagrand ["Oasis"] = 100, ["South Seas"] = 300, ["Lake Everstill"] = 150, ["Blackwind"] = 500, ["Ere'Noru"] = 500, ["Jorune"] = 500, ["Silmyr"] = 500, ["Cannon's Inferno"] = 1, ["Fire Plume Ridge"] = 1, ["Marshlight Lake"] = 450, ["Sporewind Lake"] = 450, ["Serpent Lake"] = 450, ["Binan Village"] = 750, -- seems to be higher here, for some reason }; function FishLib:GetFishingLevel(zone, subzone) subzone = self:GetBaseSubZone(subzone); local continent = GetCurrentMapContinent(); if (continent ~= 7 and subzoneskills[subzone]) then return subzoneskills[subzone]; else return LT:GetFishingLevel(zone); end end -- table taken from El's Anglin' pages -- More accurate than the previous (skill - 75) / 25 calculation now local skilltable = {}; tinsert(skilltable, { ["level"] = 100, ["inc"] = 1 }); tinsert(skilltable, { ["level"] = 200, ["inc"] = 2 }); tinsert(skilltable, { ["level"] = 300, ["inc"] = 2 }); tinsert(skilltable, { ["level"] = 450, ["inc"] = 4 }); tinsert(skilltable, { ["level"] = 525, ["inc"] = 6 }); tinsert(skilltable, { ["level"] = 600, ["inc"] = 10 }); local newskilluptable = {}; function FishLib:SetSkillupTable(table) newskilluptable = table; end function FishLib:GetSkillupTable() return newskilluptable; end -- this would be faster as a binary search, but I'm not sure it matters :-) function FishLib:CatchesAtSkill(skill) for _,chk in ipairs(skilltable) do if ( skill < chk.level ) then return chk.inc; end end -- return nil; end function FishLib:GetSkillUpInfo() local skill, mods, skillmax = self:GetCurrentSkill(); if ( skillmax and skill < skillmax ) then local needed = self:CatchesAtSkill(skill); if ( needed ) then return self.caughtSoFar, needed; end else self.caughtSoFar = 0; end return self.caughtSoFar, nil; end -- we should have some way to believe function FishLib:SetCaughtSoFar(value) if ( FishingBuddy and FishingBuddy.GetSetting ) then self.caughtSoFar = FishingBuddy.GetSetting("CaughtSoFar") or 0; else self.caughtSoFar = value or 0; end end function FishLib:GetCaughtSoFar() return self.caughtSoFar; end -- Find an action bar for fishing, if there is one local FISHINGTEXTURE = "Interface\\Icons\\Trade_Fishing"; function FishLib:GetFishingActionBarID(force) if ( force or not self.ActionBarID ) then for slot=1,72 do if ( HasAction(slot) and not IsAttackAction(slot) ) then local t,_,_ = GetActionInfo(slot); if ( t == "spell" ) then local tex = GetActionTexture(slot); if ( tex and tex == FISHINGTEXTURE ) then self.ActionBarID = slot; break; end end end end end return self.ActionBarID; end function FishLib:ClearFishingActionBarID() self.ActionBarID = nil; end -- handle classes of fish local MissedFishItems = {}; MissedFishItems[45190] = "Driftwood"; MissedFishItems[45200] = "Sickly Fish"; MissedFishItems[45194] = "Tangled Fishing Line"; MissedFishItems[45196] = "Tattered Cloth"; MissedFishItems[45198] = "Weeds"; MissedFishItems[45195] = "Empty Rum Bottle"; MissedFishItems[45199] = "Old Boot"; MissedFishItems[45201] = "Rock"; MissedFishItems[45197] = "Tree Branch"; MissedFishItems[45202] = "Water Snail"; MissedFishItems[45188] = "Withered Kelp"; MissedFishItems[45189] = "Torn Sail"; MissedFishItems[45191] = "Empty Clam"; function FishLib:IsMissedFish(id) if ( MissedFishItems[id] ) then return true; end -- return nil; end -- utility functions local function SplitColor(color) if ( color ) then if ( type(color) == "table" ) then for i,c in pairs(color) do color[i] = SplitColor(c); end elseif ( type(color) == "string" ) then local a = tonumber(string.sub(color,1,2),16); local r = tonumber(string.sub(color,3,4),16); local g = tonumber(string.sub(color,5,6),16); local b = tonumber(string.sub(color,7,8),16); color = { a = a, r = r, g = g, b = b }; end end return color; end local function AddTooltipLine(l) if ( type(l) == "table" ) then -- either { t, c } or {{t1, c1}, {t2, c2}} if ( type(l[1]) == "table" ) then local c1 = SplitColor(l[1][2]) or {}; local c2 = SplitColor(l[2][2]) or {}; GameTooltip:AddDoubleLine(l[1][1], l[2][1], c1.r, c1.g, c1.b, c2.r, c2.g, c2.b); else local c = SplitColor(l[2]) or {}; GameTooltip:AddLine(l[1], c.r, c.g, c.b, 1); end else GameTooltip:AddLine(l,nil,nil,nil,1); end end function FishLib:AddTooltip(text, tooltip) if ( not tooltip ) then tooltip = GameTooltip; end local c = color or {{}, {}}; if ( text ) then if ( type(text) == "table" ) then for _,l in pairs(text) do AddTooltipLine(l, tooltip); end else -- AddTooltipLine(text, color); tooltip:AddLine(text,nil,nil,nil,1); end end end function FishLib:FindChatWindow(name) local frame; for i = 1, NUM_CHAT_WINDOWS do local frame = getglobal("ChatFrame" .. i); if (frame.name == name) then return frame, getglobal("ChatFrame" .. i .. "Tab"); end end -- return nil, nil; end function FishLib:GetChatWindow(name) if (canCreateFrame) then local frame, frametab = self:FindChatWindow(name); if ( frame ) then if ( not frametab:IsVisible() ) then -- Dock the frame by default if ( not frame.oldAlpha ) then frame.oldAlpha = frame:GetAlpha() or DEFAULT_CHATFRAME_ALPHA; end FCF_DockFrame(frame, (#FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)+1), true); FCF_FadeInChatFrame(FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK)); FCF_DockUpdate(); end return frame, frametab; else local frame = FCF_OpenNewWindow(name); return self:FindChatWindow(name); end end -- if we didn't find our frame, something bad has happened, so -- let's just use the default chat frame return DEFAULT_CHAT_FRAME, nil; end -- Secure action button local SABUTTONNAME = "LibFishingSAButton"; function FishLib:ResetOverride() local btn = self.sabutton; if ( btn ) then btn.holder:Hide(); ClearOverrideBindings(btn); end end local function ClickHandled(self) self.fl:ResetOverride(); if ( self.postclick ) then self.postclick(); end end function FishLib:CreateSAButton() local btn = getglobal(SABUTTONNAME); if ( not btn ) then local holder = CreateFrame("Frame", nil, UIParent); btn = CreateFrame("Button", SABUTTONNAME, holder, "SecureActionButtonTemplate"); btn.holder = holder; btn:EnableMouse(true); btn:RegisterForClicks(nil); btn:Show(); holder:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0); holder:SetFrameStrata("LOW"); holder:Hide(); end if (not self.buttonevent) then self.buttonevent = "RightButtonUp"; end btn:SetScript("PostClick", ClickHandled); btn:RegisterForClicks(self.buttonevent); self.sabutton = btn; btn.fl = self; end FishLib.MOUSE1 = "RightButtonUp"; FishLib.MOUSE2 = "Button4Up"; FishLib.MOUSE3 = "Button5Up"; FishLib.CastButton = {}; FishLib.CastButton[FishLib.MOUSE1] = "RightButton"; FishLib.CastButton[FishLib.MOUSE2] = "Button4"; FishLib.CastButton[FishLib.MOUSE3] = "Button5"; FishLib.CastKey = {}; FishLib.CastKey[FishLib.MOUSE1] = "BUTTON2"; FishLib.CastKey[FishLib.MOUSE2] = "BUTTON4"; FishLib.CastKey[FishLib.MOUSE3] = "BUTTON5"; function FishLib:GetSAMouseEvent() if (not self.buttonevent) then self.buttonevent = "RightButtonUp"; end return self.buttonevent; end function FishLib:GetSAMouseButton() return self.CastButton[self:GetSAMouseEvent()]; end function FishLib:GetSAMouseKey() return self.CastKey[self:GetSAMouseEvent()]; end function FishLib:SetSAMouseEvent(buttonevent) if (not buttonevent) then buttonevent = "RightButtonUp"; end if (self.CastButton[buttonevent]) then self.buttonevent = buttonevent; local btn = getglobal(SABUTTONNAME); if ( btn ) then btn:RegisterForClicks(nil); btn:RegisterForClicks(self.buttonevent); end return true; end -- return nil; end function FishLib:InvokeFishing(useaction) local btn = self.sabutton; if ( not btn ) then return; end local _, name = self:GetFishingSkillInfo(); local findid = self:GetFishingActionBarID(); if ( not useaction or not findid ) then btn:SetAttribute("type", "spell"); btn:SetAttribute("spell", name); btn:SetAttribute("action", nil); else btn:SetAttribute("type", "action"); btn:SetAttribute("action", findid); btn:SetAttribute("spell", nil); end btn:SetAttribute("item", nil); btn:SetAttribute("target-slot", nil); btn.postclick = nil; end function FishLib:InvokeLuring(id) local btn = self.sabutton; if ( not btn ) then return; end btn:SetAttribute("type", "item"); if ( id ) then btn:SetAttribute("item", "item:"..id); btn:SetAttribute("target-slot", INVSLOT_MAINHAND); else btn:SetAttribute("item", nil); btn:SetAttribute("target-slot", nil); end btn:SetAttribute("spell", nil); btn:SetAttribute("action", nil); btn.postclick = nil; end function FishLib:OverrideClick(postclick) local btn = self.sabutton; if ( not btn ) then return; end local buttonkey = self:GetSAMouseKey(); fishlibframe.fl = self; btn.fl = self; btn.postclick = postclick; SetOverrideBindingClick(btn, true, buttonkey, SABUTTONNAME); btn.holder:Show(); end function FishLib:ClickSAButton() local btn = self.sabutton; if ( not btn ) then return; end btn:Click(self:GetSAMouseButton()); end -- Taken from wowwiki tooltip handling suggestions local function EnumerateTooltipLines_helper(...) local lines = {}; for i = 1, select("#", ...) do local region = select(i, ...) if region and region:GetObjectType() == "FontString" then local text = region:GetText() -- string or nil tinsert(lines, text or ""); end end return lines; end function FishLib:EnumerateTooltipLines(tooltip) return EnumerateTooltipLines_helper(tooltip:GetRegions()) end -- Fishing bonus. We used to be able to get the current modifier from -- the skill API, but now we have to figure it out ourselves local match; function FishLib:FishingBonusPoints(item, inv) local points = 0; if ( item and item ~= "" ) then if ( not match ) then local _,skillname = self:GetFishingSkillInfo(true); match = {}; match[1] = "%+(%d+) "..skillname; match[2] = skillname.." %+(%d+)"; -- Equip: Fishing skill increased by N. match[3] = skillname.."[%a%s]+(%d+)%."; if ( GetLocale() == "deDE" ) then match[4] = "+(%d+) Angelfertigkeit"; end end local tooltip = self:GetFishTooltip(); tooltip:ClearLines(); if (inv) then tooltip:SetInventoryItem("player", item); else local _, id, _ = self:SplitFishLink(item); if (not id) then item = "item:"..item; end tooltip:SetHyperlink(item); end local lines = EnumerateTooltipLines_helper(tooltip:GetRegions()) for i=1,#lines do local bodyslot = lines[i]:gsub("^%s*(.-)%s*$", "%1"); if (string.len(bodyslot) > 0) then for _,pat in ipairs(match) do local _,_,bonus = string.find(bodyslot, pat); if ( bonus ) then points = points + bonus; end end end end end return points; end -- if we have a fishing pole, return the bonus from the pole -- and the bonus from a lure, if any, separately function FishLib:GetPoleBonus() if (self:IsFishingPole()) then -- get the total bonus for the pole local total = self:FishingBonusPoints(INVSLOT_MAINHAND, true); local hmhe,_,_,_,_,_ = GetWeaponEnchantInfo(); if ( hmhe ) then -- IsFishingPole has set mainhand for us local itemLink = self:GetMainHandItem(); local _, id, _, enchant = self:SplitLink(itemLink); -- get the raw value of the pole without any temp enchants local pole = self:FishingBonusPoints(id); return total, total - pole; else -- no enchant, all pole return total, 0; end end return 0, 0; end function FishLib:GetOutfitBonus() local bonus = 0; -- we can skip the ammo and ranged slots for i=1,16,1 do bonus = bonus + self:FishingBonusPoints(slotinfo[i].id, 1); end -- Blizz seems to have capped this at 50, plus there seems -- to be a maximum of +5 in enchants. Need to do some more work -- to verify. -- if (bonus > 50) then -- bonus = 50; -- end local pole, lure = self:GetPoleBonus(); return bonus + pole, lure; end -- return a list of the best items we have for a fishing outfit function FishLib:GetFishingOutfitItems(wearing, nopole, ignore) local ibp = function(link) return self:FishingBonusPoints(link); end; -- find fishing gear -- no affinity, check all bags local outfit = nil; local itemtable = {}; for invslot=1,17,1 do local slotid = slotinfo[invslot].id; local ismain = (slotid == INVSLOT_MAINHAND); if ( not nopole or not ismain ) then local slotname = slotinfo[invslot].name; local maxb = -1; local link; -- should we include what we're already wearing? if ( wearing ) then link = GetInventoryItemLink("player", slotid); if ( link ) then maxb = self:FishingBonusPoints(link); if (maxb > 0) then outfit = outfit or {}; outfit[invslot] = { link=link, slot=slotid }; end end end -- this only gets items in bags, hence the check above for slots wipe(itemtable); itemtable = GetInventoryItemsForSlot(slotid, itemtable); for location,id in pairs(itemtable) do if (not ignore or not ignore[id]) then local player, bank, bags, void, slot, bag = EquipmentManager_UnpackLocation(location); if ( bags and slot and bag ) then link = GetContainerItemLink(bag, slot); else link = nil; end if ( link ) then local b = self:FishingBonusPoints(link); local go = false; if ( ismain ) then go = self:IsFishingPole(link); end if (go or (b > 0)) then local usable, _ = IsUsableItem(link); if ( usable and (b > maxb) ) then maxb = b; outfit = outfit or {}; outfit[slotid] = { link=link, bag=bag, slot=slot, slotname=slotname }; end end end end end end end return outfit; end function FishLib:GetBagItemStats(bag, slot) local link; local c, i, n; if ( bag ) then link = GetContainerItemLink (bag,slot); else link = GetInventoryItemLink("player", slot); end if (link) then c, i, n = self:SplitFishLink(link); end return c, i, n; end -- look in a particular bag function FishLib:CheckThisBag(bag, id, skipcount) -- get the number of slots in the bag (0 if no bag) local numSlots = GetContainerNumSlots(bag); if (numSlots > 0) then -- check each slot in the bag for slot=1, numSlots do local c, i, n = self:GetBagItemStats(bag, slot); if ( i and id == i ) then if ( skipcount == 0 ) then return slot, skipcount; end skipcount = skipcount - 1; end end end return nil, skipcount; end -- look for the item anywhere we can find it, skipping if we're looking -- for more than one function FishLib:FindThisItem(id, skipcount) if ( not id ) then return nil,nil; end local skipcount = skipcount or 0; -- force id to be a number local n,l,_,_,_,_,_,_ = GetItemInfo(id); if (not n) then n,l,_,_,_,_,_,_ = GetItemInfo("item:"..id); end _, id, _ = self:SplitFishLink(l); -- check each of the bags on the player for bag = BACKPACK_CONTAINER, NUM_BAG_SLOTS do local slot; slot, skipcount = self:CheckThisBag(bag, id, skipcount); if ( slot ) then return bag, slot; end end local _,_,slotnames = self:GetSlotInfo(); for _,si in ipairs(slotnames) do local slot = si.id; local c, i, n = self:GetBagItemStats(nil, slot); if ( i and id == i ) then if ( skipcount == 0 ) then return nil, slot; end skipcount = skipcount - 1; end end -- return nil, nil; end -- Is this item openable? function FishLib:IsOpenable(item) local _, id, _ = self:SplitFishLink(item); if (not id) then item = "item:"..item; end local canopen = false; local locked = false; local tooltip = self:GetFishTooltip(); tooltip:ClearLines(); tooltip:SetHyperlink(item); local lines = EnumerateTooltipLines_helper(tooltip:GetRegions()) for i=1,#lines do local line = lines[i]; if ( line == _G.ITEM_OPENABLE ) then openable = true; elseif ( line == _G.LOCKED ) then locked = true; end end return canopen, locked; end -- Find out where the player is. Based on code from Astrolabe and wowwiki notes function FishLib:GetCurrentPlayerPosition() local x, y = GetPlayerMapPosition("player"); local lC, lZ = GetCurrentMapContinent(), GetCurrentMapZone(); -- if the current location is 0,0 we need to call SetMapToCurrentZone() if ( x <= 0 and y <= 0 ) then -- find out where we are now SetMapToCurrentZone(); -- if we haven't changed zones yet, the zoom is incorrect SetMapZoom(GetCurrentMapContinent()); local C, Z = GetCurrentMapContinent(), GetCurrentMapZone(); x, y = GetPlayerMapPosition("player"); -- put everything back, if we need to if ( C ~= lC or Z ~= lZ ) then SetMapZoom(lC, lZ); --set map zoom back to what it was before end if ( x <= 0 and y <= 0 ) then -- we are in an instance or otherwise off the continent map return C, Z, 0, 0; else return C, Z, x, y; end end return lC, lZ, x, y; end -- translation support functions -- replace #KEYWORD# with the value of keyword (which might be a color) local function FixupThis(target, tag, what) if ( type(what) == "table" ) then local fixed = {}; for idx,str in pairs(what) do fixed[idx] = FixupThis(target, tag, str); end for idx,str in pairs(fixed) do what[idx] = str; end return what; elseif ( type(what) == "string" ) then local pattern = "#([A-Z0-9_]+)#"; local s,e,w = string.find(what, pattern); while ( w ) do if ( type(target[w]) == "string" ) then local s1 = strsub(what, 1, s-1); local s2 = strsub(what, e+1); what = s1..target[w]..s2; s,e,w = string.find(what, pattern); -- elseif ( Crayon and Crayon["COLOR_HEX_"..w] ) then -- local s1 = strsub(what, 1, s-1); -- local s2 = strsub(what, e+1); -- what = s1.."ff"..Crayon["COLOR_HEX_"..w]..s2; -- s,e,w = string.find(what, pattern); else -- stop if we can't find something to replace it with w = nil; end end return what; end -- do nothing return what; end function FishLib:FixupEntry(constants, tag) FixupThis(constants, tag, constants[tag]); end local function FixupStrings(target) local fixed = {}; for tag,_ in pairs(target) do fixed[tag] = FixupThis(target, tag, target[tag]); end for tag,str in pairs(fixed) do target[tag] = str; end end local function FixupBindings(target) for tag,str in pairs(target) do if ( string.find(tag, "^BINDING") ) then setglobal(tag, target[tag]); target[tag] = nil; end end end local missing = {}; local function LoadTranslation(source, lang, target, record) local translation = source[lang]; if ( translation ) then for tag,value in pairs(translation) do if ( not target[tag] ) then target[tag] = value; if ( record ) then missing[tag] = value; end end end end end function FishLib:Translate(addon, source, target, forced) local locale = forced or GetLocale(); target.VERSION = GetAddOnMetadata(addon, "Version"); LoadTranslation(source, locale, target); if ( locale ~= "enUS" ) then LoadTranslation(source, "enUS", target, forced); end LoadTranslation(source, "Inject", target); FixupStrings(target); FixupBindings(target); if (forced) then return missing; end end -- Pool types FishLib.SCHOOL_FISH = 0; FishLib.SCHOOL_WRECKAGE = 1; FishLib.SCHOOL_DEBRIS = 2; FishLib.SCHOOL_WATER = 3; FishLib.SCHOOL_TASTY = 4; FishLib.SCHOOL_OIL = 5; FishLib.SCHOOL_CHURNING = 6; FishLib.SCHOOL_FLOTSAM = 7; FishLib.SCHOOL_FIRE = 8; local FLTrans = {}; function FLTrans:Setup(lang, school, ...) self[lang] = {}; -- as long as string.lower breaks all UTF-8 equally, this should still work self[lang].SCHOOL = string.lower(school); local n = select("#", ...); local schools = {}; for idx=1,n,2 do local name, kind = select(idx, ...); tinsert(schools, { name = name, kind = kind }); end -- add in the fish we know are in schools self[lang].SCHOOLS = schools; end FLTrans:Setup("enUS", "school", "Floating Wreckage", FishLib.SCHOOL_WRECKAGE, "Patch of Elemental Water", FishLib.SCHOOL_WATER, "Floating Debris", FishLib.SCHOOL_DEBRIS, "Oil Spill", FishLib.SCHOOL_OIL, "Stonescale Eel Swarm", FishLib.SCHOOL_FISH, "Muddy Churning Water", FishLib.SCHOOL_CHURNING, "Pure Water", FishLib.SCHOOL_WATER, "Steam Pump Flotsam", FishLib.SCHOOL_FLOTSAM, "School of Tastyfish", FishLib.SCHOOL_TASTY, "Pool of Fire", FishLib.SCHOOL_FIRE); FLTrans:Setup("koKR", "떼", "표류하는 잔해", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "정기가 흐르는 물 웅덩이", FishLib.SCHOOL_WATER, -- Patch of Elemental Water "표류하는 파편", FishLib.SCHOOL_DEBRIS, -- Floating Debris "떠다니는 기름", FishLib.SCHOOL_OIL, -- Oil Spill "거품이는 진흙탕물", FishLib.SCHOOL_CHURNING, -- Muddy Churning Water "깨끗한 물", FishLib.SCHOOL_WATER, -- Pure Water "증기 양수기 표류물", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "맛둥어 떼", FishLib.SCHOOL_TASTY); -- School of Tastyfish FLTrans:Setup("deDE", "schwarm", "Treibende Wrackteile", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "Stelle mit Elementarwasser", FishLib.SCHOOL_WATER, -- Patch of Elemental Water "Schwimmende Trümmer", FishLib.SCHOOL_DEBRIS, -- Floating Debris "Ölfleck", FishLib.SCHOOL_OIL, -- Oil Spill "Schlammiges aufgewühltes Gewässer", FishLib.SCHOOL_CHURNING, -- Muddy Churning Water "Reines Wasser", FishLib.SCHOOL_WATER, -- Pure Water "Treibgut der Dampfpumpe", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "Leckerfischschwarm", FishLib.SCHOOL_TASTY); -- School of Tastyfish FLTrans:Setup("frFR", "banc", "Débris flottants", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "Remous d'eau élémentaire", FishLib.SCHOOL_WATER, -- Patch of Elemental Water "Débris flottant", FishLib.SCHOOL_DEBRIS, -- Floating Debris "Nappe de pétrole", FishLib.SCHOOL_OIL, -- Oil Spill "Eaux troubles et agitées", FishLib.SCHOOL_CHURNING, -- Muddy Churning Water "Eau pure", FishLib.SCHOOL_WATER, -- Pure Water "Détritus de la pompe à vapeur", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "Banc de courbine", FishLib.SCHOOL_TASTY); -- School of Tastyfish FLTrans:Setup("esES", "banco", "Restos de un naufragio", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "Restos flotando", FishLib.SCHOOL_DEBRIS, -- Floating Debris "Vertido de petr\195\179leo", FishLib.SCHOOL_OIL, -- Oil Spill "Agua pura", FishLib.SCHOOL_WATER, -- Pure Water "Restos flotantes de bomba de vapor", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "Banco de pezricos", FishLib.SCHOOL_TASTY); -- School of Tastyfish FLTrans:Setup("zhCN", "鱼群", "漂浮的残骸", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "元素之水", FishLib.SCHOOL_WATER, -- Patch of Elemental Water "漂浮的碎片", FishLib.SCHOOL_DEBRIS, -- Floating Debris "油井", FishLib.SCHOOL_OIL, -- Oil Spill "石鳞鳗群", FishLib.SCHOOL_FISH, -- Stonescale Eel Swarm "混浊的水", FishLib.SCHOOL_CHURNING, -- Muddy Churning Water "纯水", FishLib.SCHOOL_WATER, -- Pure Water "蒸汽泵废料", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "可口鱼", FishLib.SCHOOL_TASTY); -- School of Tastyfish FLTrans:Setup("zhTW", "群", "漂浮的殘骸", FishLib.SCHOOL_WRECKAGE, -- Floating Wreckage "元素之水", FishLib.SCHOOL_WATER, -- Patch of Elemental Water "漂浮的碎片", FishLib.SCHOOL_DEBRIS, -- Floating Debris "油井", FishLib.SCHOOL_OIL, -- Oil Spill "混濁的水", FishLib.SCHOOL_CHURNING, -- Muddy Churning Water "純水", FishLib.SCHOOL_WATER, -- Pure Water "蒸汽幫浦漂浮殘骸", FishLib.SCHOOL_FLOTSAM, -- Steam Pump Flotsam "斑點可口魚魚群", FishLib.SCHOOL_TASTY); -- School of Tastyfish FishLib:Translate("LibFishing", FLTrans, FishLib); FLTrans = nil;