• You've discovered RedGuides, an EverQuest multi-boxing and scripting community 🧙‍♀️⚙️. We want you to play several EQ characters at once, come join us and say hello! 👋

  • A TLP without truebox has thawed (Very Vanilla ready)
    Frostreaver

Question - Lua bug help - full lua provided

Soandso2

Well-known member
Joined
Mar 13, 2023
RedCents
937¢
I am converting my macro to Lua, and I have run into a bug that I cannot figure out. I have a few clues, though, but no matter what I try, the problem persists.

It seems to occur when a target dies, because it does not happen while the target is alive. It also never happens when fighting mobs that do not die, like combat dummies. At first I thought that the problem happened because I had ONE check for being in combat, having a target, etc, and that quite a lot of stuff would still happen, even if the target died in the middle of the in-combat-check.

I tried to put checks for weather I have a target or not before everything I do, but still, when the target dies, the Lua crashes with the following message: attempt to compare nil with a number on row 107
And yes, nil in this case comes from mq.TLO.Target.Distance()

The whole Lua goes like this. I hope you can tell me why this fails and how to remedy it.

[CODE lang="Lua" title="bam.Lua" highlight="107"]mq = require('mq')


-- INIT VARIABLES

local terminate = false

local long = false

local foraging = false

local haveBegged = false

-- "Short Disciplines" are disciplines that are instant cast OR place a buff in either the buff or song windows, and generally have a short reuse timer.
local shortDiscs={
["Vigorous Shuriken"]=175,
["Curse of Sixteen Shadows"]=50,
["Bloodwalker's Synergy"]=50,
["Buffeting of Fists"]=50,
["Zlexak's Fang"]=50,
["Bloodwalker's Precision Strike"]=200,
["Dragons's Poise"]=50,
["Ecliptic Form"]=50,
["Heron Stance"]=50
}

-- "Long Disciplines" are disciplines that start the timer countdown in the discipline window, and generally have a long use (and reuse) timer. Use for raid targets or longer fights.
local longDiscs={
"Eye of the Storm",
"Heel of Zagali",
"Terrorpalm Discipline",
"Speed Focus Discipline",
"Ironfist Discipline"
}

-- "combatAAs" are Alt Abilities that must be activated (by hot button) in combat.
local combatAAs={
["1012"]={"Five Point Palm",100},
["1235"]={"Two-Finger Wasp Touch",50},
["377"]={"Focused Destructive Force",15},
["7001"]={"Zan Fi's Whistle",50}
}

local reflex = mq.TLO.Spell("Decisive Reflexes").RankName()


-- STARTUP MESSAGES

print('Running Bam.Lua')
print('Only using short duration disciplines')
print('Not foraging')
print('/bam terminate to end Bam.Lua')
print('/bam long on/off for using long duration combat discs')
print('/bam forage on/off for automatic foraging when not in combat')


-- HANDLE BINDS

local function binds(cmd,val)
if cmd == 'terminate' then
print('Terminating bam.Lua')
terminate = true
elseif cmd == "long" then
if val == "on" then
long = true
print('Using long duration disciplines')
else
long = false
print('Only using short duration disciplines')
end
elseif cmd == "forage" then
if val == "on" then
foraging = true
print('Foraging')
else
foraging = false
print('Not foraging')
end
else
print(cmd..' is not a reqognised command')
end
end

mq.bind('/bam', binds)


-- CHECKING THAT WE ARE ACTUALLY ABLE TO DO WHATEVER WE WANT TO DO

local function goodToGo()
-- Credit: LeRogue.Lua by @rawmotion at RedGuides.com
return not mq.TLO.Me.Stunned()
and not mq.TLO.Me.Dead()
and not mq.TLO.Me.Feigning()
and not mq.TLO.Me.Ducking()
and not mq.TLO.Me.Silenced()
and not mq.TLO.Me.Charmed()
and not mq.TLO.Me.Mezzed()
and not mq.TLO.Me.Invulnerable()
and not mq.TLO.Me.Casting()
end


-- HANDLE COMBAT DISCIPLINES: SHORT AND LONG

local function useShortDisc(discName,targetDist)
local shortDisc = mq.TLO.Spell(discName).RankName
if mq.TLO.Target.ID() and goodToGo() and mq.TLO.Me.CombatAbilityReady(shortDisc)() and mq.TLO.Target.Distance() < targetDist then
mq.cmdf('/doability "%s"',shortDisc)
printf('Short disc: \ay%s',shortDisc)
mq.delay(1000)
end
end

local function useLongDisc(discName)
local longDisc = mq.TLO.Spell(discName).RankName
if mq.TLO.Target.ID() and goodToGo() and not mq.TLO.Me.ActiveDisc.ID() and mq.TLO.Me.CombatAbilityReady(longDisc)() then
mq.cmdf('/doability "%s"',longDisc)
printf('Long disc: \am%s',longDisc)
mq.delay(1000)
end
end

local function useDiscs(useLong)
for disc,range in pairs(shortDiscs) do
if mq.TLO.Target.ID() and goodToGo() then
useShortDisc(disc,range)
end
end
if useLong then
for _,disc in pairs(longDiscs) do
useLongDisc(disc)
end
end
end


-- HANDLE COMBAT AAs AND COMBAT SKILLS

local function useCombatAAs()
for act,aaData in pairs (combatAAs) do
if mq.TLO.Target.ID() and goodToGo() and mq.TLO.Me.AltAbilityReady(aaData[1])() and mq.TLO.Target.Distance() < aaData[2] then
mq.delay(500)
mq.cmdf('/alt activate "%s"',act)
printf('Combat AA: \ag%s',aaData[1])
end
end
end

local function useCombatSkill(combatSkill)
if mq.TLO.Target.ID() and mq.TLO.Target.Distance()<15 and goodToGo() and mq.TLO.Me.AbilityReady(combatSkill)() then
mq.cmdf('/doability "%s"',combatSkill)
printf('Combat Skill: \at%s',combatSkill)
mq.delay(500)
end
end

local function useCombatSkills()
if mq.TLO.Me.AbilityReady("Begging")() and not haveBegged and mq.TLO.Target.PctHPs() < 90 then
mq.cmd("/attack off")
mq.delay(100)
mq.cmd("/doability Begging")
print("CombatSkill: Begging")
haveBegged = true
mq.delay(500)
mq.cmd("/attack on")
end

useCombatSkill("Disarm")
useCombatSkill("Intimidation")

if mq.TLO.Me.PctHPs() < 50 then
useCombatSkill("Mend")
end
end


-- HANDLE FORAGING

local function useForage()
mq.cmd("/doability Forage")
mq.delay(500)
if mq.TLO.Cursor.ID() ~= nil then
printf('Foraged: %s',mq.TLO.Cursor.Name())
mq.cmd("/autoinv")
mq.delay(500)
if mq.TLO.Cursor.ID() ~= nil then
printf('Bonus: %s',mq.TLO.Cursor.Name())
mq.cmd("/autoinv")
end
end
end


-- HANDLE CLICKYS

local function useClickys(clickyName)
if clickyName == "combat" then
if mq.TLO.Spell("Celestial Tranquility").Stacks() and not mq.TLO.Me.Buff("Celestial Tranquility").ID() then
mq.cmdf('/useitem "%s"',"Celestial Fists")
end
if mq.TLO.Spell("Seven Chakras Heel Technique").Stacks and not mq.TLO.Me.Song("Seven Chakras Heel Technique").ID() and mq.TLO.FindItem("Soulforge Tunic of the Celestial Zenith").TimerReady() == 0 then
mq.cmdf('/useitem "%s"',"Soulforge Tunic of the Celestial Zenith")
end
if not mq.TLO.FindItem("Transcended Fistwraps of Immortality").TimerReady() then
mq.cmdf('/useitem "%s"',"Transcended Fistwraps of Immortality")
end
else
if mq.TLO.FindItem(clickyName).ID() > 0 and mq.TLO.FindItem(clickyName).TimerReady() == 0 then
mq.cmdf('/useitem "%s"',clickyName)
end
end
end


-- MAIN LOOP

while not terminate do

-- Combat indifferent, instant click/use, always use
if not mq.TLO.Me.Buff(reflex).ID() and mq.TLO.Me.CombatAbilityReady(reflex)() and mq.TLO.Spell(reflex).Stacks() and goodToGo() then
mq.cmdf('/doability "%s"',reflex)
end

-- Do this when in combat
if mq.TLO.Me.Combat() and mq.TLO.Target.ID() and goodToGo() then
useDiscs(long)
useCombatSkills()
useCombatAAs()
useClickys("combat")
end

-- Do this when not in combat
if not mq.TLO.Me.Combat() and goodToGo() then

if haveBegged then
haveBegged = false
end

if not mq.TLO.Me.Aura.ID() and not mq.TLO.Me.Moving() then
mq.cmd("/disc Master's Aura")
end

if foraging and mq.TLO.Me.AbilityReady("Forage") then
useForage()
end

if not mq.TLO.Me.Buff("Illusion: Human").ID() and not mq.TLO.Me.Moving() then
useClickys("Circlet of Disguise")
end

if mq.TLO.Me.Buff("Familiar: Fungal Underbulk").ID() == nil and not mq.TLO.Me.Moving() then
useClickys("Fungal Underbulk")
mq.delay(5050)
mq.cmd("/familiar leave")
end

end

end[/CODE]
 
You would have to do a single if check to determine if you have a target before checking any other conditions:

line 107: if mq.TLO.Target.ID() and goodToGo() and mq.TLO.Me.CombatAbilityReady(shortDisc)() and mq.TLO.Target.Distance() < targetDist then

pseudo:

Do I have a target? No -- return

Do I have target? Yes - Run other condition checks.
 
Last edited:
You would have to do a single if check to determine if you have a target before checking any other conditions:

line 107: if mq.TLO.Target.ID() and goodToGo() and mq.TLO.Me.CombatAbilityReady(shortDisc)() and mq.TLO.Target.Distance() < targetDist then

pseudo:

Do I have a target? No -- return

Do I have target? Yes - Run other condition checks.
Alright, that is easy enough to do and I will try that first chance I get. Would you. however, care to explain why this is necessary? Does the entire IF-statement get evaluated, even though the first part returns nil/false/not true etc?
 
Note that if you run the Lua it should type out the error in the MQ window. (or at least leed to it)
 
Alright, that is easy enough to do and I will try that first chance I get. Would you. however, care to explain why this is necessary? Does the entire IF-statement get evaluated, even though the first part returns nil/false/not true etc?

Its not necessary, there is no difference. These will already short circuit.

What may be happening is that your target is despawning between checks.

something you can do is (mq.TLO.Target.Distance() or 0) < targetDist
 
Actually, I retract my last proposed idea. Your problem is because ID() will return 0 if a spawn is invalid, not nil. 0 is truthy in Lua, so you need to check if its > 0
 
So, change if mq.TLO.Target.ID() and to if mq.TLO.Target.ID()>0 and all throughout the script and problem solved? Ok, I will try this. Many thanks!
Also, regarding not using () after RankName above. In this case, does it matter? The Lua runs fine (as long as the target is valid) and the discs are used. It does not seem to matter if the spell name is of userdata type or string type (I am assuming that is what the type would be) Or is it just bad form to not cast the value to the proper type?
 
So, change if mq.TLO.Target.ID() and to if mq.TLO.Target.ID()>0 and all throughout the script and problem solved? Ok, I will try this. Many thanks!
Also, regarding not using () after RankName above. In this case, does it matter? The lua runs fine (as long as the target is valid) and the discs are used. It does not seem to matter if the spell name is of userdata type or string type (I am assuming that is what the type would be) Or is it just bad form to not cast the value to the proper type?

yes it matters, .RankName returns a userdata object (representing the stored TLO expression), .RankName() returns a string. You're relying on something to implicitly convert your parameter to a string by using tostring(), which might in some cases return different results.
 
yes it matters, .RankName returns a userdata object (representing the stored TLO expression), .RankName() returns a string. You're relying on something to implicitly convert your parameter to a string by using tostring(), which might in some cases return different results.
Gotcha. Bad form, then. In this case it just "happens" to work. I will remedy this! Thanks!
 
All righty, that worked splendidly. I changed the Lua as per above and it runs flawlessly now. :) Thank you, everyone, for your input!

Question 1: Should I keep all the valid target checks through out the code, or just have the check prior to the actual use of a disc or ability? The Lua runs fine now so it does not appear like the abundance of checks has any kind of impact on performace (nor did I think that they would). I am more concerned about what is considered good practice.

Question 2: Do mq.TLO.WhatEver.ID always return 0 or a higher integer value? Or do they sometimes return something else? (I.e. can you always presume ID gives 0 or a number, or do you always have to know/make sure/find out?)

In the forage function if mq.TLO.Cursor.ID() ~= nil then appears to work, but perhaps that, again, is just sheer luck?
 
I prefer to check stuff like
if mq.TLO.Target() then
instead of worrying about whether ID may be nil or 0 or another number. It will vary per TLO, I think Pet.ID is another that will be 0 instead of nil. It could be that item refs, like what is on your cursor, will be nil if there was no item. You could check Spell too and see.

if mq.TLO.Spell('my spell')() then
if mq.TLO.FindItem('my item')() then
if mq.TLO.Target() then

of course even that has exceptions.... mq.TLO.Pet() returns "NO PET" or something when you have no pet.
 
I prefer to check stuff like
if mq.TLO.Target() then
instead of worrying about whether ID may be nil or 0 or another number. It will vary per TLO, I think Pet.ID is another that will be 0 instead of nil. It could be that item refs, like what is on your cursor, will be nil if there was no item. You could check Spell too and see.

if mq.TLO.Spell('my spell')() then
if mq.TLO.FindItem('my item')() then
if mq.TLO.Target() then

of course even that has exceptions.... mq.TLO.Pet() returns "NO PET" or something when you have no pet.
Yeah. I was just about to comment on my own question and make a note that ID seems to result in different values when "void". Sometimes nil, sometimes 0 and, like you say, sometimes other stuff.
 
Question - Lua bug help - full lua provided

Users who are viewing this thread

Back
Top
Cart