|
Lullaby Gearswap Diddy
By Sammeh 2017-02-12 22:37:45
So for those of us who have had to be bard in mass-lullaby spam (Reisin T4's, bcnms, etc) it's hard to keep track of slept mobs.
I think I have a solve.
I wrote into my gear-swap a table to track mobs that I've cast lullaby on (either by target or associated by AOE (horde lullaby)).
I then keep track of each of those mobs in memory to see if they start doing anything and/or moving (or heaven forbid timer actually runs out). If so I know they wake up.
If you target the mob it highlights it - so you can see which mob is going to wake up when.
Made a design decision to keep them in table until the mob dies and/or you zone.
A few things:
1) This could easily be modified for any debuff, however only 'sleep' or slept mobs are really easily identifiable if that debuff is lost. Everything else can be erased, dispelled, overridden, etc that you wouldn't know.
2) This won't keep track if someone overwrites your lullaby with another spell. (Your lullaby is overwritten w/ a Sleep II or Repose)
Anyway - small video showing it off. Hopefully not too buggy =)
YouTube Video Placeholder
Updating a few lines down in thread Please see this data:
Ok.
For those anyone using this - I've simplified it and made it its own lua (sort of). I also updated it with every form of REM (lvl 75-99AG) and a handful of additional harps and other gearsets. I also check for merits / job points more global - "assume your'e maxed" mentality.
https://github.com/SammehFFXI/FFXIAddons/blob/master/GearSwap/lullaby.lua
In your gearswap do a:
Code
1
include('lullaby.lua')
in your get_sets section.
The only thing 'gearswap' this uses however is the custom commands so if you already use your own, this may overwrite / replace, or yours may do the same to mine - not sure processing order of Gearswap w/ includes and duplicate functions). I left it gearswap based however, because of ease of use of what I believe to be most users in this forum.
Commands:
//gs c show ---- Shows the txt box
//gs c hide ---- Hides the txt box (default)
//gs c reset ---- Resets the mob list (this is issued on zone. But if you sleep and run away while staying in zone it will hang out in memory forever).
If you prefer this to be its own AddOn. Download it here:
https://github.com/SammehFFXI/FFXIAddons/blob/master/Sleeper/sleeper.lua
Asura.Chiaia
VIP
Server: Asura
Game: FFXI
Posts: 1652
By Asura.Chiaia 2017-02-13 00:40:48
I didn't test this but something like this would take care of checking JP count automatically and into each effect. Code
--[[
Custom TXT Box for Lullaby Duration
]]
--texts = require('texts')
res = require 'resources'
packets = require('packets')
lullaby_txt = {}
lullaby_txt.pos = {}
lullaby_txt.pos.x = -180
lullaby_txt.pos.y = 45
lullaby_txt.text = {}
lullaby_txt.text.font = 'Arial'
lullaby_txt.text.size = 10
lullaby_txt.flags = {}
lullaby_txt.flags.right = true
lullaby_box = texts.new('${value}', lullaby_txt)
local lullaby_list = {}
function reset_lullaby()
for i,v in pairs(lullaby_list) do
lullaby_list[i] = nil
end
end
function new_lullaby(target, duration)
local lullabytime = os.clock()
local mob = windower.ffxi.get_mob_by_id(target)
lullaby_list[target] = {start=lullabytime,lullabyduration=duration,x=mob.x,y=mob.y,z=mob.z}
end
function count_lullaby_list()
local num = 0
for i,v in pairs(lullaby_list) do
num = num +1
end
return num
end
windower.raw_register_event('prerender', function()
local t = windower.ffxi.get_mob_by_index(windower.ffxi.get_player().target_index or 0)
local mob_id,value
if lullaby_list and count_lullaby_list() > 0 then
local lullaby_txtbox = 'Sleep List: '..count_lullaby_list()
for mob_id,value in pairs(lullaby_list) do
local mob = windower.ffxi.get_mob_by_id(mob_id)
if mob then
if mob.x and value.x then
x_delta = mob.x - value.x
else
x_delta = 0
end
if mob.y and value.y then
y_delta = mob.y - value.y
else
y_delta = 0
end
if x_delta > 5 or x_delta < -5 or y_delta > 5 or y_delta < -5 then
lullaby_list[mob_id].lullabyduration = 0
end
local start_time = lullaby_list[mob_id].start
local duration = lullaby_list[mob_id].lullabyduration
local now = os.clock()
local remaining_time = string.format("%.1f", duration - (now - start_time))
if mob.status == 1 or mob.status == 0 then
if t then
if t.id == mob_id then
-- Print the txt in Green!
if duration - (now - start_time) < 0 then
lullaby_txtbox = lullaby_txtbox.."\n\\cs(0,255,0)Mob: "..mob.name.." Awake!\\cs(255,255,255)"
else
lullaby_txtbox = lullaby_txtbox.."\n\\cs(0,255,0)Mob: "..mob.name.." Is Asleep! Remaining:"..remaining_time.."\\cs(255,255,255)"
end
else
if duration - (now - start_time) < 0 then
lullaby_txtbox = lullaby_txtbox.."\nMob: "..mob.name.." Awake!"
else
lullaby_txtbox = lullaby_txtbox.."\nMob: "..mob.name.." Is Asleep! Remaining:"..remaining_time
end
end
else
if duration - (now - start_time) < 0 then
lullaby_txtbox = lullaby_txtbox.."\nMob: "..mob.name.." Awake!"
else
lullaby_txtbox = lullaby_txtbox.."\nMob: "..mob.name.." Is Asleep! Remaining:"..remaining_time
end
end
else
lullaby_list[mob_id] = nil
end
end
end
lullaby_box.value = lullaby_txtbox
if state.LullabyList.value == "On" then
lullaby_box:visible(true)
else
lullaby_box:visible(false)
end
else
lullaby_box:visible(false)
end
end)
last_spell = ''
lullaby_spell_ids = S{376, 377, 463, 471}
windower.raw_register_event('incoming chunk', function(id,original,modified,injected,blocked)
local self = windower.ffxi.get_player()
if id == 0x028 then
local packet = packets.parse('incoming', original)
local now = os.clock()
if packet['Category'] == 8 and packet.Actor == self.id then
last_spell = packet['Target 1 Action 1 Param']
end
if packet['Category'] == 4 and packet.Actor == self.id and lullaby_spell_ids:contains(last_spell) then
local numtargets = packet['Target Count']
local count = 0
if packet.Actor == self.id then
while count < numtargets do
count = count + 1
local target_id = packet['Target '..count..' ID']
local spell_duration = calculate_duration_raw(last_spell)
local message = packet['Target '..count..' Action 1 Message']
--print("Target ID:",target_id,message)
if message == 270 or message == 236 then
new_lullaby(target_id, spell_duration)
end
end
end
end
if lullaby_list[packet.Actor] and ((now - lullaby_list[packet.Actor].start) > 5) then
--lullaby_list[packet.Actor] = nil
lullaby_list[packet.Actor] = {start=lullaby_list[packet.Actor].start,lullabyduration=0}
end
end
end)
windower.raw_register_event('zone change',function()
reset_lullaby()
end)
function calculate_duration_raw(spell_id)
local jobpointsspent = 0
for k, v in pairs(player.job_points.brd) do
jobpointsspent = jobpointsspent + (v^2 + v)/2
end
spell = res.spells[spell_id]
local mult = 1
if player.equipment.range == 'Daurdabla' then mult = mult + 0.3 end -- change to 0.25 with 90 Daur
if player.equipment.range == "Gjallarhorn" then mult = mult + 0.4 end -- change to 0.3 with 95 Gjall
if player.equipment.range == "Marsyas" then mult = mult + 0.5 end --
if player.equipment.main == "Carnwenhan" then mult = mult + 0.5 end -- 0.1 for 75, 0.4 for 95, 0.5 for 99/119
if player.equipment.main == "Legato Dagger" then mult = mult + 0.05 end
if player.equipment.main == "Kali" then mult = mult + 0.05 end
if player.equipment.sub == "Legato Dagger" then mult = mult + 0.05 end
if player.equipment.neck == "Aoidos' Matinee" then mult = mult + 0.1 end
if player.equipment.neck == "Moonbow Whistle" then mult = mult + 0.2 end
if player.equipment.body == "Fili Hongreline +1" then mult = mult + 0.12 end
if player.equipment.legs == "Inyanga Shalwar +1" then mult = mult + 0.15 end
if player.equipment.feet == "Brioso Slippers" then mult = mult + 0.1 end
if player.equipment.feet == "Brioso Slippers +1" then mult = mult + 0.11 end
if player.equipment.feet == "Brioso Slippers +2" then mult = mult + 0.13 end
if player.equipment.hands == 'Brioso Cuffs +1' then mult = mult + 0.1 end
if jobpointsspent >= 1200 then
mult = mult + 0.05
end
if buffactive.Troubadour then
mult = mult*2
end
if spell.en == "Foe Lullaby II" or spell.en == "Horde Lullaby II" then
base = 60
elseif spell.en == "Foe Lullaby" or spell.en == "Horde Lullaby" then
base = 30
end
totalDuration = math.floor(mult*base)
if jobpointsspent >= 1 then
local player = windower.ffxi.get_player()
-- add_to_chat(8,'Adding ' ..player.job_points.brd.lullaby_duration.. ' seconds to Timer for Lullaby Job Points')
totalDuration = totalDuration + player.job_points.brd.lullaby_duration
if buffactive['Clarion Call'] then
-- add_to_chat(8,'Adding '..player.job_points.brd.clarion_call_effect * 2.. ' seconds to Timer for Clarion Call Job Points')
totalDuration = totalDuration + (player.job_points.brd.clarion_call_effect * 2)
end
if buffactive['Tenuto'] then
-- add_to_chat(8,'Adding '..player.job_points.brd.tenuto_effect..' seconds to Timer for Tenuto Job Points')
totalDuration = totalDuration + player.job_points.brd.tenuto_effect
end
if buffactive['Marcato'] then
-- add_to_chat(8,'Adding '..player.job_points.brd.marcato_effect..' seconds to Timer for Marcato Job Points')
totalDuration = totalDuration + player.job_points.brd.marcato_effect
end
end
return totalDuration
end
[+]
By Quendi210 2017-02-13 09:12:13
The extra 20 seconds for Tenuto aren't applied to anyone but yourself. The extra 20 seconds for Marcato won't apply if you have Soul Voice. I think this was mentioned in the guide thread but it would be good to code it that way too.
By Sammeh 2017-02-13 09:31:46
The extra 20 seconds for Marcato won't apply if you have Soul Voice. I think this was mentioned in the guide thread but it would be good to code it that way too.
Interesting and thanks. Could be fixed by a simple if/then statement. I hate to say I get super lazy on code and at this stage see it as behavioral - I'd never Marcto for example if I had Soul Voice so the likelihood of me fixing it is slim haha.
And admittedly - most of the duration function was copy/pasted from other parts in my gearswap that I had to modify a good amount because it relied on the default variable map of pre/mid/aftercast called 'spell'.
-Sam
Leviathan.Vow
Server: Leviathan
Game: FFXI
Posts: 125
By Leviathan.Vow 2017-02-13 18:46:20
I think this is pretty cool. Since you're parsing the action packet anyway, you could move it outside of GearSwap and make it a standalone addon. Unless I overlooked something, the only GearSwap-specific tables used are the player.equipment table and buffactive table, which can be reconstructed with windower's API.
A few quick thoughts (feel free to ignore):
It might not be the case anymore, but I'm pretty sure using packets.parse on the action packet was initially very inefficient. Using the actions library would be more efficient in that case.
I've run into a few cases where Luacore got hammered by redrawing large text objects frequently. It may be beneficial to break the text object up into smaller objects.
Always search by index if it's available rather than by id. The windower.ffxi.get_mob_by_<value> functions all have to iterate over the mob tables to match the value, whereas windower.ffxi.get_mob_by_index is a quick lookup.
By Sammeh 2017-02-13 18:54:53
I think this is pretty cool. Since you're parsing the action packet anyway, you could move it outside of GearSwap and make it a standalone addon. Unless I overlooked something, the only GearSwap-specific information used is the player.equipment table, which is available in windower's API as well.
It might not be the case anymore, but I'm pretty sure using packet.parse on the action packet was initially very inefficient. Using the actions library would be more efficient in that case.
I've run into a few cases where Luacore got hammered by redrawing large text objects frequently. It may be beneficial to break the text object up into smaller objects.
Performance problems may have been from before I started coding, and/or I have a PC fast enough to handle it. I'm definitely going to say my code is probably very inefficient.
If I post this as a separate LUA - people here on ffxiah tend to freak. Gearswap is "sanctioned" or at minimum 'accepted' and as long as people can copy/paste it in there, they're far more likely to adopt it/use it/play with it.
This is the first time I've done this much work into gearswap - normally I'd put it into a separate LUA.
By knoxvegas 2017-02-14 16:03:50
Thxz man i think its pretty awesome what you have made here
Fenrir.Snaps
Server: Fenrir
Game: FFXI
Posts: 1139
By Fenrir.Snaps 2017-02-14 16:28:51
I agree this should be made into an addon and not a gearswap include and also adding more gear (differing versions of Carn, etc.) You could examine each slot and check against a multiplier table ([gear item id] = multiplier) for that slot instead of using the name.
By Sammeh 2017-02-14 16:54:03
Can you guys help me understand why it should be something separate? It would add no extra functionality and my oppinion, no extra performance. Is it that you don't use gearswap and simply want timers based on in-game equipsets?
I don't mind - wouldn't take 30 min as basically it's a copy/paste and simply set a variables for the gear (windower.ffxi.get_items).
I'm just not seeing the value ?
-Sam
Fenrir.Snaps
Server: Fenrir
Game: FFXI
Posts: 1139
By Fenrir.Snaps 2017-02-14 17:24:52
I mostly just disagree with baking too many things into gearswap. In this case you're probably only going to want it on BRD and will probably want it every time you play BRD, so I see no problem with it. Also adding Sleep/Repose support might be nice (in which case you would want it as a gearswap include)
By Sammeh 2017-02-23 19:46:50
Apparently I had some calculations wrong on the Lullaby when Trob was in effect.
A recognition of this:
Precast Bihu Justaucorps gives 4 seconds per-Merit. However only has to be equipped when the JA goes off - not in pre/midcast of the spell.
Because of this (unless logging the outbound packet and setting a variable for later use which can be done but adds a ton of complexity where its pretty guaranteed that it will work as precast for JA's should be 100%) I added a static '20 seconds' for if buffactive Troubadour. Also Realized that Clarion Call bonus is doubled to 80 seconds when Trouboudour is active.
Here should be the new formula:
Code
function calculate_duration_raw(spell_id)
spell = res.spells[spell_id]
local mult = 1
if player.equipment.range == 'Daurdabla' then mult = mult + 0.3 end -- change to 0.25 with 90 Daur
if player.equipment.range == "Gjallarhorn" then mult = mult + 0.4 end -- change to 0.3 with 95 Gjall
if player.equipment.range == "Marsyas" then mult = mult + 0.5 end --
if player.equipment.main == "Carnwenhan" then mult = mult + 0.5 end -- 0.1 for 75, 0.4 for 95, 0.5 for 99/119
if player.equipment.main == "Legato Dagger" then mult = mult + 0.05 end
if player.equipment.main == "Kali" then mult = mult + 0.05 end
if player.equipment.sub == "Legato Dagger" then mult = mult + 0.05 end
if player.equipment.neck == "Aoidos' Matinee" then mult = mult + 0.1 end
if player.equipment.neck == "Moonbow Whistle" then mult = mult + 0.2 end
if player.equipment.body == "Fili Hongreline +1" then mult = mult + 0.12 end
if player.equipment.legs == "Inyanga Shalwar +1" then mult = mult + 0.15 end
if player.equipment.feet == "Brioso Slippers" then mult = mult + 0.1 end
if player.equipment.feet == "Brioso Slippers +1" then mult = mult + 0.11 end
if player.equipment.feet == "Brioso Slippers +2" then mult = mult + 0.13 end
if player.equipment.hands == 'Brioso Cuffs +1' then mult = mult + 0.1 end
if player.equipment.hands == 'Brioso Cuffs +2' then mult = mult + 0.1 end
if MaxJobPoints == 1 then
mult = mult + 0.05
end
if buffactive.Troubadour then
mult = mult*2
end
if spell.en == "Foe Lullaby II" or spell.en == "Horde Lullaby II" then
base = 60
elseif spell.en == "Foe Lullaby" or spell.en == "Horde Lullaby" then
base = 30
end
totalDuration = math.floor(mult*base)
if MaxJobPoints == 1 then
-- add_to_chat(8,'Adding 20 seconds to Timer for Lullaby Job Points')
totalDuration = totalDuration + 20
if buffactive['Clarion Call'] then
if buffactive.Troubadour then
-- Doubles Clarion Call Gain for 80 seconds
totalDuration = totalDuration + 80
else
-- add_to_chat(8,'Adding 20 seconds to Timer for Clarion Call Job Points')
totalDuration = totalDuration + 40
end
end
if buffactive['Tenuto'] then
-- add_to_chat(8,'Adding 20 seconds to Timer for Tenuto Job Points')
totalDuration = totalDuration + 20
end
if buffactive['Marcato'] then
-- add_to_chat(8,'Adding 20 seconds to Timer for Marcato Job Points')
totalDuration = totalDuration + 20
end
end
if buffactive.Troubadour then
totalDuration = totalDuration + 20 -- Assuming 20 seconds for capped Trobodour and you actually pre-cast with a Bihu Justaucorps.
end
return totalDuration
end
Sylph.Reain
Server: Sylph
Game: FFXI
Posts: 396
By Sylph.Reain 2017-02-23 20:13:00
The enhances Troubadour effect adds duration to Troubadour rather than to songs cast with Troubadour.
I think the extra 20 seconds is coming from Troubadour doubling the Lullaby job points.
By Sammeh 2017-02-23 22:12:10
I had already accounted for the Job Points. I hadn't for the enhances Troubadour and/or the doubling of Clarion Call while under Troubadour.
Asura.Sechs
Server: Asura
Game: FFXI
Posts: 9893
By Asura.Sechs 2017-02-24 01:33:25
If I recall the only thing that doesn't get doubled by Troub is the +secs offered by Marcato (which do not work if you have SV up btw)
By Fuuki 2017-02-24 22:26:28
10/10 would love as an .LUA please Sammeh :Q
By Sammeh 2017-02-26 06:31:08
If I recall the only thing that doesn't get doubled by Troub is the +secs offered by Marcato (which do not work if you have SV up btw)
Can you confirm?
Code if buffactive['Marcato'] then
-- add_to_chat(8,'Adding 20 seconds to Timer for Marcato Job Points')
totalDuration = totalDuration + 20
end
Should change to: Code if buffactive['Marcato'] and not buffactive['Soul Voice'] then
-- add_to_chat(8,'Adding 20 seconds to Timer for Marcato Job Points')
totalDuration = totalDuration + 20
end
?
Asura.Sechs
Server: Asura
Game: FFXI
Posts: 9893
By Asura.Sechs 2017-02-26 07:25:24
Yes.
Marcato gives +20 seconds (not multiplied by NiTro) but these seconds are a side-effect of the 1,5 potency bonus granted by Marcato.
This 1,5 potency bonus won't be in effect if you have SV up (which is 2,0 bonus), which means you won't receive the 20 seconds bonus either.
Sylph.Reain
Server: Sylph
Game: FFXI
Posts: 396
By Sylph.Reain 2017-02-26 11:33:34
I think you've misunderstood me. Code if MaxJobPoints == 1 then
-- add_to_chat(8,'Adding 20 seconds to Timer for Lullaby Job Points')
totalDuration = totalDuration + 20
When Troubadour is up this +20 gets doubled to +40 similar to clarion call.
The Bihu body shouldn't be adding any duration to songs.
By Sammeh 2017-02-26 14:24:06
I think you've misunderstood me. Code if MaxJobPoints == 1 then
-- add_to_chat(8,'Adding 20 seconds to Timer for Lullaby Job Points')
totalDuration = totalDuration + 20
When Troubadour is up this +20 gets doubled to +40 similar to clarion call.
The Bihu body shouldn't be adding any duration to songs.
You do get +20 if you had Bihu during precast of the JA troubadour. (Well 4 seconds * # of Troub merits).
Asura.Sechs
Server: Asura
Game: FFXI
Posts: 9893
By Asura.Sechs 2017-02-26 14:29:29
That gives duration to the JA (Troubadour), not to the songs themselves :x
By Sammeh 2017-02-26 14:34:27
Ok.
For those anyone using this - I've simplified it and made it its own lua (sort of). I also updated it with every form of REM (lvl 75-99AG) and a handful of additional harps and other gearsets. I also check for merits / job points more global - "assume your'e maxed" mentality.
https://github.com/SammehFFXI/FFXIAddons/blob/master/GearSwap/lullaby.lua
In your gearswap do a:
in your get_sets section.
The only thing 'gearswap' this uses however is the custom commands so if you already use your own, this may overwrite / replace, or yours may do the same to mine - not sure processing order of Gearswap w/ includes and duplicate functions). I left it gearswap based however, because of ease of use of what I believe to be most users in this forum.
Commands:
//gs c show ---- Shows the txt box
//gs c hide ---- Hides the txt box (default)
//gs c reset ---- Resets the mob list (this is issued on zone. But if you sleep and run away while staying in zone it will hang out in memory forever).
If you prefer this to be its own AddOn. Download it here:
https://github.com/SammehFFXI/FFXIAddons/blob/master/Sleeper/sleeper.lua
[+]
By Sammeh 2017-02-26 14:46:31
That gives duration to the JA (Troubadour), not to the songs themselves :x
Well - let me fix up some stuff. Obviously it worked out the same in this case (who doesn't have 5/5 troub?)
I feel like a tard though for not realizing what you all were saying.
[+]
By Sammeh 2017-02-26 14:52:44
Ok fixed. Sorry about that. To be clear had nothing to do w/ the body piece had everything to do w/ Troub doubling the job point gain.... ughhhhhhhhhh lol
So if you looked in the last 5 min - look again. Otherwise I'll start versioning after this.
By Sammeh 2017-02-26 15:55:40
Sorry for the spam today.. I really feel like an idiot. 1.0.2 released (mispelled troubadour haha!)
Quetzalcoatl.Langly
Server: Quetzalcoatl
Game: FFXI
Posts: 684
By Quetzalcoatl.Langly 2017-02-26 16:19:44
Sammeh for mayor!
Necro Bump Detected!
[37 days between previous and next post]
Asura.Lewyo
Server: Asura
Game: FFXI
Posts: 84
By Asura.Lewyo 2017-04-04 16:54:53
Did today's update 4/4 2017 break this addon since i cannot get it to work?
By Sammeh 2017-04-04 17:41:57
Did today's update 4/4 2017 break this addon since i cannot get it to work?
works fine for me O.o
-Sam
By Sammeh 2017-04-04 17:48:42
with that said may want to add in a : Code if legs.en == "Inyanga Shalwar +2" then mult = mult + 0.17 end
So for those of us who have had to be bard in mass-lullaby spam (Reisin T4's, bcnms, etc) it's hard to keep track of slept mobs.
I think I have a solve.
I wrote into my gear-swap a table to track mobs that I've cast lullaby on (either by target or associated by AOE (horde lullaby)).
I then keep track of each of those mobs in memory to see if they start doing anything and/or moving (or heaven forbid timer actually runs out). If so I know they wake up.
If you target the mob it highlights it - so you can see which mob is going to wake up when.
Made a design decision to keep them in table until the mob dies and/or you zone.
A few things:
1) This could easily be modified for any debuff, however only 'sleep' or slept mobs are really easily identifiable if that debuff is lost. Everything else can be erased, dispelled, overridden, etc that you wouldn't know.
2) This won't keep track if someone overwrites your lullaby with another spell. (Your lullaby is overwritten w/ a Sleep II or Repose)
Anyway - small video showing it off. Hopefully not too buggy =)
YouTube Video Placeholder
Updating a few lines down in thread Please see this data:
Ok.
For those anyone using this - I've simplified it and made it its own lua (sort of). I also updated it with every form of REM (lvl 75-99AG) and a handful of additional harps and other gearsets. I also check for merits / job points more global - "assume your'e maxed" mentality.
https://github.com/SammehFFXI/FFXIAddons/blob/master/GearSwap/lullaby.lua
In your gearswap do a:
Code
1
include('lullaby.lua')
in your get_sets section.
The only thing 'gearswap' this uses however is the custom commands so if you already use your own, this may overwrite / replace, or yours may do the same to mine - not sure processing order of Gearswap w/ includes and duplicate functions). I left it gearswap based however, because of ease of use of what I believe to be most users in this forum.
Commands:
//gs c show ---- Shows the txt box
//gs c hide ---- Hides the txt box (default)
//gs c reset ---- Resets the mob list (this is issued on zone. But if you sleep and run away while staying in zone it will hang out in memory forever).
If you prefer this to be its own AddOn. Download it here:
https://github.com/SammehFFXI/FFXIAddons/blob/master/Sleeper/sleeper.lua
|
|