Page 1 of 2

Adding methods to monsters

Posted: Tue Jan 08, 2013 11:16 pm
by Diarmuid
Hi there,

A quick question to coders around with more experience than me. Is there a neat way to add methods to a monster?

For example, I developped a function which can freeze a monster (real blue freeze) at will for a duration. Right now, I have it called by:

Code: Select all

exsp.freezeMonster(monster, duration)
However, I would love to have this as monster:freeze(duration). I know I can make a wrapper for it, and have it called by:

Code: Select all

exsp.extend(monster):freeze(duration)
or

Code: Select all

monster = exsp.extend(monster)
monster:freeze(duration)
Now, is it worth it? Which version is more legible and user-friendly, the function or the method?

Re: Adding methods to monsters

Posted: Tue Jan 08, 2013 11:34 pm
by undeaddemon
I don't know what any of that means... but I know I like it.

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 12:17 am
by HaunterV
is this a way of adding new abilities to monsters? someone inject some idiot into this thread so I can understand it, because as it stands right now, I'm stumped.

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 1:11 am
by Diarmuid
HaunterV wrote:is this a way of adding new abilities to monsters? someone inject some idiot into this thread so I can understand it, because as it stands right now, I'm stumped.
Yes, I have a whole new monster module in the next version of exsp, with tons of new stuff:
monster:hold(duration)
monster:unhold()
monster:isHeld()
monster:onUnhold(function(self))
monster:freeze(duration)
monster:isFrozen()
monster:onUnfreeze(function(self))
monster:makeInvulnerable(duration)
monster:unmakeInvulnerable()
monster:isInvulnerable()
monster:onUnmakeInvulnerable(function(self))

I just have to figure how to implement this so that it's the easier to use by everyone. After rethinking a bit after I posted, I think I'll go with the method route, it will be more expandable and consistent with current way scripting works in LoG.

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 5:38 am
by undeaddemon
[embarrassed to the point tears..]

Check Boxes?

:P

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 5:53 am
by msyblade
Do it Diarmuid. Checkboxes. You know u can, you know it's the next step of functionality. Do it. Checkboxes :)

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 6:53 am
by Diarmuid
Checkboxes? I feel like I'm missing the joke... :)

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 9:41 pm
by JKos
There is now way to add methods to entities, but you can fake it like I did with extended timer, see the sources. Actually I have been thinking this too, but didn't have the time to implement it.


the basic idea is like this:

Code: Select all

function wrap(monster)
 local wrapper = {
    id = monster.id,
    level = monster.level,
    x = monster.x,
    y = monster.y,
    getLevel = function(self)
       return findEntity(self.id):getLevel()
    end,
   freeze(self)
      ...freeze monster...
   end
 }
 return wrapper
end

Re: Adding methods to monsters

Posted: Wed Jan 09, 2013 10:33 pm
by Diarmuid
Yes, that's exactly what I did. I stared blankly at your timers code for like three hours and then suddenly understood how tables could be objects...

Here's the code I made:

Code: Select all

function extend(entity)
	if not(entity) then return nil end
	objects[entity.id] = {}
	objects[entity.id].name = entity.name
	objects[entity.id].id = entity.id
	objects[entity.id].level = entity.level
	objects[entity.id].x = entity.x
	objects[entity.id].y = entity.y
	objects[entity.id].facing = entity.facing
	objects[entity.id].class = entity.class
	return addExtendedMethods(objects[entity.id])
end

function addExtendedMethods(object)
	local methods
	if object.class == "Monster" then
		methods = {
			-- Default Grimrock methods
			setAIState = function(self, state)
				findEntity(self.id):setAIState(state)
			end,
			setHealth = function(self, health)
				findEntity(self.id):setHealth(health)
			end,
			getHealth = function(self)
				return findEntity(self.id):getHealth()
			end,
			setLevel = function(self, level)
				findEntity(self.id):setLevel(level)
			end,
			getLevel = function(self)
				return findEntity(self.id):getLevel()
			end,
			setPosition = function(self, x, y, level, facing)
				findEntity(self.id):setPosition(x, y, level, facing)
			end,
			addItem = function(self, item)
				findEntity(self.id):addItem(item)
			end,
			destroy = function(self)
				findEntity(self.id):destroy()
			end,
			-- Extended methods
			hold = exsp._hold,
			unhold = exsp._unhold,
			isHeld = exsp._isHeld,
			onUnhold = exsp._onUnhold,
			freeze = exsp._freeze,
			unfreeze = exsp._unfreeze,
			isFrozen = exsp._isFrozen,
			onUnfreeze = exsp._onUnfreeze,
			makeInvulnerable = exsp._makeInvulnerable,
			unmakeInvulnerable = exsp._unmakeInvulnerable,
			isInvulnerable = exsp._isInvulnerable,
			onUnmakeInvulnerable = exsp._onUmakeInvulnerable,
			move = exsp._move,
			addMonsterHooks = exsp.addMonsterHooks,
			executeMonsterHooks = exsp.executeMonsterHooks,
		}
	else
		return nil
	end
	for k, v in pairs(methods) do
		object[k] = v
	end
	return object
end
My main issue was that the wrapper was not dynamic for level, x, y, facing. Then I did some research and found about metatables, and was like, YEEEEES!!!!! and then saw Legend of Grimrock didn't support them and went NOOOOOO...

But I had a very fast timer (0.05) detecting spells in the dungeon running anyway, so I put the following code inside:

Code: Select all

		for id, object in pairs(objects) do
			if findEntity(id) then
				object.level = findEntity(id).level
				object.x = findEntity(id).x
				object.y = findEntity(id).y
				object.facing = findEntity(id).facing
			else
				object = nil
			end
		end
That way the dynamic properties are really updated in (almost) real time, and if the monster dies, the extended object is deleted instantly as well.

Thus:

Code: Select all

local monster = exsp.extend(snail_1)
print(monster.x, monster.y)
Will always return the true current x and y properties. So now monster objects can be "extended" and used in scripts exactly like normal monster objects, but with new methods and the advantage that you can store them in variables without serialization errors.

I also plan to extend other things, like Projectiles, so I made the exsp.extend wrapper flexible.

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 2:45 am
by Komag
You can put code linked in the new gui hook and it will run "every frame" if I understand how it works properly, so it might be more reliable than a 0.05s timer