Modding infodump

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Modding infodump

Post by AndakRainor »

minmay wrote:If you need an object every frame, I'd suggest spawning it once and putting it on a map or in a ContainerItemComponent, instead of spawning and destroying it every frame - you don't save any memory by doing that, and the impact on saving performance by one object (or ten) is vanishingly small.
To be more precise, I mainly use locally spawned then destroyed items every frame when I need to show their icons in a GUI that don't come from any real item. For example, my mortar and pestle GUI is not a real container and only shows ingredients and resulting potion, that not longer exist or does not exist yet. If I had to change this system I could store real items somewhere or again put all items definitions used by the mod in a table...
minmay wrote:I can't reproduce this. For me, that exact situation works perfectly. Are you sure you aren't doing something weird with sortOffset and/or depth bias? For that matter, you may be able to use sortOffset to fix it (I'm not sure exactly how it works w/r/t particles, it might only be relevant for triangles/particles that are coplanar and intersecting...).
That's right, adding a sortOffset to my ParticleComponent resolved the problem. In my case, the ParticleComponent belongs to an object with SkyComponent, and the model components have sortOffset fields (same as the original forest sky). Now that the particle also has it the problem is gone for some reason!
minmay wrote:It's the same problem with destroying the ItemComponent: the re-created SurfaceComponent won't have any hooks. Of course it will be fine if you don't use hooks.
It sounds like ContainerItemComponent would work better for this since you wouldn't have to add the object to the map at all in the process.
Hooks on the SurfaceComponent, onInsertItem and onRemoveItem? In that case, no I certainly don't use it. If you talk about hooks on items, I cannot predict it, any item in an inventory could be placed in my object if the player decides to dismiss a champion. Also, when I considered the ConainerItemComponent option, I found 2 problems; can we get any capacity? (32 slots needed to store a full inventory)? What about containers in that inventory that don't fit in other containers?

Edit : could I use setPosition to put an object with no map on a map, then destroy it to collect the garbage?
minmay
Posts: 2789
Joined: Mon Sep 23, 2013 2:24 am

Re: Modding infodump

Post by minmay »

AndakRainor wrote:
minmay wrote:If you need an object every frame, I'd suggest spawning it once and putting it on a map or in a ContainerItemComponent, instead of spawning and destroying it every frame - you don't save any memory by doing that, and the impact on saving performance by one object (or ten) is vanishingly small.
To be more precise, I mainly use locally spawned then destroyed items every frame when I need to show their icons in a GUI that don't come from any real item. For example, my mortar and pestle GUI is not a real container and only shows ingredients and resulting potion, that not longer exist or does not exist yet. If I had to change this system I could store real items somewhere or again put all items definitions used by the mod in a table...
Well, first of all, you only need to get the gfxIndex from the item once, then you can store it until the player closes the mortar or changes the recipe. And you can just spawn the item on a map in this case, in which case destroying it is no problem and there will be no resource leaking.
Also keep in mind that a single item doesn't use a lot of memory. Losing the occasional item is not really something to worry about, since it will go away when the player loads. I wouldn't deliberately do it every frame though.
AndakRainor wrote:
minmay wrote:It's the same problem with destroying the ItemComponent: the re-created SurfaceComponent won't have any hooks. Of course it will be fine if you don't use hooks.
It sounds like ContainerItemComponent would work better for this since you wouldn't have to add the object to the map at all in the process.
Hooks on the SurfaceComponent, onInsertItem and onRemoveItem? In that case, no I certainly don't use it. If you talk about hooks on items, I cannot predict it, any item in an inventory could be placed in my object if the player decides to dismiss a champion. Also, when I considered the ConainerItemComponent option, I found 2 problems; can we get any capacity? (32 slots needed to store a full inventory)? What about containers in that inventory that don't fit in other containers?
Hooks on the surface, yeah. Hooks on the items will stay; as far as I know, destroying the surface doesn't molest the items except for removing them from the surface.
AndakRainor wrote:Edit : could I use setPosition to put an object with no map on a map, then destroy it to collect the garbage?
minmay wrote:- The following methods will CRASH if you call them with an object that is not on a map:
GameObject:destroy()
GameObject:destroyDelayed()
GameObject:getPosition()
GameObject:getSubtileOffset()
GameObject:removeAllComponents()
GameObject:removeComponent()
GameObject:setPosition()
GameObject:setSubtileOffset()
GameObject:setWorldPosition()
GameObject:spawn()
If the object has an ItemComponent, I'm pretty sure using SurfaceComponent:addItem() puts it on the map safely, as long as you remove it from any inventories first. So put a dummy surface in some inaccessible place and when you want to destroy an item, you can add it to that and then call destroy() on the item. Putting it in a monster's inventory and then killing the monster would also definitely work (again, as long as you remember to remove it from any other inventories first), if you prefer that for some reason.
Edit: After testing this, putting the item on a surface or socket directly from inventory and then destroying the surface or socket does not really work (the item ends up tainted and picking it up will cause an error), so use the monster approach instead.
AndakRainor wrote:Also, when I considered the ConainerItemComponent option, I found 2 problems; can we get any capacity? (32 slots needed to store a full inventory)? What about containers in that inventory that don't fit in other containers?
My mistake, it does appear that containers in Grimrock 2 have fixed capacity. You could store any number of items by using more containers (possibly placing containers inside containers), of course, though this may not be the best solution and is certainly not a graceful one. The scripting interface will allow you to put items with fitContainer = false into a container without complaining; that flag is basically just there so that players can't put a container inside itself (put sack 1 in sack 2, then put sack 2 in sack 1, infinite recursion results - this could happen in Grimrock 1 if a dungeon had more than one mortar and pestle in it).
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
minmay
Posts: 2789
Joined: Mon Sep 23, 2013 2:24 am

Re: Modding infodump

Post by minmay »

Ok, here is how you add an item to the map in Grimrock 2:

Code: Select all

defineObject{
	name = "dummy_monster",
	placement = "floor",
	components = {
		{
			class = "Model",
			model = "assets/models/monsters/snail.fbx", -- simplest existing valid monster model (would be easy to make a new one though)
			storeSourceData = true,
			enabled = false,
		},
		{
			class = "Animation",
			animations = {idle = "assets/animations/monsters/snail/snail_idle.fbx"},
			currentLevelOnly = true,
			enabled = false,
		},
		{
			class = "Monster",
			meshName = "snail_mesh",
			hitSound = "snail_hit",
			dieSound = "snail_die",
			hitEffect = "hit_goo",
			health = 100,
			capsuleHeight = 0,
			capsuleRadius = 0,
			collisionRadius = 0,
			exp = 0,
		},
	}
}

Code: Select all

-- Add an item to the map at the specified position. First argument should be an ItemComponent or table of ItemComponents.
function addItemToMap(item,level,x,y,facing,elevation,wpos)
	if item.go.map then
		Console.warn(string.format("Item %s already has a map, aborting.",item.go.id))
		return false
	else
		local m = spawn("dummy_monster",level,x,y,facing,elevation)
		m.monster:addItem(item)
		m.monster:dropAllItems()
		m:destroy()
	end
end
dummy_monster can be optimized a little more by making a new model.

Note that you still need to remove the item from any existing inventories beforehand (setMouseItem(nil), Champion:removeItem(), ContainerItemComponent:removeItem(), etc), and this function (with the map check removed) still does not remove items from surfaces/sockets.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
THOM
Posts: 1280
Joined: Wed Nov 20, 2013 11:35 pm
Location: Germany - Cologne
Contact:

Re: Modding infodump

Post by THOM »

Sorry, minmay, I don't get it. Are you saying the way of your script is the only way to spawn an item into the game?
THOM formaly known as tschrage
_______________________________________________
My MOD (LoG1): Castle Ringfort Thread
My MOD (LoG2): Journey To Justice Thread | Download
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Modding infodump

Post by AndakRainor »

No the goal here is to destroy items that are not on a map to avoid them taking memory space until the next reload of a saved game. We are talking about very small memory allocations though, so generally you don"t have to worry about that.

In my case I feel (but did not bother to measure any actual numbers for that!) this is important because I have several systems using items not on the map, as a mortar and pestle UI showing icons from non real existing items every frame (but those can be destroyed the usual way as a temporary spawned item has a map). For example when you drop an herb in my mortar UI, the mouseItem is set to nil and the mortar only remembers the name and quantity of that herb for that slot. I also have merchants and out of the party champions wandering in taverns, so this can add up to the number of items I can manipulate frequently!

For my mortar and pestle UI I used this to destroy items not on any map, in a script entity named champions:

Code: Select all

self.go:createComponent("Surface")
self.go.surface:setSize(vec(1.5, 1.5))

...

icon = {}
function getItemIconByName(name)
  if not icon[name] then
    local o = spawn(name)
    local atlas, x, y, stackable = getItemIcon(o.item)
    icon[name] = {atlas, x, y, stackable}
    destroyItem(o.item)
  end
  return unpack(icon[name])
end

...

destroyItem(mouseItem)

...

function destroyItem(item)
  if not item.go.map then
    --print("surface count before adding item : "..self.go.surface:count())
    self.go.surface:addItem(item)
  end
  item.go:destroy()
end
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Modding infodump

Post by AndakRainor »

Is it even useful to destroy the dummy monster if you hide it and use it every time you want to give a map to an item?
minmay
Posts: 2789
Joined: Mon Sep 23, 2013 2:24 am

Re: Modding infodump

Post by minmay »

THOM wrote:Sorry, minmay, I don't get it. Are you saying the way of your script is the only way to spawn an item into the game?
It's the only way I know of to add an existing item to the map without putting it in a surface or socket. This is important because it's the only way to remove an item from a champion or ContainerItemComponent's inventory and put it on the ground.
AndakRainor wrote:No the goal here is to destroy items that are not on a map to avoid them taking memory space until the next reload of a saved game.
If you are destroying the item then you can just use a surface or socket instead of a monster. This approach is for items that you actually want to keep on the map.
AndakRainor wrote:Is it even useful to destroy the dummy monster if you hide it and use it every time you want to give a map to an item?
I suppose not.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Modding infodump

Post by AndakRainor »

minmay wrote:
THOM wrote:Sorry, minmay, I don't get it. Are you saying the way of your script is the only way to spawn an item into the game?
It's the only way I know of to add an existing item to the map without putting it in a surface or socket. This is important because it's the only way to remove an item from a champion or ContainerItemComponent's inventory and put it on the ground.
AndakRainor wrote:No the goal here is to destroy items that are not on a map to avoid them taking memory space until the next reload of a saved game.
If you are destroying the item then you can just use a surface or socket instead of a monster. This approach is for items that you actually want to keep on the map.
AndakRainor wrote:Is it even useful to destroy the dummy monster if you hide it and use it every time you want to give a map to an item?
I suppose not.
Yeah thank you it works perfectly from what I see so far. It is also a good way to simplify the code, I am grouping all item storage I used into one script only, with the dummy monster to store and a surface to destroy all items. The benefit is I no longer need to manage surfaces components creation and destruction, and items stored directly on the ground are available to go anywhere else without even the need for a remove from storage function.

Two questions come to my mind; could I write an optimized replacement for the findEntity() function now that I know the exact tile where all my hidden items are stored? To use a single eternal dummy monster, would it be better to use math.huge for its health or something (re-spawn it only when not found)?
minmay
Posts: 2789
Joined: Mon Sep 23, 2013 2:24 am

Re: Modding infodump

Post by minmay »

AndakRainor wrote:Two questions come to my mind; could I write an optimized replacement for the findEntity() function now that I know the exact tile where all my hidden items are stored?
No, findEntity() is very fast, even on slow computers you could call it 1000 times per frame without problems. Finding an object via Map:entitiesAt() is much, much slower. In fact, just calling Map:entitiesAt() and throwing away the iterator takes several times longer than calling findEntity() even if there are no entities in the square. SurfaceComponent:contents() and MonsterComponent:contents() might be slightly faster than findEntity() if there are very few items in the surface/monster, but I can't imagine that's worth it. Just use findEntity().
AndakRainor wrote:To use a single eternal dummy monster, would it be better to use math.huge for its health or something (re-spawn it only when not found)?
MonsterComponent:setMonsterFlag("CantDie",true)
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
Duncan1246
Posts: 404
Joined: Mon Jan 19, 2015 7:42 pm

Re: Modding infodump

Post by Duncan1246 »

I have read only some parts of your huge contribution for now, so perhaps the answer to my question is already in here: I wonder if it is possible to changes the sky (by example from a winter sky to a forest day sky) dynamically, I means after the party go out the level for the next one then go in it. I think it is not possible, but I don't know?
The Blue Monastery (LOG1)
download at:http://www.nexusmods.com/grimrock/mods/399/?

Finisterrae(LOG2)
download at:http://www.nexusmods.com/legendofgrimrock2/mods/61/?
Post Reply