diff --git a/addons/AutoRA/AutoRA.lua b/addons/AutoRA/AutoRA.lua index 22d8d03d9..49dd1bb19 100644 --- a/addons/AutoRA/AutoRA.lua +++ b/addons/AutoRA/AutoRA.lua @@ -37,7 +37,7 @@ end local haltontp = function() settings.HaltOnTp = not settings.HaltOnTp - if settings.HaltonTp then + if settings.HaltOnTp then windower.add_to_chat(17, 'AutoRA will halt upon reaching 1000 TP') else windower.add_to_chat(17, 'AutoRA will no longer halt upon reaching 1000 TP') diff --git a/addons/EmpyPopTracker/EmpyPopTracker.lua b/addons/EmpyPopTracker/EmpyPopTracker.lua index f2803e6d6..cf178946e 100644 --- a/addons/EmpyPopTracker/EmpyPopTracker.lua +++ b/addons/EmpyPopTracker/EmpyPopTracker.lua @@ -29,7 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. _addon.name = 'Empy Pop Tracker' _addon.author = 'Dean James (Xurion of Bismarck)' _addon.commands = { 'ept', 'empypoptracker' } -_addon.version = '2.2.2' +_addon.version = '2.3.0' config = require('config') res = require('resources') diff --git a/addons/EmpyPopTracker/README.md b/addons/EmpyPopTracker/README.md index 5ff3d1214..24684f744 100644 --- a/addons/EmpyPopTracker/README.md +++ b/addons/EmpyPopTracker/README.md @@ -8,10 +8,16 @@ Originally developed to track Abyssea Empyrean weapon NMs, hence the name. Key i All text colours are configurable via the auto-generated settings.xml file. +## Installation + +Empy Pop Tracker is now available via the Windower 4 addons list. + ## Load `//lua load empypoptracker` +Note: You won't have to do this if you obtained this addon via Windower. + ## Track an NM `//ept track glavoid` tracks Glavoid pop items/key items. diff --git a/addons/EmpyPopTracker/nms/brulo.lua b/addons/EmpyPopTracker/nms/brulo.lua new file mode 100644 index 000000000..1c100b413 --- /dev/null +++ b/addons/EmpyPopTracker/nms/brulo.lua @@ -0,0 +1,50 @@ +--[[ +Copyright © 2020, Dean James (Xurion of Bismarck) +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 Empy Pop Tracker 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 Dean James (Xurion of Bismarck) 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. +]] + +return { + name = 'Brulo', + pops = { { + id = 1652, --Emerald demilune abyssite + type = 'key item', + dropped_from = { + name = 'Koios (Conflux #5)', + pops = { { + id = 1565, --Colorful demilune abyssite + type = 'key item', + dropped_from = { + name = 'Fire/Earth Elemental', + pops = { { + id = 1564, --Clear demilune abyssite + type = 'key item', + dropped_from = { name = 'Any Cruor Prospector' } + } } + } + } } + } + } } +} diff --git a/addons/EmpyPopTracker/nms/index.lua b/addons/EmpyPopTracker/nms/index.lua index 425a2b4c5..a2ab5b604 100644 --- a/addons/EmpyPopTracker/nms/index.lua +++ b/addons/EmpyPopTracker/nms/index.lua @@ -32,6 +32,7 @@ local nms = { 'arch dynamis lord', 'azdaja', 'briareus', + 'brulo', 'bukhis', 'carabosse', 'chloris', @@ -41,6 +42,8 @@ local nms = { 'isgebind', 'itzpapalotl', 'kukulkan', + 'maere', + 'ogopogo', 'orthrus', 'sedna', 'sobek', diff --git a/addons/EmpyPopTracker/nms/maere.lua b/addons/EmpyPopTracker/nms/maere.lua new file mode 100644 index 000000000..cfcc57214 --- /dev/null +++ b/addons/EmpyPopTracker/nms/maere.lua @@ -0,0 +1,50 @@ +--[[ +Copyright © 2020, Dean James (Xurion of Bismarck) +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 Empy Pop Tracker 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 Dean James (Xurion of Bismarck) 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. +]] + +return { + name = 'Maere', + pops = { { + id = 1654, --Indigo demilune abyssite + type = 'key item', + dropped_from = { + name = 'Gamayun (Conflux #8)', + pops = { { + id = 1565, --Colorful demilune abyssite + type = 'key item', + dropped_from = { + name = 'Air/Dark Elemental', + pops = { { + id = 1564, --Clear demilune abyssite + type = 'key item', + dropped_from = { name = 'Any Cruor Prospector' } + } } + } + } } + } + } } +} diff --git a/addons/EmpyPopTracker/nms/ogopogo.lua b/addons/EmpyPopTracker/nms/ogopogo.lua new file mode 100644 index 000000000..52a64ca25 --- /dev/null +++ b/addons/EmpyPopTracker/nms/ogopogo.lua @@ -0,0 +1,50 @@ +--[[ +Copyright © 2020, Dean James (Xurion of Bismarck) +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 Empy Pop Tracker 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 Dean James (Xurion of Bismarck) 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. +]] + +return { + name = 'Ogopogo', + pops = { { + id = 1653, --Vermillion demilune abyssite + type = 'key item', + dropped_from = { + name = 'Chione (Conflux #7)', + pops = { { + id = 1565, --Colorful demilune abyssite + type = 'key item', + dropped_from = { + name = 'Ice/Water Elemental', + pops = { { + id = 1564, --Clear demilune abyssite + type = 'key item', + dropped_from = { name = 'Any Cruor Prospector' } + } } + } + } } + } + } } +} diff --git a/addons/NoCampaignMusic/NoCampaignMusic.lua b/addons/NoCampaignMusic/NoCampaignMusic.lua new file mode 100644 index 000000000..a45e9ff28 --- /dev/null +++ b/addons/NoCampaignMusic/NoCampaignMusic.lua @@ -0,0 +1,129 @@ +--[[ +Copyright © 2020, Dean James (Xurion of Bismarck) +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 No Campaign Music 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 Dean James (Xurion of Bismarck) 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 = 'No Campaign Music' +_addon.author = 'Dean James (Xurion of Bismarck)' +_addon.version = '2.0.1' +_addon.commands = {'nocampaignmusic', 'ncm'} + +packets = require('packets') +config = require('config') + +defaults = { + Notifications = false, +} + +settings = config.load(defaults) + +campaign_id = 247 +solo_id = 101 +party_id = 215 +solo_dungeon_id = 115 +party_dungeon_id = 216 + +zone_music_map = {} +zone_music_map[80] = { 254, 254, solo_id, party_id } --Southern San d'Oria [S] +zone_music_map[81] = { 251, 251, solo_id, party_id } --East Ronfaure [S] +zone_music_map[82] = { 0, 0, solo_id, party_id } --Jugner Forest [S] +zone_music_map[83] = { 0, 0, solo_id, party_id } --Vunkerl Inlet [S] +zone_music_map[84] = { 252, 252, solo_id, party_id } --Batallia Downs [S] +zone_music_map[85] = { 44, 44, solo_dungeon_id, party_dungeon_id } --La Vaule [S] +zone_music_map[87] = { 180, 180, solo_id, party_id } --Bastok Markets [S] +zone_music_map[88] = { 253, 253, solo_id, party_id } --North Gustaberg [S] +zone_music_map[89] = { 0, 0, solo_id, party_id } --Grauberg [S] +zone_music_map[90] = { 0, 0, solo_id, party_id } --Pashhow Marshlands [S] +zone_music_map[91] = { 252, 252, solo_id, party_id } --Rolanberry Fields [S] +zone_music_map[92] = { 44, 44, solo_dungeon_id, party_dungeon_id } --Beadeaux [S] +zone_music_map[94] = { 182, 182, solo_id, party_id } --Windurst Waters [S] +zone_music_map[95] = { 141, 141, solo_id, party_id } --West Sarutabaruta [S] +zone_music_map[96] = { 0, 0, solo_id, party_id } --Fort Karugo-Narugo [S] +zone_music_map[97] = { 0, 0, solo_id, party_id } --Meriphataud Mountains [S] +zone_music_map[98] = { 252, 252, solo_id, party_id } --Sauromugue Champaign [S] +zone_music_map[99] = { 44, 44, solo_dungeon_id, party_dungeon_id } --Castle Oztroja [S] +zone_music_map[136] = { 0, 0, solo_id, party_id } --Beaucedine Glacier [S] +zone_music_map[137] = { 42, 42, solo_id, party_id } --Xarcabard [S] +zone_music_map[138] = { 43, 43, solo_dungeon_id, party_dungeon_id } --Castle Zvahl Baileys [S] +zone_music_map[155] = { 43, 43, solo_dungeon_id, party_dungeon_id } --Castle Zvahl Keep [S] +zone_music_map[164] = { 0, 0, solo_dungeon_id, party_dungeon_id } --Garlaige Citadel [S] +zone_music_map[171] = { 0, 0, solo_dungeon_id, party_dungeon_id } --Crawlers' Nest [S] +zone_music_map[175] = { 0, 0, solo_dungeon_id, party_dungeon_id } --The Eldieme Necropolis [S] + +windower.register_event('incoming chunk', function(id, data) + if id ~= 0x00A and id ~= 0x05F then return end + + local parsed = packets.parse('incoming', data) + local zone_music = zone_music_map[parsed['Zone'] or windower.ffxi.get_info().zone] + + if not zone_music then return end + + if id == 0x00A then --Zone update (zoned in) + if parsed['Day Music'] == campaign_id and zone_music then + parsed['Day Music'] = zone_music[1] + parsed['Night Music'] = zone_music[2] + parsed['Solo Combat Music'] = zone_music[3] + parsed['Party Combat Music'] = zone_music[4] + + return packets.build(parsed) + end + else --Music update (campaign possibly started/finished) + local info = windower.ffxi.get_info() + if parsed['Song ID'] == campaign_id then + + if settings.Notifications and parsed['BGM Type'] == 0 then --only log to the chat once + windower.add_to_chat(8, 'Prevented campaign music.') + end + + parsed['Song ID'] = zone_music[parsed['BGM Type'] + 1] + return packets.build(parsed) + end + end +end) + +commands = {} + +commands.notify = function() + settings.Notifications = not settings.Notifications + settings:save() + windower.add_to_chat(8, 'Campaign notifications: ' .. tostring(settings.Notifications)) +end + +commands.help = function() + windower.add_to_chat(8, 'No Campaign Music:') + windower.add_to_chat(8, ' //ncm notify - toggles campaign notifications (default false)') + windower.add_to_chat(8, ' //ncm help - shows this help') +end + +windower.register_event('addon command', function(command) + command = command and command:lower() or 'help' + + if commands[command] then + commands[command]() + else + commands.help() + end +end) diff --git a/addons/NoCampaignMusic/README.md b/addons/NoCampaignMusic/README.md new file mode 100644 index 000000000..63ec719c0 --- /dev/null +++ b/addons/NoCampaignMusic/README.md @@ -0,0 +1,27 @@ +# FFXI - No Campaign Music + +Prevents all campaign battle music from playing in Final Fantasy XI, so you can listen to that sweet music that is normally obnoxiously interrupted. + +## Load + +``` +//lua load ncm +``` + +## Campaign notifications + +If you want to know when campaign is happening, you can toggle notifications: + +``` +//ncm notify +``` + +## Note + +When campaign music is prevented from playing (whether it's as you zone in, or campaign starts in the zone) a message will log to the chatlog stating "Prevented campaign music." This not only confirms the addon is working, but shows you there's a campaign active should you want to take part. + +## Contributing + +If you notice something not quite right, please [raise an issue](https://github.com/xurion/ffxi-no-campaign-music/issues). + +Or better yet, [pull requests](https://github.com/xurion/ffxi-no-campaign-music/pulls) are welcome! diff --git a/addons/addons.xml b/addons/addons.xml index ed64f3a4f..6286a66f4 100644 --- a/addons/addons.xml +++ b/addons/addons.xml @@ -370,6 +370,13 @@ https://github.com/Windower/Lua/issues https://discord.gg/b275nMv + + NoCampaignMusic + Prevents campaign battle music from playing in Shadowreign areas. + Dean James (Xurion of Bismarck) + https://github.com/xurion/ffxi-no-campaign-music/issues + https://www.ffxiah.com/forum/topic/52507/sick-of-campaign-music-prevent-it-with-this-addon/ + Nostrum Creates a click-able on-screen macro to help avoid targeting problems while curing. @@ -815,5 +822,5 @@ Block graphical effects from Geomancer's Indi- spells. https://github.com/Windower/Lua/issues https://github.com/lili-ffxi - + diff --git a/addons/battlemod/battlemod.lua b/addons/battlemod/battlemod.lua index 494ed3fc3..f8d9ed365 100644 --- a/addons/battlemod/battlemod.lua +++ b/addons/battlemod/battlemod.lua @@ -352,9 +352,9 @@ windower.register_event('incoming chunk',function (id,original,modified,is_injec if am.message_id > 169 and am.message_id <179 then if am.param_1 > 2147483647 then - skill = 'like level -1 ('..ratings_arr[am.param_2-63]..')' + skill = 'to be level -1 ('..ratings_arr[am.param_2-63]..')' else - skill = 'like level '..am.param_1..' ('..ratings_arr[am.param_2-63]..')' + skill = 'to be level '..am.param_1..' ('..ratings_arr[am.param_2-63]..')' end end local outstr = (res.action_messages[am.message_id][language] diff --git a/addons/battlemod/statics.lua b/addons/battlemod/statics.lua index a6dc6d33b..01e9ddd46 100644 --- a/addons/battlemod/statics.lua +++ b/addons/battlemod/statics.lua @@ -25,7 +25,7 @@ --SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. language = 'english' skillchain_arr = {'Light:','Darkness:','Gravitation:','Fragmentation:','Distortion:','Fusion:','Compression:','Liquefaction:','Induration:','Reverberation:','Transfixion:','Scission:','Detonation:','Impaction:','Radiance:','Umbra:'} -ratings_arr = {'TW','EEP','EP','DC','EM','T','VT','IT'} +ratings_arr = {'TW','IEP','EP','DC','EM','T','VT','IT'} current_job = 'NONE' default_filt = false rcol = string.char(0x1E,0x01) diff --git a/addons/libs/packets.lua b/addons/libs/packets.lua index dd2b90a58..4b61df184 100644 --- a/addons/libs/packets.lua +++ b/addons/libs/packets.lua @@ -10,7 +10,6 @@ require('strings') require('functions') require('pack') -local list, math, string, functions = _libs.lists, _libs.maths, _libs.strings local table = require('table') local packets = {} @@ -69,7 +68,7 @@ local sizes = { } -- This defines whether to treat a type with brackets at the end as an array or something special -local non_array_types = S{'bit', 'data', 'char'} +local non_array_types = S{'bit', 'data', 'char'} -- Pattern to match variable size array local pointer_pattern = '(.+)%*' @@ -100,8 +99,8 @@ local size size = function(fields, count) -- A single field if fields.ctype then - local bits, type_count, type = parse_type(fields) - return bits or count * sizes[type] + local bits, _, type = parse_type(fields) + return bits or type == 'char' and 8 or count and count * sizes[type] or 0 end -- A reference field @@ -394,6 +393,11 @@ function packets.new(dir, id, values, ...) return packet end +local lookup = function(packet, field) + local val = packet[field.label] + return field.enc and val:encode(field.enc) or val +end + -- Returns binary data from a packet function packets.build(packet) local fields = packets.fields(packet._dir, packet._id, packet._raw, unpack(packet._args or {})) @@ -403,7 +407,7 @@ function packets.build(packet) end local pack_string = fields:map(make_pack_string):concat() - local data = pack_string:pack(fields:map(table.lookup-{packet, 'label'}):unpack()) + local data = pack_string:pack(fields:map(lookup+{packet}):unpack()) local rem = #data % 4 if rem ~= 0 then data = data .. 0:char():rep(4 - rem) diff --git a/addons/libs/packets/fields.lua b/addons/libs/packets/fields.lua index 0d91a2167..414947519 100644 --- a/addons/libs/packets/fields.lua +++ b/addons/libs/packets/fields.lua @@ -20,12 +20,23 @@ local func = { } -- String decoding definitions -local ls_name_msg = T('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':split()) -ls_name_msg[0] = 0:char() -local item_inscr = T('0123456798ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{':split()) -item_inscr[0] = 0:char() -local ls_name_ext = T(('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' .. 0:char():rep(11)):split()) -ls_name_ext[0] = '`' +local ls_enc = { + charset = T('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':split()):update({ + [0] = '`', + [60] = 0:char(), + [63] = 0:char(), + }), + bits = 6, + terminator = function(str) + return (#str % 4 == 2 and 60 or 63):binary() + end +} +local sign_enc = { + charset = T('0123456798ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{':split()):update({ + [0] = 0:char(), + }), + bits = 6, +} -- Function definitions. Used to display packet field information. local res = require('resources') @@ -348,6 +359,7 @@ enums['action'] = { [0x0F] = 'Switch target', [0x10] = 'Ranged attack', [0x12] = 'Dismount Chocobo', + [0x13] = 'Tractor Dialogue', [0x14] = 'Zoning/Appear', -- I think, the resource for this is ambiguous. [0x19] = 'Monsterskill', [0x1A] = 'Mount', @@ -1433,7 +1445,7 @@ fields.incoming[0x017] = L{ {ctype='unsigned char', label='Mode', fn=chat}, -- 04 {ctype='bool', label='GM'}, -- 05 {ctype='unsigned short', label='Zone', fn=zone}, -- 06 Set only for Yell - {ctype='char[16]', label='Sender Name'}, -- 08 + {ctype='char[0x10]', label='Sender Name'}, -- 08 {ctype='char*', label='Message'}, -- 18 Max of 150 characters } @@ -2366,6 +2378,7 @@ enums['ah itype'] = { [0x0A] = 'Open menu confirmation', [0x0B] = 'Sell item confirmation', [0x0D] = 'Sales item status', + [0x0E] = 'Purchase item result', } func.incoming[0x04C] = {} @@ -2414,6 +2427,11 @@ enums['sale stat'] = { [0x0B] = 'Not sold', [0x10] = 'Checking', } +enums['buy stat'] = { + [0x01] = 'Success', + [0x02] = 'Placing', + [0xC5] = 'Failed', +} -- 0x0A, 0x0B and 0x0D could probably be combined, the fields seem the same. -- However, they're populated a bit differently. Both 0x0B and 0x0D are sent twice @@ -2475,6 +2493,25 @@ func.incoming[0x04C][0x0D] = L{ {ctype='unsigned int', label='Timestamp', fn=utime}, -- 38 } +func.incoming[0x04C][0x0E] = L{ + {ctype='unsigned char', label='_unknown1'}, -- 05 + {ctype='unsigned char', label='Buy Status', fn=e+{'buy stat'}}, -- 06 + {ctype='unsigned char', label='_unknown2'}, -- 07 + {ctype='unsigned int', label='Price', fn=gil}, -- 08 + {ctype='unsigned short', label='Item ID', fn=item}, -- 0C + {ctype='unsigned short', label='_unknown3'}, -- 0E + {ctype='unsigned short', label='Count'}, -- 10 + {ctype='unsigned int', label='_unknown4'}, -- 12 + {ctype='unsigned short', label='_unknown5'}, -- 16 + {ctype='char[16]', label='Name'}, -- 18 Character name (pending buy only) + {ctype='unsigned short', label='Pending Item ID', fn=item}, -- 28 Only filled out during pending packets + {ctype='unsigned short', label='Pending Count'}, -- 2A Only filled out during pending packets + {ctype='unsigned int', label='Pending Price', fn=gil}, -- 2C Only filled out during pending packets + {ctype='unsigned int', label='_unknown6'}, -- 30 + {ctype='unsigned int', label='_unknown7'}, -- 34 + {ctype='unsigned int', label='Timestamp', fn=utime}, -- 38 Only filled out during pending packets +} + func.incoming[0x04C][0x10] = L{ {ctype='unsigned char', label='_unknown1', const=0x00}, -- 05 {ctype='unsigned char', label='Success', fn=bool}, -- 06 @@ -3243,7 +3280,7 @@ func.incoming[0x0C9][0x01] = L{ {ctype='bit[4]', label='_junk1'}, -- 11 {ctype='unsigned char', label='Main Job', fn=job}, -- 12 {ctype='unsigned char', label='Sub Job', fn=job}, -- 13 - {ctype='char[16]', label='Linkshell', enc=ls_name_msg}, -- 14 6-bit packed + {ctype='data[15]', label='Linkshell', enc=ls_enc}, -- 14 6-bit packed {ctype='unsigned char', label='Main Job Level'}, -- 24 {ctype='unsigned char', label='Sub Job Level'}, -- 25 {ctype='data[42]', label='_unknown5'}, -- 26 At least the first two bytes and the last twelve bytes are junk, possibly more @@ -3264,7 +3301,7 @@ fields.incoming[0x0CC] = L{ {ctype='unsigned int', label='Timestamp', fn=time}, -- 88 {ctype='char[16]', label='Player Name'}, -- 8C {ctype='unsigned int', label='Permissions'}, -- 98 - {ctype='char[16]', label='Linkshell', enc=ls_name_msg}, -- 9C 6-bit packed + {ctype='data[15]', label='Linkshell', enc=ls_enc}, -- 9C 6-bit packed } -- Found Item @@ -3426,12 +3463,17 @@ fields.incoming[0x0F6] = L{ {ctype='unsigned int', label='Type', fn=e+{'ws mark'}}, -- 04 } +enums['reraise'] = { + [0x01] = 'Raise dialogue', + [0x02] = 'Tractor dialogue', +} + -- Reraise Activation fields.incoming[0x0F9] = L{ {ctype='unsigned int', label='ID', fn=id}, -- 04 {ctype='unsigned short', label='Index', fn=index}, -- 08 - {ctype='unsigned char', label='_unknown1'}, -- 0A - {ctype='unsigned char', label='_unknown2'}, -- 0B + {ctype='unsigned char', label='Category', fn=e+{'reraise'}}, -- 0A + {ctype='unsigned char', label='_unknown1'}, -- 0B } -- Furniture Interaction diff --git a/addons/libs/strings.lua b/addons/libs/strings.lua index 6f653acd0..cd7ac1e20 100644 --- a/addons/libs/strings.lua +++ b/addons/libs/strings.lua @@ -41,8 +41,7 @@ function string.psplit(str, sep, maxsplit, include) return str:split(sep, maxsplit, include, false) end --- Splits a string into a table by a separator string. -function string.split(str, sep, maxsplit, include, raw) +local rawsplit = function(str, sep, maxsplit, include, raw) if not sep or sep == '' then local res = {} local key = 0 @@ -51,12 +50,7 @@ function string.split(str, sep, maxsplit, include, raw) res[key] = c end - if _meta.L then - res.n = key - return setmetatable(res, _meta.L) - end - - return setmetatable(res, _meta.T and _meta.T or nil) + return res, key end maxsplit = maxsplit or 0 @@ -98,12 +92,23 @@ function string.split(str, sep, maxsplit, include, raw) end end + return res, key +end + +-- Splits a string into a table by a separator string. +function string.split(str, sep, maxsplit, include, raw) + local res, key = rawsplit(str, sep, maxsplit, include, raw) + if _meta.L then res.n = key return setmetatable(res, _meta.L) end - return setmetatable(res, _meta.T and _meta.T or nil) + if _meta.T then + return setmetatable(res, _meta.T) + end + + return res end -- Alias to string.sub, with some syntactic sugar. @@ -189,12 +194,12 @@ function string.capitalize(str) return table.concat(res, ' ') end --- Takes a padding character pad and pads the string str to the left of it, until len is reached. pad defaults to a space. +-- Takes a padding character pad and pads the string str to the left of it, until len is reached. function string.lpad(str, pad, len) return (pad:rep(len) .. str):sub(-(len > #str and len or #str)) end --- Takes a padding character pad and pads the string str to the right of it, until len is reached. pad defaults to a space. +-- Takes a padding character pad and pads the string str to the right of it, until len is reached. function string.rpad(str, pad, len) return (str .. pad:rep(len)):sub(1, len > #str and len or #str) end @@ -425,22 +430,19 @@ function string.chunks(str, size) end end --- Returns a string decoded given the appropriate information. -string.decode = (function() - local chunk_size = function(t) - local e, f = math.frexp(#t) - return f + math.ceil(e - 1.5) - end +-- Returns a string decoded given the appropriate encoding. +string.decode = function(str, encoding) + return (str:binary():chunks(encoding.bits):map(table.get+{encoding.charset} .. tonumber-{2}):concat():gsub('%z.*$', '')) +end - return function(str, charset) - if type(charset) == 'string' then - local tmp = charset - charset = charset:sub(2):split() - charset[0] = charset:sub(1, 1) - end - return str:binary():chunks(chunk_size(charset)):map(table.get+{charset} .. tonumber-{2}):concat():gsub('%z+$', '') +-- Returns a string encoded given the appropriate encoding. +string.encode = function(str, encoding) + local binary = str:map(string.zfill-{encoding.bits} .. math.binary .. table.find+{encoding.charset}) + if encoding.terminator then + binary = binary .. encoding.terminator(str) end -end)() + return binary:rpad('0', (#binary / 8):ceil() * 8):parse_binary() +end -- Returns a plural version of a string, if the provided table contains more than one element. -- Defaults to appending an s, but accepts an option string as second argument which it will the string with. diff --git a/addons/porter/README.md b/addons/porter/README.md index 4cab50a62..4efb5ce2a 100644 --- a/addons/porter/README.md +++ b/addons/porter/README.md @@ -14,10 +14,17 @@ porter [ []] [owned] * **_slip_:** the number of the slip you want to show. * **_page_:** the page of the slip you want to show. * **owned:** shows only the items you own. - +``` +porter find +``` +Shows storable items found in all inventory bags. ---- ##changelog## +### v1.20200419 +* **add**: New command, porter find. +* **change**: Adjusted resource handling. + ### v1.20130529 * **fix**: Fixed parameters validation. * **change**: Aligned to Windower's addon development guidelines. diff --git a/addons/porter/porter.lua b/addons/porter/porter.lua index 8481599a9..b10c2eae3 100644 --- a/addons/porter/porter.lua +++ b/addons/porter/porter.lua @@ -32,63 +32,15 @@ require 'chat' require 'logger' require 'sets' require 'strings' - +res = require 'resources' slips = require 'slips' - -_addon.name = 'porter' -_addon.version = '1.20130529' +_addon.name = 'porter' +_addon.version = '1.20200419' _addon.command = 'porter' _addon.author = 'Zohno' -item_names = T{} -resources = { - ['armor'] = '../../plugins/resources/items_armor.xml', - ['weapons'] = '../../plugins/resources/items_weapons.xml', - ['general'] = '../../plugins/resources/items_general.xml' -} - -function load_resources() - local slips_items_ids = T() - for _, slip in pairs(slips.items) do - slips_items_ids:extend(slip) - end - - slips_items_ids = S(slips_items_ids) - - for kind, resource_path in pairs(resources) do - resource = io.open(windower.addon_path..resource_path, 'r') - - if resource ~= nil then - while true do - local line = resource:read() - - if line == nil then - break - end - - local id, name = line:match('id="(%d+)".+>([^<]+)<') - - if id ~= nil then - id = tonumber(id, 10) - - if slips_items_ids:contains(id) then - item_names[id] = name:lower() - end - end - end - else - error(kind..' resource file not found') - end - - resource:close() - end -end - function show_slip(slip_number, slip_page, owned_only) - if item_names:length() == 0 then - load_resources() - end owned_only = owned_only or false @@ -137,7 +89,7 @@ function show_slip(slip_number, slip_page, owned_only) windower.add_to_chat( 55, ('slip '..printable_slip_number..'/page '..tostring(slip_page and slip_page or math.ceil(item_position / 16)):lpad('0', 2)..':'):color(259)..' '.. - item_names[item_id]:color(is_contained and 258 or 261) + res.items[item_id].name:color(is_contained and 258 or 261) ) end end @@ -145,6 +97,24 @@ function show_slip(slip_number, slip_page, owned_only) end end +function show_bags() + + local n = 0 + + for _, bag in ipairs(slips.default_storages) do + for _, item in ipairs(windower.ffxi.get_items(bag)) do + local slip_id = slips.get_slip_id_by_item_id(item.id) + + if slip_id then + n = n + 1 + windower.add_to_chat(207, 'slip %02d: %s %s':format(slips.get_slip_number_by_id(slip_id), bag, res.items[item.id].name:color(258))) + end + end + end + + windower.add_to_chat(207, 'Found %s storable items in all bags':format(n)) +end + windower.register_event('addon command',function (slip_number, slip_page, owned_only) if tonumber(slip_number) == nil then slip_page = nil @@ -152,6 +122,9 @@ windower.register_event('addon command',function (slip_number, slip_page, owned_ if slip_number == 'owned' then slip_number = nil owned_only = true + elseif slip_number == 'find' then + show_bags() + return elseif slip_number ~= nil then error('That\'s not a valid slip number, kupo!') @@ -181,4 +154,4 @@ windower.register_event('addon command',function (slip_number, slip_page, owned_ end show_slip(slip_number, slip_page, owned_only) -end) \ No newline at end of file +end) diff --git a/addons/salvage2/data/settings.xml b/addons/salvage2/data/settings.xml deleted file mode 100644 index 08ba0f7f9..000000000 --- a/addons/salvage2/data/settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - 1000 - 250 - - \ No newline at end of file diff --git a/addons/salvage2/salvage2.lua b/addons/salvage2/salvage2.lua index 4e8fb289a..1d9eb7cde 100644 --- a/addons/salvage2/salvage2.lua +++ b/addons/salvage2/salvage2.lua @@ -30,142 +30,146 @@ require('tables') require('strings') require('maths') require('logger') +texts = require ('texts') config = require('config') ----------------------------- -settingtab = config.load() --variables - posx = 1000 - posy = 250 - if settingtab['posx'] ~= nil then - posx = settingtab['posx'] - posy = settingtab['posy'] - end - pathos_ident = {'Main Weapon/Sub-Weapon restriction', 'Ranged Weapon/Ammo restriction', 'Head/Neck equipment restriction', 'Body equipment restriction', 'Hand equipment restriction', 'Earrings/Rings restriction', 'Back/Waist equipment restriction', 'Leg/Foot equipment restriction', 'Support Job restriction', 'Job Abilities restriction', 'Spellcasting restriction', 'Max HP Down', 'Max MP Down', 'STR Down', 'DEX Down', 'AGI Down', 'MND Down', 'INT Down', 'CHR Down', 'VIT Down'} - pathos_short = {'Weapon', 'Ranged', 'Head/Neck', 'Body', 'Hand', 'Earrings/Rings', 'Back/Waist', 'Leg/Foot', 'Support Job', 'Job Abilities', 'Spellcasting', 'Max HP', 'Max MP', 'STR', 'DEX', 'AGI', 'MND', 'INT', 'CHR', 'VIT'} +pathos_ident = {'Main Weapon/Sub-Weapon restriction', 'Ranged Weapon/Ammo restriction', 'Head/Neck equipment restriction', 'Body equipment restriction', 'Hand equipment restriction', 'Earrings/Rings restriction', 'Back/Waist equipment restriction', 'Leg/Foot equipment restriction', 'Support Job restriction', 'Job Abilities restriction', 'Spellcasting restriction', 'Max HP Down', 'Max MP Down', 'STR Down', 'DEX Down', 'AGI Down', 'MND Down', 'INT Down', 'CHR Down', 'VIT Down'} +pathos_short = {'Weapon', 'Ranged', 'Head/Neck', 'Body', 'Hand', 'Earrings/Rings', 'Back/Waist', 'Leg/Foot', 'Support Job', 'Job Abilities', 'Spellcasting', 'MaxHP', 'MaxMP', 'STR', 'DEX', 'AGI', 'MND', 'INT', 'CHR', 'VIT'} +salvage_zones = S{73, 74, 75, 76} -function settings_create() - -- get player's name - player = windower.ffxi.get_player()['name'] - -- set all pathos as needed - for i=1, #pathos_ident do - if pathos_ident[i] ~= nil then - pathos_ident[pathos_ident[i]] = 1 - end - end -end +defaults = {} +defaults.pos = {} +defaults.pos.x = 1000 +defaults.pos.y = 150 +defaults.color = {} +defaults.color.alpha = 200 +defaults.color.red = 200 +defaults.color.green = 200 +defaults.color.blue = 200 +defaults.bg = {} +defaults.bg.alpha = 200 +defaults.bg.red = 30 +defaults.bg.green = 30 +defaults.bg.blue = 30 + +settings = config.load(defaults) +salvage_box2 = texts.new('No pathos', settings) windower.register_event('addon command',function (...) local params = {...}; - if #params < 1 then - return - end - if params[1] then - if params[1]:lower() == "help" then - print('Salvage2 available commands:') - print('s2 help : Shows this help message') - print('s2 pos : Positions the list') - print('s2 [hide/show] : Hides the box') - print('s2 timer [start/stop] : Starts or stops the zone timer') - print('s2 remove : Removes the pathos from the remaining list') - elseif params[1]:lower() == "pos" then - if params[3] then - local posx, posy = tonumber(params[2]), tonumber(params[3]) - windower.text.set_location('salvage_box2', posx, posy) - end - elseif params[1]:lower() == "hide" then - windower.text.set_visibility('salvage_box2', false) - elseif params[1]:lower() == "show" then - windower.text.set_visibility('salvage_box2', true) - elseif params[1]:lower() == "timer" then - if params[2] == "start" then - windower.send_command('timers c Remaining 6000 up') - elseif params[2] == "stop" then - windower.send_command('timers d Remaining') - end - elseif params[1]:lower() == "debug" then - if params[2]:lower() == "start" then - windower.send_command('timers c Remaining 6000 up') - settings_create() - windower.text.set_visibility('salvage_box2', true) - initialize() - elseif params[2]:lower() == "stop" then - windower.send_command('timers d Remaining') - windower.text.set_visibility('salvage_box2', false) - end - elseif params[1]:lower() == "remove" then - for i=1, #pathos_short do - if pathos_short[i]:lower() == params[2]:lower() then - pathos_ident[pathos_ident[i]] = 0 - initialize() - end - end - end - end + if #params < 1 then + return + end + if params[1] then + if params[1]:lower() == "help" then + print('Salvage2 available commands:') + print('s2 help : Shows this help message') + print('s2 pos : Positions the list') + print('s2 [hide/show] : Hides the box') + print('s2 timer [start/stop] : Starts or stops the zone timer') + print('s2 remove : Removes the pathos from the remaining list') + elseif params[1]:lower() == "pos" then + if params[3] then + local posx, posy = tonumber(params[2]), tonumber(params[3]) + windower.text.set_location('salvage_box2', posx, posy) + end + elseif params[1]:lower() == "hide" then + salvage_box2:hide() + elseif params[1]:lower() == "show" then + salvage_box2:show() + elseif params[1]:lower() == "timer" then + if params[2] == "start" then + windower.send_command('timers c Remaining 6000 up') + elseif params[2] == "stop" then + windower.send_command('timers d Remaining') + end + elseif params[1]:lower() == "debug" then + if params[2]:lower() == "start" then + windower.send_command('timers c Remaining 6000 up') + settings_create() + salvage_box2:show() + initialize() + elseif params[2]:lower() == "stop" then + windower.send_command('timers d Remaining') + salvage_box2:hide() + end + elseif params[1]:lower() == "remove" then + for i=1, #pathos_short do + if pathos_short[i]:lower() == params[2]:lower() then + pathos_ident[pathos_ident[i]] = 0 + initialize() + end + end + end + end end) windower.register_event('login', function(name) - player = name + player = name end) -salvage_zones = S{73, 74, 75, 76} +function settings_create() + -- get player's name + player = windower.ffxi.get_player()['name'] + -- set all pathos as needed + for i=1, #pathos_ident do + if pathos_ident[i] ~= nil then + pathos_ident[pathos_ident[i]] = 1 + end + end +end windower.register_event('zone change', function(id) - if salvage_zones:contains(id) then - windower.send_command('timers c Remaining 6000 up') - settings_create() - initialize() - windower.text.set_visibility('salvage_box2', true) - else - windower.send_command('timers d Remaining') - settings_create() - initialize() - windower.text.set_visibility('salvage_box2', false) - end + if salvage_zones:contains(id) then + windower.send_command('timers c Remaining 6000 up') + settings_create() + initialize() + salvage_box2:show() + else + windower.send_command('timers d Remaining') + settings_create() + initialize() + salvage_box2:hide() + end end) windower.register_event('incoming text',function (original, new, color) - - a,b,pathos,name = string.find(original,'..(.*) removed for (%w+)\46') - - if pathos ~= nil then - if name == player then - -- Insert code to remove pathos from list - for i=1, #pathos_ident do - if pathos_ident[i]:lower() == pathos:lower() then - if pathos_ident[pathos_ident[i]] == 1 then - pathos_ident[pathos_ident[i]] = 0 - initialize() - end - end - end - end - return new, color - end + original = original:strip_format() + local pathos, name = original:match('(.*) removed for (%w+)') + if pathos ~= nil then + --print('Pathos found '..pathos) + if name == player then + for i=1, #pathos_ident do + if pathos_ident[i]:lower() == pathos:lower() then + if pathos_ident[pathos_ident[i]] == 1 then + pathos_ident[pathos_ident[i]] = 0 + initialize() + end + end + end + end + return new, color + end end) function initialize() - pathos_remain = (" Pathos Remaining: \n ") - for i=1, #pathos_ident do - if pathos_ident[pathos_ident[i]] == 1 then - item = pathos_short[i] - pathos_remain = (pathos_remain..item..' \n ') - end - end - windower.text.create('salvage_box2') - windower.text.set_bg_color('salvage_box2',200,30,30,30) - windower.text.set_color('salvage_box2',255,200,200,200) - windower.text.set_location('salvage_box2',posx,posy) - windower.text.set_bg_visibility('salvage_box2',1) - windower.text.set_font('salvage_box2','Arial',12) - windower.text.set_text('salvage_box2', pathos_remain) - if pathos_remain == (" Pathos Remaining: \n ") then - windower.text.set_visibility('salvage_box2',false) - end + pathos_remain = (" Pathos Remaining: \n ") + for i=1, #pathos_ident do + if pathos_ident[pathos_ident[i]] == 1 then + item = pathos_short[i] + pathos_remain = (pathos_remain..item..' \n ') + end + end + salvage_box2:text(pathos_remain) + if pathos_remain == (" Pathos Remaining: \n ") then + salvage_box2:hide() + end end windower.register_event('unload',function () - windower.text.delete('salvage_box2') - windower.send_command('timers d Remaining') + windower.text.delete('salvage_box2') + windower.send_command('timers d Remaining') end ) diff --git a/addons/thtracker/thtracker.lua b/addons/thtracker/thtracker.lua index 1a045ccd1..e3be84364 100644 --- a/addons/thtracker/thtracker.lua +++ b/addons/thtracker/thtracker.lua @@ -30,8 +30,8 @@ _addon.author = 'Krizz' _addon.version = 1.1 _addon.commands = {'thtracker', 'th'} -config = require 'config' -texts = require 'texts' +config = require ('config') +texts = require ('texts') require('logger') defaults = {} @@ -75,32 +75,32 @@ windower.register_event('addon command', function(command, ...) end) windower.register_event('incoming text', function(original, new, color) - original = original:strip_format() - local name, count = original:match('Additional effect: Treasure Hunter effectiveness against[%s%a%a%a]- (.*) increases to (%d+).') - - if name and count then - name = name.gsub(name, "the ", "") - mob = name - th:text(' '..name..'\n TH: '..count); - th:show() - end + original = original:strip_format() + local name, count = original:match('Additional effect: Treasure Hunter effectiveness against[%s%a%a%a]- (.*) increases to (%d+).') + + if name and count then + name = name.gsub(name, "the ", "") + mob = name + th:text(' '..name..'\n TH: '..count); + th:show() + end - local deadmob = original:match('%w+ defeats[%s%a%a%a]- (.*).') - - if deadmob then - deadmob = deadmob.gsub(deadmob, "the ", "") - end - - if deadmob == mob then - - th:text('No current mob') - th:hide() - mob = nil - end + local deadmob = original:match('%w+ defeats[%s%a%a%a]- (.*).') + + if deadmob then + deadmob = deadmob.gsub(deadmob, "the ", "") + end + + if deadmob == mob then + + th:text('No current mob') + th:hide() + mob = nil + end end) windower.register_event('zone change', function() - th:text('No current mob') - th:hide() + th:text('No current mob') + th:hide() end) \ No newline at end of file