Python Forum
[pygame] Inventory items not working
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[pygame] Inventory items not working
#11
I feel as if I have done everything you suggested (except of course the time.sleep problem) and I still have 923 lines of code, of course about 200 of those lines are probably no code and spreading dictionaries over multiple lines. The total lines of code if I compacted everything would be about 700 - 750

I just compacted everything in a copy of the game and found that my guess was close. The total lines in the end were 779.
Reply
#12
(May-27-2019, 03:35 AM)SheeppOSU Wrote: I have a time.sleep after some of the buttons to make sure it doesn't get clicked twice. How would you suggest I make sure it isn't clicked twice

Your button class is improperly coded in button.optClick. You need to check collision/clicks in the event loop.

for event in pygame.event.get():
    if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
        function()
I would read this tutorial thoroughly. Your collision detection for the button is also overly complicated than it needs to be and can be reduced.

    def get_event(self, event):
        if event.type == pg.MOUSEBUTTONDOWN and event.button == 1: #if event is a mouse button 1 press
            if self.rect.collidepoint(pg.mouse.get_pos()): # if mouse is inside button 
                self.command()
(May-27-2019, 05:17 AM)SheeppOSU Wrote: I feel as if I have done everything you suggested (except of course the time.sleep problem) and I still have 923 lines of code, of course about 200 of those lines are probably no code and spreading dictionaries over multiple lines. The total lines of code if I compacted everything would be about 700 - 750
I just compacted everything in a copy of the game and found that my guess was close. The total lines in the end were 779.
You need to read this. There should only be a single call, not calls all over the code. Even though its relating to text game, it can still be applied to GUI programming.

Your loading images currently are not what i meant. Somethine along the lines of...

images = {}
for f in os.listdir(IMAGEPATH):
    image = pygame.image.load(f)
    filename = f #strip off .png and path here to only get filename /user/Destination Pics/DiamondSword.png -> DiamondSword
    images.update({filename:image})

...
#use image
blit(images['DiamondSword'], images['DiamondSword'].rect)
This is what i mean by reducing your code. Your loading of images could be reduced from 80 lines to just 5 lines. The same with your other locations. They are not really resolved. you just condensed them a little making them actually harder to read.

Everywhere you have 2 or more copies of a line can be reduced. That is the rule of thumb for programming. That is a tall tale sign of bad coding.

Quote:
    def character(self):
        if not self.dead:
            if self.direct == 'Left':
                self.weapon = gD.blit(self.weapType[3], (self.rect[0] - round(self.width*.8), self.rect[1] + round(self.height/2)))
                self.armor = gD.blit(self.color[3], (self.rect[0], self.rect[1]))
                self.weaponPos = (self.rect[0] - round(self.width*.8), self.rect[1] + round(self.height/2))
            elif self.direct == 'Right':
                self.weapon = gD.blit(self.weapType[1], (self.rect[0] + round(self.width*.8), self.rect[1] + round(self.height/2)))
                self.armor = gD.blit(self.color[1], (self.rect[0], self.rect[1]))
                self.weaponPos = (self.rect[0] + round(self.width*.8), self.rect[1] + round(self.height/2))
            elif self.direct == 'Up':
                self.weapon = gD.blit(self.weapType[0], (self.rect[0] + round(self.width/2), self.rect[1] - round(self.height*.8)))
                self.armor = gD.blit(self.color[0], (self.rect[0], self.rect[1]))
                self.weaponPos = (self.rect[0] + round(self.width/2), self.rect[1] - round(self.height*.8))
            elif self.direct == 'Down':
                self.weapon = gD.blit(self.weapType[2], (self.rect[0] + round(self.width/2), self.rect[1] + round(self.height*.8)))
                self.armor = gD.blit(self.color[2], (self.rect[0], self.rect[1]))
self.weaponPos = (self.rect[0] + round(self.width/2), self.rect[1] + round(self.height
            if plrX > self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX > self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = 'Down'
                self.rect = (self.rect[0], self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = 'Up'
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX > self.rect[0]:
                if num < 3:
                    self.direct = 'Right'
                self.rect = (self.rect[0] + 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX < self.rect[0]:
                if num < 3:
                    self.direct = 'Left'
                self.rect = (self.rect[0] - 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY == self.rect[1]:
                if num < 3:
self.direct = random.choice(AnyDirect)


as well as unknown magic numbers all over the place.
Quote:
            if k < 6:
                i += 30
            else:
                k = 0
                i = 0
                self.y += 30
            slot = self.slots/6
            if self.y >= self.Y + 30*slot:
self.y = self.Y
as well as obviously still using self.x and self.y instead of pygame rects.

You have logic (SlotBtn creation and click method) in your draw method. As well as weapon changing. What does the weapon changing or buttons have anything to do with drawing? The draw method should only be blitting....no logic. This is why we have so much trouble find things.
    def draw(self, p, equips=None):
        i = 0
        k = 0
        self.opened = True
        SD.message_display(gD, 'Inventory', 50, black, self.x + 90, self.y - 50)
        for n in range(1, self.slots + 1):
            k += 1
            if n in self.availableSlots:
                gD.blit(self.thumbnails[n], (self.x + i + 1, self.y + 1))
                    
            if n in self.equips:
                SlotBtn = IBT((self.x + i, self.y, 20, 20))
                SlotBtn.optClick(command=partial(self.equipSub, n))
                pygame.draw.rect(gD, red, (self.x + i, self.y, 20, 20), 1)
                if n in self.availableSlots:
                    for weapon in weaponTuple:
                        if self.thumbnails[n] in weapon:
                            self.item['weapon'] = weapon
                            p.weapChange(weapon)
                            strItemChange(weapon)
                    for armor in armorTuple:
                        if self.thumbnails[n] in armor:
                            self.item['armor'] = armor
                            p.armorChange(armor)
                            strItemChange(armor)
            else:
                SlotBtn = IBT((self.x + i, self.y, 20, 20))
                SlotBtn.optClick(command=partial(self.equipChange, False, self.equips + (n,), n))
pygame.draw.rect(gD, black, (self.x + i, self.y, 20, 20), 1)
Recommended Tutorials:
Reply
#13
To be honest it is better to start over properly than to try to morph bad code into good. It takes more work than just rewriting it from scratch. And the people helping you are getting burnt out. However that does not help at all if you rewrite more spaghetti code the second time. I have thrown out a lot of code when i first started while learning how to structure the code. So my suggestion would be to read tutorials and follow along with tutorials. Do each section and post back. Even if you understand the concept in the tutorial and it works, post back. Because tutorials cna give you bad coding structure. We will easily be able to fine tune your skills if we can work with 20 or less lines of pygame code relating to each segment of gaming, than dealing with an entire program. Then when you understand each section you can put them all together to make an RPG game. You would have more responses as more people tend to respond with 20 lines of code, you would have better responses as people can work with you in 20 lines of code where 1K is too much to deal with. It would be faster for your understanding of the concept.

The easiest what to get help on forums is to reduce your problem to 20 lines or less. And yes this can be done with pygame. The idea is you take out the issue of your program, and recreate it in a small environment of 20 lines or less (the goal). It should only be enough code to produce the issue. Sometimes it might go up to 50 lines, but it should never go above 100. And your goal should be 20. So every time you have an issue, you create a whole new program based around that issue. you minimize the code as much as possible to give to the forums. You keep minimizing the code until the issue is impossible to address without that code. Then submit it to the forum. The more work you put into reducing the code to 20 lines, the better the responses are and the more there are of them. Try it. How many responses do you get from generic questions about a for loop that has 5-10 lines of code. From how many different people. They will keep responding if you still have problems. As a person asking questions on forums you have to do the work of making it easy to answer your question.

In pygame terms, it should have no resources for the person to require to run the code (unless the issue is with the resource). So
image = pygame.image.load(PATH)
would convert to

image = pygame.Surface([50,50])
Where the size (50,50) would be the size of whatever image it replaced.

And you only need a max of 2 surfaces ever. And that is only if you are having an issue with collision. All other images in your program can be ignored. You dont need fancy backgrounds. you dont need walls or makes sure the player leaves the screen, etc. You dont need all that in the mini example you post to the forums. You only need to illustrate your problem.
Recommended Tutorials:
Reply
#14
I decided to start it from scratch. I fro one wanted your opinion on how I am starting and I also need to know how to fix one of my problems. Originally I had the login and two text boxes. The button was only detecting clicks at certain times though so I separated it to give it a bigger range of detection. It still won't work though.
Here is the full code if you could take a quick scan and grade it
import pygame, random, os, sys
from functools import partial
from pathlib import Path
from DestinationFunc import Screen_Display as SD, Button as BT, TextBox as TB

pygame.init()

width = 800
height = 800
fps = 80

Screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

UserData = {'Sheepposu' : {'password' : 'password'}}

class Player():
    def __init__():
        pass


class Enemy():
    def __init__():
        pass


def SignIn():
    #Intro
    sizeVar = 1
    OrganizationTup = (((205, 0, 0), 405, 295), ((215, 0, 0), 404, 296), ((225, 0, 0), 403, 297),
                       ((235, 0, 0), 402, 298), ((245, 0, 0), 401, 299), ((255, 0, 0), 400, 300))
    while sizeVar < 120:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                EndGame(True)
        sizeVar += 1
        Screen.fill((0, 0, 255))
        for x in OrganizationTup:
            SD.message_display(Screen, 'Destination', sizeVar, x[0], x[1], x[2])
        pygame.display.update()
        clock.tick(fps)

    Logging = True
    while Logging:
        #Login
        username = TB((200, 500, 400, 100), 'Type Username', 50, (0, 0, 255))
        password = TB((200, 650, 400, 100), 'Type Password', 50, (0, 0, 255))
        login = BT((650, 550, 100, 50), 'Log In', 40, (0, 0, 255))
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    EndGame(True)
            username.start(Screen, (255, 0, 0), 20)
            password.start(Screen, (255, 0, 0), 20)
            if username.text != '' and password.text != '':
                break
            pygame.display.update()
            clock.tick(fps)

        #Verification
        done = False
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    EndGame()
            login.draw(Screen, (0, 255, 0))
            done = login.optClick(Screen, (0, 200, 0), (0, 255, 0))
            pygame.display.update()
            clock.tick(fps)
        UserName = username.text
        PassWord = password.text
        for name in UserData:
            if UserName.lower() == name.lower():
                if PassWord == UserData[name]['password']:
                    Logging = False
                else:
                    pygame.draw.rect(Screen, (0, 0, 255), (0, 350, 800, 50))
                    SD.message_display(Screen, 'Invalid Password', 40, (255, 0, 0), 400, 450)
            else:
                pygame.draw.rect(Screen, (0, 0, 255), (0, 350, 800, 50))
                SD.message_display(Screen, 'Invalid Username', 40, (255, 0, 0), 400, 450)

def LevelMaker():
    pass


def Main():
    SignIn()


def EndGame(leaving=None):
    if leaving:
        pass
    
    else:
        pass

Main()
pygame.quit()
sys.exit()
Button Code -
for event in pygame.event.get():
            clicked = pygame.mouse.get_pressed()
            if event.type == pygame.MOUSEBUTTONDOWN and clicked[0] == 1 and pygame.Rect((200, 650, 400, 100)).collidepoint(pygame.mouse.get_pos()):
                #code
Reply
#15
(May-27-2019, 07:18 PM)SheeppOSU Wrote: The button was only detecting clicks at certain times though so I separated it to give it a bigger range of detection. It still won't work though.
That is not the proper fix. That means there is an issue, and expanding the collision with your mouse range only worsens the problem.

Quote:if event.type == pygame.MOUSEBUTTONDOWN and clicked[0] == 1 and pygame.Rect((200, 650, 400, 100)).collidepoint(pygame.mouse.get_pos()):
Get rid of the last and condition for the rect. Its too much in one check.

It should be more like this.
btn = Button(rect=(200, 650, 400, 100))
...
if event.type == pygame.MOUSEBUTTONDOWN and clicked[0] == 1:
    if btn.rect.collidepoint(pygame.mouse.get_pos()):
        #code
Here btn is the Button object of your class. btn.rect is the rect you gave it when initializing the button object.

and this
pygame.Rect((200, 650, 400, 100)).collidepoint(pygame.mouse.get_pos())
is not an efficient way to check collision. You are creating an entire new rect when doing this. You want to reuse the button rect from its class.

Quote:
                if PassWord == UserData[name]['password']:
                    Logging = False
                else:            if plrX > self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX > self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = 'Down'
                self.rect = (self.rect[0], self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = 'Up'
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX > self.rect[0]:
                if num < 3:
                    self.direct = 'Right'
                self.rect = (self.rect[0] + 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX < self.rect[0]:
                if num < 3:
                    self.direct = 'Left'
                self.rect = (self.rect[0] - 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY == self.rect[1]:
                if num < 3:
                    self.direct = random.choice(AnyDirect)
                    pygame.draw.rect(Screen, (0, 0, 255), (0, 350, 800, 50))
                    SD.message_display(Screen, 'Invalid Password', 40, (255, 0, 0), 400, 450)
            else:
                pygame.draw.rect(Screen, (0, 0, 255), (0, 350, 800, 50))
                SD.message_display(Screen, 'Invalid Username', 40, (255, 0, 0), 400, 450)
The pygame.draw.rect lines should be saved. Its a horrible process when you have them littering your entire code. Why are you drawing new rects when your button class already has a draw method? You should have be reusing the your Button class.

Quote:
if self.rect[0] + self.rect[2] > mouse[0] > self.rect[0] and self.rect[1] + self.rect[3] > mouse[1] > self.rect[1]:
Your collision detection for your button is over-complicated. Simplify it by just doing
if self.rect.collidepoint(pygame.mouse.get_pos()):
Quote:
    def optClick(self, gD, ShadowColor, ButColor, command=None, command2=None):
        mouse = pygame.mouse.get_pos()
        click = pygame.mouse.get_pressed()
        if self.rect[0] + self.rect[2] > mouse[0] > self.rect[0] and self.rect[1] + self.rect[3] > mouse[1] > self.rect[1]:
            pygame.draw.rect(gD, ShadowColor, (self.rect))
            gD.blit(self.textSurf, self.textRect)
            if click[0] == 1:
                    if command != None:
                        command()
                    if command2 != None:
                        command2()
            else:
                pygame.draw.rect(gD, ButColor, (self.rect))
                gD.blit(self.textSurf, self.textRect)
In your button class...You should use a toggle variable to switch to and from ShadowColor to ButColor. Then assign that to a general color variable and blit that color. Here is a function that checks for mouse hover over the button. Notice that there is no drawing here. Just below that method is the draw method. In it you can see that color gets blit no matter what. But color here is assigned a different color as hover color or clicked color based on other actions. Here it is sent to draw (another method because its a rounded rect instead of a pure rectangle). Otherwise it was just blit right there as color. Here is the actual drawing of the button. Notice there is no logic here, it just draws color as the logic has been established beforehand.


Quote:
                    if command != None:
You only need if command:. Command is either None or it is not. You never need to check for against None unless you are comparing it to empty or None. So you can just do if command: or if not command: for the opposite. you also need to use if and elif. if and if will check both conditions. you only want it to check one and then the other...not both. Otherwise you could result in bugs.

Quote:
self.rect = (950, self.rect[1], self.rect[2], self.rect[3])
this is more confusing. you should just call self.rect.x or self.rect.y instead of self.rect[0]] etc.

Quote:
            if plrX > self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX > self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpRight)
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = random.choice(UpLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrX < self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = random.choice(DownLeft)
                self.rect = (self.rect[0] - 0.2, self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY > self.rect[1]:
                if num < 3:
                    self.direct = 'Down'
                self.rect = (self.rect[0], self.rect[1] + 0.2, self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY < self.rect[1]:
                if num < 3:
                    self.direct = 'Up'
                self.rect = (self.rect[0] + 0.2, self.rect[1] - 0.2, self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX > self.rect[0]:
                if num < 3:
                    self.direct = 'Right'
                self.rect = (self.rect[0] + 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrY == self.rect[1] and plrX < self.rect[0]:
                if num < 3:
                    self.direct = 'Left'
                self.rect = (self.rect[0] - 0.2, self.rect[1], self.rect[2], self.rect[3])
            elif plrX == self.rect[0] and plrY == self.rect[1]:
                if num < 3:
                    self.direct = random.choice(AnyDirect)

Aside from being redundant, what is the point of checking for if num < 3? As there is nothing else compared.
Recommended Tutorials:
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Adding an inventory and a combat system to a text based adventure game detkitten 2 6,873 Dec-17-2019, 03:40 AM
Last Post: detkitten
  [pygame] Inventory problems. Weapons equipped to wrong slot SheeppOSU 6 4,012 May-07-2019, 02:46 AM
Last Post: SheeppOSU
  [pygame] Blitting armor and weapons with inventory SheeppOSU 3 2,883 Apr-29-2019, 02:31 AM
Last Post: SheeppOSU
  [pygame] Equiping inventory slots with invisible buttons SheeppOSU 6 4,726 Apr-26-2019, 08:45 PM
Last Post: SheeppOSU
  [pyGame] Key Event not Working !... JamieVanCadsand 7 20,591 Sep-29-2017, 08:59 PM
Last Post: Mekire

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020