8. ITEM OBJECTS
items, pick, give, get, inventory




About items and inventory

Item objects are dynamic brushes that can be picked up into player's inventory. Later, they can be dropped down, or used on various action objects. They are the means of solving most of the puzzles, like fixing brocken things or satisfying game characters that might need them.

Start from the default template data and open the map in the editor. Choose the tile with the id 211, and select the mapping from 32,32 of 16x16 pixels, representing a bucket. Open the properties dialog and set the brush type to dynamic, the brush id to 100 and the brush class to item. Set the draw property to 1 (img) since dynamic objects can't write in the material map anyway. Also it is custom to set the shader to opaque for items and to place them on a heigher layer, for example layer 4, so they will be easily visible in front of other action objects.

Now, you must set a name for this item. This name will be displayed in the inventory, allowing you to identify the item when you want to drop or use it.

Open the game.gs script file and add the folowing line of code, at the end of the ObjectsSetNames callback.

Save the file and run the game. Press action in front of the bucket and see it in the inventory. Close the inventory, move a little, open the inventory again and drop the bucket down.


ObjSetName(idx, name)

Items are dynamic brushes with the class property set to item and a valid id.
This function exported by the engine sets a name for an item object specified through it's internal index in the dynamic list of brushes (use ObjFind function to get it). Item's names are usually set in the ObjectsSetNames callback from the game.gs file, that is called each time a new game begins. During the game the name of an item can be changed, but you must write additional code to deal with the loading of saved games.

There's no need to set names for other objects, because only items display their names in the inventory. But all items must have names, or you will see an empty line in the inventory dialog.


Giving items

Let's see how to give (use) an item to a game character that needs it. Open the map in the editor again and add an action character, in the same way as presented in the previous chapter. This time add Dylan (tile=175, id=1000, class=action). Save the map.

You must now add a special callback function to respond to the event of dropping an item while standing in front of Dylan. This is considered giving, or using the item with the action object. Add the following callback in the game.gs file.
Then run the game, pick the bucket and give it to Dylan.



UseObject_ID( idx )

This latent callback is requested from the action handler when the player uses (tries to drop) an item object, in front of an action object with the same id as specified in the callback's name (ID) function. The idx parameter is the internal index of the item object.
This way you can write one UseObject callback for an action object and check all the items used on it, by their indexes.

The ActionObject_ID callback has priority over the UseObject_ID callback, and if both are present, only the ActionObject_ID will be called.

In the above example, Dylan will say the same thing, no matter what item is given to him, because the index of the item is not tested in the callback. Let's correct that.
Add another item, a red bucket (set the brush color in the editor) with id=101. Give it the name "RED BUCKET". Edit the Dylan's UseObject callback as follows, then save, run and see the results.




Receiving items

Receiving an item as the result of a puzzle, is a very simple thing. In fact, the item is there all the time, but it's brush is disabled by default, preventing you to see or to pick it up. When the puzzle is solved, the item is enabled so you can pick it up and use it further.

Add another item, this time a small green leaf from the tile=218. Make sure you make it type=dynamic and class=item, with the brush id=102. Name it "LEAFS". In the properties dialog, set the disable property to 1. Place it near Dylan, on his left. Now, edit Dylan's UseObject callback as showed below and run the game. Give the red bucket to Dylan. In exchange he will give you the leafs, that were unavailable before.




The Inventory

You have already seen the player's inventory and now you'll learn a few details about how it works.

The Inventory keeps track the owned items by storing their internal indexes, in a small list (a few global variables starting with G_INVENTORY). When an item is picked up, it is in fact left there, but it is disabled, by setting it's O_DISABLE property to 1. It's index is added to the inventory list. When it's dropped down again, it is repositioned to the current player's position, by setting it's O_X and O_Y properties and it is enabled again, by setting the O_DISABLE property back to 0.

By default, the inventory can hold up to three items. This is as much as you can carry with you, at once. You can change this setting from the gamedef.gs file, the MAXINVENTORY define.

Most of the inventory's code is in the inventory.gs file.

InventoryCount()
This function returns the number of items owned in the inventory.

InventoryAdd( idx )
This function add an item to the inventory, by using it's internal index. If there is no more free space in the inventory, this function will fail and will return 0 instead of 1. So, if you want to receive an item directly into the inventory, instead of having it enabled on the ground to be picked up, always test if there is space in the inventory, before calling the InventoryAdd function.

InventorySub( idx )
This function removes an item from the inventory, by using it's internal index. If the item with the specified index is not found in the inventory, this function will fail and will return 0 instead of 1.

InventoryClear( idx )
This will empty the inventory (remove all items).

InventoryFind( idx )
This can be used to know it an item is present in the inventory. It returns -1 if the item is not in the inventory and the inventory position if it's owned.

InventoryHasItem(id) This is a simpler version of the above function. You can use it to know if an item is present in the inventory, using it's id this time (not the index). It returns 1 if the item is owned in the inventory and 0 if it's not.

OpenDialogInventory()
This latent function will open the inventory dialog, allowing the player to select an item to use. If such an item is selected, the inventory dialog is closed and the function returns the item's index. If the "exit" option is selected, the function returns -1.



More advanced interactions

You have learned how to respond to the action event, using the ActionObject callback and how to respond to the use item event, using the UseObject callback. But sometimes, you may need a character to inform you first, about what he needs, with a simple message, before you start giving him various items. So you will need a combination of both callbacks.

Try to add an ActionObject callback for Dylan, in our example. Make him say he needs his red bucket.
You will see that each time you hit action in front of him, this ActionObject callback will be called, and the UseObject callback you wrote before, will be ignored. That's because the ActionObject callback has priority over the UseObject callback. So, in this callback you must work.



Of course, you can do the whole job in the ActionObject callback, by testing if the returned index, from the inventory dialog, corresponds to the red bucket, and show the specific messages here, without the UseObject callback. Both ways are correct.


Homework

The puzzle from this chapter has a little flaw. If you give Dylan an item different from the red bucket, he will always say the same thing. That is not be very appropriate after he got his bucket back, not to mention when you give him back the leafs. Make him to ignore you, after the puzzle is solved, and make the used item to be dropped down on the ground. You will have to use Dylan's status property to store the puzzle's soleved status. You will use the DropObject function in the UseObject callback, to drop down a refused object.

Here is a possible solution.

Another extension of the puzzle would be to make the red bucket not so easy to get. Make it disabled and add another character, like Grand Dizzy, who could paint your white bucket with red paint. But he doesn't have any red paint... This is quite a puzzle and a very good exercice for you now.

Try your best to implement it
and then check our solution.

For more details check the reference chapters:
Default Template inventory implementation
The OpenDialogInventory function