Page 1 of 2
Object id suddenly doesn't exist in inventory :/
Posted: Wed Aug 08, 2018 3:56 am
by TheAdder
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? :/
Re: Object id suddenly doesn't exist in inventory :/
Posted: Wed Aug 08, 2018 7:43 am
by minmay
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.)
Re: Object id suddenly doesn't exist in inventory :/
Posted: Wed Aug 08, 2018 10:17 pm
by TheAdder
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.
Re: Object id suddenly doesn't exist in inventory :/
Posted: Sat Aug 11, 2018 6:54 pm
by TheAdder
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?
Re: Object id suddenly doesn't exist in inventory :/
Posted: Sat Aug 11, 2018 7:43 pm
by minmay
Use ProjectileComponent:setGravity() and ProjectileComponent:setFallingVelocity() to set its gravity and falling velocity to 0.
Re: Object id suddenly doesn't exist in inventory :/
Posted: Sat Aug 11, 2018 8:01 pm
by Zo Kath Ra
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.
Re: Object id suddenly doesn't exist in inventory :/
Posted: Sun Aug 12, 2018 11:41 pm
by TheAdder
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!
Re: Object id suddenly doesn't exist in inventory :/
Posted: Mon Aug 13, 2018 12:19 pm
by Pompidom
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.
Re: Object id suddenly doesn't exist in inventory :/
Posted: Thu Aug 16, 2018 5:24 pm
by TheAdder
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)
Re: Object id suddenly doesn't exist in inventory :/
Posted: Thu Aug 16, 2018 6:00 pm
by Zo Kath Ra
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".