question based on monster.lua

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
Marble Mouth
Posts: 52
Joined: Sun Feb 10, 2013 12:46 am
Location: Dorchester, MA, USA

Re: question based on monster.lua

Post by Marble Mouth »

Hi Ryeath_Greystalk. We certainly could start a new thread if you like. I would prefer not to use PM's unless it's something that really shouldn't be public (e.g. spoilers for a work-in-progress.) I suspect that hyteria won't mind if we keep using this thread, since he or she hasn't posted here since Jan 4.
Ryeath_Greystalk wrote:I see you have onDie and onDamage in both the monsters.lua and bossScript. Are they calling the same functions or are they separate? For example could I use
monsters.lua
onDie = function( monster )
return bossScript.isItDeadYet( monster )
end,

with bossScript
function isItDeadYet(monster)
Yes, that would work just fine. In the script I posted, there are actually two completely distinct functions referenced by the name "onDie" and two distinct functions referenced by the name "onDamage". Lua can tell apart the two "onDie" functions. The first "onDie" function (in monsters.lua) is a value in an anonymous table, corresponding to the key "onDie". That table is then used as the argument to the Grimrock API function cloneObject. The second "onDie" function (in bossScript) is stored in bossScript's non-local variable onDie. I prefer to reuse the names like this when the purpose of the two functions is so closely related (the function in monsters.lua does nothing except call the function in bossScript), but if you prefer to use distinct names for distinct functions, there's nothing wrong with that approach, either.
Ryeath_Greystalk wrote:Another unclear this is alreadyEscaped variable. I don't see it assigned to anything until after the monster has escaped where you give it alreadyEscaped = true.
That's correct.
Ryeath_Greystalk wrote: Wouldn't that bring up an error for a nil value? I tend to think very linear. So I would think it would happen like this, I hit monster, monster takes damage, call bossScript.onDamage, function tries to compare not alreadyEscaped..lua say "whoa, what is this alreadyEscaped thing I haven't seen that yet, guess I will give it a nil value."
Everything you said here is true, except for bringing up an error. Nil is a legitimate value in lua. The value of any variable which has not yet been initialized is nil. The same is true for values within a table which have not yet been initialized. So when lua checks alreadyEscaped before a value has been assigned to this variable, its value is nil. When nil is used as a condition or within complex logical operations:

Code: Select all

if nil then
   --lua will never reach this line
end
if (not nil) then
   --lua will always reach this line
end
if ( nil or x ) then
   --lua will reach this line if and only if the variable x is a "positive" value
   --anything except for nil or false is a "positive" value
end
if ( nil and y ) then
   --lua will never reach this line
   --in fact, lua will never even check the value of y because ( nil and whatever ) is always a "negative" result
end
then nil behaves the same as false. If you like, you could add this line:

Code: Select all

alreadyEscaped = false
at the top of bossScript. That line will not change the behavior of bossScript in any way, but it may clarify for you what is happening.

You have probably seen errors occur from attempting to index a nil value. This happens when you try to treat a nil value as if it was a table. For example:

Code: Select all

local entity = findEntity("NoSuchEntity")
entity:destroy()
Assuming that you do not have an entity called "NoSuchEntity" in your dungeon, then this code will produce an error. That's because the line "entity:destroy()" only makes sense if entity is a table (and that table contains a pair with key "destroy", and the value of that pair is a function), but in this case, entity is nil because findEntity couldn't find an entity with id "NoSuchEntity". That's why it's good practice to make sure the value you're working with isn't nil before you treat it like a table:

Code: Select all

local entity = findEntity("NoSuchEntity")
if entity then
   entity:destroy()
end
This code shouldn't crash. If you wanted to be even more defensive with your code, you could write:

Code: Select all

local entity = findEntity("NoSuchEntity")
if ( type(entity) == "table" ) and ( type(entity.destroy) == "function" ) then
   entity:destroy()
end
In this code, I've used the native lua function "type". This function returns a string corresponding to the type of value of the argument. There are only eight native types in lua, of which I know that six (nil, boolean, number, string, table, function) are supported in Grimrock. Dungeon objects are always of type table. Nil is a special type. There is only one possible value of this type: nil itself. Its only real purpose is to be a special "non-value" that you can check for, for example when findEntity can't find an entity with the id you specified. As mentioned above, nil behaves the same as false in most cases. One important exception is that the Grimrock API hooks (e.g. onDie) are interested in the return value false, and only false. If a hook function returns nil, then Grimrock treats this the same as if that hook had returned true. If you want to cancel the pending action that triggered a hook, you must return false.

It seems I've rambled quite a bit already. I hope some of this has helped :)
Ryeath_Greystalk
Posts: 366
Joined: Tue Jan 15, 2013 3:26 am
Location: Oregon

Re: question based on monster.lua

Post by Ryeath_Greystalk »

Thanks for taking the time to explain all that. My only programming experience was basica about 25 years ago, and this new stuff is so far removed from that (no line numbers for starters.)
I'm still in the 'must assign stuff to variables' and 'functions are goto statements' mindset. I'm slowly figuring this out with help from the generous folks here.
Marble Mouth
Posts: 52
Joined: Sun Feb 10, 2013 12:46 am
Location: Dorchester, MA, USA

Re: question based on monster.lua

Post by Marble Mouth »

BASIC was a long time ago for me too. :D [/nostalgia] I honestly don't remember if you were allowed to use uninitialized variables in that language. I do remember that in BASIC, each variable had a type. For example A was always a number and $A (or maybe A$ ... I don't remember) was always a string. In lua, variables don't have types, only values have types. So you could do something like this:

Code: Select all

local x = 0
x = "zero"
x = false
x = nil
to assign four different values (overwriting the previous value each time) with four different types to the variable x. It's generally good practice to restrict each variable to values of a specific type (e.g. table) (with a special allowance that any variable may have the value nil), but lua doesn't require you to do this. There are rare cases where this freedom proves quite useful.

Lua doesn't really have an equivalent to goto. Lua functions are much more like gosub from BASIC, because when the function is finished, program control resumes just after the point where the function was called from.
Post Reply