Files
RecipeRadar/RecipeRadar.lua
T

629 lines
17 KiB
Lua

-- RecipeRadar.lua: main event code and general utility functions
-- $Id: RecipeRadar.lua 464 2006-09-18 18:05:57Z jnmiller $
function RecipeRadar_OnLoad()
tinsert(UISpecialFrames, "RecipeRadarFrame")
UIPanelWindows["RecipeRadarFrame"] = nil
SLASH_RecipeRadar1 = "/rrdr"
SLASH_RecipeRadar2 = "/reciperadar"
SlashCmdList["RecipeRadar"] = RecipeRadar_SlashHandler
this:RegisterEvent("VARIABLES_LOADED")
this:RegisterEvent("ZONE_CHANGED_NEW_AREA")
this:RegisterEvent("MINIMAP_UPDATE_ZOOM")
this:RegisterEvent("WORLD_MAP_UPDATE")
this:RegisterEvent("CRAFT_SHOW")
this:RegisterEvent("TRADE_SKILL_SHOW")
this:RegisterEvent("CHAT_MSG_SKILL")
this:RegisterEvent("CHAT_MSG_SYSTEM")
this:RegisterEvent("PLAYER_ENTERING_WORLD")
-- RecipeRadar_Print("Recipe Radar loaded!")
end
function RecipeRadar_OnEvent()
if (event == "VARIABLES_LOADED") then
RecipeRadar_Globals_Init()
RecipeRadar_MinimapButton_Init()
RecipeRadar_Availability_Init()
RecipeRadar_Maps_Init()
RecipeRadar_SkillDB_Init()
RecipeRadar_MainFrame_Init()
RecipeRadar_DropDowns_Init()
RecipeRadar_SelectFirstRecipe()
elseif (event == "ZONE_CHANGED_NEW_AREA") then
if (RecipeRadar_RegionHasLearnableRecipe(GetRealZoneText())) then
RecipeRadar_MinimapButton_Flash()
end
RecipeRadar_Minimap_OnNewRegion()
if (RecipeRadar_Options.AutoSelectRegion) then
RecipeRadar_NewRegion(GetRealZoneText())
end
elseif (event == "ADDON_LOADED" and arg1 == "RecipeRadar") then
--
elseif (event == "PLAYER_ENTERING_WORLD") then
if (RecipeRadar_Filters.Teams["Horde"] == nil) then
-- this should be the first run of the addon, so set team filters
RecipeRadar_Filters.Teams[UnitFactionGroup("player")] = false
RecipeRadar_Filters.Teams
[RecipeRadar_GetOpposingFaction("player")] = true
end
elseif (event == "MINIMAP_UPDATE_ZOOM") then
RecipeRadar_Minimap_SetIndoorness()
elseif (event == "WORLD_MAP_UPDATE") then
RecipeRadar_Worldmap_UpdateButtons()
elseif (event == "CRAFT_SHOW") then
RecipeRadar_SkillDB_Refresh("craft")
elseif (event == "TRADE_SKILL_SHOW") then
RecipeRadar_SkillDB_Refresh("trade")
elseif (event == "CHAT_MSG_SKILL") then
-- arg1 = eg. "Your skill in Cooking has increased to 221."
RecipeRadar_ParseChatMessage(arg1)
elseif (event == "CHAT_MSG_SYSTEM") then
-- arg1 = eg. "You have learned how to create a new item: Bristle Whisker Catfish."
RecipeRadar_ParseChatMessage(arg1)
end
end
function RecipeRadar_OnShow()
--PlaySound("igCharacterInfoOpen")
end
function RecipeRadar_OnHide()
PlaySound("igCharacterInfoClose")
RecipeRadarFrame:StopMovingOrSizing()
end
function RecipeRadar_SlashHandler(msg)
local _, _, cmd, arg1, arg2 = string.find(msg, "(%w+)%s*(%w*)%s*(%w*)")
if (cmd == "debug") then
if (arg1 == "vendor") then
RecipeRadar_Options.PositionDebug = true
end
if (arg1 == "skills") then
RecipeRadar_SkillDB_Print(arg2)
end
if (arg1 == "clear") then
RecipeRadar_SkillDB_Clear(arg2)
end
return
end
if (cmd == "radar") then
local region = RecipeRadar_RegionData[GetRealZoneText()]
if (not region) then return end
for _, vendor in region.Vendors do
RecipeRadar_Print(RecipeRadar_GetColoredTeamString(
vendor.Team, vendor.Name) .. " (" ..
string.format("%d", vendor.Coordinates[1].x * 100) .. ", " ..
string.format("%d", vendor.Coordinates[1].y * 100) .. "):")
table.sort(vendor.Recipes, RecipeRadar_CompareSkills)
table.foreach(vendor.Recipes, RecipeRadar_PrintRecipe)
end
return
end
RecipeRadar_Toggle()
end
-- Function to be called on selecting a new region to initialize the
-- main frame and any active filters.
function RecipeRadar_NewRegion(region)
if (not region or region == "") then return end
RecipeRadar_Options.CurrentRegion = region
RecipeRadar_Options.CurrentContinent = RecipeRadar_GetCurrentContinent()
RecipeRadar_InitVendorFilters()
RecipeRadar_SelectFirstRecipe()
RecipeRadar_FrameUpdate()
end
-- Returns whether the given region contains at least one recipe that
-- is learnable by the player or (optionally) alts
function RecipeRadar_RegionHasLearnableRecipe(region_name)
local region_data = RecipeRadar_RegionData[region_name]
if (not region_data) then return false end
for _, vendor in region_data.Vendors do
for _, recipe in vendor.Recipes do
if (RecipeRadar_Availability_IsLearnable(recipe)) then
return true
end
end
end
return false
end
-- Given a table of recipes, returns the number of recipes that are
-- learnable by the player or (optionally) alts.
function RecipeRadar_CountLearnableRecipes(recipes)
if (not recipes) then return 0 end
local total = 0
for _, recipe in recipes do
if (RecipeRadar_Availability_IsLearnable(recipe)) then
total = total + 1
end
end
return total
end
-- Returns the index of the current region's continent, or 0 if the
-- player is not in a known map zone (eg. she's in an instance).
function RecipeRadar_GetCurrentContinent()
local region = RecipeRadar_RegionData[RecipeRadar_Options.CurrentRegion]
-- this mess derives the current continent from the current region
if (region) then
return region.Continent
else
-- region is not one with recipes, so we search for it
for cont_id, _ in { GetMapContinents() } do
for _, zone in { GetMapZones(cont_id) } do
if (RecipeRadar_Options.CurrentRegion == zone) then
return cont_id
end
end
end
return 0
end
end
-- Converts a format string to one that LUA can understand. I've
-- shamelessly stolen this from Innerfire in order to (I hope) prevent
-- the combat chatlog parsing errors.
function RecipeRadar_ConvertFormat(fmt)
-- remove non-English format params like "%1$s"
local find = string.gsub(fmt, "%%%d%$", "%%")
-- remove any special characters with escaped versions
find = string.gsub(find, "([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1")
-- finally replace standard "%s" and "%d"
find = string.gsub(find, "%%s", "(.+)")
find = string.gsub(find, "%%d", "(%%d+)")
return find
end
-- Parses the input for a couple of known message formats to gain information
-- about changes in the character's skills.
function RecipeRadar_ParseChatMessage(msg)
-- messages of the form 'Your skill in %s has increased to %d.'
local start, _, prof, rank =
string.find(msg, RecipeRadar_ConvertFormat(ERR_SKILL_UP_SI))
if (start) then
RecipeRadar_SkillDB_SetRank(prof, rank)
end
-- messages of the form 'You have unlearned %s.'
start, _, prof =
string.find(msg, RecipeRadar_ConvertFormat(ERR_SPELL_UNLEARNED_S))
if (prof) then
RecipeRadar_SkillDB_GetPlayerProfessions()[prof] = nil
end
end
-- Foreach function that prints a recipe link and its cost.
function RecipeRadar_PrintRecipe(index, recipe)
RecipeRadar_Print(" " .. RecipeRadar_GetHyperlinkByID(recipe.ID) ..
", " .. RecipeRadar_GetGSCString(recipe.Cost))
end
-- Returns a string describing an item link from an item ID.
function RecipeRadar_GetHyperlinkByID(id)
local name, link, color = RecipeRadar_GetSafeItemInfo(id)
return RecipeRadar_GetHyperlinkByParts(name, link, color)
end
-- Returns a string describing an item link from link components.
function RecipeRadar_GetHyperlinkByParts(name, link, color)
return string.format("%s|H%s|h[%s]|h|r", color, link, name)
end
-- If an item is not in your local cache, GetItemInfo() fails. To work
-- around this, we just return 'uncached' along with the details we do know.
-- If an item is recognized by the client, we cache the name so that future
-- requests don't end up fully 'uncached'. The final return value is a
-- boolean indicating whether it was a cached ID.
function RecipeRadar_GetSafeItemInfo(id)
local name, link, rarity, _, _, _, _, _, texture = GetItemInfo(id)
-- initialize our name cache if necessary
if (not RecipeRadar_NameCache) then RecipeRadar_NameCache = { } end
if (name == nil) then
-- set returned name according to whether we've already seen the id
if (RecipeRadar_NameCache[id]) then
name = RecipeRadar_NameCache[id]
else
name = RRS("Uncached Recipe")
end
-- create our own tooltip for uncached items
return name, "item:" .. id .. ":0:0:0",
RecipeRadar_ColorToCode(RecipeRadar_Colors.UncachedRecipe),
RecipeRadar_Colors.UncachedRecipe.r,
RecipeRadar_Colors.UncachedRecipe.g,
RecipeRadar_Colors.UncachedRecipe.b,
RECIPERADAR_IMAGE_ROOT .. "Misc\\QuestionMark",
false -- boolean to indicate whether we got a real recipe
else
RecipeRadar_NameCache[id] = name -- cache names
local r, g, b, colorcode = GetItemQualityColor(rarity)
return name, link, colorcode, r, g, b, texture, true
end
end
function RecipeRadar_RegionNameToID(region_name, continent)
local index = 0
for _, zone in { GetMapZones(continent) } do
index = index + 1
if (region_name == zone) then
return index
end
end
RecipeRadar_Print("ASSERT FAILED: region '" .. region_name ..
"' not found by GetMapZones()!")
return nil
end
-- Formats the subtext that applies to a few vendors. include_parens is
-- a boolean that specifies whether the string should be in parens.
function RecipeRadar_FormatVendorNote(note, include_parens)
if (include_parens) then
return format(TEXT(PARENS_TEMPLATE), format(RRS("%s Vendor"), note))
end
return format(RRS("%s Vendor"), note)
end
-- Get gold, silver, copper from total copper.
function RecipeRadar_GetGSC(copper)
if (copper == nil) then
copper = 0
end
local g = math.floor(copper / 10000)
local s = math.floor((copper - (g * 10000)) / 100)
local c = math.floor(copper - (g * 10000) - (s * 100))
return g, s, c
end
-- Format text by color for gold, silver and copper (from EnhTooltip).
function RecipeRadar_GetGSCString(copper)
local TEXT_NONE = "0"
local GSC_GOLD = "ffd100"
local GSC_SILVER = "e6e6e6"
local GSC_COPPER = "c8602c"
local GSC_FMT = "|cff%s%d|r"
local GSC_PART = ".|cff%s%02d|r"
local GSC_NONE = "|cffa0a0a0" .. TEXT_NONE .. "|r"
local g, s, c = RecipeRadar_GetGSC(copper)
local gsc = ""
if (g > 0) then
gsc = string.format(GSC_FMT, GSC_GOLD, g)
if (s > 0) then
gsc = gsc .. string.format(GSC_PART, GSC_SILVER, s)
end
if exact then
gsc = gsc .. string.format(GSC_PART,GSC_COPPER, c)
end
elseif (s > 0) then
gsc = string.format(GSC_FMT, GSC_SILVER, s)
if (c > 0) then
gsc = gsc .. string.format(GSC_PART, GSC_COPPER, c)
end
elseif (c > 0) then
gsc = gsc .. string.format(GSC_FMT, GSC_COPPER, c)
else
gsc = GSC_NONE
end
return gsc
end
-- Returns the name of the player's opposing faction.
-- These strings shouldn't need to be localized.
function RecipeRadar_GetOpposingFaction()
if (UnitFactionGroup("player") == "Alliance") then
return "Horde"
elseif (UnitFactionGroup("player") == "Horde") then
return "Alliance"
end
return "Unknown"
end
-- Formats a string in the color specified by the key 'team'.
function RecipeRadar_GetColoredTeamString(team, s)
if (not RecipeRadar_Teams[team]) then return end
return RecipeRadar_ColorToCode(RecipeRadar_Teams[team].Color) ..
s .. FONT_COLOR_CODE_CLOSE
end
-- Returns whether the skill is an applicable profession.
function RecipeRadar_IsProfession(skill_name)
for name, _ in RecipeRadar_Professions do
if (name == skill_name) then
return true
end
end
return false
end
-- Returns the profession under which a trade skill specialty belongs,
-- or false if skill_name is not a known specialty.
function RecipeRadar_IsProfessionSpecialty(skill_name)
for spec, prof in RecipeRadar_Specialties do
if (spec == skill_name) then
return prof
end
end
return false
end
-- Returns whether the supplied faction is one that Recipe Radar cares about.
function RecipeRadar_IsKnownFaction(faction)
for _, name in RecipeRadar_Factions do
if (name == faction) then
return true
end
end
return false
end
-- Returns whether the given vendor has at least one unfiltered recipe.
function RecipeRadar_VendorHasRecipes(vendor)
for _, recipe in vendor.Recipes do
if (not RecipeRadar_IsRecipeFiltered(recipe)) then
return true
end
end
return false
end
-- Returns whether the given recipe has at least one unfiltered location.
function RecipeRadar_RecipeHasLocations(recipe)
for _, location in recipe.Locations do
if (not location.IsFiltered) then
return true
end
end
return false
end
-- Returns a string describing the requirements to buy the given recipe
-- according to the recipe's Notes/Faction member, or nil.
function RecipeRadar_GetRecipeReqsString(recipe)
if (recipe.Notes and recipe.Notes ~= "") then
return recipe.Notes
end
if (recipe.Faction and recipe.Faction ~= "") then
return recipe.Faction .. ", " ..
getglobal("FACTION_STANDING_LABEL" .. recipe.Level)
end
return nil
end
-- Given a recipe name of the form 'Label: Name', returns 'Name'. It
-- does this by checking for a ':' and looking ahead two characters.
-- TODO: verify this with Asian/other languages.
function RecipeRadar_TrimRecipeLabel(recipe_name)
local label_end = string.find(recipe_name, ":")
if (label_end) then
return string.sub(recipe_name, label_end + 2)
end
return recipe_name
end
-- Sorts the vendors in a given region.
function RecipeRadar_SortVendors(region_data)
if (region_data.IsVendorTableSorted) then
return
end
table.sort(region_data.Vendors, RecipeRadar_CompareNames)
region_data.IsVendorTableSorted = true
end
-- Sorts the recipes on a given vendor.
function RecipeRadar_SortRecipes(container)
if (container.IsRecipeTableSorted) then
return
end
table.sort(container.Recipes, RecipeRadar_CompareSkills)
container.IsRecipeTableSorted = true
end
-- Sorts the locations on a given recipe.
function RecipeRadar_SortLocations(recipe_data)
if (recipe_data.IsLocationsTableSorted) then
return
end
table.sort(recipe_data.Locations, RecipeRadar_CompareRegions)
recipe_data.IsRecipeTableSorted = true
end
-- Sort comparison function on required skill first, then the recipe's
-- ID (to insure a deterministic order).
function RecipeRadar_CompareSkills(recipe1, recipe2)
if (recipe1.Skill == recipe2.Skill) then
return recipe1.ID < recipe2.ID
end
return recipe1.Skill < recipe2.Skill
end
-- Sort comparison function on vendor names.
function RecipeRadar_CompareNames(vendor1, vendor2)
return vendor1.Name < vendor2.Name
end
-- Sort comparison function on location names.
function RecipeRadar_CompareRegions(location1, location2)
if (location1.Region == location2.Region) then
return location1.Vendor < location2.Vendor
end
return location1.Region < location2.Region
end
-- Returns the result of the "<" operator on both major and minor portions.
function RecipeRadar_IsVersionLessThan(version1, version2)
local major1, minor1 = string.match(version1, "(%d+)\.(%d+)")
local major2, minor2 = string.match(version2, "(%d+)\.(%d+)")
if (major1 ~= major2) then
return major1+0 < major2+0
end
return minor1+0 < minor2+0
end
-- Returns a color code string converted from RGB decimals.
function RecipeRadar_ColorToCode(color)
return string.format("|cff%02x%02x%02x",
255 * color.r, 255 * color.g, 255 * color.b)
end
-- Returns the given string surrounded by the highlighted version of
-- base_color.
function RecipeRadar_HighlightString(s, base_color)
local highlight = {
r = math.min(1.0, base_color.r + RecipeRadar_Colors.Highlight),
g = math.min(1.0, base_color.g + RecipeRadar_Colors.Highlight),
b = math.min(1.0, base_color.b + RecipeRadar_Colors.Highlight), }
return RecipeRadar_ColorToCode(highlight) .. s .. FONT_COLOR_CODE_CLOSE
end
-- Basic chat frame printing function.
function RecipeRadar_Print(msg, r, g, b)
DEFAULT_CHAT_FRAME:AddMessage(msg,
(r or RecipeRadar_Colors.MainFont.r),
(g or RecipeRadar_Colors.MainFont.g),
(b or RecipeRadar_Colors.MainFont.b))
end