HowTo: Developing New LUA Functions

Discussions on development of both the EQ2Emulator LUA Script Engine and Script specifications

Moderator: Team Members

Post Reply
User avatar
John Adams
Retired
Posts: 9684
Joined: Thu Jul 26, 2007 6:27 am
EQ2Emu Server: EQ2Emulator Test Center
Characters: John
Location: Arizona
Contact:

HowTo: Developing New LUA Functions

Post by John Adams » Fri Aug 02, 2013 5:57 pm

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
John Adams
EQ2Emulator - Project Ghost
"Everything should work now, except the stuff that doesn't" ~Xinux

Jabantiz
Lead Developer
Posts: 2912
Joined: Wed Jul 25, 2007 2:52 pm
Location: California

Re: HowTo: Develop new LUA Functions

Post by Jabantiz » Fri Aug 02, 2013 6:40 pm

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)

User avatar
John Adams
Retired
Posts: 9684
Joined: Thu Jul 26, 2007 6:27 am
EQ2Emu Server: EQ2Emulator Test Center
Characters: John
Location: Arizona
Contact:

Re: HowTo: Developing New LUA Functions

Post by John Adams » Fri Aug 02, 2013 7:19 pm

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

Jabantiz
Lead Developer
Posts: 2912
Joined: Wed Jul 25, 2007 2:52 pm
Location: California

Re: HowTo: Developing New LUA Functions

Post by Jabantiz » Fri Aug 02, 2013 8:44 pm

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);

User avatar
thefoof
Retired
Posts: 630
Joined: Wed Nov 07, 2012 7:36 pm
Location: Florida

Re: HowTo: Developing New LUA Functions

Post by thefoof » Fri Aug 02, 2013 8:59 pm

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

User avatar
John Adams
Retired
Posts: 9684
Joined: Thu Jul 26, 2007 6:27 am
EQ2Emu Server: EQ2Emulator Test Center
Characters: John
Location: Arizona
Contact:

Re: HowTo: Developing New LUA Functions

Post by John Adams » Fri Aug 02, 2013 9:03 pm

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.

Jabantiz
Lead Developer
Posts: 2912
Joined: Wed Jul 25, 2007 2:52 pm
Location: California

Re: HowTo: Develop new LUA Functions

Post by Jabantiz » Fri Aug 02, 2013 9:12 pm

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.

User avatar
John Adams
Retired
Posts: 9684
Joined: Thu Jul 26, 2007 6:27 am
EQ2Emu Server: EQ2Emulator Test Center
Characters: John
Location: Arizona
Contact:

Re: HowTo: Develop new LUA Functions

Post by John Adams » Fri Aug 02, 2013 9:26 pm

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 :)

User avatar
John Adams
Retired
Posts: 9684
Joined: Thu Jul 26, 2007 6:27 am
EQ2Emu Server: EQ2Emulator Test Center
Characters: John
Location: Arizona
Contact:

Re: HowTo: Developing New LUA Functions

Post by John Adams » Sat Aug 03, 2013 10:50 am

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.

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests