Search code examples
pythoninventory

Python: Creating a slot based inventory system


I am trying to create an inventory system in Python using the module pygame. The inventory system is similar to Minecraft. It is a slot-based inventory system, meaning that each item is attached to a slot, and can be moved around the inventory.

This is the code:

#Module that allows me to create a screen and add images to the screen
import pygame
pygame.init()
win = pygame.display.set_mode((800,600))

#Variable that will keep track of the index of what slot the player is 
#selecting
selectedSlot = None

#Function that checks whether there is collision or not 
def collision(x,y,x2,y2,w):
    if x + w > x2 > x and y+w > y2 > y:
        return True
    else:
        return False

#Slot Class
class slotClass:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def draw(self,win):
        #Draws the slots using the x and y values 
        pygame.draw.rect(win,(255,0,0),(self.x,self.y,50,50))

        #Uses a function to test for collision with the mouse's x and y values 
        if collision(self.x,self.y,mx,my,66):
            global selectedSlot
            pygame.draw.rect(win,(128,0,0),(self.x,self.y,50,50))

            #This will set an integer value to a varaible, dependent on what the index of the element the player selecting is 
            selectedSlot = slotArray.index(self)

        #Problem with code: 
        #When the following 2 lines are uncommmented, the variable selectedSlot is set to "None", regardless of whether there is collision or not
        #else:
            #selectedSlot = None

#Slot array 
slotArray = []
#Slot information
slotCount = 9


#Populates the slotArray with the desired slotCount
while len(slotArray) != slotCount:
    slotArray.append(slotClass(100+len(slotArray)*70,50))

#main loop
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    print(selectedSlot)

    #Mouse x and y value to set to the vars mx and my
    mx,my = pygame.mouse.get_pos()
    win.fill((0,0,0))


    #For every element in the slotArray, the function draw is called from the slotClass
    for i in slotArray:
        i.draw(win)

    pygame.display.update()
pygame.quit()

How it works is I have a class, which will hold the information for each slot. Such as the x value, y value and what item is in every slot. Then I have an array, which will contain every slot instance. I also defined how many slots I want entirely.

To populate this array with the slots, I first started by constantly appending to this array of slots until it equals the desired amount of slots in each row. I multiply 55 by the number of elements in the array to spread apart the slots.

The problem I am having comes when trying to create the ability for the player to mouse over/select each slot. What I want is for the player to simply be able to hover over a slot, that slot will turn a different colour, and then the player can select an item out of said slot.

I created a collision function for that and I'm calling that function within the draw function inside the slotClass. I also have a variable called slotSelected, which keeps track of the index of the slot that the player is mousing over with/hovering over.

The problem I am experiencing is, that whenever the player hovers over a slot and then stops hovering over any slots, the slot index that I am setting still remains to be the index of the slot the player was just on. When I add an else statement to check if there is no collision with a slot, and set the var slotSelected to something like None for example (to show that the player isn't colliding with any slot) the var slotSelected is constantly set to None, regardless of whether there is collision or not. I have a print statement that prints the value of slotSelected. You'll notice that it prints the index of the slot the player is colliding with. However, when you uncomment the else statement I have contained in the slotClass, the var slotSelected will always be set to None.

Any suggestions on how to fix this?


Solution

  • Perhaps you could try testing for another collision with the background as well. I'm not an expert at pygame, but this is the pseudo-code I would use:

    if collision("with game window"): # If mouse is within your game screen
        global selectedSlot
        if collision(self.x, self.y, mx, my, 66): # mouse is on screen AND on a box
            selectedSlot = slotArray.index(self) 
        else: # mouse on screen, but not on box -> return None
          selectedSlot = None
    

    So that you can assign None when the mouse is in your game window but not on an item slot.

    EDIT

    I figured out why it is responding as such. You have the lines:

    for i in slotArray:
        i.draw(win)
    

    Which will go to slot 0, see it is selected -> set selectedSlot = 0, then go to slot 1, see it is unselected -> set selectedSlot = None OVERWRITING the value you had previously set. You will need to break your loop if selectedSlot is not None!! Here is code to solve that:

    #Module that allows me to create a screen and add images to the screen
    import pygame
    pygame.init()
    win = pygame.display.set_mode((800,600))
    
    #Variable that will keep track of the index of what slot the player is
    #selecting
    selectedSlot = None
    
    #Function that checks whether there is collision or not
    def collision(x,y,x2,y2,w):
        if x + w > x2 > x and y+w > y2 > y:
            return True
        else:
            return False
    
    #Slot Class
    class slotClass:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def draw(self, win):
            #Draws the slots using the x and y values
            pygame.draw.rect(win, (255, 0, 0), (self.x, self.y, 50, 50))
    
            #Uses a function to test for collision with the mouse's x and y values
            if collision(self.x, self.y, mx, my, 66):
                global selectedSlot
                pygame.draw.rect(win, (128, 0, 0), (self.x, self.y, 50, 50))
    
                #This will set an integer value to a varaible, dependent on what the index of the element the player selecting is
                selectedSlot = slotArray.index(self)
    
            #Problem with code:
            #When the following 2 lines are uncommmented, the variable selectedSlot is set to "None", regardless of whether there is collision or not
            else:
                selectedSlot = None
    
    #Slot array
    slotArray = []
    #Slot information
    slotCount = 9
    
    
    #Populates the slotArray with the desired slotCount
    while len(slotArray) != slotCount:
        slotArray.append(slotClass(100+len(slotArray)*70,50))
    
    #main loop
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        #Mouse x and y value to set to the vars mx and my
        mx,my = pygame.mouse.get_pos()
        win.fill((0,0,0))
    
    
        #For every element in the slotArray, the function draw is called from the slotClass
        selectedSlot = None
        for i in slotArray:
            i.draw(win)
            if selectedSlot is not None:
                break
    
        print(selectedSlot)
    
        pygame.display.update()
    pygame.quit()
    

    The problem is you've combined drawing the items and selecting them into one function (very bad). So now when the loop is broken the rest of the boxes are not drawn!!