Object id suddenly doesn't exist in inventory :/

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!
TheAdder
Posts: 26
Joined: Sat Mar 28, 2015 4:56 pm

Object id suddenly doesn't exist in inventory :/

Post by TheAdder » Wed Aug 08, 2018 3:56 am

Hello All,

I'm getting quite far in creating a massive Grimrock 2 mod which is full of high quality puzzles and environments and is intended to feel like a return to Nex for an entirely new adventure worthy of the original (hopefully).

I'm enjoying the process a lot, but have just encountered a really weird thing that doesn't make any sense to me at all.

I have a scroll in the dungeon (it starts in an alcove) with the id of "magscroll". It is a magic scroll. In the party:onMove hook, I have the following line which is supposed to change the text on the magic scroll to say where the party is on the level:

magscroll.scrollitem:setScrollText("The party is at "..tostring(party.x)..","..tostring(party.y))

This works, as long as the scroll is in the alcove, on the floor, or otherwise not in anyone's inventory. The player can stand in one place, picking up and putting down the scroll as much as they like, and as long as they put the scroll down and walk off, everything is fine. However, if they pick up the scroll and have it in the mouse cursor or their inventory, the game crashes the moment they move, saying that magscroll is a nil value.

How is it that lua suddenly "forgets" the id of an item unless it's on the floor or in an alcove? The item is still in existence, it's still in the game, I haven't changed or deleted its ID. What is it's ID suddenly unknown to lua just because its not on the floor?

What gives? :/

minmay
Posts: 2656
Joined: Mon Sep 23, 2013 2:24 am

Re: Object id suddenly doesn't exist in inventory :/

Post by minmay » Wed Aug 08, 2018 7:43 am

When you use an object id like that, the game actually converts it to a findEntity call.

Code: Select all

magscroll.scrollitem:setScrollText(...)
is functionally equivalent to

Code: Select all

findEntity("magscroll").scrollitem:setScrollText(...)
(as long as you haven't defined a variable named magscroll yourself).

And findEntity() can only find objects that are on a map. If an object is in an inventory (champion, monster, ContainerItemComponent...) then it's not on a map, and findEntity() won't find it.

As it happens, I've already written a script to find an object no matter where it is. You can copy and paste this into any ScriptComponent. Here it is:

Code: Select all

-- Find the object with this id, even if it's in something's inventory.
--
-- id: id of the object to find
-- startingLevel: level number to begin the search on. If you know the item is
--     most likely on a specific level you should provide this argument. After
--     this level is searched, if the item wasn't found there, every other
--     level (starting from 1) will be searched.
--     If startingLevel is nil it simply starts from level 1.
--     Searching the entire dungeon is expensive!
--
-- Returns the GameObject if it was found, nil otherwise.
function findAnywhere(id, startingLevel)
	local mouse = getMouseItem()
	if mouse then
		if mouse.go.id == id then
			return mouse.go
		else
			for _,c in mouse.go:componentIterator() do
				if c:getClass() == "ContainerItemComponent" then
					local rval = _findInContainer(id, c)
					if rval then return rval end
				end
			end
		end
	end

	-- If it's on a map, it's fast to find.
	local rval = findEntity(id)
	if rval then
		return rval
	end

	if startingLevel then
		rval = _findOnLevel(id, startingLevel)
	end
	if not rval then
		for l=1,Dungeon.getMaxLevels() do
			if l ~= startingLevel then rval = _findOnLevel(id,l) end
			if rval then break end
		end
	end
	return rval
end
function _findOnLevel(id, levelNumber)
	for e in Dungeon.getMap(levelNumber):allEntities() do
		for _,c in e:componentIterator() do
			local cls = c:getClass()
			if cls == "ContainerItemComponent" then
				local rval = _findInContainer(id, c)
				if rval then return rval end
			elseif cls == "PartyComponent" then
				for i=1,4 do
					local champ = c:getChampion(i)
					for _,itm in champ:carriedItems() do
						if itm.go.id == id then
							return itm.go
						else
							for _,c in itm.go:componentIterator() do
								if c:getClass() == "ContainerItemComponent" then
									local rval = _findInContainer(id, c)
									if rval then return rval end
								end
							end
						end
					end
				end
			elseif cls == "MonsterComponent" then
				for _,itm in c:contents() do
					if itm.go.id == id then
						return itm.go
					else
						for _,c in itm.go:componentIterator() do
							if c:getClass() == "ContainerItemComponent" then
								local rval = _findInContainer(id, c)
								if rval then return rval end
							end
						end
					end
				end
			end
		end
	end
end
function _findInContainer(id, c)
	for index=1,c:getCapacity() do
		local itm = c:getItem(index)
		if itm then
			if itm.go.id == id then
				return itm.go
			else
				for _,comp in itm.go:componentIterator() do
					if comp:getClass() == "ContainerItemComponent" then
						local rval = _findInContainer(id, comp)
						if rval then return rval end
					end
				end
			end
		end
	end
end
This can find items that are in the mouse slot, anywhere in a champion's inventory (including their equipment), arbitrarily deeply nested containers, in monster inventories, and in arbitrarily deeply nested containers that are themselves in monster or champion inventories.
Then, you'd use

Code: Select all

script_entity_1.script.findAnywhere("magscroll").scrollitem:setScrollText(...)
Do whatever you like with the script, I don't need or want credit.

(If for some reason you put an item in a monster's inventory and then put that monster into a container or another monster or champion's inventory, this script won't find it, but if you really need that you can pretty easily modify 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.

TheAdder
Posts: 26
Joined: Sat Mar 28, 2015 4:56 pm

Re: Object id suddenly doesn't exist in inventory :/

Post by TheAdder » Wed Aug 08, 2018 10:17 pm

Wow, thank you. That's exactly what I need.

I had already got around it (sort of) by having a wall text that reported the information, but obviously the party can't carry wall text around with them - so this is perfect.

In case you're wondering what I'm doing:

The party can control a minion (as in Dungeon Master 2) which can go off and move around under control of the party, fetching back items from normally inaccessible places. Obviously, if the minion is out of sight and you can't see it moving around, you need to be able to tell where it is - hence a magic scroll which reports the party and minion positions on the map.

TheAdder
Posts: 26
Joined: Sat Mar 28, 2015 4:56 pm

Re: Object id suddenly doesn't exist in inventory :/

Post by TheAdder » Sat Aug 11, 2018 6:54 pm

Ok, here's something else that's bugging me.

When I throw something, it gets pulled down by gravity until it hits the floor. I want a series of teleports in my game that turn objects right so that when you throw something it gets rotated around the room until it hits a wall and falls onto a pressure pad - but at the moment, a thrown rock (for example), gets rotated by the first teleporter and then falls to the ground before reaching the second.

How do I make an object fly at the same height forever until it hits something?

minmay
Posts: 2656
Joined: Mon Sep 23, 2013 2:24 am

Re: Object id suddenly doesn't exist in inventory :/

Post by minmay » Sat Aug 11, 2018 7:43 pm

Use ProjectileComponent:setGravity() and ProjectileComponent:setFallingVelocity() to set its gravity and falling velocity to 0.
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.

Zo Kath Ra
Posts: 829
Joined: Sat Apr 21, 2012 9:57 am
Location: Germany

Re: Object id suddenly doesn't exist in inventory :/

Post by Zo Kath Ra » Sat Aug 11, 2018 8:01 pm

TheAdder wrote:
Sat Aug 11, 2018 6:54 pm
Ok, here's something else that's bugging me.

When I throw something, it gets pulled down by gravity until it hits the floor. I want a series of teleports in my game that turn objects right so that when you throw something it gets rotated around the room until it hits a wall and falls onto a pressure pad - but at the moment, a thrown rock (for example), gets rotated by the first teleporter and then falls to the ground before reaching the second.

How do I make an object fly at the same height forever until it hits something?
This code only works if you place the rock in a champion's hand, and then throw it by right-clicking:

Code: Select all

defineObject{
	name = "rock_new",
	baseObject = "base_item",
	components = {
		{
			class = "Model",
			model = "assets/models/items/rock.fbx",
		},
		{
			class = "Item",
			uiName = "Rock New",
			gfxIndex = 45,
			impactSound = "impact_blunt",
			stackable = true,
			projectileRotationSpeed = 10,
			projectileRotationZ = -30,
			weight = 0.7,
			traits = { "throwing_weapon" },
		},
		{
			class = "ThrowAttack",
			attackPower = 4,
			cooldown = 4,
                	onPostAttack = function(self, champion, slot)
         	           self.go.projectile:setGravity(0)
        	        end,
		},
		{
			class = "AmmoItem",
			ammoType = "rock",
		},
	},
	tags = { "weapon", "weapon_throwing" },
}
But it doesn't work if you throw the rock manually, i.e. left-click on the rock to make it the mouse cursor, then left-click on the air in front of the party.
It also doesn't work if you fire the rock with a sling.

TheAdder
Posts: 26
Joined: Sat Mar 28, 2015 4:56 pm

Re: Object id suddenly doesn't exist in inventory :/

Post by TheAdder » Sun Aug 12, 2018 11:41 pm

Thank you :)

I've made the puzzle work using the serpent staff, which is obviously consistent, but at least I now have some code to learn from!

Pompidom
Posts: 476
Joined: Sun May 06, 2018 9:42 pm

Re: Object id suddenly doesn't exist in inventory :/

Post by Pompidom » Mon Aug 13, 2018 12:19 pm

So basically the serpent staff spit ability travels through all teleports on default? That's a nice find!
Interesting, maybe I'll build something similar in my dungeon :)

Using the serpent staff spit is an excellent example how to avoid the whole stone throwing issue you ran into :)
I like it very much.

You just gave me an idea how to add extra content to my mod where you can open up extra doors in previously visited areas once you acquire unlocking items like the serpent staff.

TheAdder
Posts: 26
Joined: Sat Mar 28, 2015 4:56 pm

Re: Object id suddenly doesn't exist in inventory :/

Post by TheAdder » Thu Aug 16, 2018 5:24 pm

Using the serpent staff also allows you to make the whole puzzle only solvable when the party has the staff, which adds an element to the whole equation. Obviously, rocks are everywhere.

I also like the idea of playing with the idea that different objects fly different distances, so having pressure pads with no wall behind them so that you have to calculate the object that will fly the right distance (assuming you can't move backwards)

Zo Kath Ra
Posts: 829
Joined: Sat Apr 21, 2012 9:57 am
Location: Germany

Re: Object id suddenly doesn't exist in inventory :/

Post by Zo Kath Ra » Thu Aug 16, 2018 6:00 pm

TheAdder wrote:
Thu Aug 16, 2018 5:24 pm
Obviously, rocks are everywhere.
If I made a mod, there would only be one rock.
And it'd be called a "grey gem".

Post Reply