Page 2 of 2

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 3:22 am
by Diarmuid
Komag, this is an excellent suggestion, thank you so much!

jKos, after thinking some more about it, I would suggest that this should not be directly integrated in exsp but should be a separate module that you add to the LoG framework, called extend.

The code (EDIT: tested and it works fine) would be the following:

Code: Select all

-- Universal Object Extender

objects = {}

-- create methods Table and define default Grimrock Methods
methods = {
	Monster = {
		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,	
	}
}

function entity(self, entity)
	if not(entity) then return nil end
	if not(self.methods[entity.class]) then return entity end
	if self.objects[entity.id] then return self.objects[entity.id] end
	self.objects[entity.id] = {}
	local object = self.objects[entity.id]
	object.name = entity.name
	object.id = entity.id
	object.level = entity.level
	object.x = entity.x
	object.y = entity.y
	object.facing = entity.facing
	object.class = entity.class
	return addMethods(object)
end

function addMethods(object)
	if not(extend.methods[object.class]) then return nil end
	for k, v in pairs(extend.methods[object.class]) do
		object[k] = v
	end
	return object
end

function entities(self,t)
	for i, v in ipairs(t) do
		v = self.entity(v)
		t[i] = t		
	end
	return t
end

function registerMethods(self, class, methods)
	if not(self.methods[class]) then self.methods[class] = {} end
	for k, v in pairs(methods) do
		self.methods[class][k] = v
	end
end

-- Object Detector
function objectDetector()
	for id, object in pairs(objects) do
		if object.class == "Monster" and 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
end
If you think that integrating this "Universal Extender" to the framework is a good idea, I volunteer for completing the code with the default definitions for all standard classes.

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 9:10 pm
by JKos
Separate module is good idea, but I don't really like the idea iterating all objects on every frame (no offence Komag :) ), it can cause performance problems on low end machines if you have lots of extended entities and complex custom gui. You should test it anyway, spawn hundreds of extended monsters and check if it has effect on performance.

Other idea: What about implementing a find method which extends the object on the fly, this should be a stadard way to access the extended entities

Code: Select all

find(self,entity_id)
   local entity = findEntity(entity_id)
   if not entity then return nil end   
   return self:entity(entity)
end
...

m = e:find('snail_1')
print(m.x)

This way you don't even have to store entities to a table because they are extended on the fly when the are accessed.

Another idea: implement a get method for properties.

Code: Select all

m.get('x')
m.get('y')
m.get('level')
m.get('facing')
or implement refresh method

Code: Select all

m.refresh() -- refreshes all properties

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 10:09 pm
by Diarmuid
JKos wrote:This way you don't even have to store entities to a table because they are extended on the fly when the are accessed.
But they are stored in e.objects anyway, right? If not adding methods is not possible. And m is just a pointer to e.objects.snail_1. (If I understand how it works...)

But I don't want it to be usable only when generated on the fly, because I want to pre-extend monsters for spell hooks, to make scripting transparent. I've rewritten my entire code, and you can now write something like that to make fireballs stun monsters for 5 seconds if the caster's skill is more than 25 (caster magic skills are stored in the spell object when it's cast to allow easy retrival):

Code: Select all

exsp:cloneSpell("fireball",{
	baseSpell = "fireball",
	onMonsterHit = function(self, monster)
		if self.caster.fire_magic > 25 then
			monster:hold(5)
		end
	end
	}
)
But it's true that if it's just for using methods, we don't need the constant updating (I've tested it with 20-30 monsters with no noticeable performance drop, though). I like the refresh idea, this way you can call it only if your script needs to manipulate the object position in realtime.

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 11:07 pm
by JKos
sorry, of course you have to store them to a table, I just wasn't thinking clearly. But refresh could maybe be the best solution.
One more idea: You could add a monster:onMove and monster:onTurn hooks which updates the properties, but it won't work if monster is teleported. And it only works with monsters and party.

Code: Select all

updateProperties = function(monster,dir)
	extender.objects[monster.id].x = monster.x
	extender.objects[monster.id].y = monster.y
	extender.objects[monster.id].level = monster.level
	extender.objects[monster.id].facing = monster.facing
end

fw.setHook('monsters.extender.onMove',updateProperties,1) -- use hook ordinal 1 so they are called before any other hooks
fw.setHook('monsters.extender.onTurn',updateProperties,1)

Re: Adding methods to monsters

Posted: Thu Jan 10, 2013 11:18 pm
by Diarmuid
I've thought about the hooks, but they are not reliable enough, because the monster will still be in the previous tile for half the time it takes it to move from one tile to the next. For about 0.1 to 0.4 seconds, object[id].x and findEntity(id).x will return different values.

You cannot imagine how much trouble I had with that in exsp hook detection algorithms, because collision detection is done in "real" 3d space, while entities properties are "tile-based". So a spell and a monster could return the same x, y coords, but the spell was missing the monster because the monster was "leaving" the side of square while the spell was passing in the center. The same way, if a skeleton warrior attacks you he moves towards the party, and if you use a standard "spawn" method to spawn a spell in his square, the spell will be spawned behind the monster and just fly away. I had to put in all kinds of second and third methods of detection to fail-safe all those corner cases.