diff --git a/RaidBrowser/RaidBrowser.toc b/RaidBrowser/RaidBrowser.toc index 2359211..31521ea 100644 --- a/RaidBrowser/RaidBrowser.toc +++ b/RaidBrowser/RaidBrowser.toc @@ -1,8 +1,9 @@ ## Interface: 30300 ## Title: Raid Browser -## Version: v1.1.1 +## Version: v1.2.0 ## Notes: A raid finder that parses chat channel text and displays any found raids in the raid browser LFR frame. ## Author: Horsebreed +## SavedVariablesPerCharacter: raid_browser_character_raidsets, raid_browser_character_current_raidset ## OptionalDeps: GearScoreLite ## X-Embeds: Ace3 @@ -13,3 +14,4 @@ event.lua timer.lua stats.lua gui.lua +raidset_frame.lua \ No newline at end of file diff --git a/RaidBrowser/core.lua b/RaidBrowser/core.lua index a54656f..62cedec 100644 --- a/RaidBrowser/core.lua +++ b/RaidBrowser/core.lua @@ -262,10 +262,10 @@ local raid_list = { { name = 'molten core', instance_name = 'Molten Core', - size = 25, + size = 40, patterns = { 'molte?n[%s]*core?', - '[%s-_,.%^]+mc'..sep..'*2?5?[%s-_,.$]+', + '[%s-_,.%^]+mc'..sep..'*4?0?[%s-_,.$]+', }, }, @@ -290,7 +290,7 @@ local raid_list = { }, { - name = 'aq10', + name = 'aq20', instance_name = 'Ruins of Ahn\'Qiraj', size = 20, patterns = { @@ -300,36 +300,34 @@ local raid_list = { }, } -local role_patterns = { - ranged_dps = { +local role_patterns = { + dps = { + '[0-9]*[%s-_,.]*dps', + + -- melee dps + '[0-9]*[%s-_,.]*m[dp][dp]s', + '[0-9]*[%s-_,.]*rogue', + '[0-9]*[%s-_,.]*kitt?y?', + '[0-9]*[%s-_,.]*feral', + '[0-9]*[%s-_,.]*ret[%s-_,.]*pal[al]?[dy]?i?n?', + + -- ranged dps "[0-9]*[%s-_,.]*r[dp][dp]s", '[0-9]*[%s-_,.]*w?a?r?lock', '[0-9]*[%s-_,.]*spri?e?st', '[0-9]*[%s-_,.]*elem?e?n?t?a?l?', '[0-9]*[%s-_,.]*mage', - '[0-9]*[%s-_,.]*boomy?k?i?n?', + '[0-9]*[%s-_,.]*boo?mm?y?k?i?n?', '[0-9]*[%s-_,.]*hunte?r?s?', }, - melee_dps = { - '[0-9]*[%s-_,.]*m[dp][dp]s', - '[0-9]*[%s-_,.]*rogue', - '[0-9]*[%s-_,.]*kitt?y?', - '[0-9]*[%s-_,.]*feral', - '[0-9]*[%s-_,.]*ret[%s-_,.]*pal[al]?[dy]?i?n?', - }, - - dps = { - '[0-9]*[%s-_,.]*dps', - }, - healer = { '[0-9]*[%s-_,.]*he[a]?l[er|ers]*', -- LF healer - '[0-9]*[%s-_,.]*rd[ru][ud][iu]d?', -- LF rdruid/rdudu + '[0-9]*[%s-_,.]*re?s?t?o?'..sep..'*d[ru][ud][iu]d?', -- LF rdruid/rdudu '[0-9]*[%s-_,.]*tree', -- LF tree '[0-9]*[%s-_,.]*re?s?t?o?[%s-_,.]*shamm?y?', -- LF rsham - '[0-9]*'..sep..'*di?s?c?'..sep..'*pri?e?st', -- disc priest - '[0-9]*[%s-_,.]*hpala', -- LF hpala + '[0-9]*'..sep..'*di?s?c?o?'..sep..'*pri?e?st', -- disc priest + '[0-9]*[%s-_,.]*ho?l?l?y?'..sep..'*pala', -- LF hpala }, tank = { @@ -352,20 +350,21 @@ local gearscore_patterns = { local lfm_patterns = { 'lf[0-9]*m', - 'looking[%s]*for[%s]*all', - 'looking'..sep..'*for'..sep..'*an?', - 'looking[%s]*for[%s]*[0-9]*[%s]*more', -- looking for 9 more - 'lf[%s]*.*for', -- LF for - 'lf[%s]*[0-9]*[%s]*he[a]?l[er|ers]*', -- LF healer - 'lf[%s]*[0-9]*[%s]*t[a]?nk[s]?', -- LF 5 tanks - 'lf[%s]*[0-9]*[%s]*[mr]?dps', -- LF 9 DPS - 'seek[%s]*[0-9]*[%s]*he[a]?l[er|ers]*', -- seek healer - 'seek[%s]*[0-9]*[%s]*t[a]?nk[s]?', -- seek 5 tanks - 'seek[%s]*[0-9]*[%s]*[mr]?dps', -- seek 9 DPS - 'lf[%s]*all', + 'lf'..sep..'*all', 'need', - 'need[%s]*all', - 'whispe?r?[%s]*me', + 'need'..sep..'*all', + 'seek'..sep..'*[0-9]*'..sep..'*he[a]?l[er|ers]*', -- seek healer + 'seek'..sep..'*[0-9]*'..sep..'*t[a]?nk[s]?', -- seek 5 tanks + 'seek'..sep..'*[0-9]*'..sep..'*[mr]?dps', -- seek 9 DPS + 'looking'..sep..'*for[%s]*all', + 'looking'..sep..'*for'..sep..'*an?'..sep, + 'looking'..sep..'*for'..sep..'*[0-9]*'..sep..'*more', -- looking for 9 more + 'lf'..sep..'*.*for', -- LF for + 'looking'..sep..'*for'..sep..'*.*'..sep..'for', -- LF for + 'lf'..sep..'*[0-9]*'..sep..'*he[a]?l[er|ers]*', -- LF healer + 'lf'..sep..'*[0-9]*'..sep..'*t[a]?nk[s]?', -- LF 5 tanks + 'lf'..sep..'*[0-9]*'..sep..'*[mr]?dps', -- LF 9 DPS + 'whispe?r?'..sep..'*me', --'[%s]/w[%s]*[%a]+', -- Too greedy } @@ -434,6 +433,28 @@ local function remove_http_links(message) local http_pattern = 'https?://*[%a]*.[%a]*.[%a]*/?[%a%-%%0-9_]*/?'; return string.gsub(message, http_pattern, ''); end + +local function find_roles(roles, message, pattern_table, role) + local found = false; + for _, pattern in ipairs(pattern_table[role]) do + local result = string.find(message, pattern) + + -- If a raid was found then save it to our list of roles and continue. + if result then + found = true; + + -- Remove the substring from the message + message = string.gsub(message, pattern, '') + end + end + + if not found then + return roles, message; + end + + table.insert(roles, role); + return roles, message; +end function raid_browser.raid_info(message) message = string.lower(message) @@ -481,19 +502,10 @@ function raid_browser.raid_info(message) -- Get any roles that are needed local roles = {}; - for r, patterns in pairs(role_patterns) do - for _, pattern in ipairs(patterns) do - local result = string.find(message, pattern) - - -- If a raid was found then save it to our list of roles and continue. - if result then - table.insert(roles, r) - - -- Remove the substring from the message - message = string.gsub(message, pattern, '') - end - end - end + + roles, message = find_roles(roles, message, role_patterns, 'dps'); + roles, message = find_roles(roles, message, role_patterns, 'tank'); + roles, message = find_roles(roles, message, role_patterns, 'healer'); -- If there is only an LFM message, then it is assumed that all roles are needed if #roles == 0 then @@ -537,13 +549,25 @@ end function raid_browser:OnEnable() raid_browser:Print("loaded. Type /rb to toggle the raid browser.") + + if not raid_browser_character_current_raidset then + raid_browser_character_current_raidset = 'Active'; + end + + if not raid_browser_character_raidsets then + raid_browser_character_raidsets = { + primary = {}, + secondary = {}, + }; + end -- LFM messages expire after 60 seconds raid_browser.expiry_time = 60; raid_browser.lfm_messages = {} raid_browser.timer = raid_browser.set_timer(10, refresh_lfm_messages, true) - raid_browser.listener = raid_browser.add_event_listener("CHAT_MSG_CHANNEL", event_handler ) + raid_browser.listener = raid_browser.add_event_listener("CHAT_MSG_CHANNEL", event_handler) + raid_browser.gui.raidset.initialize(); end function raid_browser:OnDisable() diff --git a/RaidBrowser/gui.lua b/RaidBrowser/gui.lua index 5745c0d..555351d 100644 --- a/RaidBrowser/gui.lua +++ b/RaidBrowser/gui.lua @@ -90,7 +90,7 @@ for i = 1, NUM_LFR_LIST_BUTTONS do if button.raid_locked then GameTooltip:AddLine('\nYou are |cffff0000saved|cffffd100 for ' .. button.raid_info.name); - local _, reset_time = raid_browser.stats.raid_lock_info(button.raid_info) + local _, reset_time = raid_browser.stats.raid_lock_info(button.raid_info.instance_name, button.raid_info.size) GameTooltip:AddLine('Lockout expires in ' .. format_seconds(reset_time)); else GameTooltip:AddLine('\nYou are |cff00ff00not saved|cffffd100 for ' .. button.raid_info.name); @@ -139,7 +139,7 @@ local function assign_lfr_button(button, host_name, lfm_info, index) -- Raid name button.class:SetText(button.raid_info.name); - button.raid_locked = raid_browser.stats.raid_lock_info(button.raid_info); + button.raid_locked = raid_browser.stats.raid_lock_info(button.raid_info.instance_name, button.raid_info.size); button.type = "party"; button.partyIcon:Show(); diff --git a/RaidBrowser/raidset_frame.lua b/RaidBrowser/raidset_frame.lua new file mode 100644 index 0000000..eafaf92 --- /dev/null +++ b/RaidBrowser/raidset_frame.lua @@ -0,0 +1,121 @@ +raid_browser.gui.raidset = {}; + +local frame = CreateFrame("Frame", "RaidBrowserRaidSetMenu", LFRBrowseFrame, "UIDropDownMenuTemplate") + +local current_selection = nil; + +local function is_active_selected(option) + return ('Active' == current_selection); +end + +local function is_primary_selected(option) + return ('Primary' == current_selection); +end + +local function is_secondary_selected(option) + return ('Secondary' == current_selection); +end + +local function set_selection(selection) + local text = ''; + + if selection == 'Active' then + text = 'Active'; + else + local spec, gs = raid_browser.stats.get_raidset(selection); + if not spec then + text = 'Open'; + else + text = spec .. ' ' .. gs .. 'gs'; + end + end + + UIDropDownMenu_SetText(RaidBrowserRaidSetMenu, text) + current_selection = selection; +end + +local function on_active() + set_selection('Active'); + raid_browser.stats.select_current_raidset('Active'); +end + +local function on_primary() + set_selection('Primary'); + raid_browser.stats.select_current_raidset('Primary'); +end + +local function on_secondary() + set_selection('Secondary'); + raid_browser.stats.select_current_raidset('Secondary'); +end + +local menu = { + { + text = 'Active', + func = on_active, + checked = is_active_selected, + }, + + { + text = "Primary", + func = on_primary, + checked = is_primary_selected, + }, + + { + text = "Secondary", + func = on_secondary, + checked = is_secondary_selected, + }, +} + +-- Get the menu option text +local function get_option_text(option) + local spec = raid_browser.stats.get_raidset(option); + if not spec then + return (option .. ': Open'); + end + + return (option .. ': ' .. spec); +end + +-- Setup dropdown menu for the raidset selection +frame:SetPoint("CENTER", LFRBrowseFrame, "CENTER", 30, 165) +UIDropDownMenu_Initialize(frame, EasyMenu_Initialize, nil, nil, menu); + +local function show_menu() + menu[2].text = get_option_text('Primary'); + menu[3].text = get_option_text('Secondary'); + ToggleDropDownMenu(1, nil, frame, frame, 25, 10, menu); +end + +RaidBrowserRaidSetMenuButton:SetScript('OnClick', show_menu) + +local function on_raidset_save() + if current_selection == 'Primary' then + raid_browser.stats.save_primary_raidset(); + + elseif current_selection == 'Secondary' then + raid_browser.stats.save_secondary_raidset(); + end + + local spec, gs = raid_browser.stats.current_raidset(); + raid_browser:Print('Raidset saved: ' .. spec .. ' ' .. gs .. 'gs'); + set_selection(current_selection); +end + +function raid_browser.gui.raidset.initialize() + set_selection(raid_browser_character_current_raidset); +end + +-- Create raidset save button +local button = CreateFrame("BUTTON","RaidBrowserRaidSetSaveButton", LFRBrowseFrame, "OptionsButtonTemplate") +button:SetPoint("CENTER", LFRBrowseFrame, "CENTER", -40, 168) +button:EnableMouse(true) +button:RegisterForClicks("AnyUp") + +button:SetText("Save Raid Set"); +button:SetWidth(120); +button:SetScript("OnClick", on_raidset_save); +button:Show(); + diff --git a/RaidBrowser/stats.lua b/RaidBrowser/stats.lua index d8ac9dd..368d99d 100644 --- a/RaidBrowser/stats.lua +++ b/RaidBrowser/stats.lua @@ -2,38 +2,38 @@ raid_browser.stats = {}; local raid_achievements = { icc = { - 4604, -- Storming the Citadel 25-man 4531, -- Storming the Citadel 10-man - 4632, -- Storming the Citadel 25-man HC + 4604, -- Storming the Citadel 25-man 4628, -- Storming the Citadel 10-man HC - 4605, -- The Plagueworks 25-man + 4632, -- Storming the Citadel 25-man HC 4528, -- The Plagueworks 10-man - 4633, -- The Plagueworks 25-man HC + 4605, -- The Plagueworks 25-man 4629, -- The Plagueworks 10-man HC - 4606, -- The Crimson Hall 25-man + 4633, -- The Plagueworks 25-man HC 4529, -- The Crimson Hall 10-man - 4634, -- The Crimson Hall 25-man HC + 4606, -- The Crimson Hall 25-man 4630, -- The Crimson Hall 10-man HC - 4607, -- The Frostwing Halls 25-man + 4634, -- The Crimson Hall 25-man HC 4527, -- The Frostwing Halls 10-man - 4635, -- The Frostwing Halls 25-man HC + 4607, -- The Frostwing Halls 25-man 4631, -- The Frostwing Halls 10-man HC - 4597, -- The Frozen Throne (LK25 NM) + 4635, -- The Frostwing Halls 25-man HC 4530, -- The Frozen Throne (LK10 NM) - 4584, -- The Light of Dawn (LK25 HC) + 4597, -- The Frozen Throne (LK25 NM) 4583, -- Bane of the Fallen King (LK10 HC) + 4584, -- The Light of Dawn (LK25 HC) }, toc = { - 3916, -- Call of the Crusade 25-man 3917, -- Call of the Crusade 10-man - 3812, -- Call of the Grand Crusade (25 HC) + 3916, -- Call of the Crusade 25-man 3918, -- Call of the Grand Crusade (10 HC) + 3812, -- Call of the Grand Crusade (25 HC) }, rs = { - 4815, -- The Twilight Destroyer 25 4817, -- The Twilight Destroyer 10 + 4815, -- The Twilight Destroyer 25 4818, -- The Twilight Destroyer 10 HC 4816, -- The Twilight Destroyer 25 HC }, @@ -72,7 +72,7 @@ local function find_best_achievement(raid) end end -local function get_active_spec() +function raid_browser.stats.active_spec_index() local index = 1; local _, _, points = GetTalentTabInfo(index); for i = 2, 3 do @@ -82,18 +82,27 @@ local function get_active_spec() end end - return GetTalentTabInfo(index) + return index end -function raid_browser.stats.raid_lock_info(raid_info) - if not raid_info then - return false; +function raid_browser.stats.active_spec() + local index = 1; + local _, _, points = GetTalentTabInfo(index); + for i = 2, 3 do + local _, _, p = GetTalentTabInfo(i); + if (p > points) then + index = i; + end end + return GetTalentTabInfo(raid_browser.stats.active_spec_index()); +end + +function raid_browser.stats.raid_lock_info(instance_name, size) for i = 1, GetNumSavedInstances() do - local name, _, reset, _, _, _, _, _, size = GetSavedInstanceInfo(i); + local saved_name, _, reset, _, _, _, _, _, saved_size = GetSavedInstanceInfo(i); - if name == raid_info.instance_name and size == raid_info.size then + if saved_name == instance_name and saved_size == size then return true, reset; end end @@ -101,20 +110,53 @@ function raid_browser.stats.raid_lock_info(raid_info) return false, nil; end -function raid_browser.stats.build_inv_string(raid_name) - local message = 'inv '; - local class = UnitClass("player"); - - local gs = ''; +function raid_browser.stats.get_active_raidset() + local spec = nil; + local gs = nil; -- Retrieve gearscore if GearScoreLite is installed if GearScore_GetScore then gs = GearScore_GetScore(UnitName('player'), 'player'); - gs = gs .. 'gs '; end - local spec = get_active_spec(); - message = message .. gs .. spec .. ' ' .. class; + spec = raid_browser.stats.active_spec(); + return spec, gs; +end + +function raid_browser.stats.get_raidset(set) + local raidset = raid_browser_character_raidsets[set]; + if not raidset then return end; + return raidset.spec, raidset.gs; +end + +function raid_browser.stats.current_raidset() + if raid_browser_character_current_raidset == 'Active' then + return raid_browser.stats.get_active_raidset(); + end + + return raid_browser.stats.get_raidset(raid_browser_character_current_raidset); +end + +function raid_browser.stats.select_current_raidset(set) + raid_browser_character_current_raidset = set; +end + +function raid_browser.stats.save_primary_raidset() + local spec, gs = raid_browser.stats.get_active_raidset(); + raid_browser_character_raidsets['Primary'] = {spec = spec, gs = gs}; +end + +function raid_browser.stats.save_secondary_raidset() + local spec, gs = raid_browser.stats.get_active_raidset(); + raid_browser_character_raidsets['Secondary'] = {spec = spec, gs = gs}; +end + +function raid_browser.stats.build_inv_string(raid_name) + local message = 'inv '; + local class = UnitClass("player"); + + local spec, gs = raid_browser.stats.current_raidset(); + message = message .. gs .. 'gs ' .. spec .. ' ' .. class; -- Remove difficulty and raid_name size from the string raid_name = string.gsub(raid_name, '[1|2][0|5](%w+)', ''); @@ -126,17 +168,4 @@ function raid_browser.stats.build_inv_string(raid_name) end return message; -end - - - - - - - - - - - - - +end \ No newline at end of file diff --git a/tests/raid_info.lua b/tests/raid_info.lua index 9b89cbf..9419096 100644 --- a/tests/raid_info.lua +++ b/tests/raid_info.lua @@ -11,7 +11,7 @@ local test_cases = { message = 'LFM need kitty and icc 25 rshammy 5.5+', should_fail = false, raid = 'icc25nm', - roles = {'melee_dps', 'healer'}, + roles = {'dps', 'healer'}, gs = '5.5', }, @@ -67,7 +67,7 @@ local test_cases = { message = 'LFM ICC10N NEED 2 TNAKS AND 1 HEALER AND RDPS 5.3GS MIN. WHISPER GS AND EXP. NO GS AND EXP=IGNORE', should_fail = false, raid = 'icc10nm', - roles = {'tank', 'healer', 'ranged_dps'}, + roles = {'tank', 'healer', 'dps'}, gs = '5.3', }, @@ -100,7 +100,7 @@ local test_cases = { message = ' ICC10 nm/hc lf 5k7+ boe reserved link achiv// spec // 6/10 need 2 Heals // 1 tank // 1 Rdps (preflock)', should_fail = false, raid = 'icc10nm', - roles = {'healer', 'tank', 'ranged_dps'}, + roles = {'healer', 'tank', 'dps'}, gs = '5.7', }, @@ -147,7 +147,7 @@ local test_cases = { { message = 'LFM [Ruins of Ahn\'Qiraj] Achievement / Transmorg Run. Dark Edge Reserved. In progress. Pst WIll summon. Pst. All welcome.', should_fail = false, - raid = 'aq10', + raid = 'aq20', roles = {'tank', 'dps', 'healer'}, gs = ' ', }, @@ -456,13 +456,65 @@ local test_cases = { message = 'LFM ICC 25 nm need RPDS Fresh Run 5.6k+ FULL GEMS and ENCHANTS! (B+P res) /w best achi. Make sure you have TIME!', should_fail = false, raid = 'icc25nm', - roles = {'ranged_dps'}, + roles = {'dps'}, + gs = '5.6', + }, + + { + message = 'ICC 10 NM/HC raid Need 1 Bommy1 SHAMMY healer !! min GS 5.7k --B+P are reserverd 8/10 [Fall of the Lich King (10 player)]', + should_fail = false, + raid = 'icc10nm', + roles = {'dps', 'healer'}, + gs = '5.7', + }, + + { + message = ' RS 10 Norm Need Resto Druid 6k+ [Heroic: The Twilight Destroyer (25 player)]', + should_fail = false, + raid = 'rs10nm', + roles = {'healer'}, + gs = '6.0', + }, + + { + message = 'LF 1 tank for Ruby Sanctum 10 man normal, 5,6k gs req', + should_fail = false, + raid = 'rs10nm', + roles = {'tank'}, gs = '5.6', }, + + { + message = 'ICC 25MAN LOOKING FOR 3 HEALERS AND 9 DPS 5.5K GS+ FOR FRASH ICC RUN B/E RES /W ME GS AND ACHIV AIM 7+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@', + should_fail = false, + raid = 'icc25nm', + roles = {'healer', 'dps'}, + gs = '5.5', + }, + + { + message = 'TOC 25 man HC need 1 prot pala and 1 heal { pala } and 2 mdps { rogue / feral } and 3 rdps { mage / shamy ele } min gs 6k /// nothing ress /// all item rolled /// must have time /// link achvi /// come VH for inspect // 20 /25', + should_fail = false, + raid = 'toc25hc', + roles = {'healer', 'tank', 'dps'}, + gs = '6.0', + }, + + -- Idea: Convert raid/roles/gs into intermediate text such as so that the following could + -- be parsed as: for /HC.. Gs Req ... [The Frostwing Halls (10 player)]...9/10 + -- This could be a more powerful technique for distinguishing between LFM messages and other messages + --[[{ + message = '1 Lock for ICC 10 NM/HC.. Gs Req 5.7... [The Frostwing Halls (10 player)]...9/10', + should_fail = false, + raid = 'icc10nm', + roles = {'dps'}, + gs = '5.7', + },]]-- } + local function array_contains(table, element) for _, k in ipairs(table) do if k == element then @@ -474,6 +526,10 @@ local function array_contains(table, element) end local function compare_arrays(table1, table2) + if #table1 ~= #table2 then + return false; + end + for _, x in ipairs(table1) do if not array_contains(table2, x) then return false;