Go easy on me, this is the first code I have ever written.
I originally had something else in mind for how this would work, but testing showed my original idea did not work so I came up with this idea.
Basically it is just a finite state machine that controls targeting/mode for CWTN macros. Big props to Sic and CWTN without your plugins I would not have been inspired to try this.
I think this code is fairly self-explanatory but if you have a question just ask.
There are probably a ton of improvements that can be made. If someone more skilled than me wants to take that on and share with the community that would be awesome. I feel like to make this really good it would require dannet observers and figuring out the logic of which mobs each tank is tanking, but that is way beyond my capability so I just threw this together.
If you have more than 1 offtank you wll have to modify the code. I don't drive from a tank but if you did it would probably work 100%. In my testing I have to run a modified version of this to make sure my primary tank attacks xtarget1. Unfortunately the CWTN plugins don't allow you to set this. No big deal.
Another improvement would maybe be a master list of non mezzable mobs that could be loaded based on zone you're in? I just threw in something simple as I was testing. Like I said I don't do this as professional and this is my first time ever doing anything like this so I was just amazed it worked at all.
Alas, here is the code.
[CODE lang="Lua" title="offtank.Lua"]local mq = require('mq')
--variables
local short_name = (mq.TLO.Me.Class.ShortName())
local mob_target = tostring(mq.TLO.Target.CleanName())
local manual_mode = 0
local chase_mode = 2
local assist_mode = 1
local tank_mode = 4
local not_mezzable = {
['a spite golem'] = true,
['an ashenbone drake'] = true
}
local assist_loop
local attack_loop
--functions
local function check_los()
if (mq.TLO.Target() and mq.TLO.Target.LineOfSight() and mq.TLO.Target.Type() == "NPC" and mq.TLO.Target.Type() ~= "Corpse") then
return true
else
return false
end
end
local function check_distance()
local mob_radius_check = 100
if (mq.TLO.Target() and mq.TLO.Target.Distance3D() < mob_radius_check and mq.TLO.Target.Type() ~= "Corpse") then
return true
else
return false
end
end
local function is_not_mezzable(mob_name)
return not_mezzable[mob_name] ~= nil
end
local function set_mode(class_name, class_mode)
mq.delay(50)
mq.cmdf('/%s mode %s',class_name,class_mode)
end
-- Define the state machine with the initial state set to 'assist'
local stateMachine = {
state = 'assist'
}
-- Define a function for each state that will be called when entering the state
local stateFunctions = {
assist = function()
print('Entering assist state')
-- Perform actions for this state
local targets = mq.TLO.Me.XTarget()
-- set our tank to chase the main assist
set_mode(short_name, chase_mode)
while(assist_loop) do
mq.delay(50)
local xtarget_number = mq.TLO.Me.XTarget()
if xtarget_number > 1 then
print("we have an add")
transition('check')
update()
end
end
end,
check = function()
print('Entering check state')
-- Perform actions for this state
mq.delay(50)
set_mode(short_name, manual_mode)
mq.cmd("/attack off")
mq.delay(5)
mq.cmd("/xtarget 2")
mob_target = mq.TLO.Target.CleanName()
if check_distance() and check_los() and is_not_mezzable(mob_target) then
transition('attack')
update()
else
transition('assist')
update()
end
end,
attack = function()
print('Entering attack state')
-- Perform actions for this state
mq.cmd("/xtarget 2")
set_mode(short_name,tank_mode)
while(attack_loop) do
if mq.TLO.Target.ID() == 0 or mq.TLO.Target.Type() == "Corpse" or mq.TLO.Target.Type() == nil then
transition('assist')
update()
end
end
end
}
-- Define a function to transition between states
function transition(newState)
-- Check if the new state is valid
if stateFunctions[newState] then
-- Call the exit function for the current state
local exitFunction = stateFunctions[stateMachine.state..'_exit']
if exitFunction then
exitFunction()
end
-- Update the current state
stateMachine.state = newState
-- Call the enter function for the new state
local enterFunction = stateFunctions[newState..'_enter']
if enterFunction then
enterFunction()
end
else
print('Invalid state transition requested')
end
end
-- Define a function to update the current state
function update()
-- Call the function for the current state
local stateFunction = stateFunctions[stateMachine.state]
if stateFunction then
stateFunction()
end
end
-- Define the enter and exit functions for each state (optional)
function stateFunctions.assist_enter()
print('Assist state entered')
assist_loop = true
end
function stateFunctions.assist_exit()
print('Assist state exited')
assist_loop = false
end
function stateFunctions.check_enter()
print('Check state entered')
end
function stateFunctions.check_exit()
print('Check state exited')
end
function stateFunctions.attack_enter()
print('Attack state entered')
attack_loop = true
end
function stateFunctions.attack_exit()
print('Attack state exited')
attack_loop = false
end
transition('assist')
update() [/CODE]
I originally had something else in mind for how this would work, but testing showed my original idea did not work so I came up with this idea.
Basically it is just a finite state machine that controls targeting/mode for CWTN macros. Big props to Sic and CWTN without your plugins I would not have been inspired to try this.
I think this code is fairly self-explanatory but if you have a question just ask.
There are probably a ton of improvements that can be made. If someone more skilled than me wants to take that on and share with the community that would be awesome. I feel like to make this really good it would require dannet observers and figuring out the logic of which mobs each tank is tanking, but that is way beyond my capability so I just threw this together.
If you have more than 1 offtank you wll have to modify the code. I don't drive from a tank but if you did it would probably work 100%. In my testing I have to run a modified version of this to make sure my primary tank attacks xtarget1. Unfortunately the CWTN plugins don't allow you to set this. No big deal.
Another improvement would maybe be a master list of non mezzable mobs that could be loaded based on zone you're in? I just threw in something simple as I was testing. Like I said I don't do this as professional and this is my first time ever doing anything like this so I was just amazed it worked at all.
Alas, here is the code.
[CODE lang="Lua" title="offtank.Lua"]local mq = require('mq')
--variables
local short_name = (mq.TLO.Me.Class.ShortName())
local mob_target = tostring(mq.TLO.Target.CleanName())
local manual_mode = 0
local chase_mode = 2
local assist_mode = 1
local tank_mode = 4
local not_mezzable = {
['a spite golem'] = true,
['an ashenbone drake'] = true
}
local assist_loop
local attack_loop
--functions
local function check_los()
if (mq.TLO.Target() and mq.TLO.Target.LineOfSight() and mq.TLO.Target.Type() == "NPC" and mq.TLO.Target.Type() ~= "Corpse") then
return true
else
return false
end
end
local function check_distance()
local mob_radius_check = 100
if (mq.TLO.Target() and mq.TLO.Target.Distance3D() < mob_radius_check and mq.TLO.Target.Type() ~= "Corpse") then
return true
else
return false
end
end
local function is_not_mezzable(mob_name)
return not_mezzable[mob_name] ~= nil
end
local function set_mode(class_name, class_mode)
mq.delay(50)
mq.cmdf('/%s mode %s',class_name,class_mode)
end
-- Define the state machine with the initial state set to 'assist'
local stateMachine = {
state = 'assist'
}
-- Define a function for each state that will be called when entering the state
local stateFunctions = {
assist = function()
print('Entering assist state')
-- Perform actions for this state
local targets = mq.TLO.Me.XTarget()
-- set our tank to chase the main assist
set_mode(short_name, chase_mode)
while(assist_loop) do
mq.delay(50)
local xtarget_number = mq.TLO.Me.XTarget()
if xtarget_number > 1 then
print("we have an add")
transition('check')
update()
end
end
end,
check = function()
print('Entering check state')
-- Perform actions for this state
mq.delay(50)
set_mode(short_name, manual_mode)
mq.cmd("/attack off")
mq.delay(5)
mq.cmd("/xtarget 2")
mob_target = mq.TLO.Target.CleanName()
if check_distance() and check_los() and is_not_mezzable(mob_target) then
transition('attack')
update()
else
transition('assist')
update()
end
end,
attack = function()
print('Entering attack state')
-- Perform actions for this state
mq.cmd("/xtarget 2")
set_mode(short_name,tank_mode)
while(attack_loop) do
if mq.TLO.Target.ID() == 0 or mq.TLO.Target.Type() == "Corpse" or mq.TLO.Target.Type() == nil then
transition('assist')
update()
end
end
end
}
-- Define a function to transition between states
function transition(newState)
-- Check if the new state is valid
if stateFunctions[newState] then
-- Call the exit function for the current state
local exitFunction = stateFunctions[stateMachine.state..'_exit']
if exitFunction then
exitFunction()
end
-- Update the current state
stateMachine.state = newState
-- Call the enter function for the new state
local enterFunction = stateFunctions[newState..'_enter']
if enterFunction then
enterFunction()
end
else
print('Invalid state transition requested')
end
end
-- Define a function to update the current state
function update()
-- Call the function for the current state
local stateFunction = stateFunctions[stateMachine.state]
if stateFunction then
stateFunction()
end
end
-- Define the enter and exit functions for each state (optional)
function stateFunctions.assist_enter()
print('Assist state entered')
assist_loop = true
end
function stateFunctions.assist_exit()
print('Assist state exited')
assist_loop = false
end
function stateFunctions.check_enter()
print('Check state entered')
end
function stateFunctions.check_exit()
print('Check state exited')
end
function stateFunctions.attack_enter()
print('Attack state entered')
attack_loop = true
end
function stateFunctions.attack_exit()
print('Attack state exited')
attack_loop = false
end
transition('assist')
update() [/CODE]

