-
Notifications
You must be signed in to change notification settings - Fork 424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add finalAlert (new Addon) #2191
base: dev
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# finalAlert | ||
Displays Final Fantasy 7 style alerts when enemies use skills or magic. Allows you to emphasize skills by name (useful for interrupts). | ||
|
||
## Commands: | ||
|
||
### Test window | ||
``` | ||
//fa test ws | ||
``` | ||
Shows a test alert (accepts 'ws' for TP moves, 'ma' for magic, 'int' for interrupts). | ||
|
||
### Emphasize a WS or spell | ||
``` | ||
//fa emphasize Firaga VI | ||
``` | ||
Toggles emphasis for "Firaga VI" (plays a different sound). | ||
|
||
### Change position | ||
``` | ||
//fa pos 960 200 | ||
``` | ||
Moves the display to 960 X (horizontal) and 200 Y (vertical). | ||
|
||
### Change size | ||
``` | ||
//fa size small | ||
``` | ||
Sets the display size to small (accepts 'regular' and 'small'). | ||
|
||
### Change duration | ||
``` | ||
//fa duration 5 | ||
``` | ||
Sets the display duration to 5 seconds. | ||
|
||
### Turn sounds on/off | ||
``` | ||
//fa sounds off | ||
``` | ||
Turns off sounds except for emphasized abilities (accepts 'on' and 'off'). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
Copyright © 2022, Godchain | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The entire copyright notice is not wrapped in comments. This way, the addon won't load. Wrap it in |
||
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 <addon name> nor the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the addon name here :) |
||
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 Godchain 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 = "finalAlert" | ||
_addon.author = "Godchain (Asura)" | ||
_addon.version = "1.3" | ||
_addon.commands = {"finalAlert", "fa"} | ||
|
||
config = require("config") | ||
texts = require("texts") | ||
res = require("resources") | ||
|
||
caption = texts.new({}) | ||
|
||
background_ability = "background_ability" | ||
background_magic = "background_magic" | ||
background_interrupt = "background_interrupt" | ||
background_emphasize = "background_emphasize" | ||
|
||
-- Timing | ||
showing = false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This variable can be removed entirely and be replaced with |
||
last_trigger = 0 | ||
|
||
-- IDs | ||
weapon_skill_category = 7 | ||
magic_category = 8 | ||
interrupt_id = 28787 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How was this determined? If this is a zone resource, it might be different for every zone, and subject to changes after updates. |
||
|
||
defaults = {} | ||
defaults.x_position = windower.get_windower_settings().x_res / 2 | ||
defaults.y_position = 100 | ||
defaults.background_size = "regular" | ||
defaults.emphasize = S {} | ||
defaults.trigger_duration = 3 | ||
defaults.sounds = "on" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would use boolean values instead of |
||
|
||
settings = config.load(defaults) | ||
|
||
windower.register_event( | ||
"load", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't couple this to the |
||
function() | ||
create_backgrounds(settings.x_position - 250, settings.y_position) | ||
caption:bg_visible(false) | ||
caption:bold(true) | ||
end | ||
) | ||
|
||
windower.register_event( | ||
"postrender", | ||
function() | ||
if showing then | ||
local x, y = caption:extents() | ||
local x_offset = settings.x_position - x / 2 | ||
local y_offset = | ||
settings.background_size == "regular" and settings.y_position + 10 or settings.y_position + 3 | ||
caption:pos(x_offset, y_offset) | ||
if os.time() - last_trigger > settings.trigger_duration then | ||
hide_caption() | ||
end | ||
else | ||
end | ||
end | ||
) | ||
|
||
windower.register_event( | ||
"addon command", | ||
function(cmd, ...) | ||
local args = L {...} | ||
|
||
if not cmd or cmd == "help" then | ||
local bullet = windower.to_shift_jis("» ") | ||
print("=== Usage Examples ===") | ||
print(bullet .. "//fa test ws") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above about the |
||
print("Shows a test alert (accepts 'ws' for TP moves, 'ma' for magic, 'int' for interrupts).") | ||
print(bullet .. "//fa emphasize Firaga VI") | ||
print("Toggles emphasis for "Firaga VI" (plays a different sound).") | ||
print(bullet .. "//fa pos 960 200") | ||
print("Moves the display to 960 X (horizontal) and 200 Y (vertical).") | ||
print(bullet .. "//fa size small") | ||
print("Sets the display size to small (accepts 'regular' and 'small').") | ||
print(bullet .. "//fa duration 5") | ||
print("Sets the display duration to 5 seconds.") | ||
print(bullet .. "//fa sounds off") | ||
print("Turns off sounds except for emphasized abilities (accepts 'on' and 'off').") | ||
elseif cmd == "test" then | ||
if args[1] == "ws" then | ||
show_caption("Self-Destruct", "ws") | ||
elseif args[1] == "ma" then | ||
show_caption("Tornado II", "ma") | ||
elseif args[1] == "int" then | ||
show_caption("Interrupted!", "int") | ||
else | ||
print('Please specify "ws", "ma" or "int".') | ||
end | ||
elseif cmd == "emphasize" then | ||
local estring = args:concat(" "):gsub("%s+", ""):lower() | ||
local verb = settings.emphasize:contains(estring) and "Removed" or "Added" | ||
print("Emphasize: " .. verb .. ' "' .. args:concat(" ") .. '".') | ||
|
||
if settings.emphasize:contains(estring) then | ||
settings.emphasize:remove(estring) | ||
else | ||
settings.emphasize:add(estring) | ||
end | ||
|
||
settings:save() | ||
elseif cmd == "pos" then | ||
local x = tonumber(args[1]) | ||
local y = tonumber(args[2]) | ||
|
||
if type(x) == "number" and type(y) == "number" then | ||
settings.x_position = x | ||
settings.y_position = y | ||
settings:save() | ||
refresh_backgrounds() | ||
print("Moved display to: " .. args[1] .. ", " .. args[2]) | ||
else | ||
print("Please specify x and y coordinates.") | ||
end | ||
elseif cmd == "size" then | ||
local size = args[1] | ||
|
||
if size == "small" or size == "regular" then | ||
settings.background_size = size | ||
settings:save() | ||
refresh_backgrounds() | ||
print("Display size set to " .. size .. ".") | ||
else | ||
print('Please specify "small" or "regular" for the size.') | ||
end | ||
elseif cmd == "duration" then | ||
local duration = tonumber(args[1]) | ||
|
||
if type(duration) == "number" and duration > 0 then | ||
settings.trigger_duration = duration | ||
print("Display duration set to " .. duration .. " secs.") | ||
else | ||
print("Please specify a positive number.") | ||
end | ||
elseif cmd == "sounds" then | ||
local state = args[1] | ||
|
||
if state == "on" or state == "off" then | ||
settings.sounds = state | ||
settings:save() | ||
print("Sounds have been turned " .. state .. ".") | ||
else | ||
print('Please specify "on" or "off" for sounds.') | ||
end | ||
else | ||
print("Unrecognized command.") | ||
end | ||
end | ||
) | ||
|
||
windower.register_event( | ||
"action", | ||
function(act) | ||
local target | ||
local t = windower.ffxi.get_mob_by_target("t") | ||
local bt = windower.ffxi.get_mob_by_target("bt") | ||
|
||
if t and t.is_npc and not t.in_party and not t.in_alliance then | ||
target = t.id | ||
elseif bt then | ||
target = bt.id | ||
else | ||
return | ||
end | ||
|
||
if act.category == weapon_skill_category and act.actor_id == target then | ||
local skill_name = | ||
res.monster_abilities[act.targets[1].actions[1].param] and | ||
res.monster_abilities[act.targets[1].actions[1].param].name or | ||
"???" | ||
|
||
if act.param == interrupt_id then | ||
skill_name = "Interrupted!" | ||
show_caption(skill_name, "int") | ||
else | ||
show_caption(skill_name, "ws") | ||
end | ||
elseif act.category == magic_category and act.actor_id == target then | ||
local spell_name = | ||
res.spells[act.targets[1].actions[1].param] and res.spells[act.targets[1].actions[1].param].name or | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It bothers me a bit that this is broken up into three lines in the WS block, but only two lines in the magic block :\ I'm starting to get the feeling you're using a fixed width and wrap lines exceeding it, in which case I'd vote to just leave them be. Fixed-width line wraps are never a good idea, despite what the Python style guide says :| |
||
"???" | ||
|
||
if act.param == interrupt_id then | ||
spell_name = "Interrupted!" | ||
show_caption(spell_name, "int") | ||
else | ||
show_caption(spell_name, "ma") | ||
end | ||
end | ||
end | ||
) | ||
|
||
function refresh_backgrounds() | ||
create_backgrounds(settings.x_position - 250, settings.y_position) | ||
end | ||
|
||
function create_backgrounds(x, y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function uses the |
||
windower.prim.create(background_ability) | ||
windower.prim.set_fit_to_texture(background_ability, true) | ||
windower.prim.set_texture( | ||
background_ability, | ||
windower.addon_path .. "images/" .. settings.background_size .. "/background_ability.png" | ||
) | ||
windower.prim.set_position(background_ability, x, y) | ||
windower.prim.set_visibility(background_ability, false) | ||
|
||
windower.prim.create(background_magic) | ||
windower.prim.set_fit_to_texture(background_magic, true) | ||
windower.prim.set_texture( | ||
background_magic, | ||
windower.addon_path .. "images/" .. settings.background_size .. "/background_magic.png" | ||
) | ||
windower.prim.set_position(background_magic, x, y) | ||
windower.prim.set_visibility(background_magic, false) | ||
|
||
windower.prim.create(background_interrupt) | ||
windower.prim.set_fit_to_texture(background_interrupt, true) | ||
windower.prim.set_texture( | ||
background_interrupt, | ||
windower.addon_path .. "images/" .. settings.background_size .. "/background_interrupt.png" | ||
) | ||
windower.prim.set_position(background_interrupt, x, y) | ||
windower.prim.set_visibility(background_interrupt, false) | ||
|
||
windower.prim.create(background_emphasize) | ||
windower.prim.set_fit_to_texture(background_emphasize, true) | ||
windower.prim.set_texture( | ||
background_emphasize, | ||
windower.addon_path .. "images/" .. settings.background_size .. "/background_emphasize.png" | ||
) | ||
windower.prim.set_position(background_emphasize, x, y) | ||
windower.prim.set_visibility(background_emphasize, false) | ||
end | ||
|
||
function show_caption(text, type) | ||
local event_type | ||
|
||
hide_caption() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels weird that you hide it only to show it again. If this is about hiding the currently active box, I'd either maintain which box is currently active in its own variable, or extract the box-hiding code into a new function and call that from both here and |
||
showing = true | ||
caption:text(text) | ||
caption:show() | ||
|
||
if (type == "ws") then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for the parantheses here, as well as in the following |
||
event_type = "ability" | ||
windower.prim.set_visibility(background_ability, true) | ||
elseif (type == "ma") then | ||
event_type = "magic" | ||
windower.prim.set_visibility(background_magic, true) | ||
elseif (type == "int") then | ||
event_type = "interrupt" | ||
windower.prim.set_visibility(background_interrupt, true) | ||
end | ||
|
||
if (settings.emphasize:contains(text:gsub("%s+", ""):lower())) then | ||
windower.play_sound(windower.addon_path .. "sounds/emphasize.wav") | ||
windower.prim.set_visibility(background_emphasize, true) | ||
elseif (settings.sounds == "on") then | ||
windower.play_sound(windower.addon_path .. "sounds/" .. event_type .. "_alert.wav") | ||
end | ||
|
||
last_trigger = os.time() | ||
end | ||
|
||
function hide_caption() | ||
showing = false | ||
caption:hide() | ||
windower.prim.set_visibility(background_ability, false) | ||
windower.prim.set_visibility(background_magic, false) | ||
windower.prim.set_visibility(background_interrupt, false) | ||
windower.prim.set_visibility(background_emphasize, false) | ||
end | ||
|
||
function print(str) | ||
windower.add_to_chat(207, str) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly not sure if this command should be even present in the finished addon, feel even less enthused about it being documented. But up to you. It seems like a debug command and those should not be user facing imo.