Working on this today i got perpetuance working. I believe composure too but i didn't wanna change to rdm to find out (sorry). i'll include it and the dependencies for it in this post. First picture to show what it looks like (my part is the one in the center not the upper right hand corner.) Gonna be a lot in this post so i'll keep this short.
Quck Edit: This uses ihm's timers plugin by utilizing custom timers. So if you wish to move them you need to edit plugins/settings/timers.xml. The following is the part you will need to change:
<customX>360</customX>
<customY>350</customY>
addons/bTimers/bTimers.lua
Code:
--[[
Copyright (c) 2013, Ricky Gall 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 the organization 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 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.
]]
require 'tablehelper'
require 'logger'
function event_load()
player = get_player()
buffs = T(get_player()['buffs'])
watchbuffs = {
Haste=180,
Refresh=150,
Protect=1800,
Shell=1800,
Regen=60,
Phalanx=180
} -- only adding a few so you can get the idea
createdTimers = T{}
buffExtension = T{
Perpetuance=2.5,
LightArts=1.8,
Rasa=2.28,
Composure=3
}
extendables = T { 'Regen','Refresh','Blink','Stoneskin','Aquaveil','Haste','Temper','Phalanx' }
extend = T{}
t = 0
end
function event_unload()
for u = 1, #createdTimers do
send_command('timers d "'..createdTimers[u]..'"')
end
end
function event_addon_command(arg1,arg2,arg3)
if arg1 == 'newtimer' then
createTimer(arg2,arg3)
end
end
function event_gain_status(id,name)
if id == 469 then
extend['Perpetuance'] = os.clock()
end
if id == 366 then
extend['Accession'] = os.clock()
end
createTimer(name)
end
function event_lose_status(id,name)
deleteTimer(1,name)
end
function createTimer(name,target)
if watchbuffs[name] ~= nil then
if target == nil then
target = 'Self'
elseif target:lower() == player['name']:lower() then
target = 'Self'
else
target = target
end
for u = 1, #createdTimers do
if createdTimers[u] == name..' ('..target..')' then
send_command('timers d "'..name..' ('..target..')"')
createdTimers:remove(u)
send_command('wait .1;lua c bTimers newtimer '..name..' '..target)
return
end
end
if extendables:contains(name) then
buffs = T(get_player()['buffs'])
timer = tonumber(watchbuffs[name]) - 5
if extend ~= nil then
e = os.clock()-60
if extend['Perpetuance'] ~= nil then
if e < extend['Perpetuance'] then
if target == 'self' then
t = t + 1
if math.even(t) then
extend['Perpetuance'] = nil
t = 0
end
else
extend['Perpetuance'] = nil
end
if name == 'Regen' then
timer = tonumber(watchbuffs[name]) * buffExtension['LightArts'] * buffExtension['Perpetuance'] - 5
else
timer = tonumber(watchbuffs[name]) * buffExtension['Perpetuance'] - 5
end
end
end
elseif buffs:contains(377) then
if name == 'Regen' then
if player['main_job_id'] == 20 then
timer = tonumber(watchbuffs[name]) * buffExtension['Rasa'] - 5
end
end
elseif buffs:contains(358) then
if name == 'Regen' then
if player['main_job_id'] == 20 then
timer = tonumber(watchbuffs[name]) * buffExtension['LightArts'] - 5
end
end
elseif buffs:contains(419) then
timer = tonumber(watchbuffs[name]) * buffExtension['Composure'] - 5
end
else
timer = tonumber(watchbuffs[name]) - 5
end
send_command('timers c "'..name..' ('..target..')" '..timer..' down '..name)
createdTimers[#createdTimers+1] = name..' ('..target..')'
end
end
function deleteTimer(mode,effect,target)
if mode == 1 then -- This mode is for when a buff drops off you (the gain buff triggers faster then the chat message
for u = 1, #createdTimers do
if createdTimers[u] == effect..' (Self)' then
send_command('timers d "'..effect..' (Self)"')
createdTimers:remove(u)
end
end
elseif mode == 2 then
for u = 1, #createdTimers do
if createdTimers[u] == effect..' ('..target..')' then
send_command('timers d "'..effect..' ('..target..')"')
createdTimers:remove(u)
end
end
else
return
end
end
function event_incoming_text(old,new,color)
if T{64,56,191}:contains(color) then
a,b,caster,caster_spell,target,target_effect = string.find(old,'(%w+) casts ([%w%s]+)..(%w+) gains the effect of (%w+).')
c,d,tWear,eWear = string.find(old,'(%w+)\'s (%w+) effect wears off.')
e,f,tar2,eff2 = string.find(old,'(%w+) gains the effect of (%w+).')
if a ~= nil then
if caster:lower() == player['name']:lower() then
createTimer(target_effect,target)
elseif target:lower() == player['name']:lower() then
createTimer(target_effect,target)
end
elseif c ~= nil then
deleteTimer(2,eWear,tWear)
elseif e ~= nil then
if tar2:lower() == player['name']:lower() then
createTimer(eff2,tar2)
end
end
end
return new, color -- must be here or crashes will ensue
end
addons/libs/tablehelper.lua --Arcon wrote this
Code:
--[[
Lua supports two different kinds of tables, numerically indexed tables, and string-indexed tables. String-indexed tables will return 0 as their length with the # operator, regardless of their actual element count. They can be iterated over with the pairs(t) function. Numerically indexed tables can be iterated over with ipairs(t) function. Since they use different functions for iteration, a check if #t == 0 will often be found, and serves to distinguish between the different kinds of tables.
]]
require 'mathhelper'
_table_meta = {__index=table}
-- Constructor for T-tables.
-- t = T{...} for explicit declaration.
-- t = T(regular_table) to cast to a T-table.
function T(t)
-- Sets the metatable of T to _table_meta, which specifies the table namespace for all T-tables.
-- This makes every function that tables have also available for T-tables.
return setmetatable(t, _table_meta)
end
-- Returns true if searchval is in t.
function table.contains(t, searchval)
for key, val in pairs(t) do
if val == searchval then
return true
end
end
return false
end
-- Appends to the end of an array table.
function table.append(t, val)
t[#t+1] = val
return t
end
-- Returns if the key searchkey is in t.
function table.containskey(t, searchkey)
return t[searchkey] ~= nil
end
-- Returns a partial table sliced from t, equivalent to t[x:y] in certain languages.
-- Negative indices will be used to access the table from the other end.
function table.slice(t, from, to)
from = from or 1
if from < 0 then
-- Modulo the negative index, to get it back into range.
from = from%#t+1
end
to = to or #t
if to < 0 then
-- Modulo the negative index, to get it back into range.
to = (to-1)%#t+1
end
-- Copy relevant elements into a blank T-table.
local res = T{}
for i = from, to do
res[#res+1] = t[i]
end
return res
end
-- Applies function fn to all elements of the table and returns the resulting table.
function table.map(t, fn)
local res = T{}
for key, val in pairs(t) do
-- Evaluate fn with the element and store it.
res[key] = fn(val)
end
return res
end
-- Returns a table with all elements from t that satisfy the condition fn.
function table.filter(t, fn)
local res = T{}
for key, val in pairs(t) do
-- Only copy if fn(val) evaluates to true
if fn(val) then
res[key] = val
end
end
return res
end
-- Returns a table with all elements from t whose keys satisfy the condition fn.
function table.filterkey(t, fn)
local res = T{}
for key, val in pairs(t) do
-- Only copy if fn(key) evaluates to true
if fn(key) then
res[key] = val
end
end
return res
end
-- Returns the result of applying the function fn to the first two elements of t, then again on the result and the next element from t, until all elements are accumulated.
-- init is an optional initial value to be used. If provided, init and t[1] will be compared first, otherwise t[1] and t[2].
function table.reduce(t, fn, init)
t = T(t)
-- Return the initial argument if table is empty
if t:isempty() then
return init
end
-- Set the accumulator variable to the init value (which can be nil as well)
acc = init
for key, val in pairs(t) do
-- If the accumulator is nil, which can only happen on the first iteration and if no initial value was provided, set acc to the first value val.
if acc == nil then
acc = val
-- If not, which will hold true for all subsequent values, apply the funtion to the accumulated value and the next table value and store the result.
else
acc = fn(acc, val)
end
end
return acc
end
-- Return true if any element of t satisfies the condition fn.
function table.any(t, fn)
for key, val in pairs(t) do
if(fn(val) == true) then
return true
end
end
return false
end
-- Return true if all elements of t satisfy the condition fn.
function table.all(t, fn)
for key, val in pairs(t) do
if(fn(val) ~= true) then
return false
end
end
return true
end
-- Concatenates all elements with a whitespace in between.
function table.sconcat(t)
return table.concat(t, ' ')
end
-- Sum up all elements of a table.
function table.sum(t)
return table.reduce(t, math.sum, 0)
end
-- Multiply all elements of a table.
function table.mult(t)
return table.reduce(t, math.mult, 1)
end
-- Check if table is empty.
function table.isempty(t)
return next(t) == nil
end
addons/libs/logger.lua --Arcon wrote this
Code:
--[[
This library provides a set of functions to aid in debugging.
]]
require 'tablehelper'
-- Prints the arguments provided to the FFXI chatlog, in the same color used for Campaign/Bastion alerts and Kupower messages. Can be changed below.
-- Converts any kind of object type to a string, so it's type-safe.
-- Concatenates all provided arguments with whitespaces.
function log(...)
local args = T{...}
local strtable = T(args:map(tostring))
add_to_chat(160, strtable:sconcat())
end
-- Prints a table in explicit Lua syntax: {...}
function table.print(t)
-- Convert all values to strings, to make sure everything is ready for string concatenation.
t = T(t):map(tostring)
tstr = ''
-- Iterate over table.
for key, val in pairs(t) do
-- Append to the string.
tstr = tstr..key..'='..val
-- Add commata, unless it's the last value.
if next(t, key) ~= nil then
tstr = tstr..', '
end
end
-- Output the result, enclosed in braces.
log('{'..tstr..'}')
end
-- Prints a table vertically in explicit Lua syntax, with every element in its own line:
--- {
--- ...
--- }
function table.vprint(t)
-- Convert all values to strings, to make sure everything is ready for string concatenation.
t = T(t):map(tostring)
log('{')
for key, val in pairs(t) do
-- Output one line with indent.
log(' '..key..'='..val)
end
log('}')
end
addons/libs/mathhelper.lua -- Arcon made this one too
Code:
--[[
A few math helper functions.
]]
-- Rounds to prec decimal digits. Accepts negative numbers for precision.
function math.round(num, prec)
local mult = 10^(prec or 0)
return math.floor(num * mult + 0.5) / mult
end
-- Returns the sign of num, -1 for a negative number, +1 for a positive number and 0 for 0.
function math.sgn(num)
return num/math.abs(num)
end
-- Returns true, if num is even, false otherwise.
function math.even(num)
return num%2 == 0
end
-- Returns true, if num is odd, false otherwise.
function math.odd(num)
return num%2 == 1
end
-- Adds two numbers.
function math.sum(val1, val2)
return val1+val2
end
-- Multiplies two numbers.
function math.mult(val1, val2)
return val1*val2
end