Checking viable property change in Python using If/Else statement

Alright, so I’m creating a Python script for an inventory scene. Nothing crazy, like item stacks and items with individual stats like durability or charges or anything. It essentially just is mesh swap, where upon receiving a message from a different scene it will change a property that controls what mesh is visible. So what happens is there are four properties, Slot1, Slot2, Slot3, and Slot4. I have it designed so that when it reads:

if own[‘Slot1’] = 0 and addswordmessage.positive:
own[‘Slot1’] = 4 #This is the property number to activate the mesh swap for the sword

and then later down in the script it has the stuff for actually swapping the mesh, as controlled by the properties.

My question is, how would I create an else statement where if Slot1 isn’t zero, then it’ll change the property of Slot2 instead? And on and on, looping back to Slot1 if all slots are filled. So this way it isn’t filling all slots at once, or only filling one inventory slot and refusing to recognize the others. Like “If Slot1 is 0 fill that, else look at Slot2. If Slot2 is not 0, check Slot3. If Slot3 is not 0, check Slot4. If Slot4 is not 0, go back and check Slot1 again.”

Also if someone knows how to receive a message using only Python and without a sensor that would be extremely useful. Having tons of sensors is problematic because of clutter. It seems odd that the Python documentation shows how to send messages, but not how to receive them.

Property name is a string. You can add to a string, and convert an integer to a string. If we know what the current slot is, we can add or substract to it, then add the number to the word. Maybe you can do it like so,

x = own["Current_Slot"]                  #here, store slot number
current_slot = own["Slot"+str(x)]
if current_slot == 0:
    #Stuff
else:
    own["Current_Slot"]  =+ 1            #add to slot number,
    if "Slot"+str(x) not in own:         #when there is no next slot,
        own["Current_Slot"]  = 1         #go back to square one

NINJAEDIT: it’s string lyeb, not strig.

you can use “elif” that is pythons version of else if.

Right, so, I tried Liebranca’s method. Which worked, but can be cumbersome when adding the “meshReplace.shortsword” sort of things to it.

I also tried elif as you suggested, and… Well, the console’s not giving me any errors. But Slot1, Slot2, and Slot3’s integer properties aren’t increasing at all. I think it might be how I formatted the indents for the elif parts. Here’s the abridged code, as I wrote it down:

def invlist (cont):
    
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    keyboard = bge.logic.keyboard
    JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED

#Blah blah blah all stuff like listing and changing the meshes and menu button colours and whatever#

    if own['slot1'] == 0:
        if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
            own['slot1'] = 1
            
    elif own['slot1'] >= 0:
        if own['slot2'] == 0:
            if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
                own['slot2'] = 1
                
    elif own ['slot2'] >= 0:
        if own['slot3'] == 0:
            if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
                own['slot3'] = 1

I’m mostly theory-crafting here because I don’t know the full picture, but question: since you’re expecting a message, couldn’t you grab the string for the mesh’s name from the body of the message? This is usually how I get away with using fewer sensors.

For instance,

#send the message
mesh_name = "shortsword" #this could be a property, to use as variable
obj_name = "name of object that listens for this message"
sendMessage("replaceMesh",mesh_name,obj_name)

#listen for replaceMesh then grab mesh_name from recieved message
if message_sensor.positive and len(message_sensor.bodies) > 0:
    new_mesh = message_sensor.bodies[-1]  #get body of last received message
    current_mesh = obj.meshes[0].name
    if isinstance(current_slot,str) and new_mesh != current_mesh:
        obj.replaceMesh(new_mesh)         #check if name of current mesh
                                          #matches received message's body
                                          #and replace mesh when not

Just an example, there are many ways to go about it and I might not be fully understanding what you want to do. But if you provide some more context I can try to further elaborate on this/adapt it to your use case.

Cheers.

have you checked that “keyboard.events” does what you expect .

The eventual plan is to have sixteen slots of inventory. These slots can either hold solitary single-use items like say potions or poison heals, or equipment that can be used in the equipment slots on the same screen. The equipment slots act just like the inventory slots, as both are using the same script and in the same scene. It’s just changing the mesh and textures from a blank plate to whatever that equipment is in that slot, be it a weapon or armour. It uses globalDict to save the equipment slot’s property, as an integer, and loads it in the main scene to change the character’s appearance by using replaceMesh. The messages are temporary for an iterative design, as I add more items for things like quests and puzzles. It’s easier for me personally than updating an ever growing list for globalDict. That’ll be saved for when the items are finalized.

What I want to happen is that there are the inventory slot meshes, Slot1, Slot 2, Slot 3, and Slot4. Then the camera itself runs the script, being InventoryList.py. It has the properties slot1, slot2, slot3, and slot4, all of which are integers. Upon receiving a message from the main scene when the character collides with the item, it will either change the slot integer from 0 (default state with an empty mesh), to the number of the item (1 being a helmet, 4 being shortsword, and so on). When the property has changed, it will have something like

if own['slot1'] == 4:
     Slot1.replaceMesh("shortsword")

Which will replace the empty inventory mesh with one for a shortsword. Likewise when you equip it, it will use the same function but replace [‘slot1’] with something like [‘rHand’], and free the inventory slot while now taking up the equipment slot instead. This will be separate from the message system that actually changes the integer value itself, which would be placed in a block higher up in the script and hold functions like what I previously posted. So the end result would be

def invlist (cont):
    
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    keyboard = bge.logic.keyboard
    JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED

#Blah blah blah all stuff like listing and changing the meshes and menu button colours and whatever#

    if own['slot1'] == 0:
        if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
            own['slot1'] = 1
            
    elif own['slot1'] >= 0:
        if own['slot2'] == 0:
            if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
                own['slot2'] = 1
                
    elif own ['slot2'] >= 0:
        if own['slot3'] == 0:
            if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
                own['slot3'] = 1

# Just a space to delineate the different parts, since they have different functions

    if own['slot1'] == 0:
        Slot1.replaceMesh("Slot1")

    if own['slot1'] == 4:
        Slot1.replaceMesh("shortsword")

    if own['slot2'] == 0:
        Slot2.replaceMesh("Slot1")

    if own['slot2'] == 4:
        Slot2.replaceMesh("shortsword"

You see what I mean?

That is very odd indeed. You’re right, it isn’t registering the KX event at all, even when I put it in it’s own argument without the elif statement. Something further up the script must be breaking it.

you can get rid of a few if statements in your code

if own['slot1'] == 0:
    if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
        own['slot1'] = 1

elif own['slot2'] == 0: #only run if slot1 is not zero
    if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
        own['slot2'] = 1

elif own['slot3'] == 0: #only run if slot1 and slot2 is not zero
    if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
        own['slot3'] = 1

Aye, I think I follow.

The same functionality can be achieved with less evaluations though. Rather than checking every slot for every possible int value, I’d have that integer correspond to an index on a static list.
Suppose you had something like this,

#                0        1       2        3             4
weapon_list = ["empty","hammer","axe","fishing rod","shortsword"]

Then weapon_dict[4] equals shortsword, way you can do

slot_mesh = weapon_list[own["slot"]]
slot.replaceMesh(slot_mesh)

You’d have to write a list, yes, but it’s much less if spaghetti, and you wouldn’t have to copy and paste the same code 16 times for all slots, so less redundancy.

Furthermore, if you grab a reference to the object whose mesh needs replacing at the time you receive the message, you can call a function there, and do both things in one go.

if own['slot1'] == 0:
    if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
        own['slot1'] = 1
        onEquip(Slot1,1) #send slot obj and integer to function

#somewhere else,
def onEquip(slot,item_id): #onEquip(slot=Slot1, item_id=1)
    slot_mesh = weapon_list[item_id] #weapon_list[1] == "hammer"
    slot.replaceMesh(slot_mesh)      #Slot1.replaceMesh("hammer")

It could also be a static dictionary instead of a list if there’s need to access data other than the name of the mesh (damage, healing, duration and whatnot). integers can be used as keys on a dict.
Sometimes I’ll bundle certain constant values in a separate .py file so it can be accesed anywhere else by importing it like one would a module. it’s one way of being orderly, there’s something about cramming everything together within the bge global dict that doesn’t always sit well with me.

I’m very sleepy now. Hope you can find this useful.

this does the same as all the if/elif/else statements does,but uses a loop instead.

slots = ["slot1","slot2","slot3","slot4","slot5","slot6","slot7","slot8",]

for slot in slots:
    if own[slot] == 0:
        if keyboard.events[bge.events.ZEROKEY] == JUST_ACTIVATED: #This is just for testing, will be replaced with a message
            own[slot] = 1
        break

I don’t believe there is a way to recieve a message in python. without a logic brick. I could be wrong tho.
But you could get the scene object, and it’s property instead.

1 Like

the message system in bge is half baked, lots of functionality missing.

I’ve figured out the KX events not activating. Apparently all of the code was working. I reformatted it to run as a module instead, and found out I’d accidentally misnamed one of the objects in the scene. Thanks to all of your guys’ help in streamlining it.

I think it would be easier to implement when you go a step back and look at the situation from a little distance. Ask yourself what you really want to get.

I mean:

Is pretty easy to implement:

else:

or

if own[‘Slot1’] = 0

How would you formulate what you want when you are not that close on the code?

Maybe: Find a free slot.

This way you can have any implementation that satisfies your needs.

Even here you can go a step more back:

Maybe: Add item to inventory.

Sure your should care special situations too (e.g. the inventory is full, the item already is in the inventory, …).

Then you step forwards again and implement the details. You might discover that your current implementation already does what you want, or does not or does too much, or another solution would fit better.

Just some thoughts.