Iterating through inventory question

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
Post Reply
Treybor
Posts: 93
Joined: Wed Jun 06, 2012 3:13 am

Iterating through inventory question

Post by Treybor »

I was playing around with this script. It also me to check and see if a champion in the adventures group is wearing Boots of Valor, and if not open a secret door:

Code: Select all

function itemCheckbootsvalor()
local thisPlayer = 1
local itemSlot = 1
local currItem

for thisPlayer=1,4,1 do
for itemSlot=1,31,1 do
if party:getChampion(thisPlayer):getItem(itemSlot) ~= nil then 
currItem = party:getChampion(thisPlayer):getItem(itemSlot).name
if currItem == “boots_valor” then
return true
else
secret_door:open()
end
end 
itemSlot = itemSlot + 1
end
thisPlayer = thisPlayer + 1 
end 
end

Can this be easily modified to iterate through a set of items, instead of specifying a set of identical functions?
I tried some basic iteration and I think my logic is flawed....
Can I call an itteration function from this one and then call back to this function, so the "local currItem" is always different?
Or am I approaching this the wrong way?
dasarby
Posts: 28
Joined: Sun Dec 30, 2012 10:46 pm

Re: Iterating through inventory question

Post by dasarby »

I see a couple of problems with your script as is.

Firstly, you don't need to increment your counters on the for loops by hand. That is what the third number is in the for loop (1 for your cases, meaning increment by one each time it loops) as it stands, you are actual skipping every other slot.

Secondly, if your if statement opens the door if there is any equipped item that is not the boots. Your return is correct, but the open should be at the end of the function (do it if you never found the boots)

Lastly, you are checking all the way to slot 31, which means having the boots in the backpack counts. Not sure if this is what you want, but checking just slot 4 will be enough for the boots.

As far as the second part of your question, you could make the item name a parameter, something like this:

Code: Select all

function checkItemEquiped(itemName)
	local thisPlayer
	local itemSlot 
	local currItem

	for thisPlayer=1,4,1 do
		for itemSlot=1,10,1 do
			if party:getChampion(thisPlayer):getItem(itemSlot) ~= nil then 
				currItem = party:getChampion(thisPlayer):getItem(itemSlot)
				if currItem ~= nil and currItem.name == “boots_valor” then
					return true
				end
			end 
		end
	end
	return false
end
Then, you write a smaller, simpler function to check for the boots (or whatever other item you want)

Code: Select all

function checkBootsOfValor()
	if checkItemEquiped("boots_valor") then
		secret_door:close()
	else
		secret_door:open()
	end
end
Treybor
Posts: 93
Joined: Wed Jun 06, 2012 3:13 am

Re: Iterating through inventory question

Post by Treybor »

dasarby wrote: I see a couple of problems with your script as is.

Firstly, you don't need to increment your counters on the for loops by hand. That is what the third number is in the for loop (1 for your cases, meaning increment by one each time it loops) as it stands, you are actual skipping every other slot.
I thought the counters started at zero and that I'd have to specify them as starting at 1 instead. Thanks.
dasarby wrote: Secondly, if your if statement opens the door if there is any equipped item that is not the boots. Your return is correct, but the open should be at the end of the function (do it if you never found the boots)
Lastly, you are checking all the way to slot 31, which means having the boots in the backpack counts. Not sure if this is what you want, but checking just slot 4 will be enough for the boots.
I should have given more details. I'm trying to ensure the charcaters drop a specific set of items before the trigger opens a the exit.
This includes all inventory as well.
dasarby wrote:
As far as the second part of your question, you could make the item name a parameter, something like this:

Code: Select all

function checkItemEquiped(itemName)
	local thisPlayer
	local itemSlot 
	local currItem

	for thisPlayer=1,4,1 do
		for itemSlot=1,10,1 do
			if party:getChampion(thisPlayer):getItem(itemSlot) ~= nil then 
				currItem = party:getChampion(thisPlayer):getItem(itemSlot)
				if currItem ~= nil and currItem.name == “boots_valor” then
					return true
				end
			end 
		end
	end
	return false
end
Then, you write a smaller, simpler function to check for the boots (or whatever other item you want)

Code: Select all

function checkBootsOfValor()
	if checkItemEquiped("boots_valor") then
		secret_door:close()
	else
		secret_door:open()
	end
end
Thanks for this. Can I do a an "and" in this if statement? So it looks for all the items at once for each champion?

EXAMPLE:
if currItem ~= nil and currItem.name == “boots_valor” and currItem.name == “item2” and currItem.name == “item3" then
dasarby
Posts: 28
Joined: Sun Dec 30, 2012 10:46 pm

Re: Iterating through inventory question

Post by dasarby »

That's probably not what you want. Each time the if statement is called, you have a specific item held in the currItem variable.
Therefore a statement like

Code: Select all

currItem.name == "item1" and currItem.name == "item2"
will never actually be true, because currItem's name can't be both item1 and item2

Probably what you want here is an "or", but you should still check for nil, so it looks like this:

Code: Select all

if currItem ~= nil and (currItem.name == 'boots_valor" or currItem.name == "item2" or currItem.name == "item3") then 
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Iterating through inventory question

Post by Xanathar »

I know it's a bit of advertisement :lol:, but I would suggest you try to use grimq (see my signature for link) for this. Grimq is a library meant to simplify (after a steep learning curve at first, I admit) these kind of problems.

In grimq the code for boots of valor case would be:

Code: Select all

local hasboots = grimq.fromPartyInventory(true, nil, true):where("name", "boots_valor"):any()

if (not hasboots) then
	secret_door:open()
end
In this way you are covered also for the cases where the player puts the boots in a sack, or on the mouse cursor or even in a mortar kept inside a sack held with the mouse. Handling those cases from scratch is more difficult.

Nitpicking note: technically there is still a way to cheat the system - if you put the boots inside a mortar, the mortar inside a sack, keep the mortar open and put the sack into an alcove, you can still fetch the boots although they are in the alcove, but this is an engine bug, so we can't do anything about this :)

Disclaimer: code not tested :)
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
Komag
Posts: 3654
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Iterating through inventory question

Post by Komag »

Xanathar wrote:Nitpicking note: technically there is still a way to cheat the system - if you put the boots inside a mortar, the mortar inside a sack, keep the mortar open and put the sack into an alcove, you can still fetch the boots although they are in the alcove, but this is an engine bug, so we can't do anything about this :)
Holy cow that's crazy! Has anyone tested this in the original game, such as the Sword of Nex at the Fighter's Challenge door?
Finished Dungeons - complete mods to play
Treybor
Posts: 93
Joined: Wed Jun 06, 2012 3:13 am

Re: Iterating through inventory question

Post by Treybor »

Wow! Thanks for the tips guys. I'll try it when I get home tonight.
Post Reply