diff options
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/BattleStations/battlestations.lua')
-rw-r--r-- | Data/BuiltIn/Libraries/lua-addons/addons/BattleStations/battlestations.lua | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/BattleStations/battlestations.lua b/Data/BuiltIn/Libraries/lua-addons/addons/BattleStations/battlestations.lua new file mode 100644 index 0000000..5c2922a --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/BattleStations/battlestations.lua @@ -0,0 +1,597 @@ +--[[ +Copyright © 2018, Sjshovan (Apogee) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Battle Stations nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Sjshovan (Apogee) BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +]] + +_addon.name = 'Battle Stations' +_addon.description = 'Change or remove the default battle music.' +_addon.author = 'Sjshovan (Apogee) sjshovan@gmail.com' +_addon.version = '0.9.1' +_addon.commands = {'battlestations', 'stations', 'bs'} + +local _logger = require('logger') +local _config = require('config') +local _packets = require('packets') + +require('functions') +require('constants') +require('helpers') + +local defaults = { + stations = { + solo = 107.3, + party = 107.3 + } +} + +local settings = _config.load(defaults) + +local help = { + commands = { + buildHelpSeperator('=', 28), + buildHelpTitle('Commands'), + buildHelpSeperator('=', 28), + buildHelpCommandEntry('list [radios|stations] [category#]', 'Display the available radios and or stations.'), + buildHelpCommandEntry('set <station> [radio]', 'Set radio(s) to the given station.'), + buildHelpCommandEntry('get [radio]', 'Display currently set station on the given radio(s).'), + buildHelpCommandEntry('default [radio]', 'Set radio(s) to the default station (Current Zone Music).'), + buildHelpCommandEntry('normal [radio]', 'Set radio(s) to the original game music.'), + buildHelpCommandEntry('reload', 'Reload Battle Stations.'), + buildHelpCommandEntry('about', 'Display information about Battle Stations.'), + buildHelpCommandEntry('help', 'Display Battle Stations commands.'), + buildHelpSeperator('=', 28), + }, + + radios = { + buildHelpSeperator('=', 25), + buildHelpTitle('Radios'), + buildHelpSeperator('=', 25), + buildHelpRadioEntry(stations.receivers.solo:ucfirst(), 'Plays Solo Battle Music'), + buildHelpRadioEntry(stations.receivers.party:ucfirst(), 'Plays Party Battle Music'), + buildHelpSeperator('=', 25), + }, + + about = { + buildHelpSeperator('=', 23), + buildHelpTitle('About'), + buildHelpSeperator('=', 23), + buildHelpTypeEntry('Name', _addon.name), + buildHelpTypeEntry('Description', _addon.description), + buildHelpTypeEntry('Author', _addon.author), + buildHelpTypeEntry('Version', _addon.version), + buildHelpSeperator('=', 23), + }, + + aliases = { + list = { + stations = T{ + 's', + 'station', + 'stations', + }, + radios = T{ + 'r', + 'radio', + 'radios', + 'receiver', + 'receivers' + }, + categories = T{ + 'c', + 'cat', + 'category', + 'categories' + }, + all = T{ + '*', + 'a', + 'all', + } + } + } +} + +function displayHelp(table_help) + for index, command in pairs(table_help) do + displayResponse(command) + end +end + +function displayStations(range) + displayResponse(buildHelpSeperator('=', 27)) + displayResponse(buildHelpTitle('Stations')) + displayResponse(buildHelpSeperator('=', 27)) + + if range ~= nil then + if categoryValid(range) then + displayRangeFrequencies(range) + end + else + for i=100, 107, 1 do + range = tostring(i) + displayRangeFrequencies(range) + end + end + displayResponse(buildHelpSeperator('=', 26)) +end + +function displayRangeFrequencies(range, name) + local categories = stations.categories + + if categoryValid(range) then + local name = categories[range] + displayResponse(buildHelpStationCategoryEntry(range, name)) + displayFrequencies(range) + end +end + +function displayFrequencies(range) + for i=1, 9, 1 do + local frequency = range .. '.%s':format(tostring(i)) + if frequencyValid(frequency) then + local frequencyObj = getFrequencyObjByValue(frequency) + local response = buildHelpStationEntry(frequency, frequencyObj.callSign) + displayResponse(response) + end + end +end + +function displayCategories() + displayResponse(buildHelpSeperator('=', 27)) + displayResponse(buildHelpTitle('Categories')) + displayResponse(buildHelpSeperator('=', 27)) + for i=100, 107, 1 do + local range = tostring(i) + if categoryValid(range) then + local name = stations.categories[range] + displayResponse(buildHelpStationCategoryEntry(range, name)) + end + end + displayResponse(buildHelpSeperator('=', 27)) +end + +function getStations() + return settings.stations +end + +function setStation(radio, frequency) + if radio == stations.receivers.solo then + settings.stations.solo = frequency + elseif radio == stations.receivers.party then + settings.stations.party = frequency + else + settings.stations.solo = frequency + settings.stations.party = frequency + end + + settings:save() +end + +function resolveCurrentStations() + local current_stations = getStations() + local radio = 'solo' + local frequency = tostring(defaults.stations.solo) + local message_template = '%s station found in settings was not valid and was set to the default %s (%s).' + + if not frequencyValid(current_stations.solo) then + current_stations.solo = frequency + setStation(radio, frequency) + + displayResponse( + buildWarningMessage( + message_template:format( + radio:ucfirst():color(colors.secondary), + frequency:color(colors.primary), + getFrequencyObjByValue(frequency).callSign + ) + ) + ) + end + + if not frequencyValid(current_stations.party) then + radio = 'party' + frequency = tostring(defaults.stations.party) + current_stations.party = party_default + + setStation(radio, frequency) + + displayResponse( + buildWarningMessage( + message_template:format( + radio:ucfirst():color(colors.secondary), + frequency:color(colors.primary), + getFrequencyObjByValue(frequency).callSign + ) + ) + ) + end + + return current_stations +end + +function injectBattleMusic() + local current_stations = resolveCurrentStations() + local song = getFrequencyObjByValue(current_stations.solo).song + local music_type = music.types.battle_solo + + if playerInParty() then + music_type = music.types.battle_party + song = getFrequencyObjByValue(current_stations.party).song + end + + song = getConditionalSongTranslation(song) + + _packets.inject(_packets.new('incoming', packets.inbound.music_change.id, { + ['BGM Type'] = music_type, + ['Song ID'] = song + })) +end + +function getFrequencyObjByValue(frequency) + return stations.frequencies[tostring(frequency)] +end + +function getCurrentTime(formatted) + local timestamp = tostring(windower.ffxi.get_info().time) + local hours = (timestamp / 60):floor() + local minutes = timestamp % 60 + if formatted then + return "%s:%s":format(hours, minutes) + end + return timestamp +end + +function getZoneBGMTable() + local data = windower.packets.last_incoming(packets.inbound.zone_update.id) + local packet = _packets.parse('incoming', data) + return { + day = packet['Day Music'], + night = packet['Night Music'], + solo = packet['Solo Combat Music'], + party = packet['Party Combat Music'] + } +end + +function getConditionalSongTranslation(song) + local zone_bgm_table = getZoneBGMTable() + if song == music.songs.others.zone then + if timeIsDaytime() then + song = zone_bgm_table.day + else + song = zone_bgm_table.night + end + + if playerInReive() then + song = music.songs.seekers_of_adoulin.breaking_ground + end + + elseif song == music.songs.others.normal then + if playerInParty() then + song = zone_bgm_table.party + else + song = zone_bgm_table.solo + end + end + return song +end + +function getPlayerBuffs() + return T(windower.ffxi.get_player().buffs) +end + +function timeIsDaytime() + local current_time = tonumber(getCurrentTime()) + return current_time >= 6*60 and current_time <= 18*60 +end + +function playerIsFighting() + return windower.ffxi.get_player().status == player.statuses.fighting +end + +function playerInParty() + return windower.ffxi.get_party().alliance_count > 1 +end + +function playerInReive() + return getPlayerBuffs():contains(player.buffs.reiveMark) +end + +function frequencyValid(frequency) + return stations.frequencies[tostring(frequency)] ~= nil +end + +function radioValid(radio) + return stations.receivers[radio] ~= nil or radio == '*' +end + +function categoryValid(category) + return stations.categories[category] ~= nil +end + +function listTypeValid(list_type) + return help.lists[list_type] ~= nil +end + +function handleInjectionNeeds() + if needs_inject then + injectBattleMusic() + needs_inject = false; + end +end + +windower.register_event('load', function () + injectBattleMusic() +end) + +windower.register_event('unload', function() + local music_type = music.types.battle_solo + local zone_bgm_table = getZoneBGMTable() + local song = zone_bgm_table.solo + + if playerInParty() then + music_type = music.types.battle_party + song = zone_bgm_table.solo + end + + _packets.inject(_packets.new('incoming', packets.inbound.music_change.id, { + ['BGM Type'] = music_type, + ['Song ID'] = song, + })) +end) + +windower.register_event('action', function(act) + if act.actor_id == windower.ffxi.get_player().id then + if act.category == 4 and act.recast == 225 and act.targets[1].actions[1].animation == 939 then + if not playerInParty() then + functions.loop(injectBattleMusic, 1, 5) + end + end + end +end) + +windower.register_event('outgoing chunk', function(id, data) + if id == packets.outbound.action.id then + local packet = _packets.parse('outgoing', data) + if packet.Category == packets.outbound.action.categories.engage then + injectBattleMusic() + end + end +end) + +windower.register_event('addon command', function(command, ...) + if command then + command = command:lower() + else + displayHelp(help.commands) + return + end + + local command_args = {...} + + local respond = false + local response_message = '' + local success = true + + if command == 'list' or command == 'l' then + local list_type = (command_args[1] or '*'):lower() + local category = command_args[2] + + if help.aliases.list.stations:contains(list_type) then + if category then + if categoryValid(category) then + displayStations(category) + + else + respond = true + success = false + response_message = 'Category not recognized.' + end + else + displayStations() + end + + elseif help.aliases.list.radios:contains(list_type) then + displayHelp(help.radios) + + elseif help.aliases.list.categories:contains(list_type) then + displayCategories() + + elseif help.aliases.list.all:contains(list_type) then + displayHelp(help.radios) + displayStations() + + else + respond = true + success = false + response_message = 'List type not recognized.' + end + + elseif command == 'set' or command == 's' then + respond = true + + local frequency = command_args[1]:lower() + + local radio = (command_args[2] or '*'):lower() + + if not frequencyValid(frequency) then + success = false + response_message = 'Frequency not recognized.' + + elseif not radioValid(radio) then + success = false + response_message = 'Radio not recognized.' + else + needs_inject = true + + local context = 'radio' + + setStation(radio, tonumber(frequency)) + + if radio == '*' then + context = 'radios' + radio = 'Solo and Party' + end + + response_message = buildSetResponseMessage( + radio:ucfirst(), + context, + frequency, + getFrequencyObjByValue(frequency).callSign + )..'.' + end + + elseif command == 'get' or command == 'g' then + respond = true + + local current_stations = resolveCurrentStations() + local radio = (command_args[1] or '*'):lower() + local frequency = current_stations[radio] + local frequency2 = current_stations.party + local individual = false + + if not radioValid(radio) then + success = false + response_message = 'Radio not recognized.' + else + local context = 'radio is' + + if radio == '*' then + frequency = current_stations.solo + + if current_stations.party ~= current_stations.solo then + individual = true + else + context = 'radios are' + radio = 'Solo and Party' + end + end + + if not individual then + response_message = buildGetResponseMessage( + radio:ucfirst(), + context, + frequency, + getFrequencyObjByValue(frequency).callSign + )..'.' + + else + local solo_message = buildGetResponseMessage( + stations.receivers.solo:ucfirst(), + context, + frequency, + getFrequencyObjByValue(frequency).callSign + ) + + local party_message = buildGetResponseMessage( + stations.receivers.party:ucfirst(), + context, + frequency2, + getFrequencyObjByValue(frequency2).callSign + ) + + response_message = solo_message..' and '..party_message..'.' + end + end + + elseif command == 'default' or command == 'd' then + respond = true + + local radio = (command_args[1] or '*'):lower() + + if not radioValid(radio) then + success = false + response_message = 'Radio not recognized.' + else + needs_inject = true + + local frequency = defaults.stations.solo + local context = 'radio' + + setStation(radio, tonumber(frequency)) + + if radio == '*' then + context = 'radios' + radio = 'Solo and Party' + end + + response_message = buildSetResponseMessage( + radio:ucfirst(), + context, + frequency, + getFrequencyObjByValue(frequency).callSign + )..'.' + end + + elseif command == 'normal' or command == 'n' then + respond = true + + local radio = (command_args[1] or '*'):lower() + + if not radioValid(radio) then + success = false + response_message = 'Radio not recognized.' + else + needs_inject = true + + local frequency = '107.2' + local context = 'radio' + + setStation(radio, tonumber(frequency)) + + if radio == '*' then + context = 'radios' + radio = 'Solo and Party' + end + + response_message = buildSetResponseMessage( + radio:ucfirst(), + context, + frequency, + getFrequencyObjByValue(frequency).callSign + )..'.' + end + + elseif command == 'reload' or command == 'r' then + windower.send_command('lua r battlestations') + + elseif command == 'about' or command == 'a' then + displayHelp(help.about) + + elseif command == 'help' or command == 'h' then + displayHelp(help.commands) + + else + displayHelp(help.commands) + end + + if respond then + displayResponse( + buildCommandResponse(response_message, success) + ) + end + + handleInjectionNeeds() +end)
\ No newline at end of file |