How to make a simple adventure game inventory

Dear friends. I have made an example of a first level with Email3lang which contains a simple inventory, type Adventure game, i.e. the player collect items and use them on objects in the scene to go on. I like to explain here how to make its features.

Features of the inventory:

  • it prints the items of the inventory as text to the screen if a key is pressed,
  • it adds objects from the scene with mouse click to the inventory when the player is near to the object,
  • taken objects can be put back to their original place when the player is near to it,
  • taken objects will disappear from the scene and will stay disappeared when the scene is reloaded,
  • inventory will be saved to level 2,
  • status of the ‘objects which can be taken’ will be used to trigger s.th., p.e. a message,
  • objects of the inventory can be used on objects in the scene to trigger s.th., p.e. open a locked door with a key from the inventory.

Features of the demo .blends:
See Contents beneath of Credits

Desadvantage of this inventory:

Each kind of “getable item” needs an own keyboard key (or a combination of two), which limits the amount of them in the game.
If you like to see my progress with this issue, visit https://blenderartists.org/forum/showthread.php?413884-Start-making-an-inventory-accessed-by-keys&p=3139628&viewfull=1#post3139628 Start making an inventory accessed by key.

Credits :

The .blend which I will explain in this thread till post#7 can be found here: https://blenderartists.org/forum/showthread.php?408743-Email-account-with-3-languages-and-manual&p=3129809&viewfull=1#post3129809 / The related credits too.
The explanations from Email3lang_ex1stLvl_01.blend are taken out of a greater context as the one in the demo .blend.

At the bottom of this post you can find a little demo .blend which I made to show the elementary code of the inventory in one piece and how it works. For some details of it see chapter ### Preliminary ###. I put also some commentaries in the module file. And I made some progresses with that demo, described in post #12 till #19.

### Contents ###
Preliminary -> 3129829
Initializing the inventory
Printing the inventory to screenMaking the text disappear

Increasing the text resolution

Foreword to the get object from the scene to the inventory procedure -> 3130146How the order of scenes is organized

Adding an object from the scene to the inventory -> 3130150
Adding an object to the scene from layer 2 -> 3130152
How works 'def init():'Hiding the objects in the inventory from the scene

Using status of the objects which can be taken to trigger s.th. -> 3130155
Dropping an object from the inventory back to its origin
Using objects of the inventory on objects of the scene -> 3130159Features of the demo .blends:
Dropping an item of the inventory to more than one place -> 3130471Making items stackable in the inventory -> 3131524
How to interact with a container -> 3132468Showing a message if the player tries to drop an item to the ground

To interact with Box_01 -> 3132476

Using textwrap.fill to show the inventory -> 3132815
How to show an object name with mouse over any
How to get an certain “added object, part 1” -> 3133122
How to get an certain “added object, part 2” -> 3134355
How to sort a dictionary by key or value

In the next post we will start with some preliminaries.

Greetings,
achisp

A simplified Demo.blend made with Blender 2.74, which I will describe from post #12 to #19:
(For the descriptions from post#1 till post#7 I have used another .blend, the link is above.)

Revision history:
2016-12-23.: SimpleInventory_Demo_06.blend

  • Moved Logic bricks from object on 2. layer to player object. Details and .blend see here -> 3133122

2016-12-22.: SimpleInventory_Demo_05.blend

  • Amplified: (1.) textwrap.fill() is used for the inventories; (2.) The item names are shown with Mouse over any. Details and .blend see here -> 3132815

    2016-12-21.: SimpleInventory_Demo_04.blend

  • Amplified: Interaction with a container is added. Details and .blend see here -> 3132468

2016-12-19.: SimpleInventory_Demo_03.blend

  • Amplified: The item is stackable in the inventory. Details and .blend see here -> 3131524

    2016-12-16.: SimpleInventory_Demo_02.blend

  • Amplified: The same item can be dropped/taken at two points in the scene. Details and .blend see here -> 3130471

    2016-12-20.: SimpleInventory_Demo_012.blend

  • Rebuild of the .blend with module file from Demo_011

2016-12-16.: SimpleInventory_Demo_011.blend

  • Correction of the commentaries to homekey_add(cont): .

    2016-12-14.: SimpleInventory_Demo_01.blend

Attachments

SimpleInventory_Demo_012.blend (540 KB)

### Preliminary ###
The code is written for module mode. To use the following examples of Playhome.py, a header for the module file is also necessary.
I don’t post here the header of Email3lang_pe1stLevel.blend, which contains extra code related with the use of 3 languages and the amount of scenes loaded when Playhome.py is used. This is the header of SimpleInventory_Demo_01.blend :

import bge
bl = bge.logic
cont = bl.getCurrentController()
own = cont.owner

bl.addScene('HUD')

scene = bl.getCurrentScene()
Empty_homekey = scene.objects['Empty_homekey']

Empty_homekey['homekey'] = True
Invent_01 = {1 : 'Credit card'}

With this header you can’t use three languages and you need an overlay scene named HUD.
SimpleInventory_Demo_01.blend contains a scene named Scene_1 , an overlay scene named HUD and the code to spawn the item Homekey to the scene, to add it to the inventory, to print the inventory as text to screen and to drop the item back to its origin.
We start the demo scene with Scene_1 , which will load the HUD scene that contains a Text object named HUD_text_202 to print messages and the inventory to screen.

The explanations beneath are referred to the code from Email3lang_ex1stLvl_01.blend.

NOTE 1 : To play the Email3lang_ex1stLvl_02.blend (or later) properly, you have to start with the Control_scene and you have to delete bgeconf from your gamefolder, if it exists and you want to use the Start button in the Start menu. For the Continue button you will need the bgeconf if you have played the first level to its end before.
NOTE 2 : The inventory is written to use with Email3lang, so it contains the names of the items and the messages in three languages. For an inventory without using 3 languages you have to delete the extra code from the original code, i.e. if MLang == ‘…’: or and MLang == ‘…’ and use only one of the three same codes which will rest thereafter, ideally the one which prints the desired language, and delete the other two. Then adjust the indent level of the resting code, i.e. one level less.
NOTE 3 : I have used bl = bge.logic in the header to fit for the examples. If you use in your code bge.logic , don’t mind, it will not disturb.

### Initializing the dictionary ###

The inventory starts near the top of Playhome.py with the initializing of a dictionary named Invent_01 . It is initialized for each language :

if MLang == 'en':
     Invent_01 = {1: 'Credit card'}
... # repeated for the other languages

It is also possible to initialize an empty dictionary. In that case it is not necessary to do it in each language, just use Invent_01 = {} instead of the two lines from above.
I put a credit card in the inventory {1: “Credit card”} . More items are added with a comma, p.e. {1: “Credit card”, 2: “passport”} .

The next two paragraphs are taken from a tutorial written for Python 2.5: http://infohost.nmt.edu/tcc/help/pubs/lang/pytut/dict.html / New Mexico Tech, thank you.

The general form used to create a new dictionary in Python looks like this:


dict = {key1 : value1, key2 : value2, ...}

The : separates the key from the value. If a key and/or a value is a string, the string has to be in quotes.NOTE : We will use the keys of the dictionary to access the items with the keyboard (p.e. chapter ### Using objects of the inventory on objects of the scene ###), so we use the same number for the dictionary key as for the number key of an item. P.e to use the passport, the player have to use the number key 2 (together with the U key).

### Printing the inventory to screen ### When the player is in the Home_player scene, the inventory is shown when the I key is pressed. After a while the text disappears. The text object HUD_text_202 which prints the inventory is in the HUD_02 scene. For this the Player object has a keyboard sensor named Inv attached to a Python controller set to module mode . The key of the keyboard sensor is I , and the name of the module function is Playhome.print_inv . def print_inv() , the function for printing the inventory to screen is in Playhome.py beneath """ To print the inventory to screen: """.

def print_inv(cont):
    own = cont.owner
    open_inv = cont.sensors["Inventory"]

    scene = bl.getSceneList()[4]
    HUD_text_202 = scene.objects['HUD_text_202']
    ... # to be continued beneath

Parts of the next two paragraphs are taken from: https://blenderartists.org/forum/showthread.php?372606-send-info-to-suspended-scene-via-message&p=2880533&viewfull=1#post2880533 post#4 / cypher2012, thank you.

To access the Text object HUD_text_202 from the Player object we use scene = bl.getSceneList()[4] to get a scene list, where [4] stands for the fifth scene loaded and active, so [0] would be the first scene loaded and active. Active can be overlay, background and normal scenes. Suspended scenes stay active, removed not. In this case the fifth scene is HUD_02 which contains HUD_text_202 .
Then we use scene.objects to declare HUD_text_202 and make it accessible from the Player object, because the Player object (like all other objects) knows nothing about the scenes and its objects except itself if you don’t tell him.

def print_inv(cont):
    ... # Continuation from above: 

    if open_inv.positive:
        HUD_text_202['Text'] = "Inventory = " + str(Invent_01)

When the I key is pressed ( if open_inv.positive: ), HUD_text_202 prints to screen.
Invent_01 is a dictionary, but a Text object can print only strings, integers and floats. So we have to convert the dictionary with str() to a string to make it printable with [‘Text’] . Every Text object have the fix Text property [‘Text’] , and we can set this property to the desired text like shown above. This will print on screen the text at the right side of the Textobject.
Strings and spaces in quotes will be printed as text to the screen, in this case Inventory = . Variables, in this case Invent_01 will be printed as their value, in this case {1: “Credit card”} , or more objects inside the {} if there are more in the inventory dictionary, p.e.: {1: “Credit card”, 2: “passport”} . The + connects the different elements to print.

### Making the text disappear ###
Text object: HUD_text_202 .

def clear_text_202():
    ...

    HUD_text_202.text = ""

To make the text disappear from the screen after a while, we add to HUD_text_202 in the Logic editor a Delay sensor and a Python controller and connect them together. In the Delay sensor we change the Delay to 250 and we check the Repeat option. We set the Python controller to module mode with the module function Playhome.clear_text_202 .
This means, that the Delay sensor waits 250 logic frames before sending a pulse to the controller, which calls the function. The function sets the Text object to overwrite the earlier printed message with an empty string, so the text disappears from the screen.
The Repeat option allows us to use the Delay sensor every time when we press the I key. With the Repeat option disabled (unchecked), we can use the Delay sensor the first time we press the I key, and thereafter not. The result is that after the second pressing of the I key the text won’t disappear.

### Increasing the text resolution ###
The following code is taken from: http://www.cgmasters.net/free-tutorials/fps-mouselook-script-plus-real-text/#chapter5 / Chris Plush, thank you.


def text_res(cont):
    own = cont.owner

    own.resolution = 2

The text of the messages from HUD_text_202 is a little pixeled, so we increase the text resolution a bit. To do so, we add to HUD_text_202 in the Logic editor an Always sensor and a Python controller and connect them together. We set the Python controller to module mode with the module function Playhome.text_res .

The following paragraph is inspired by: http://blender.stackexchange.com/questions/8399/why-is-my-text-pixelated / Mike Pan, thank you.
Please be aware that rendering text at 2x resolution will take up more graphic memory. (128x128 will become 256x256). Use the smallest number you can stand.

In the next post I will write about the basic organization of this inventory.

Greetings,
achisp

The inventory starts near the top of Playhome.py with the initializing of a dictionary named Invent_01 . It is initialized for each language :

if MLang == 'en': 
    Invent_01 = {1: 'Credit card'}
... # repeated for the other languages

It is also possible to initialize an empty dictionary. In that case it is not necessary to do it in each language, just use Invent_01 = {} instead of the two lines from above.
I put a credit card in the inventory {1: “Credit card”} . More items are added with a comma, p.e. {1: “Credit card”, 2: “passport”} .

The next two paragraphs are taken from a tutorial written for Python 2.5: http://infohost.nmt.edu/tcc/help/pubs/lang/pytut/dict.html / New Mexico Tech, thank you.

The general form used to create a new dictionary in Python looks like this:

dict = {key1 : value1, key2 : value2, ...}

The : separates the key from the value. If a key and/or a value is a string, the string has to be in quotes.

NOTE : We will use the keys of the dictionary to access the items with the keyboard (p.e. chapter ### Using objects of the inventory on objects of the scene ###), so we use the same number for the dictionary key as for the number key of an item. P.e to use the passport, the player have to use the number key 2 (together with the U key).

### Printing the inventory to screen ###
When the player is in the Home_player scene, the inventory is shown when the I key is pressed. After a while the text disappears. The text object HUD_text_202 which prints the inventory is in the HUD_02 scene.
For this the Player object has a keyboard sensor named Inv attached to a Python controller set to module mode . The key of the keyboard sensor is I , and the name of the module function is Playhome.print_inv .
def print_inv() , the function for printing the inventory to screen is in Playhome.py beneath “”" To print the inventory to screen: “”".


def print_inv(cont):
    own = cont.owner
    open_inv = cont.sensors["Inventory"]

    scene = bl.getSceneList()[4]
    HUD_text_202 = scene.objects['HUD_text_202']
    ... # to be continued beneath

Parts of the next two paragraphs are taken from: Send_info_to_suspended_scene_via_message-Blender_artists.html#4 / cypher2012, thank you.
To access the Text object HUD_text_202 from the Player object we use scene = bl.getSceneList()[4] to get a scene list, where [4] stands for the fifth scene loaded and active, so [0] would be the first scene loaded and active. Active can be overlay, background and normal scenes. Suspended scenes stay active, removed not. In this case the fifth scene is HUD_02 which contains HUD_text_202 .
Then we use scene.objects to declare HUD_text_202 and make it accessible from the Player object, because the Player object knows nothing about the scenes and its objects except itself if you don’t tell him.


def print_inv(cont):
    ... # Continuation from above:
    if open_inv.positive:
        HUD_text_202['Text'] = "Inventory = " + str(Invent_01)

When the I key is pressed ( if open_inv.positive: ), HUD_text_202 prints to screen.
Invent_01 is a dictionary, but a Text object can print only strings, integers and floats. So we have to convert the dictionary with str() to a string to make it printable with [‘Text’] . Every Text object have the fix Text property [‘Text’] , and we can set this property to the desired text like shown above. This will print on screen the text at the right side of the Textobject.
Strings and spaces in quotes will be printed as text to the screen, in this case Inventory = . Variables, in this case Invent_01 will be printed as their value, in this case {1: “Credit card”} , or more objects inside the {} if there are more in the inventory dictionary, p.e.: {1: “Credit card”, 2: “passport”} . The + connects the different elements to print.

### Making the text disappear ###
Text object: HUD_text_202 .


def clear_text_202():
    ...

    HUD_text_202.text = ""

To make the text disappear from the screen after a while, we add to HUD_text_202 in the Logic editor a Delay sensor and a Python controller and connect them together. In the Delay sensor we change the Delay to 250 and we check the Repeat option. We set the Python controller to module mode with the module function Playhome.clear_text_202 .
This means, that the Delay sensor waits 250 logic frames before sending a pulse to the controller, which calls the function. The function sets the Text object to overwrite the earlier printed message with an empty string, so the text disappears from the screen.
The Repeat option allows us to use the Delay sensor every time when we press the I key. With the Repeat option disabled (unchecked), we can use the Delay sensor the first time we press the I key, and thereafter not. The result is that after the second pressing of the I key the text won’t disappear.

### Increasing the text resolution ###
The following code is taken from: FPS Mouselook script plus real text in the Game engine-CG masters.html#changing_font_resolution / Chris Plush, thank you.


def text_res(cont):
    own = cont.owner

    own.resolution = 2

The text of the messages from HUD_text_202 is a little pixeled, so we increase the text resolution a bit. To do so, we add to HUD_text_202 in the Logic editor an Always sensor and a Python controller and connect them together. We set the Python controller to module mode with the module function Playhome.text_res .
The following paragraph is inspired by: http://blender.stackexchange.com/questions/8399/why-is-my-text-pixelated / Mike Pan, thank you.

Please be aware that rendering text at 2x resolution will take up more graphic memory. (128x128 will become 256x256). Use the smallest number you can stand.
In the next post I will write about the basic organization of this inventory.
Greetings,
achisp

I have organized the “get object” procedure in following manner: The Near sensor to allow that the object disappears from the scene is at the ‘object to get’ together with two Mouse sensors and the correspondent module file.NOTE : It is also possible to put the Near sensor and the two Mouse sensors to the player, then we would use the Mouse event Mouse over any with the Mouse sensor instead of the Mouse over event. And we would use the property field of the Near sensor and the Mouse over any sensor to look only for the objects with a certain property, or we would use hitobject with them like I have done in this example (see chapter ### Using objects of the inventory on objects of the scene ###). The result would be that the ‘objects to get’ don’t need this sensors, only the player. (This is realized in Post #20 + #21, ### How to get a certain “added object”, part 1 + 2 ### and its .blend) The scene with less Near and Mouse over sensors is for sure better for the performance, but right now I know the way to do it, but don’t know enough (Blender) Python to realize it. Probably I will make it later when I have all the parts of the puzzle together.
Funny ! Programming is sometimes like playing an adventure video game: “you can’t leave this level before …” Well, let’s go on with this level:

Furthermore I spawn the ‘objects to get’ to the scene, i.e. I don’t place them in the scene, where they reappear every time the scene is reloaded, but I put an empty at its place in the scene and the object itself to the second layer to spawn it to the empty every time when I like to (see chapter ### Adding an object to the scene from layer 2 ###). So I can avoid that an object, which is taken from the scene to the inventory, is spawned when the scene is reloaded. Every time when the player goes from “Home_player” to “Laptop_01”, the Home_player scene dies, and when he return to it, it will be reloaded and all objects in it will be put to it original place, if we don’t inhibit it.

### How the order of scenes is organized ###

Why dies the Home_player scene ?

If we use multiple scene in one level in Blender, there are some restrictions. In the case of the first level of Email3lang we have a Control scene, 3 Overlay scenes (Laptop_01, HUD_01, HUD_02) and one scene, the Home_player scene, to set and remove. If the player leaves an overlay scene, it will be suspended and stays active (i.e its data stay accessible in the memory and its graphical content stays on the screen), while a removed scene dies (i.e. its data isn’t no more accessible in the memory and its graphical content is removed from the screen) = isn’t active.

Why don’t we use only overlay scenes ?

Here is one restriction: if an overlay/background scene has graphical contents (models, …), this content stays on the screen while the overlay/background scene is suspended. HUD_01 and HUD_02 have no graphical content, with the exception of the text printed by their textobjects. So they work good as HUDs because their graphical content overlays the previous loaded scene(s) while the player can see/play the overlayed scene without problems. But if we would use the Home_player scene also as an overlay scene, and the player goes from one scene to the other, there will be a moment when the graphical content of the laptop scene overlays the one of the Home_player scene while playing the last, or on the contrary. But this effect we don’t want and isn’t playable.

The graphical order of scenes

The last loaded scene is on top of the previous loaded (except of a background scene), so if we load scene 1, then scene 2, 4 and 3, the graphical order from the view of the player is 3, 4, 2, 1.

The following paragraph is taken from: http://bgepython.tutorialsforblender3d.com/SceneActuator/AddBackgroundScene/mode / Clark Thames, thank you
The background scenes aren’t rendered on top of the current scene, but behind.
If we suspend one of these scenes, the graphical order stay the same. If we remove a scene, this scene will also be removed from the graphical order.

Summary

For this reasons if we use multiple scene in a level, we will use a mix of overlay/background scene(s) and scene(s) to set/remove. How to mix them is our decision.
In the case of Email3lang I have decided to make the laptop scene an overlay scene, because in that manner all the progresses made there stay intact during the whole level using module mode. So I had to make the Home_player scene a scene to set/remove and have to save the progress made there to the globalDict, or a dictionary/object on the control scene or a text file before removing it, and loading them when I set the Home_player scene another time.

In the next post we will add an object from the scene to the inventory.

Greetings,
achisp

We will take “”" To add homekey to inventory: “”" as example.
The object is Homekey .
Logic bricks : A Python controller which calls the get_homekey function, connected to three sensors: a Mouse over, a Mouse Left button, a Near sensor, and a Sound actuator.


def get_homekey(cont):
    own = cont.owner

    scene = bl.getSceneList()[3]
    Empty_homekey = scene.objects['Empty_homekey'] # to access the object Empty_homekey →
                                                   # from own (object Homekey)
    mOver = cont.sensors["MOver"]
    mLeft = cont.sensors["MLeft"]
    near = cont.sensors["Near_homekey"]
    takeItem = cont.actuators["Take_homekey"]

    if mOver.positive and mLeft.positive and near.positive and MLang == 'en':
        Invent_01[3] = 'Homekey' # we add as value the 'name' with 3 as key to the inventory
    ... # we repeat it for the other two languages
    bl.globalDict['Invent_01'] = Invent_01 # to actualize Invent_01 in the globalDict

    if mOver.positive and mLeft.positive and near.positive:
        takeItem.startSound() # to play a sound
        own.endObject() # we remove object Homekey from scene
        Empty_homekey['homekey'] = False # to indicate that Homekey is in the inventory
        bl.globalDict['homekey'] = Empty_homekey['homekey'] # write it to globalDict
        bl.saveGlobalDict() # and save it

I recommend to read also the commentaries from the code above. means to continue at the next commentary.
Step by step:

  • scene = bl.getSceneList()[…] is explained in ### Printing the inventory to screen ###

  • Invent_01[3] = ‘Homekey’ The structure of a dictionary in Python is explained in ### Initializing the inventory ###).

    Furthermore follows here the explanation how to add new items to a dictionary in Python:


dict[key] = value

The following paragraph is taken from: https://www.programiz.com/python-programming/dictionary #How to change or add elements in a dictionary / Programiz, thank you
Dictionary are mutable. We can add new items or change the value of existing items using assignment operator. If the key is already present, value gets updated, else a new key: value pair is added to the dictionary.

  • bl.globalDict[‘Invent_01’] = Invent_01

    We actualize Invent_01 in the globalDict to be used from other objects in this scene and to be saved later to be used in other scenes and in level 2. We need this line for this scene, because we aren’t here in indent level zero and so we don’t write Invent_01[3] = ‘Homekey’ as a global variable in the memory for module mode, only as a local variable. Local means here the object Homekey .

  • own.endObject()

    Removes object Homekey from the scene in the moment the player takes it (in reality in the next frame, but we can not see the difference if the framerate is higher than ca. 15 FPS (frames per second)).

  • Empty_homekey[‘homekey’] = False

    We use this property as False to inhibit that the object is spawned to the scene while it is in the inventory and the scene is reloaded.
    Set it to True Homekey will be added to the scene when the scene is (re)loaded (see next chapter: ### Adding an object to the scene ###).
    And we will use this property as True later p.e. to trigger the message 7 from mess.py : “I need something more to travel” when the player tries to open the door to level 2 before he has collected the homekey needed to do it (see chapter: ### Using objects from the inventory to trigger s.th. ###).

    But before we can add the object to the inventory, we have to spawn it to the scene.

This will I explain in the next post.

Greetings,
achisp

An empty is a special mesh object we can add in Object mode with Add -> Empty from the 3D view header. It is invisible in the scene. I have used the plain axes empty.

The following two paragraphs are inspired by: https://www.blender.org/manual/editors/3dview/object/properties/relations/layers.html / Blender documentation team, thank you
Blender provides twenty object layers whose visibility can be toggled with the small unlabeled buttons in the 3D Viewport header. To select a single layer, click the appropriate button with LMB.
Object(s) can be placed into one or more layers. To move selected object(s) to a different layer, press M and then select the layer you want from the pop-up dialog.

We will take “”" To add homekey to scene: “”" as example.
The object is Empty_homekey .
Logic bricks : A Python controller which calls the homekey_add function, connected to an Always sensor.


def homekey_add(cont):
    own = cont.owner

    scene = bl.getCurrentScene() # similar to getSceneList, but gets only the current scene
    Homekey = scene.objects # to declare the object Homekey for the object Empty_homekey

    def init(): # only the first time the player enters the scene (see step by step)
        if not 'init' in own:
            own['init'] = 1
            own['homekey'] = True # indicates that the object is to spawn to the scene

    def update(): # every time the homekey_add function is called
        if 'homekey' in bl.globalDict: # when property home_key is in globalDict
            bl.loadGlobalDict() # we load it
            own['homekey'] = bl.globalDict['homekey'] # we write its status from globalDict →
                                                      # to Empty_homekey['homekey']

        if own['homekey'] == True: # if the object Homekey isn't in the inventory
            scene.addObject("Homekey", "Empty_homekey", 0) # we spawn the object Homekey →
                                                           # to the scene

    init()
    update()

I recommend to read also the commentaries from the code above. means to continue at the next commentary.

Step by step:
The first time the homekey_add function is called is, when the scene starts the first time, because the Python controller of that function is attached to an Always sensor, which starts sending a pulse at the beginning of a scene.

  • def init():

GENERAL : When the Python interpreter reads a function, he operates the code from the top to the bottom. In the case of other functions included in this function like p.e. def init(): , he needs a function call on the same indent level as the ‘function to call’ to operate them. In the case of def init(): this would be init(): . We set this function call(s) underneath the function(s) to call. The plural means that we can also call more then one function like in the case of def init(): and def update(): , where the two function calls init() and update() were set underneath the code of def update(): to indicate to read the two one after the other. The next which will do the interpreter after reading the function call(s) is read the related function(s).
When the Python interpreter operates def init(): the first time, there is no property own[‘init’] (because we reserve it for this aim *). So the interpreter operates the code related with if not ‘init’ in own: (the intended code beneath it). The second time the interpreter reads this function, he will not operate that code, because than exists own[‘init’] , initialized at the first call through own[‘init’] = 1 .

  • Note that we can use own[‘init’] multiple times in our module file, if there is every time a different owner of ‘init’ .

PARTICULAR : The first time the player enters the scene, the object is to spawn to the scene, because there haven’t been the possibility to put it to the inventory. That is what own[‘homekey’] = True indicates here and is executes in def update(): .

  • def update():

GENERAL : After the init() function Python read here further because we set at the bottom under update() the two function calls init() and update() .

PARTICULAR :

  • if ‘homekey’ in bl.globalDict:

    We need to ask that, otherwise we will get a key error when we call for a property which does not exist in the globalDict and that could happen here in certain situations.

  • bl.loadGlobalDict()

    We load the globalDict for the case that the player has gone before to the laptop scene and then back to the Home_player scene, because the Home_player scene wasn’t a suspended overlay or background scene, i.e. the changes made to it are deleted when the Home_player scene is removed to go to the laptop scene.

  • own[‘homekey’] = bl.globalDict[‘homekey’]

    Here we ask for the value of homekey in bl.globalDict and set it to the property Empty_homekey[‘homekey’] in the memory.
    In the def init() function we set the property homekey to the value True when the player entered the scene the first time. We would have written and saved that value to the globalDict when the player left the scene without putting the homekey to the inventory before leaving.
    If the player would have put the Homekey in his inventory, we would have overwritten in the globalDict and in the memory the value of homekey with the value False while executing the get_homekey function. And that value would be the value in the bl.globalDict and that one we will save leaving the scene.
    If the player would have put the Homekey in his inventory and later have put it back to the scene before leaving, we would have finally written in the globalDict and in the memory the value of homekey as True while executing the drop_homekey function. And that value would be the value we will save leaving the scene.

  • if own[‘homekey’] == True:

    I.e. the homekey isn’t in the inventory.

  • scene.addObject(“Homekey”, “Empty_homekey”, 0)

    We add it to the (current) scene.

    addObject(“Homekey”, “Empty_homekey”, 0)

The next two paragraphs are taken from: https://www.blender.org/api/blender_python_api_2_74_release/bge.types.KX_Scene.html?highlight=kx_scene#bge.types.KX_Scene.addObject / Blender documentation team, thank you
Syntax : addObject(object_to_add, receiving_object, lifetime)
Note : A lifetime of 0 means the object will last forever.

### Hiding the objects in the inventory from the scene ###


def homekey_add(cont):
    ...
        ...
        if own['homekey'] == True:
            scene.addObject("Homekey", "Empty_homekey", 0)
    ...

Therefore don’t exist an extra code. If the related object property is True we spawn the object in the homekey_add function to the scene, and when it is False (= object is in the inventory), it isn’t True , so we don’t spawn it, i.e. it stays hidden on layer 2. And Empty_homekey is invisible in the scene.

In the next post we will use the items of the inventory.

Greetings,
achisp


def knocker(cont):
    ...
    def Update():
        ...
        if mOverAny.positive and mLeft.positive and near_any.positive and\
        <i>Empty_homekey['homekey'] == True</i> and MLang == 'en':
            ...:
                HUD_text_202['Text'] = text_en[7][1]
        ...
    ...

We use simply the related object property to trigger s.th. or not. By True the object is in the scene, by False it is in the inventory, i.e. in the example from above: if … and Homekey is in scene then … .

### Dropping an object from the inventory back to its origin ###
We will take “”" To drop Homekey back to scene: “”" as example.
The object is Player .
Logic bricks : A Python controller which calls the drop_homekey function, connected to two Keyboard sensors ( I and 3 keys), a Near sensor and a Sound actuator.


def drop_homekey(cont):
    own = cont.owner

    ... # Scene lists and global Dict

    item_03 = cont.sensors["Item_03"]
    open_inv = cont.sensors["Inventory"]
    near_any = cont.sensors["NearAny_01"]
    hitObj = near_any.hitObject
#    Near_homekey_list = near_any.hitObjectList
#    print(Near_homekey_list) # you can activate the two lines for debugging
    dropItem = cont.actuators["Drop_item"] # Sound actuator

    if item_03.positive and open_inv.positive and Empty_homekey['homekey'] == False\
    and MLang == 'en': # if the player tries to drop the item far from its origin
        HUD_text_202['Text'] = text_en[8][1] # message = "I don't drop it here"
    ... # we repeat it for the other two languages

    if item_03.positive and open_inv.positive and Empty_homekey['homekey'] == False\
    and near_any.positive: # if the player tries to drop the item near to its origin
        hitName = hitObj.name
        if hitName == "Near_homekey": # test if the player is near by
            own.scene.addObject("Homekey", "Empty_homekey", 0) # add Homekey to scene
            Empty_homekey['homekey'] = True # indicates that the item is in the scene
            bl.globalDict['homekey'] = Empty_homekey['homekey']
            bl.saveGlobalDict()
            del Invent_01[3] # deletes Item_03 from the dictionary Invent_01
            HUD_text_202['Text'] = "" # overwrites with nothing the "I don't drop it" text
            dropItem.startSound()

Please read the commentaries from the code above.

Parts of the next two paragraphs are taken from: https://blenderartists.org/forum/showthread.php?32734-Near-sensor-can-t-make-it-work&p=317059&viewfull=1#post317059 post#4 / Doc Holiday, thank you.
We put a plane named Near_homekey on the table where the Tablet’s origin is, to trigger the Near sensor, because an empty is not detected by it and the Tablet isn’t there when the player will drop the Tablet from the inventory. It is on layer 2 and there it is not detectable by the near sensor.
For Near_homekey we enable in the Property editor -> Physics tab the Actor and Collision bounds to make it detectable by the Near sensor. And we enable Invisible to restrict that it is rendered in the game.
How to use hitObject with the near sensor is explained in the next chapter.

In the next post we will use items of the inventory on objects of the scene.

Greetings,
achisp

We will take “”" Scene switch “”" as example.
The object is Player .
Logic bricks : A Python controller which calls the knocker function, connected to two Mouse sensors ( left button and over any events), two Keyboard sensors ( U and 3 keys) a Near sensor and a Sound actuator.


def knocker(cont):
    ...
    mOverAny = cont.sensors["MOverAny_01"]
    mLeft = cont.sensors["MLeft"]
    item_03 = cont.sensors["Item_03"]
    use_item = cont.sensors["Use_item"]
    near_any = cont.sensors["NearAny_01"]
    hitObj_01 = mOverAny.hitObject
    hitObj_02 = near_any.hitObject
    endLvl = cont.actuators["End_lvl"]

    def init():
        ...
    def Update():
        ...

        if mOverAny.positive and mLeft.positive and near_any.positive and\
        Empty_homekey['homekey'] == True and MLang == 'en': # if the Homekey isn't in the inventory
            hitName_01 = hitObj_01.name
            hitName_02 = hitObj_02.name
            if hitName_01 == "Doorknocker_01" and hitName_02 == "Doorknocker_01":
                HUD_text_202['Text'] = text_en[7][1] # message = "I need something more to leave home"
        ... # repeat it for the other tasks and in all languages

        if ...:
            ...
                HUD_text_202['Text'] = "The door is locked"
                endLvl.startSound()

        if near_any.positive and use_item.positive and item_03.positive and\
        own['set_scene2a'] == True and own['set_scene2b'] == True and\
        Empty_tablet['myTablet'] == False and Empty_homekey['homekey'] == False\
        and own['myPassport'] == False:
            hitName_02 = hitObj_02.name
            if hitName_02 == "Doorknocker_01":
                bl.saveGlobalDict()
                scene.end()
                ...
                bl.addScene('AkaDar_01')
                ...
    init()
    Update()

I recommend to read also the commentaries from the code above.

Step by step:

  • if near_any.positive … :
    [LIST]
  • hitName_02 = hitObj_02.name
  • if hitName_02 == “Doorknocker_01”:

[/LIST]
if near_any.positive … :
For a collision, near, radar, ray and mouse over (any) sensor, we can also use sensor.positive: instead of sensor.hitObject: .

The next paragraph is taken (and a little adapted) from: https://blenderartists.org/forum/showthread.php?224197-help-with-Python-syntax-(globalDict)&p=1898262&viewfull=1#post1898262 post#8 / Riyuzakisan, thank you.
This sensors send a positive pulse when they hit an object, and when they don’t hit it they send a negative pulse. Both types of pulses activate the Python script controller and run the script, so we need to specify a filter in the script that only accepts positive pulses. If we don’t specify such a filter, the script will be run with the negative pulse yet no object will be found, which results in the None value instead of an object.

hitName_02 = hitObj_02.name and if hitName_02 == “Doorknocker_01”:

We have defined before near_any = cont.sensors[“NearAny_01”] and hitObj_02 = near_any.hitObject and here we define hitName_02 = hitObj_02.name and use the statement if hitName_02 == “Doorknocker_01”: to know if the player is near or not to Doorknocker_01 .

  • if mOverAny.positive and mLeft.positive and near_any.positive and Empty_homekey[‘homekey’] == True and MLang == ‘en’:

    If the player hasn’t fulfilled all task, and click on the door standing near to it, a message appears : “I need something more to leave home”.
    We need to do it for all tasks and all languages, so if the player hasn’t fulfilled any one of them, he will be informed.

  • HUD_text_202[‘Text’] = “The door is locked”

    If the player have bought the ticket, read “Lateness”, taken Passport, Tablet and Homekey, and, standing near the door, have clicked on the doorknocker, the message “The door is locked” appears.

  • if near.positive and open_inv.positive and item_03.positive and … :

    Now the player, still standing near the door with all the items above mentioned in his inventory, have to use the homekey, i.e. the keys U and 3 together *,

  • scene.end()

  • bl.addScene(‘AkaDar_01’)

    to unlock the door and enter level 2.

  • bl.saveGlobalDict()

    But BEFORE we save globalDict to file so we can later in level 2 load the progress of level 1 from it.NOTE * : I have explained in the in-game manual of the laptop scene all the keyboard keys necessary to play level 1.

Greetings, achisp

damn thats a huge wall of text :slight_smile: anyway downloaded the blend to take a look but:

Blender Game Engine StartedPython controller found the module but could not access the function - object ‘Empty_homekey’, controller ‘Python’:
AttributeError: module ‘test’ has no attribute ‘homekey_add’
Python controller found the module but could not access the function - object ‘Player’, controller ‘Python.001’:
AttributeError: module ‘test’ has no attribute ‘print_inv’
Python controller found the module but could not access the function - object ‘Player’, controller ‘Python’:
AttributeError: module ‘test’ has no attribute ‘drop_homekey’
Blender Game Engine Finished

blender 2.78a

#edit
oh nvm readed the OP again its meant for 2.74

Hello Cotax,
I’m sorry if it don’t work (for you), but I can’t test it now. I have to go to home to see if I have send a bad .blend. Please say me which .blend you have downloaded. I will reply tomorrow.
achisp

Please say me which .blend you have downloaded

SimpleInventory_Demo_01 from first post.

But don’t worry i am using 2.78 you said its for 2.74 so errors can happen in this case, and i just wanted to take a look nothing more so no big deal.

Hello Cotax,
its seem that my post wasn’t clear. I changed it. Thanks.
Btw., there are two .blend files about the inventory, the other is on another thread and the link is in my first post here. It is the Email3lang_ex1stLvl_02.blend and is the one I explain in this thread, the Demo .blend is simplified.
achisp

Here I explain parts of the SimpleInventory_Demo_02.blend. It is at the bottom of this post and made with Blender 2.74.

### Preliminary ###
The object on the second layer, which will be added to the scene, is not added itself, but a copy of it. The original object stays always on the second layer, independent if one or more copies of it is added to the scene. This means, that we can put more than one empty in the scene where to add the same object of the second layer.
In this example we go to spawn/get Homekey at the one or the other of two empties, and we will not have more than one Homekey at the same time in the scene. For this we also need only one Homekey object on layer 2.

### Two Empty_homekeys ###
We put two Empties in the scene, named Empty_homekey and Empty_homekey_02 . And we put two planes underneath of them, named Near_homekey and Near_homekey_02 , each to his correspondent empty. For the two planes we enable in the Properties editor -> Physics panel : Actor and Collision bounds . We use the two planes because the empty isn’t detectable by the near sensor (of the player in this case).
We initialize a property, Empty_homekey[‘homekey’] = True , to indicate that the homekey_add(cont) function spawn Homekey when the scene starts. And in the game Empty_homekey[‘homekey_02’] indicates if the Homekey is in the inventory or the scene.
Empty_homekey has attached a Python controller connected to an Always sensor. The homekey_add(cont) function runs on this Python controller when the always sensor sends a pulse at game start.

### One function for two Empty_homekeys to add Homekey ###
We use the get_homekey(cont) function to add the Homekey to the inventory at both empties. If the player is near to one of them, the player can activate the function with the keys I + 3 .

### One function for two Empty_homekeys to drop Homekey ###
To drop the Homekey from the inventory to the one of the two empties the player is near by, we have the drop_homekey(cont) function.

def drop_homekey(cont):
    ...
    if item_03.positive and open_inv.positive and Empty_homekey['homekey'] == False\
        and near_any.positive:
            hitName = hitObj.name
            if hitName == "Near_homekey": # or in <b>the second if statement</b> ... == "Near_homekey_02"
                own.scene.addObject("Homekey", "Empty_homekey", 0) # or in <b>the second if statement</b>
                                                                   # ..., "Empty_homekey_02", 0)
                Empty_homekey['homekey'] = True
                del Invent_01[3]
                HUD_text_202['Text'] = ""
    ... # here follows the <b>second if statement</b>, which is the same except the two things
        # mentioned as commentary above

if hitName == “Near_homekey”: or if hitName == “Near_homekey_02”:
It spawns the Homekey to the one of the empties to which the player is near by. For that the drop_homekey(cont) function has two different if statements, the if hitName == “Near_homekey”: statement to drop to Empty_homekey and the if hitName == “Near_homekey_02”: statement to drop to Empty_homekey_02 .The two statements are nearly identical.

Empty_homekey[‘homekey’] = True
The get_homekey(cont) function sets Empty_homekey[‘homekey’] = False if the player take the item from it the scene. The drop_homekey(cont) function sets Empty_homekey[‘homekey’] = True if the player put the item to the inventory.
We need the Empty_homekey[‘homekey’] property in the game to indicate the state of Homekey , i.e. if the object is in the inventory or in the scene, for some of the if statements of this module file to work properly.

NOTE : This .blend has only the second if statement of the drop_homekey(cont) function more than SimpleInventory_011.blend, the rest code is the same, except that in the SimpleInventory_011.blend we have to declare Empty_homekey = bl.getCurrentScene.objects[‘Empty_homekey’] in the header and in the other .blend we don’t have to. Right now I don’t understand why.

Edit 2016-12-19: Now I understand why ! I have (had) a problem with the SimpleInventory.blends. I have saved all module files of the different SimpleInventory.blends as test.py. Now they work only with their proper test.py in the same folder as the .blend. If I save it with another name to the folder, the blend doesn’t work. If I save it as test.py to another folder, the blend also doesn’t work. My solution right now is to save the module file as test.py to that folder each time after opening a (new) SimpleInventory.blend. Luckily the progress in the module file will be saved, but the blend reads for the game mode test.py from the folder, and that was the reason why I saved the test.py to the folder initially, without thinking about it, satisfied to make the .blend work.

I’m sorry if you have had problems with this blend. It don’t work without Empty_homekey = bl.getCurrentScene.objects[‘Empty_homekey’] . (For me it has worked that day, because the test.py from that .blend was in the folder and I haven’t overwritten it with the test.py of another SimpleInventory.blend. But today it haven’t worked, just because the test.py was changed.) I will post a new one, I hope it works for you (now).

Edit 2016-12-20: What can I say ? … I’m sorry. I haven’t recognized my problem with the .blend file and when I have, I haven’t though in the consequences of it. For sure, the .blends haven’t worked for you.

I built a new blend for the demos and post them today.
I hope it works for you (now).

Greetings,
achisp

Revision history:
2016-12-20.: SimpleInventory_Demo_022.blend

  • Rebuild of the .blend with module file from Demo_021

2016-12-19.: SimpleInventory_Demo_021.blend

  • (1.) Added Empty_homekey = bl.getCurrentScene.objects[‘Empty_homekey’] to the header and (2.) disabled Lock camera to view in the Properties panel of the 3D view.

    2016-12-16.: SimpleInventory_Demo_02.blend


Attachments

SimpleInventory_Demo_022.blend (549 KB)

To make this demo .blend, I took SimpleInventory_Demo_02.blend and made some changes to it. The result is SimpleInventory_Demo_03.blend and is attached at the bottom of this post.

### How to make that Homekey will also be spawned to Empty_homekey_02 at the game start ###
We add the following two lines to the header:

...
scene = bl.getCurrentScene() # this line we don't add, it was there before
...
Empty_homekey_02 = scene.objects['Empty_homekey_02']
...
Empty_homekey_02['homekey_02'] = True # indicate to spawn the homekey at the game start

We initialize here the property Empty_homekey_02[‘homekey_02’] to indicate that the Homekey is to be spawned at the game start and to indicate in the game if a Homekey is in the scene at Empty_homekey_02 or not.
To spawn the Homekey to Empty_homekey_02 at the game start we add this module function:

def homekey_add_02(cont):
    own = cont.owner

    if own['homekey_02'] == True: 
        scene.addObject("Homekey", "Empty_homekey_02", 0)

To make the added function work, we add the same Logic bricks to Empty_homekey_02 as the ones of Empty_homekey and we set the module function test.homekey_add_02 in the Python controller.NOTE : To do so we can select in the 3D view first Empty_homekey_02 and then Empty_homekey , thereafter in the 3D view header we use with LMB Object -> Game -> Copy Logic bricks . When the Logic bricks are copied to Empty_homekey_02 , we RMB click on Empty_homekey_02 in the 3D view (to deselect Empty_homekey ) and open the Logic editor. Here we change in the name field of the Python controller test.homekey_add to test.homekey_add_02 .

### How to initialize an inventory with a stackable item in it ### The next code is taken from: https://blenderartists.org/forum/showthread.php?363995-Short-Tutorial-Dictionary-based-inventory&p=2825950&viewfull=1#post2825950 post#1, SIMPLE DICTIONARY / Smoking_mirror, thank you. For explanations please see there.
Invent_01 = {'1. Credit card':1} # Initialization of the inventory with a stackable item

(Optional) We replace in the header Invent_01 = {1 : ‘Credit card’} with the lines from above to add the credit card also stackable in the inventory.NOTE : We need for our inventory system the number before the item (see also code tag above) in the inventory to show the player which number key corresponds to which item.

### How to make that Homekey can be taken from both Empty_homekeys ### We will make that the Player can take the key from both Empty_homekeys , that the amount of Homekeys in the inventory is printed and that the properties of both are actualized. First we connect in the Logic editor the Near sensor NearAny_01 of the Player object with the Python controller which is attached to the Homekey object.NOTE : How to make it see: https://blenderartists.org/forum/showthread.php?408743-Email-account-with-3-languages-and-manual&p=3129816&viewfull=1#post3129816, ### Starting sounds ###.

Then we adapt the get_homekey function:

def get_homekey(cont):
    ...
    near = cont.sensors["Near_homekey"] # Near sensor of Homekey
    near_any = cont.sensors["NearAny_01"] # the player's near sensor
    hitObj = near_any.hitObject # we add it for the player's near sensor

    if mOver.positive and mLeft.positive and near.positive:
        hitName = hitObj.name # we add it for the player's near sensor
        if hitName == "Near_homekey": # we add it for the player's near sensor
            own.endObject()
            Empty_homekey['homekey'] = False
            ...

Now the Near sensor of the Player object can distinguish Empty_homekey with the help of Near_homekey . We need the later because a near sensor can’t detect an empty.NOTE 1 : How we have prepared Near_homekey see the previous chapter ### Dropping an item of the inventory to more than one place ###, along with ### Two Empty_homekeys ###.
NOTE 2 : hitObj is explained in chapter 3130159 along with the first code tag.

The next code is taken from: https://blenderartists.org/forum/showthread.php?363995-Short-Tutorial-Dictionary-based-inventory&p=2825950&viewfull=1#post2825950 post#1, ADDING ITEMS / Smoking_mirror, thank you. For explanations please see there. Note that I have adapted it for our needs *.

            ... # continued from above
            Invent_01['3. Homekey'] =  Invent_01.get('3. Homekey',0) + 1

We replace now in def get_homekey(cont): the line Invent_01[3] = ‘Homekey’ with the line from the code tag above to add the item to and to set the amount of items in the inventory.
We copy the whole if hitName == “Near_homekey” statement, paste it underneath and replace in it Near_homekey and "Empty_homekey[‘homekey’] with Near_homekey_02 and "Empty_homekey_02[‘homekey_02’] .
Now the Near sensor of the Player object can distinguish between the two empties and the Homekey can be added to the inventory from both empties if the player LMB clicks on it. The amount of Homekeys will be shown in the inventory too.

How to make that Homekey can be dropped to both Empty_homekeys ###

We add to the drop_homekey function the following statement to have the message “I don’t drop it here” when the Homekey isn’t near to Empty_homekey_02 and the player tries to drop it far away from it.

    if item_03.positive and open_inv.positive and Empty_homekey_02['homekey_02'] == False:
        HUD_text_202['Text'] = "I don't drop it here"

Furthermore we replace del Invent_01[3] in the drop_homekey function with:

The next code is taken from: https://blenderartists.org/forum/showthread.php?363995-Short-Tutorial-Dictionary-based-inventory&p=2825950&viewfull=1#post2825950 post#1, REMOVING ITEMS / Smoking_mirror, thank you. For explanations please see there. Note that I have adapted it for our needs (see NOTE * above).

def drop_homekey(cont):
            ...
            Invent_01['3. Homekey'] =  max(0,Invent_01.get('3. Homekey',0) - 1)
            ...

to remove the item and to set the amount of items in the inventory.
Thereafter we copy the whole if statement, and paste it underneath to have the same code for Empty_homekey_02 . We have to change the correspondent names in that statement to Empty_homekey_02 , Near_homekey_02 and Empty_homekey_02[‘homekey_02’] .

Greetings,
achisp

Revision history:2016-12-20.: SimpleInventory_Demo_031.blend

  • Rebuild of the .blend with module file from Demo_03. Added deleting of items in the inventory if their amount is 0.

2016-12-19.: SimpleInventory_Demo_03.blend

Attachments

SimpleInventory_Demo_031.blend (552 KB)

Hello Cotax.
I think it wasn’t a problem with the version, because I have had problems with this blend … but I haven’t recognized it until yesterday. (Details see bottom of post#12, Edit)

I built a new blend for the demos and post them today.
I hope it works for you (now).
Greetings

it works, for a part.

Empty_homekey['homekey'] == False\

\ instead of :

also you can check boolean like this

if boolean:
#it is true

if not boolean:
# it is false

its a nice effort to make/learn an inventory, but hard to understand what you actually do with the bricks and python.
so if i would add more items i need a brick for every single object?

i have my inventory here:

take a look maybe it is use full to learn a few things from it.

Hello Cotax.

What doesn’t work ?

I don’t understand, can you please post an example ? Maybe with Empty_homekey ? Or I take a look with google, later.
Ahh, I think I understand.

if Empty_home['homekey']:
    ...
if not Empty_homekey['homekey']:
   ...

I have to initialize the property before ?

I use a keyboard sensor for every item with a NUM key for it, and I need a lot of Python controllers.
Also I need for every item on Layer 2 a Near sensor and the two Mouse sensors, to take this object to the inventory, that means to click on the object to end it … . My problem is that the object added to the scene is a pointer, and i tried to get that pointer, but I can’t afford it. If I could, I could use the sensors of the player to detect the added object (the pointer) and wouldn’t need the bricks on every item of layer 2 which I want to spawn as ‘getable item’ to the scene.

Maybe I can learn from it, but for now it seems to complicated for me. I have had a look at it some weeks ago. May be later. As you said, I’m learning Python and I like to go step by step.
It’s great that you share your .blends with us. Thanks

Greetings

To make this demo .blend, I took SimpleInventory_Demo_031.blend and made the necessary changes to it. The result is SimpleInventory_Demo_04.blend and is attached at the bottom of this post.

### Some advises before starting ###

  • My code isn’t elegant, i.e. I need to write the code for every ‘item getable’ that is in game and don’t use p.e. a property and all items with that property are getable and I can write part of the code one time for the group of that items and haven’t to write it for every item. I have done what I understand right now, the other way needs more knowledge about (Blender) Python than I have. And I think this way around also have some advantages.

NOTE : I have made https://blenderartists.org/forum/showthread.php?413884-Start-making-an-inventory-accessed-by-keys&p=3139628&viewfull=1#post3139628 an inventory accessed by key where I use a for loop to iterate through an inventory (post#3 + #5) instead of searching individually for each item.

  • I employ Near sensor to detect the point to take and/or drop. I notice that it is problematic if two different meshes to detect are one near to the other. Then sometimes it happens that the Near sensor detect first one of these, but thereafter not the other. The player object with the near sensor have to leave the area and return to detect the second (and that sometimes also that won’t work). So we have to put the points to detect for minimum one/two BUs one from another and adjust appropriate the Trigger distance and the Reset distance of the Near sensor. Or perhaps use the Ray sensor, but I haven’t tested it yet.

    Let’s start with our container .blend.

A second inventory ###

Invent_02 = {'2# Sandwich' : 3}

We initialize a second inventory in the module file header and put 3 Sandwiches in there. We write ‘2# Sandwich’ to show to the player which NUM key is correspondent with this item.
For the second inventory we add a cube to the scene, name it Box_01 and in the Property editor -> Physics panel we enable for it Actor and Collision box to make it detectable by the Near sensor of the Player object.

### Showing a message if the player tries to drop an item to the ground ###

    if item_01.positive and open_inv.positive:
        HUD_text_2['Text'] = "I don't drop it here"

If we use this code to print the message to screen if the player tries to drop an item far away from its empty (or here from the container too), like we have done in SimpleInventory_022.blend, in this .blend we won’t see the inventory of the player printed to screen (because it uses the same Textobject to print to screen and the same key combination), if we drop the item to the container. So we wouldn’t know in that moment what more have been in there.

    HUD_text_2['Text'] = ""

In SimpleInventory_022.blend we have used the code above to avoid the message if needed to, in this blend we will use for all items but the apples the following code, with the limitation that in the first if statement we have to put two times the name of the cited item, in this example i have used ‘2# Sandwich’ for the item Sandwich :

    ...
    if item_02.positive and open_inv.positive and '2# Sandwich' in Invent_01\
    and Invent_01['2# Sandwich'] &gt; 0:
        if nearAny.positive:
            hitName = hitObj.name
            if hitName == "Near_apple_01":
                HUD_text_2['Text'] = "I don't drop it here"
            if hitName == "Near_apple_02":
                HUD_text_2['Text'] = "I don't drop it here"
        else:
            HUD_text_2['Text'] = "I don't drop it here"

This code avoids that the I don’t drop it here message is printed when nearAny.positive detects the container, because if our Near sensor detects an object * ( if nearAny.positive: ), it allows to print the message only if it has been Near_apple_01 or Near_apple_02 , and with that it don’t allow it for the container.NOTE * that the near sensor only detects the objects with Player and Collision bounds enabled, which isn’t the default for objects, we have to turn it on manually. So the group of detectable object is limitable by our self.

If the Near sensor doesn’t detect an object, the message will be printed too, because of the else statement.
if … and ‘2# Sandwich’ in Invent_01 and Invent_01[‘2# Sandwich’] > 0:
We put this in the first if statement to avoid that the I don’t drop it here message is printed when the correspondent item (here Sandwich) isn’t in the inventory and the player uses the I key with the correspondent NUM key to drop that no existent item.
We have to ask if there is a sandwich in the player inventory to avoid a key error if there is none, and we ask if there are more than zero, because in that case we need to print our message and furthermore it implicates that the message isn’t printed if there is none.

drop_apple(cont):
In the drop_apple(cont): function the apple has separated code to add to the empties. There is asked for Near_apple_01 respectively Near_apple_02 with a similar if statement like above for the other items, and there we put a simple HUD_text_2[‘Text’] = “” to avoid that the I don’t drop it here message is printed.

    if item_03.positive and open_inv.positive and Empty_apple_01['apple_01'] == False:\
    and '3# Apple' in Invent_01 and Invent_01['3# Apple'] &gt; 0
        if nearAny.positive:
            hitName = hitObj.name
            if hitName == "Box_01":
                HUD_text_2['Text'] # does nothing except providing the needed indention level
        else:
            HUD_text_2['Text'] = "I don't drop it here"

And when employing the I don’t drop it here message for the apple far away from its empties, the code is also separated. We need another similar code (see code tag above) to have a correct interaction with the container, in this case we ask for Box_01 . If we won’t, and the player has one or more apples in his inventory and would press the keystroke I + 3 near the container, the apple(s) would be added to the container correctly, but if there isn’t a apple left in the inventory, and the player uses another time the keystroke, the message I don’t drop it here would appear. And this is incorrect, because there wouldn’t be an apple left which could be dropped.NOTE 1 : We put HUD_text_2[‘Text’] after if hitName == “Box_01”: , which does nothing (I tried the code with the single word nothing in that place and it works too). We do it because we need s. th. here on that indention level to make the code working, that means inhibit the use of the I don’t drop it here message when the Near sensor detects Box_01 .
NOTE 1.1 : The (correct) Python way is to use here instead (of HUD_text_2[‘Text’] ) the pass statement, which consists of the word pass and does the same, i.e. be nothing more than a placeholder.
NOTE 2 : We use if nearAny.positive: separated from the first if statement in difference to the code when asking for Near_apple while adding to the empties (see above). So we can employ the else: statement correctly for our needs.

In the next post we will see the interaction with the container.

Greetings,
achisp


Attachments

SimpleInventory_Demo_04.blend (604 KB)

### To interact with Box_01 ###

### Open Box_01 ###

def interBox_01(cont):
    ...
    if contain.positive and nearAny.positive:
        hitName = hitObj.name
        if hitName == "Box_01":
            HUD_text_1['Text'] = "Box: " + str(Invent_02)

HUD_text_1[‘Text’] = "Box: " + str(Invent_02)
See post#2, chapter ### Printing the inventory to screen ###, second code tag.

Add to Box_01 or Add to the player inventory ###

We use here the code for adding the apple from the player inventory to Box_01 to explain. This code is similar for the other items to add and a reverse for adding items from Box_01 to the player inventory.
The keystroke is I + 3 .

def interBox_01(cont):
    if item_03.positive and inv.positive and nearAny.positive:
        hitName = hitObj.name
        if hitName == "Box_01":
            if '3# Apple' in  Invent_01 and Invent_01['3# Apple'] &gt; 0:
                Invent_01['3# Apple'] -= 1
                if Invent_01['3# Apple'] == 0:
                    del Invent_01['3# Apple']
                Invent_02['3# Apple'] =  Invent_02.get('3# Apple',0) + 1
                HUD_text_1['Text'] = "Box: " + str(Invent_02)
                HUD_text_2['Text'] = str(Invent_01)

if ‘3# Apple’ in Invent_01 and Invent_01[‘3# Apple’] > 0:
We have to ask if there is an apple in the player inventory ( if ‘3# Apple’ in Invent_01 ) to avoid a key error if there is none. Then we ask if there are more than zero.

Invent_01[‘3# Apple’] -= 1
If the previous two conditions are True, we substract one with each I + 3 key stroke.

if Invent_01[‘3# Apple’] == 0: and del Invent_01[‘3# Apple’]
Thereafter it is asked if the amount after the subtraction is zero to delete the Apple from the inventory if it is so.

Invent_02[‘3# Apple’] = Invent_02.get(‘3# Apple’,0) + 1
We have above substrated one in the player inventory, here we add it to the container inventory ( Invent_02 ).

HUD_text_1[‘Text’] = "Box: " + str(Invent_02) and HUD_text_2[‘Text’] = str(Invent_01)
Then we print both inventories to the screen, Invent_02 at the top of the screen, with Box: before the inventory to distinguish it, and Invent_01 at the bottom of the screen. So we have always the actual inventory after the player’s action printed to screen (refreshed).

I like to mention here, that I make this inventory because I like to (learn this way) and also that I like to share it with you.
Greetings,
achisp

Also in his post : ### How to show an object name with mouse over any ###

We can use
in a text to break the line and start a new one, but when printing our inventory to screen, we can’t because we don’t use the text as string but as variable in the code. And the value of this variable will change with the different situations of our game.

The following code is taken from: http://www.cgmasters.net/free-tutorials/fps-mouselook-script-plus-real-text/#chapter8 / Chris Plush, thank you.

import textwrap
cont = bge.logic.getCurrentController()
own = cont.owner

startmessage = "Welcome to this tutorial on text in the game engine."

own["Text"] = textwrap.fill(startmessage, 20)

import textwrap
To use textwrap, a library of Python, we import it to our module file.

own[“Text”] = textwrap.fill(startmessage, 20)
I have used in SInv_05.py HUD_text_2[‘Text’] = textwrap.fill("Inventory: " + str(Invent_01), 75) to substitute HUD_text_2[‘Text’] = "Inventory: " + str(Invent_01) of SInv_04.py. Invent_01 is our variable to print together with the string Inventory: in front of it.
To understand "Inventory: " + str(Invent_01) you can see post#2, along with the second code tag of chapter ### Printing the inventory to screen ###.
textwrap.fill(…, 75) means that one text line (printed to the screen in our case) is max. 75 characters long. It will break the line after a word before or if reaching 75 characters.

### How to show an object name with mouse over any ###
We will show the name of certain objects in the scene when the Mouse cursor is over them.
Logic bricks : An Always sensor with True level triggering enabled and a Pulse frequency set to 20 , connected to a Python controller. A Mouse sensor with the Mouse event set to Mouse over any and also connected to the Python controller, which is set to Module mode and the Module name is set to SInv_05.print_obj .NOTE that I use a Delay sensor with the Text object used by this code (see nearly at the bottom of this chapter).

The code is taken from (and adapted): https://blenderartists.org/forum/showthread.php?363941-Mouse-over-d-objects-display-names-of-object-in-wrong-scene&p=2825645&viewfull=1#post2825645 / KiiNGz, thank you.

def print_obj(cont):
    own = cont.owner
    mOverAny = cont.sensors["MOverAny_01"]
    hitObj = mOverAny.hitObject

    scene_1 = bl.getSceneList()[1]
#    print(bl.getSceneList())
    HUD_text_2 = scene_1.objects['HUD_text_2']

    if mOverAny.positive:
        hitName = hitObj.name
        if hitName == 'Box_01' or hitName == 'Apple' or hitName == 'Player':
            HUD_text_2['Text'] = str(hitObj)

if hitName == ‘Box_01’ or hitName == ‘Apple’ or hitName == ‘Player’:
We show the names of these three objects when the Mouse cursor is over them. If we would use only if mOverAny.positive: and HUD_text_2[‘Text’] = str(hitObj) without our hitObject code, the mouse cursor would show the object name of any object when over it, p.e. also the name of the walls in a room.

else: and HUD_text_2.text = “”
In the code taken from KiiNGz’s post (see above) were this two lines at the bottom of the code, with else: on the same indention level as if mouseOver.positive: . They delete the text of the shown object names if the mouse cursor was taken away from the object.
I don’t use it here, because it interferes with (deletes) the other text I will show with the same Text object HUD_text_02 .
I have HUD_text_2.text = “” in the module function def clear_text_2(): . It runs with a Python controller attached to a Delay sensor, which Delay is set to 800 logic tics (and the Repeat button is enabled), so the names of the object (and also the other text printed with this Text object) will disappear after a while instead of inmediately like in the code of KiiNGz’s post.

IndexError: list index out of range
In the console I get this error, printed one time while the game is running. If I use print(bl.getSceneList()) , I have first one time the error, and thereafter multiple times the scene list, each at each pulse of the always sensor attached to this script. So I think the error is only in the first frame, when the HUD scene isn’t set, and since the second frame, when the HUD is set, the error disappears. Also I don’t note disturbs during the game.

That’s all for now.

Happy Christmas,
achisp

Attachments

SimpleInventory_Demo_05.blend (614 KB)