Page 1 of 1

HowTo: Developing New LUA Functions

Posted: Fri Aug 02, 2013 5:57 pm
by John Adams
In all my years here, I do not think I have ever successfully developed a LUA Function on my own - today is no different. So my attempt with this post is to get some things documented so I don't have to ask questions anymore.

New LUA Function: Interrupt
Just getting the new function into the LUA system seems to take 3 parts - LuaInterface.cpp (registering), and the function/declaration in LuaFunctions.cpp/h.


Register the LUA Function (LuaInterface.cpp):

Code: Select all

	lua_register(state, "Interrupt", EQ2Emu_lua_Interrupt);
Declare a new function (LuaFunctions.h):

Code: Select all

int EQ2Emu_lua_Interrupt(lua_State* state);
Build out the new function (LuaFunctions.cpp):

Code: Select all

int EQ2Emu_lua_Interrupt(lua_State* state) 
{
    if (!lua_interface)
		return 0;
	Spawn* caster = lua_interface->GetSpawn(state); // Second param in lua_interface->get functions defaults to 1
	Spawn* target = lua_interface->GetSpawn(state, 2);
	if (!caster) {
	    lua_interface->LogError("LUA Interrupt command error: caster is not a valid spawn");
		return 0;
	}
	if(!target)  {
	    lua_interface->LogError("LUA Interrupt command error: target is not a valid spawn");
		return 0;
	}
	if (!target->IsEntity()) {
		lua_interface->LogError("LUA Interrupt command error: Target is not an entity");
		return 0;
	}
	caster->GetZone()->GetSpellProcess()->Interrupted((Entity*)target, caster, SPELL_ERROR_INTERRUPTED);
	return 0;
}
That part is easy. Now the part I do not understand is how to access all the functions outside the Lua system that will cause the target to become Interrupted by my spell/script.
UPDATED! With the guidance below, I have updated this OP to show the full build out of this function. Thanks guys!


FYI, this is for my first CoE+ spell "Provoke", and already ran into something I need help with :)

Thanks in advance

Re: HowTo: Develop new LUA Functions

Posted: Fri Aug 02, 2013 6:40 pm
by Jabantiz
First thing, interrupt seems to be broken currently but I will still show you how to make the lua function.

We want to interrupt a spell so we look in spellprocess.cpp and find this
void SpellProcess::Interrupted(Entity* caster, Spawn* interruptor, int16 error_code)
So we now know we will need at least 2 spawn pointers, the int16 error_code can be optional, error codes are in spells.h

The first thing to do in the code is to check the lua_interface pointer

Code: Select all

int EQ2Emu_lua_Interrupt(lua_State* state) 
{
    if (!lua_interface)
        return 0;
   return 0;
}
Next we need to get the spawn pointers, which will be the first 2 params of the lua function

Code: Select all

Spawn* caster = lua_interface->GetSpawn(state); // Second param in lua_interface->get functions defaults to 1
Spawn* target = lua_interface->GetSpawn(state, 2);
We now have the 2 pointers we need, lets check them to be sure they are valid.

Code: Select all

if (!caster) {
    lua_interface->LogError("LUA Interrupt command error: caster is not a valid spawn");
    return 0;
}

if(!target) {
    lua_interface->LogError("LUA Interrupt command error: target is not a valid spawn");
    return 0;
}
If the pointers aren't valid lua will give an error and return out. Now we can actually make it do what we want it to do, which is interrupt the target, to do that we call the function I listed above which is part of the spellprocess class, every zone has its own spellprocess so we have to get the zone first. If you look at the code for SpellProcess::Interrupt() you will notice the first param need to be an entity and is the person who is being interrupted, so we need to check that it is an entity before we send it to that function

Code: Select all

if (!target->IsEntity()) {
    lua_interface->LogError("LUA Interrupt command error: Target is not an entity");
    return 0;
}

caster->GetZone()->GetSpellProcess()->Interrupt((Entity*)target, caster, SPELL_ERROR_INTERRUPTED);
return 0;
And that is it, the lua function is complete. Also note if the function will return a value back to the lua script you need to do a return 1;

(This code was all written here so there may be typos)

Re: HowTo: Developing New LUA Functions

Posted: Fri Aug 02, 2013 7:19 pm
by John Adams
Awesome, Jab. Surprisingly even to me, I had the code written almost exactly like that (stealing from other functions) but got tripped up (eg, intimidated) by trying to reach out to SpellProcess :) Thanks, that helps a great deal. I'll give it a try.


Edit: Okay, coded up and added to my Provoke.lua. I don't have any "chance" that I should interrupt, but my NPC has no spells so I don't think it will do what I want until they happen to be casting on me while I cast my interrupt (??)

But here's what the log shows - so CheckInterruptSpell() must be working on some level?
19:40:43 D Command : Player 'Tankasaurus' (150), Command: useability
19:40:43 D LUA : Found LUA Spell Script: 'Spells/Fighter/Warrior/Guardian/Provoke.lua'
19:40:43 D Spell : Tankasaurus is casting Provoke on a crustose defender.
19:40:43 D Combat : 'a crustose defender' failed to interrupted spell for 'Tankasaurus': 31%
19:40:43 D Spell : Concentration is now 0 on Tankasaurus

Re: HowTo: Developing New LUA Functions

Posted: Fri Aug 02, 2013 8:44 pm
by Jabantiz
The log looks like the mob was trying to interrupt you while casting the provoke spell. You probably won't get any messages from the code if your target has no spells. To check to see if it is working properly you can add a log to SpellProcess::Interrupt()
if (spell && spell->spell && spell->spell->GetSpellData()->interruptable) {
...
}
else
LogWrite("Target was not casting);

Re: HowTo: Developing New LUA Functions

Posted: Fri Aug 02, 2013 8:59 pm
by thefoof

Code: Select all

function spawn(NPC)
	SetLuaBrain(NPC)
	SetBrainTick(NPC, 500)
end

function respawn(NPC)
    spawn(NPC)
end

function Think(NPC)
	if GetTarget(NPC) ~= nil then
		CastSpell(GetTarget(NPC), interrupt_spell_id, interrupt_spell_tier, NPC)
	end
end

Re: HowTo: Developing New LUA Functions

Posted: Fri Aug 02, 2013 9:03 pm
by John Adams
I finally figured out the 20,000 other working parts to get this working lol... i had to create a spawn_npc_spells list with a valid spell in it (duh) and give it to my opponent. So now the spawn casts on me as well. Now we're both casting, but I still didn't see any interrupting... and that was because my LUA script had "Interrupt(Target)" commented out. DUH #2. So, I fixed THAT too.

And now, when he or I cast Provoke (our only spells) I am seeing this in the console:
20:58:52 E LUA : LUA Interrupt command error: target is not a valid spawn
I'll try Foof's script, if you can think of why target would not be a valid target. I got the dude targeted.


Edit: After stepping in, "caster" appears to be my foe and "target" is definitely not initialized.

Re: HowTo: Develop new LUA Functions

Posted: Fri Aug 02, 2013 9:12 pm
by Jabantiz
Jabantiz wrote:First thing, interrupt seems to be broken currently but I will still show you how to make the lua function.
You will probably never see interrupts currently, I have ran around while casting and never got interrupted, something seems broken with them currently.
John Adams wrote:Now we're both casting, but I still didn't see any interrupting... and that was because my LUA script had "Interrupt(Target)" commented out. DUH #2. So, I fixed THAT too.
If you used the code that I posted for the lua function you need to pass 2 spawn pointers

Code: Select all

function cast(Caster, Target)
    Interrupt(Caster, Target)
end
John Adams wrote:I'll try Foof's script, if you can think of why target would not be a valid target. I got the dude targeted.
His script is a spawn script take over the AI and just cast a spell over and over. If you have them set up to cast with the DB then the results will be the same.

Re: HowTo: Develop new LUA Functions

Posted: Fri Aug 02, 2013 9:26 pm
by John Adams
Jabantiz wrote:If you used the code that I posted for the lua function you need to pass 2 spawn pointers

Code: Select all

function cast(Caster, Target)
    Interrupt(Caster, Target)
end
OMFG... ~blush~

I think I am done for the night. This is about my 12th "duh" moment, and even for me, that's too many. Thanks for the help :)

Re: HowTo: Developing New LUA Functions

Posted: Sat Aug 03, 2013 10:50 am
by John Adams
Updated the OP with the full function, hopefully this will help others (and remind ME when I do my next one) how to access Emu's many data elements from within a LUA function.

Keep in mind this is one of a billion possible things you can do with LUA... so I know there will be more questions :) Feel free to ask, our guys are pretty sharp!

Thanks Jab and Foof again for the help here.