Skip to content

Instantly share code, notes, and snippets.

@Ivaar
Last active July 11, 2020 02:00
Show Gist options
  • Save Ivaar/819e831cfb0d8cbbf1e25e5491bf7bf0 to your computer and use it in GitHub Desktop.
Save Ivaar/819e831cfb0d8cbbf1e25e5491bf7bf0 to your computer and use it in GitHub Desktop.
_addon.name = 'FollowMe'
_addon.author = 'Ivaar'
_addon.command = 'fm'
require('luau')
require('pack')
actions = true
nolockon = true
follow_leader = ''
pos = {}
local player_name = (windower.ffxi.get_mob_by_target('me') or {}).name
local zone_id = windower.ffxi.get_info().zone
windower.register_event('incoming chunk',function(id, data, modified, is_injected, is_blocked)
if id == 0x0A then
zone_id = data:unpack('H', 49)
pos = {}
end
end)
windower.register_event('outgoing chunk',function(id, data, modified, is_injected, is_blocked)
if id == 0x5C then
pos = {}
elseif id == 0x05E then
if follow_leader == player_name and data:unpack('I', 0x04+1) ~= 1903324538 then
windower.send_ipc_message('zone %d ':format(zone_id)..table.concat(last_coords, ' '))
end
zone_line = nil
end
end)
windower.register_event('ipc message', function(message)
message = message:split(' ')
if message[1] == 'set' then
if message[2] == 'follow' then
follow_leader = message[3]
pos = {}
elseif message[2] == 'actions' then
actions = message[3] == 'on'
last_coords = nil
end
return
end
if not actions then return end
leader_zone = tonumber(message[2])
if leader_zone ~= zone_id then return end
table.remove(message, 2)
if message[1] == 'pos' then
table.remove(message, 1)
table.insert(pos, message)
elseif message[1] == 'zone' then
table.remove(message, 1)
zone_line = message
end
end)
local function get_distance(a, b)
return math.sqrt((a[1]-b[1])^2 + (a[3]-b[3])^2)
end
local function run_to_point(a, b)
if nolockon and windower.ffxi.get_player().target_locked then
windower.send_command('input /lockon')
end
if not b then
windower.ffxi.run(a[4])
return
end
local x, y = a[1]-b[1], a[3]-b[3]
local r = -math.atan2(y,x)
local v = windower.ffxi.get_player().autorun
if not v or not running or running ~= r then
windower.ffxi.run(r)
windower.ffxi.turn(r)
running = r
end
end
local states = S{0,1,5,85}
function do_loop()
local player = windower.ffxi.get_mob_by_target('me')
if not player or not states:contains(player.status) then return end
local coords = {player.x,player.z,player.y,player.heading}
if coords[1] == 0 and coords[3] == 0 then return end
if player.name == follow_leader then
if not last_coords or last_coords[3] ~= coords[3] or last_coords[1] ~= coords[1] then
last_coords = coords
windower.send_ipc_message('pos %d ':format(zone_id)..table.concat(coords, ' '))
end
return
end
while pos[1] and get_distance(pos[1], coords) < 1 do
table.remove(pos, 1)
end
local dist = pos[1] and get_distance(pos[1], coords)
if dist and dist < 30 then
run_to_point(pos[1], coords)
elseif not pos[1] and zone_line then
run_to_point(zone_line)
elseif zone_id == leader_zone and running then
if windower.ffxi.get_player().autorun then
windower.ffxi.run(false)
running = false
end
end
end
--do_loop:loop(0.1)
windower.register_event('postrender', do_loop)
windower.register_event('addon command', function(...)
arg = T(arg):map(string.lower)
if not arg[1] then
elseif arg[1] == 'eval' then
assert(loadstring(table.concat(arg, ' ',2)))()
elseif arg[1] == 'off' or arg[1] == 'on' then
actions = arg[1] == 'on'
last_coords = nil
windower.send_ipc_message('set actions %s':format(arg[1]))
else
local targ = windower.ffxi.get_mob_by_target(arg[1]) or windower.ffxi.get_mob_by_name(arg[1]:ucfirst())
follow_leader = targ and targ.name or arg[1]:ucfirst()
last_coords = nil
actions = true
windower.send_ipc_message('set actions %s':format('on'))
windower.send_ipc_message('set follow %s':format(follow_leader))
end
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment