RollFor = RollFor or {} local m = RollFor ---@diagnostic disable-next-line: undefined-global local lib_stub = LibStub local version = m.get_addon_version() local M = {} local getn = m.getn local info = m.pretty_print local hl, white, grey, green, red = m.colors.highlight, m.colors.white, m.colors.grey, m.colors.green, m.colors.red local RollSlashCommand = m.Types.RollSlashCommand local RollType = m.Types.RollType local RollingStrategy = m.Types.RollingStrategy local function clear_data() M.softres_gui.clear() M.name_matcher.clear( true ) M.softres.clear( true ) M.minimap_button.set_icon( M.minimap_button.ColorType.White ) M.winner_tracker.clear() end local function update_minimap_icon() local result = M.softres_check.check_softres( true ) if result == M.softres_check.ResultType.NoItemsFound then M.minimap_button.set_icon( M.minimap_button.ColorType.White ) elseif result == M.softres_check.ResultType.SomeoneIsNotSoftRessing then M.minimap_button.set_icon( M.minimap_button.ColorType.Orange ) elseif result == M.softres_check.ResultType.FoundOutdatedData then M.minimap_button.set_icon( M.minimap_button.ColorType.Red ) else M.minimap_button.set_icon( M.minimap_button.ColorType.Green ) end end local function on_softres_status_changed() update_minimap_icon() end local function on_raid_trade( giver_name, recipient_name, item_name ) local item_id = M.dropped_loot.get_dropped_item_id( item_name ) if item_id then local quality, _ = m.get_item_quality_and_texture( m.api, item_id ) local item_link = m.fetch_item_link( item_id, quality ) M.loot_award_callback.on_loot_awarded( item_id, item_link, recipient_name, nil, true ) if item_id and M.awarded_loot.has_item_been_awarded( giver_name, item_id ) then info( string.format( "%s traded %s to %s.", hl( giver_name ), item_link, hl( recipient_name ) ) ) M.awarded_loot.unaward( giver_name, item_id ) end end end local function trade_complete_callback( recipient_name, items_given, items_received ) if not M.api().IsInGroup() then return end for i = 1, getn( items_given ) do local item = items_given[ i ] if item then local item_id = M.item_utils.get_item_id( item.link ) local item_name = item_id and M.dropped_loot.get_dropped_item_name( item_id ) if item_id and item_name then M.loot_award_callback.on_loot_awarded( item_id, item.link, recipient_name ) end end end for i = 1, getn( items_received ) do local item = items_received[ i ] if item then local item_id = M.item_utils.get_item_id( item.link ) if item_id and M.awarded_loot.has_item_been_awarded( recipient_name, item_id ) then M.unaward_item( recipient_name, item_id, item.link ) end end end end local function create_components() ---@type AceTimer M.ace_timer = lib_stub( "AceTimer-3.0" ) local db = m.Db.new( M.char_db ) ---@type EventBus M.config_event_bus = m.EventBus.new() ---@type Config M.config = m.Config.new( db( "config" ), M.config_event_bus ) local classic = M.config.classic_look() local popup_bottom_margin, popup_bottom_button_margin = classic and 37 or 24, classic and 14 or 7 local popup_side_margin = classic and 50 or 35 local popup_builder_factory = classic and m.PopupBuilder.classic or m.PopupBuilder.modern ---@type fun(): PopupBuilder ---@param bottom_margin number? ---@param side_margin number? local function popup_builder( bottom_margin, side_margin ) return popup_builder_factory( m.FrameBuilder, bottom_margin or popup_bottom_margin, popup_bottom_button_margin, side_margin or popup_side_margin ) end ---@type UiReloadPopup M.ui_reload_popup = m.UiReloadPopup.new( popup_builder( classic and 37 or 27 ), M.config ) ---@type ConfirmPopup M.confirm_popup = m.ConfirmPopup.new( popup_builder( classic and 37 or 27 ), M.config ) M.api = function() return m.api end ---@type PlayerInfo M.player_info = m.PlayerInfo.new( M.api() ) ---@type GroupRoster M.group_roster = m.GroupRoster.new( M.api(), M.player_info ) M.chat_api = m.ChatApi.new() ---@type Chat M.chat = m.Chat.new( M.chat_api, M.group_roster, M.player_info ) ---@alias GroupAwareSoftResFn fun ( softres: SoftRes ): GroupAwareSoftRes ---@type GroupAwareSoftResFn M.present_softres = function( softres ) return m.SoftResPresentPlayersDecorator.new( M.group_roster, softres ) end ---@type GroupAwareSoftResFn M.absent_softres = function( softres ) return m.SoftResAbsentPlayersDecorator.new( M.group_roster, softres ) end ---@type ItemUtils M.item_utils = m.ItemUtils ---@type TooltipReader M.tooltip_reader = m.TooltipReader.new( M.api() ) ---@type VersionBroadcast M.version_broadcast = m.VersionBroadcast.new( db( "version_broadcast" ), M.player_info, version.str ) ---@type AwardedLoot M.awarded_loot = m.AwardedLoot.new( db( "awarded_loot" ), M.group_roster, M.config ) -- TODO: Add type. M.softres_db = db( "softres" ) -- TODO: Add type. M.unfiltered_softres = m.SoftRes.new( M.softres_db ) -- TODO: Add type. M.name_matcher = m.NameManualMatcher.new( db( "name_matcher" ), M.api, M.absent_softres( M.unfiltered_softres ), m.NameAutoMatcher.new( M.group_roster, M.unfiltered_softres, 0.57, 0.4 ), on_softres_status_changed ) ---@type SoftRes M.matched_name_softres = m.SoftResMatchedNameDecorator.new( M.name_matcher, M.unfiltered_softres ) ---@type SoftRes M.awarded_loot_softres = m.SoftResAwardedLootDecorator.new( M.awarded_loot, M.matched_name_softres ) ---@type GroupAwareSoftRes M.softres = M.present_softres( M.awarded_loot_softres ) ---@type DroppedLoot M.dropped_loot = m.DroppedLoot.new( db( "dropped_loot" ) ) M.softres_check = m.SoftResCheck.new( M.matched_name_softres, M.group_roster, M.name_matcher, M.ace_timer, M.absent_softres, db( "softres_check" ) ) ---@type WinnerTracker M.winner_tracker = m.WinnerTracker.new( db( "winner_tracker" ) ) ---@type LootFacade M.loot_facade = m.LootFacade.new( m.EventFrame.new( m.api ), m.api ) -- TODO: Add type. ---@diagnostic disable-next-line: unused-local, unused-function local function get_dummy_items() ---@diagnostic disable-next-line: unused-function local function item_link( name, id, quality ) local color = m.api.ITEM_QUALITY_COLORS[ quality ].hex or "|cffffffff" return string.format( "%s|Hitem:%s::::::::20:257::::::|h[%s]|h|r", color, id or "3299", name ) end -- local ids = { 17204, 16961, 18842, 16961, 16961, 18842, 16865, 16961, 17109, 16961, 18466, 11980, 12820, 3676 } local ids = { 17109, 17109, 17109, 3676 } local result = {} ---@type MakeDroppedItemFn local make_dropped_item = m.ItemUtils.make_dropped_item local boe = m.ItemUtils.BindType.BindOnEquip ---@diagnostic disable-next-line: unused-local for i, item_id in ipairs( ids ) do local name, tooltip_link, quality, texture if m.vanilla then name, tooltip_link, quality, _, _, _, _, _, texture = m.api.GetItemInfo( item_id ) else name, tooltip_link, quality, _, _, _, _, _, _, texture = m.api.GetItemInfo( item_id ) end local link = item_link( name, item_id, quality ) local item = make_dropped_item( item_id, name, link, tooltip_link, quality, 1, texture, boe, nil, true ) table.insert( result, item ) end table.sort( result, function( a, b ) return a.quality > b.quality end ) return result end -- Enable this for testing in game. It will replace dropped items with the above. local mock_items = false ---@type LootList M.raw_loot_list = m.LootList.new( M.loot_facade, M.item_utils, M.tooltip_reader, m.BossList.zones, mock_items and get_dummy_items or nil ) ---@type SoftResLootList M.loot_list = m.SoftResLootListDecorator.new( M.raw_loot_list, M.softres ) ---@type MasterLootCandidates M.master_loot_candidates = m.MasterLootCandidates.new( M.api(), M.group_roster, M.raw_loot_list ) -- remove group_roster for testing (dummy candidates) ---@type MasterLootCandidateSelectionFrame M.player_selection_frame = m.MasterLootCandidateSelectionFrame.new( m.FrameBuilder, M.config ) local rolling_popup_db = db( "rolling_popup" ) ---@type RollingPopupContentTransformer local rolling_popup_content_transformer = m.RollingPopupContentTransformer.new( M.config ) ---@type RollingPopup M.rolling_popup = m.RollingPopup.new( popup_builder(), rolling_popup_content_transformer, rolling_popup_db, M.config ) ---@type LootFrameSkin local skin = M.config.classic_look() and m.OgLootFrameSkin.new( m.FrameBuilder ) or m.ModernLootFrameSkin.new( m.FrameBuilder ) ---@type LootFrame M.loot_frame = m.LootFrame.new( skin, db( "loot_frame" ), M.config ) ---@type LootAwardPopup M.loot_award_popup = m.LootAwardPopup.new( popup_builder( classic and 38 or 30, classic and 65 or 55 ), M.config, M.rolling_popup ) ---@type RollController M.roll_controller = m.RollController.new( M.master_loot_candidates, M.softres, M.loot_list, M.config, M.rolling_popup, M.loot_award_popup, M.player_selection_frame, M.player_info ) ---@type LootAwardCallback M.loot_award_callback = m.LootAwardCallback.new( M.awarded_loot, M.roll_controller, M.winner_tracker, M.group_roster, M.softres, M.confirm_popup, M.config) ---@type MasterLoot M.master_loot = m.MasterLoot.new( M.master_loot_candidates, M.loot_award_callback, M.loot_list, M.roll_controller ) ---@type AutoLoot M.auto_loot = m.AutoLoot.new( M.loot_list, M.api, db( "auto_loot" ), M.config, M.player_info ) ---@type DroppedLootAnnounce M.dropped_loot_announce = m.DroppedLootAnnounce.new( M.loot_list, M.chat, M.dropped_loot, M.softres, M.winner_tracker, M.player_info, M.auto_loot, M.config ) ---@type WinnersPopup M.winners_popup = m.WinnersPopup.new( popup_builder(), m.FrameBuilder, db( "winners_popup" ), M.awarded_loot, M.roll_controller, M.confirm_popup, M.config ) ---@type OptionsPopup M.options_popup = m.OptionsPopup.new( popup_builder(), M.awarded_loot, M.version_broadcast, M.config_event_bus, M.confirm_popup, M.group_roster, db( "options_popup" ), db( "config" ), M.config ) -- TODO: Add type. M.softres_gui = m.SoftResGui.new( M.api, M.import_encoded_softres_data, M.softres_check, M.softres, clear_data, M.dropped_loot_announce.reset ) -- TODO: Add type. M.trade_tracker = m.TradeTracker.new( M.ace_timer, M.chat, trade_complete_callback ) -- TODO: Add type. M.usage_printer = m.UsagePrinter.new( M.chat ) -- TODO: Add type. M.minimap_button = m.MinimapButton.new( M.api, db( "minimap_button" ), M.softres_gui.toggle, M.winners_popup.toggle, M.options_popup.toggle, M.softres_check, M.config ) -- TODO: Add type. M.master_loot_warning = m.MasterLootWarning.new( M.api, M.config, m.BossList.zones, M.player_info ) -- TODO: Add type. M.new_group_event = m.NewGroupEvent.new( M.group_roster ) -- TODO: Add type. M.auto_group_loot = m.AutoGroupLoot.new( M.loot_list, M.config, m.BossList.zones, M.player_info ) -- TODO: Add type. M.auto_master_loot = m.AutoMasterLoot.new( M.config, m.BossList.zones, M.player_info ) -- TODO: Add type. M.softres_roll_gui_data = m.SoftResRollGuiData.new( M.softres, M.group_roster ) -- TODO: Add type. M.tie_roll_gui_data = m.TieRollGuiData.new( M.group_roster ) -- TODO: Add type. M.welcome_popup = m.WelcomePopup.new( m.FrameBuilder, M.ace_timer, db( "welcome_popup" ) ) -- TODO: Add type. M.roll_for_ad = m.RollForAd.new( M.player_info ) ---@type RollingStrategyFactory M.rolling_strategy_factory = m.RollingStrategyFactory.new( M.group_roster, M.loot_list, M.master_loot_candidates, M.chat, M.ace_timer, M.winner_tracker, M.config, M.softres, M.player_info, M.awarded_loot ) ---@type RollingLogic M.rolling_logic = m.RollingLogic.new( M.chat, M.ace_timer, M.roll_controller, M.rolling_strategy_factory, M.master_loot_candidates, M.winner_tracker, M.config ) M.loot_controller = m.LootController.new( M.player_info, M.loot_facade, M.loot_list, M.loot_frame, M.roll_controller, M.softres, M.rolling_logic, M.chat ) ---@type ArgsParser M.args_parser = m.ArgsParser.new( m.ItemUtils, M.config ) -- TODO: Add type. M.roll_result_announcer = m.RollResultAnnouncer.new( M.chat, M.roll_controller, M.softres, M.config ) M.loot_facade_listener = m.LootFacadeListener.new( M.loot_facade, M.auto_loot, M.dropped_loot_announce, M.master_loot, M.auto_group_loot, M.roll_controller, M.player_info ) ---@type ClientBroadcast M.client_broadcast = m.ClientBroadcast.new( M.roll_controller, M.softres, M.config ) ---@type Client M.client = m.Client.new( M.ace_timer, M.player_info, M.rolling_popup, M.config ) M.sandbox = m.Sandbox.new() end local function subscribe_for_component_events() M.config.subscribe( "show_ml_warning", function( enabled ) if enabled then M.master_loot_warning.on_player_target_changed() else M.master_loot_warning.hide() end end ) M.new_group_event.subscribe( function() M.awarded_loot.clear() M.dropped_loot.clear() end ) M.config_event_bus.subscribe( "config_change_requires_ui_reload", function() M.ui_reload_popup.show() end ) end function M.import_softres_data( softres_data ) M.unfiltered_softres.import( softres_data ) M.name_matcher.auto_match() end function M.import_encoded_softres_data( data, data_loaded_callback ) local sr = m.SoftRes local softres_data = sr.decode( data ) if not softres_data and data and string.len( data ) > 0 then info( "Could not load soft-res data!", m.colors.red ) return elseif not softres_data then M.minimap_button.set_icon( M.minimap_button.ColorType.White ) return end M.import_softres_data( softres_data ) info( "Soft-res data loaded successfully!" ) if data_loaded_callback then data_loaded_callback( softres_data ) end update_minimap_icon() end local function on_roll_command( roll_slash_command ) return function( args ) if M.rolling_logic.is_rolling() then M.chat.info( "Rolling is in progress." ) return end if string.find( args, "^debug" ) then m.DebugBuffer.on_command( args ) return end if string.find( args, "^config" ) then M.config.on_command( args ) return end if args == "versioncheck guild" then M.version_broadcast.guild_version_request() return end if not M.api().IsInGroup() then M.chat.info( "Not in a group." ) return end if args == "versioncheck" then M.version_broadcast.group_version_request() return end if string.find( args, "^client enable" ) and M.player_info.is_master_looter() then M.client_broadcast.enable_roll_popup() return end local item, count, seconds, message = M.args_parser.parse( args ) if not item then M.usage_printer.print_usage( roll_slash_command ) return end local strategy_type = m.Types.slash_command_to_strategy_type( roll_slash_command ) if not strategy_type then info( string.format( "Unsupported command: %s", hl( roll_slash_command and roll_slash_command.slash_command or "?" ) ) ) return end if M.softres.is_item_hardressed( item.id ) then M.roll_controller.preview( item, count ) return end M.roll_controller.start( strategy_type, item, count, seconds, message ) end end local function on_show_sorted_rolls_command( args ) if M.rolling_logic.is_rolling() then info( "Rolling is in progress." ) return end if args then for limit in string.gmatch( args, "(%d+)" ) do M.rolling_logic.show_sorted_rolls( tonumber( limit ) ) return end end M.rolling_logic.show_sorted_rolls( 5 ) end local function is_rolling_check( f ) ---@diagnostic disable-next-line: unused-vararg return function( ... ) if not M.rolling_logic.is_rolling() then M.chat.info( "Rolling not in progress." ) return end f( unpack( arg ) ) end end local function in_group_check( f ) return m.in_group_check( M.api(), M.chat, f ) end local function setup_storage() -- Reset old AceDB configuration. I don't give a fuck :) if RollForDb and RollForDb.global and RollForDb.global.version then RollForDb = nil end RollForDb = RollForDb or {} RollForCharDb = RollForCharDb or {} M.db = RollForDb M.char_db = RollForCharDb if not M.db.version then M.db.version = version.str end end local function on_softres_command( args ) if args == "init" then clear_data() end M.softres_gui.toggle() end local function on_check_softres_command( args ) if string.find( args, "^a" ) then local use_raid_warning = string.find( args, "w" ) and true or false local result, players = M.softres_check.check_softres( true ) if result == M.softres_check.ResultType.SomeoneIsNotSoftRessing and m.raid_id then local msg = string.format( "https://raidres.fly.dev/res/%s", m.raid_id ) if getn( players ) < 10 then msg = msg .. " - " for i = 1, getn( players ) do local separator = i == 1 and "" or ", " local player_name = players[ i ].name local grouped_player = M.group_roster.find_player( player_name ) local next = grouped_player and m.colorize_player_by_class( grouped_player.name, grouped_player.class ) or player_name msg = msg .. separator .. next end msg = string.gsub(msg, "^(.*),%s*(.*)$", "%1 and %2") msg = msg .. " missing SR" end M.chat.announce( msg, use_raid_warning ) else m.pretty_print("No soft-res items found.") end else M.softres_check.check_softres() end end local function on_roll( player_name, roll, min, max ) local player = M.group_roster.find_player( player_name ) if not player then m.err( string.format( "Player %s could not be found.", hl( player_name ) ) ) return end M.rolling_logic.on_roll( player, roll, min, max ) end local function on_loot_method_changed() M.master_loot_warning.on_party_loot_method_changed() end local function on_master_looter_changed( player_name ) if M.player_info.get_name() == player_name and m.is_master_loot() then M.ace_timer.ScheduleTimer( M, M.config.print_raid_roll_settings, 0.1 ) end end function M.on_chat_msg_system( message ) for player_name, roll, min, max in string.gmatch( message, "([^%s]+) rolls (%d+) %((%d+)%-(%d+)%)" ) do on_roll( player_name, tonumber( roll ), tonumber( min ), tonumber( max ) ) return end if string.find( message, "^Looting changed to" ) then on_loot_method_changed() return end for player_name in string.gmatch( message, "(.-) is now the loot master%." ) do on_master_looter_changed( player_name ) return end for giver_name, item_name, recipient_name in string.gmatch( message, "([^%s]+) trades item (.+) to ([^%s]+)%." ) do on_raid_trade( giver_name, recipient_name, item_name ) return end end -- TODO: this can now be replaced by mocking LootList ---@diagnostic disable-next-line: unused-local, unused-function local function simulate_loot_dropped( args ) ---@diagnostic disable-next-line: unused-function local function mock_table_function( name, values ) M.api()[ name ] = function( key ) local value = values[ key ] if type( value ) == "function" then return value() else return value end end end ---@diagnostic disable-next-line: unused-function local function make_loot_slot_info( count, quality ) local result = {} for i = 1, count do table.insert( result, function() if i == count then m.api = m.real_api m.real_api = nil end return nil, nil, nil, quality or 4 end ) end return result end local item_links = M.item_utils.parse_all_links( args ) if m.real_api then info( "Mocking in progress." ) return end m.real_api = m.api m.api = m.clone( m.api ) M.api()[ "GetNumLootItems" ] = function() return getn( item_links ) end M.api()[ "UnitName" ] = function() return tostring( m.lua.time() ) end M.api()[ "GetLootThreshold" ] = function() return 4 end mock_table_function( "GetLootSlotLink", item_links ) mock_table_function( "GetLootSlotInfo", make_loot_slot_info( getn( item_links ), 4 ) ) M.dropped_loot_announce.on_loot_opened() end local function show_how_to_roll() M.chat.announce( "How to roll:" ) local ms = M.config.ms_roll_threshold() ~= 100 and string.format( " (%s)", M.config.ms_roll_threshold() or "100" ) or "" local sr = M.softres.get_all_rollers() local sr_count = getn( sr ) M.chat.announce( string.format( "For main-spec%s, type: /roll%s", sr_count > 0 and " and soft-res" or "", ms ) ) M.chat.announce( string.format( "For off-spec, type: /roll %s", M.config.os_roll_threshold() ) ) if M.config.tmog_rolling_enabled() then M.chat.announce( string.format( "For transmog, type: /roll %s", M.config.tmog_roll_threshold() ) ) end end local function on_reset_dropped_loot_announce_command() M.dropped_loot_announce.reset() end local function plus_ones_command( args ) local function print_usage() M.chat.info(string.format( "%s - %s", hl( "/pl" ), white( "List +1's" ) ) ) M.chat.info(string.format( "%s %s %s - %s", hl( "/pl add " ), grey( "" ), grey( "" ), white( "Add item to players +1's" ) ) ) M.chat.info(string.format( "%s %s %s - %s", hl( "/pl rm" ), grey( "" ), grey( "" ), white( "Remove item from players +1's" ) ) ) end local loot = M.awarded_loot.get_winners() local players = {} for _, award in ipairs(loot) do if award ~= nil then if not players[award.player_name] then players[award.player_name] = { award } else table.insert(players[award.player_name], award) end end end if args == "" then local plus_ones_exist = false for player_name, awards in pairs(players) do local plus_ones = m.filter(awards, (function(a) return a.plus_one end)) if getn(plus_ones) > 0 then plus_ones_exist = true local item_list = table.concat(m.map(plus_ones, (function (a) return a.item_link end)), " ") local colored_player_name = m.colorize_player_by_class( player_name, awards[1].player_class ) or grey( player_name ) M.chat.info( colored_player_name .. green(" MS +" .. getn(plus_ones)) .. ": " .. item_list) end end if not plus_ones_exist then M.chat.info("There are no +1's yet") end else local action, player_name, item_link = string.match(args, "^(%S+) (%S+) (|%w+|Hitem.+|r)$") local item_id = item_link and M.item_utils.get_item_id( item_link ) local group_players = M.group_roster.get_all_players_in_my_group() local player = player_name and m.filter(group_players, (function (p) return string.lower(p.name) == string.lower(player_name) end))[1] if action == nil and player_name == nil and item_link == nil then M.chat.info(red("Invalid usage")) print_usage() elseif action ~= "add" and action ~= "rm" and action ~= "remove" then M.chat.info(red("Invalid action")) print_usage() elseif (player == nil) then M.chat.info(red("Player not found in group")) print_usage() elseif item_link == nil or item_id == nil then M.chat.info(red("Invalid item")) print_usage() elseif action == "add" then M.chat.info("Gave " .. (m.colorize_player_by_class( player.name, player.class ) or m.colors.grey( player.name )) .. " a +1 for " .. item_link ) local roll_data = { player_name = player.name, player_class = player.class, roll_type = RollType.MainSpec, roll = 0, plus_ones = 0 } M.awarded_loot.award( player.name, item_id, roll_data, RollingStrategy.NormalRoll, item_link, player.class, nil, true) elseif action == "rm" or action == "remove" then if M.awarded_loot.has_item_been_awarded( player.name, item_id ) then M.unaward_item( player.name, item_id, item_link ) else M.chat.info(red(player.name .. " doesn't have a +1 for " .. item_link)) end end end end local function setup_slash_commands() -- Roll For commands SLASH_RF1 = RollSlashCommand.NormalRoll M.api().SlashCmdList[ "RF" ] = on_roll_command( RollSlashCommand.NormalRoll ) SLASH_ARF1 = RollSlashCommand.NoSoftResRoll M.api().SlashCmdList[ "ARF" ] = in_group_check( on_roll_command( RollSlashCommand.NoSoftResRoll ) ) SLASH_RR1 = RollSlashCommand.RaidRoll M.api().SlashCmdList[ "RR" ] = in_group_check( on_roll_command( RollSlashCommand.RaidRoll ) ) SLASH_IRR1 = RollSlashCommand.InstaRaidRoll M.api().SlashCmdList[ "IRR" ] = in_group_check( on_roll_command( RollSlashCommand.InstaRaidRoll ) ) SLASH_HTR1 = "/htr" M.api().SlashCmdList[ "HTR" ] = in_group_check( show_how_to_roll ) SLASH_CR1 = "/cr" M.api().SlashCmdList[ "CR" ] = is_rolling_check( M.roll_controller.cancel_rolling ) SLASH_FR1 = "/fr" M.api().SlashCmdList[ "FR" ] = is_rolling_check( M.roll_controller.finish_rolling_early ) SLASH_SSR1 = "/ssr" M.api().SlashCmdList[ "SSR" ] = on_show_sorted_rolls_command SLASH_RFR1 = "/rfr" M.api().SlashCmdList[ "RFR" ] = on_reset_dropped_loot_announce_command -- Soft Res commands SLASH_SR1 = "/sr" M.api().SlashCmdList[ "SR" ] = on_softres_command SLASH_SRS1 = "/srs" M.api().SlashCmdList[ "SRS" ] = M.softres_check.show_softres SLASH_SRC1 = "/src" M.api().SlashCmdList[ "SRC" ] = on_check_softres_command SLASH_SRO1 = "/sro" M.api().SlashCmdList[ "SRO" ] = M.name_matcher.manual_match SLASH_RFW1 = "/rfw" M.api().SlashCmdList[ "RFW" ] = M.winners_popup.show SLASH_RFO1 = "/rfo" M.api().SlashCmdList[ "RFO" ] = M.options_popup.show SLASH_RFT1 = "/rft" M.api().SlashCmdList[ "RFT" ] = M.sandbox.run SLASH_PL1 = "/pl" M.api().SlashCmdList[ "PL"] = plus_ones_command --SLASH_DROPPED1 = "/DROPPED" --M.api().SlashCmdList[ "DROPPED" ] = simulate_loot_dropped end function M.on_player_login() setup_storage() create_components() subscribe_for_component_events() setup_slash_commands() info( string.format( "Loaded (%s).", hl( string.format( "v%s", version.str ) ) ) ) M.version_broadcast.broadcast() M.import_encoded_softres_data( M.softres_db.data ) M.softres_gui.load( M.softres_db.data ) if M.welcome_popup.should_show() then M.welcome_popup.show() end ---@diagnostic disable-next-line: undefined-global LootFrame:UnregisterAllEvents() ---@diagnostic disable-next-line: undefined-global if pfLootFrame then pfLootFrame:UnregisterAllEvents() end end ---@diagnostic disable-next-line: unused-local, unused-function local function on_party_message( message, player ) for name, roll in string.gmatch( message, "(%a+) rolls (%d+)" ) do on_roll( name, tonumber( roll ), 1, 100 ) end for name, roll in string.gmatch( message, "(%a+) rolls os (%d+)" ) do on_roll( name, tonumber( roll ), 1, 99 ) end end function M.unaward_item( player_name, item_id, item_link ) M.awarded_loot.unaward( player_name, item_id ) info( string.format( "%s returned %s.", hl( player_name ), item_link ) ) end function M.on_group_changed() M.name_matcher.auto_match() update_minimap_icon() end function M.on_chat_msg_addon( name, message, _, sender ) if name ~= "RollFor" or not message then return end for ver in string.gmatch( message, "VERSION::(.*)" ) do M.version_broadcast.on_version( ver ) return end for channel, requesting_player_name in string.gmatch( message, "VERSION_REQUEST::(.-)::(.*)" ) do M.version_broadcast.on_version_request( channel, requesting_player_name ) return end for requesting_player_name, channel, their_name, their_class, their_version in string.gmatch( message, "VERSION_RESPONSE::(.-)::(.-)::(.-)::(.-)::(.*)" ) do M.version_broadcast.on_version_response( requesting_player_name, channel, their_name, their_class, their_version ) return end for data in string.gmatch( message, "ROLL::(.*)" ) do M.client.on_message( data, sender ) return end end ---@type KeyBindings m.key_bindings = m.KeyBindings.new( M ) m.EventHandler.handle_events( M ) return M