[Library] - GrimQ - v1.4.3

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
JKos
Posts: 464
Joined: Wed Sep 12, 2012 10:03 pm
Location: Finland
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by JKos »

Hi,

Can I suggest a little addition to your library.

I need to respawn items with the same id, so I modified the grimq a little.
I just don't want to duplicate the grimq code in my framework, so it would be nice to have these features in your library.

Code: Select all

-- loads an item from the table (added optional id argument)
function loadItem(itemTable, level, x, y, facing,id)
   local spitem = nil
   if (level ~= nil) then
	  spitem = spawn(itemTable.name, level, x, y, facing,id)
   else
	  spitem = spawn(itemTable.name,nil,nil,nil,nil,id)
   end
   if itemTable.stackSize > 0 then
	  spitem:setStackSize(itemTable.stackSize)
   end
   if itemTable.charges > 0 then
	  spitem:setCharges(itemTable.charges)
   end            
   
   if itemTable.scrollText ~= nil then
	  spitem:setScrollText(itemTable.scrollText)
   end
   
   spitem:setFuel(itemTable.fuel)
   
   if (itemTable.subItems ~= nil) then
	  for _, subTable in pairs(itemTable.subItems) do
		 local subItem = loadItem(subTable)
		 spitem:addItem(subItem, false)
	  end
   end
   
   return spitem
end

-- Respawns an item (original id can be used)
-- If level == nil the item will be respawned to object space
function respawnItem(item,level,x,y,facing,id)
       -- if id is numeric then create a new unique id for item
       -- for some reason numeric id's are not allowed as a spawn-function argument
	if (id and string.find(id, "^%d+$")) then
		id = nil
	end
	local copy = saveItem(item)
	item:destroy()
	return loadItem(copy,level,x,y,facing,id)
end
- LoG Framework 2http://sites.google.com/site/jkoslog2 Define hooks in runtime by entity.name or entity.id + multiple hooks support.
- cloneObject viewtopic.php?f=22&t=8450
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Xanathar »

Sure, JKos, thanks for the change!
I will put it into the next version of GrimQ, in the meantime you can use your edited version as I will preserve the same interface (actually I will copy/paste your code :lol:)
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
JKos
Posts: 464
Joined: Wed Sep 12, 2012 10:03 pm
Location: Finland
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by JKos »

Thanks, now my telekinesis spell will preserve the contents of the containers too, thanks to you :)
- LoG Framework 2http://sites.google.com/site/jkoslog2 Define hooks in runtime by entity.name or entity.id + multiple hooks support.
- cloneObject viewtopic.php?f=22&t=8450
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Diarmuid »

Hi Xanathar,

I'm concerned with efficiency for my Extended Spells framework, and I was wondering if there was a difference between:

Code: Select all

local doors = grimq.fromIterator(entitiesAt(level, x+dx, y+dy))
    :where(function(v) return grimq.isDoor(v) == true; end)
    :where(function(v) return v:isClosed() == true; end)
    :where(function(v) return (v.facing+2)%4 == facing; end)
    :toArray()
and

Code: Select all

local doors = grimq.fromIterator(entitiesAt(level, x+dx, y+dy))
    :where(function(v) return grimq.isDoor(v) == true and v:isClosed() == true and (v.facing+2)%4 == facing; end)
    :toArray()
in terms of the number of calls/loops done by grimQ.

Thanks!
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Xanathar »

The second is faster. Given the fact that you are performing the query on entities in a tile (which would be of limited number) I wouldn't worry, unless you have proof that you have something to worry about.
To be clear: if I were to do it, I would choose whatever I wrote first and wouldn't worry if looping on entitiesAt, and definetely write the second version if looping on allEntitiesInWorld (and probably also for allEntities(level)).

Implementation wise, every "where" call, calls the predicate for each element and builds a new table (and a new grimq object, but that overhead should be small) as a result. Again, the system seems pretty fast, so unless you are treating really many objects, I wouldn't worry.
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Diarmuid »

Great, thanks! I haven't seen a specific problem, but as there's a lot going on, I was trying to keep the code as lightweight as possible, thinking that if I add up lots of little inneficiencies, it would sum up to something noticeable in the end. Especially as most of the scripting is happening while spells are flying around, which means particle systems which eat up a lot of resources. As I'm working with timers on precise settings, I've noticed that below 0.05s, things tend to get unreliable, and wanted to keep things in check. I was also worried about less powerful machines... I have a good CPU and was a bit afraid of assuming everything's fine.

I was not concerned that the above query iterates through too many objects, but that the query itself is called frequently.
Batty
Posts: 509
Joined: Sun Apr 15, 2012 7:04 pm

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Batty »

I thought I'd post my version of load/save items because I updated it to maintain the order in containers since we have new functions.

I also maintain the IDs of items but I take the numbered unspawnable ID say "4324" and change it to "rock_4324".

I'm sure there's a more efficient way but I'm proud I got it to work. Interesting to learn that mortars have 7 slots and not 6.

*not compatible with full GrimQ because I adapted these for my own needs.

Code: Select all

function saveItem(item)
	local itemTable = { }, slots	
	itemTable.name = item.name	
	if itemTable.name == string.sub(item.id, 1, string.len(itemTable.name)) then	
		itemTable.id = item.id else
		itemTable.id = itemTable.name.."_"..item.id	
	end	
	itemTable.charges = item:getCharges()
	itemTable.fuel = item:getFuel()
	itemTable.stack = item:getStackSize()
	itemTable.text = item:getScrollText()	
	if itemTable.name == "wooden_box" or itemTable.name == "mortar" or itemTable.name == "sack" then
		if itemTable.name == "wooden_box" then	
			slots = 10 elseif 		
			itemTable.name == "mortar" then		
			slots = 7 else		
			slots = 6		
		end	
		for i = 1, slots do	
			if i == 1 then
				itemTable.subItems = { }		
			end		
			if not item:getItem(i) then		
				itemTable.subItems[i] = nil else			
				itemTable.subItems[i] = saveItem(item:getItem(i))			
			end			
		end		
	end	
	item:destroy()	
	return itemTable	
end

function loadItem(itemTable)	
	local item = spawn(itemTable.name, nil, nil, nil, nil, itemTable.id), slots
	if itemTable.stack > 1 then
		item:setStackSize(itemTable.stack)	
	end
	if itemTable.charges > 0 then
		item:setCharges(itemTable.charges)
	end            
	if itemTable.text then
		item:setScrollText(itemTable.text)
	end
	item:setFuel(itemTable.fuel)  
	if itemTable.name == "wooden_box" or itemTable.name == "mortar" or itemTable.name == "sack" then	
		if itemTable.name == "wooden_box" then		
			slots = 10 elseif 		
			itemTable.name == "mortar" then		
			slots = 7 else	
			slots = 6		
		end	
		for i = 1, slots do	
			if itemTable.subItems[i] then		
				item:insertItem(i, loadItem(itemTable.subItems[i]))			
			end		
		end	
	end  
	return item
end
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: [Library] - GrimQ - a query library for Grimrock&Lua - v

Post by Xanathar »

Thanks !
I hope to be able to review GrimQ at the light of the beta changes soon.. Christmas preparations are taking all my free time in these days :D
Your functions will surely be of help.
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: [Library] - GrimQ - a query library for Grimrock - v1.3.

Post by Xanathar »

Finally, I'm releasing 1.3.2 of GrimQ.

Changes:

1.3:
  • Now requires LoG 1.3.6 or later
  • Fixed issues on destroy and replace methods for inventory management
  • Simplified setup - no need to set MAXLEVEL any longer
  • loadItem and copyItem now preserve scroll images and container slots
  • loadItem now allows an id to be passed
  • setLogLevel to dynamically change the log level at runtime
  • directionFromPos(fromx, fromy, tox, toy) - returns a facing value given starting and end positions
  • directionFromDelta(dx, dy) - returns a direction given the differences in x and y (the opposite of getForward)
  • destroy(entity) - can be called on any item and most entities and automatically destroys the entity in the best way, without concerns about where the entity is or what the entity is
  • replace(entity, entityToSpawn, desiredId) - can be called on any item and most entities and automatically replace the entity with another in the best way, without concerns about where the entity is or what the entity is
  • find(id) - equivalent of findEntity, but works also for items in inventory or mouse cursor
  • gameover() - kills the party (equivalent to destroy(party))
  • isContainerOrAlcove(entity) - returns true if entity is either a container or an alcove/altar
1.3.2:
  • Fixed (hopefully) all corner cases
  • Extended items generalized to extended entities
  • findEx(id) – equivalent of findEntity, but works also for items in inventory or mouse cursor and returns an extended entity instead
  • getEx(entity) – returns the extended entity from an entity
  • fromContainerItemEx - returns a grimq structure filled with extended entities of the contents of a container
I highlighted in yellow the most interesting ones: those mean you can write

Code: Select all

grimq.replace(grimq.find("myitem"), "blue_gem")
to replace the item with id "myitem" with a blue gem, wherever in the world it is, including mouse cursor, alcoves, inventory, sacks held in hand, sacks held in mouse cursor, mortars held in sacks held in alcoves (no kidding), etc.

A warning: these methods may occasionally be very expensive performance-wise, so use with caution (say, not in a fast timer!).

You should even be able to write

Code: Select all

grimq.replace(grimq.find("myitem"), "blue_gem", "myitem")
to preserve the same id.

Further and deeper details in the docs!

Let me know of any issues.
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: [Library] - GrimQ - a query library for Grimrock - v1.3.

Post by Diarmuid »

That's great! Thanks so much!! :)

You may not know yet (it's super secret tech, shhhh...), but I developped for the LotNR project a way to store entire dungeon states and party inventories in save games so that this info can be transferred between different .dat files, or even between updates of the same .dat file. I've copied and altered some of your grimq functions over there (credit will be given), and I coded all kinds of workarounds for corner cases - this update will make my work so much easier... :)
Post Reply