2

I was trying to make a game where you chop down some trees and sell it, but in doing so I managed to find a mind-breaking bug - the first tree would invert all the other trees! I know it doesn't make sense, but what i mean is that I have sorted the trees into a class and I have created a variable instance called self.tree_property which determines if the tree has been cut down or not. When you chop down the first tree all the other tree_property get set to True again. When you chop down any other tree, the first tree's tree_property gets set to True again Can anyone tell me how to fix it, and why it is happening? The code is below:

import pygame
import time
pygame.init()
print('Loading game...')
icon = pygame.image.load('C:\Program Files\Foraging Simulator\icon.png')
market = pygame.image.load('C:\Program Files\Foraging Simulator\market.png')
house = pygame.image.load('C:\Program Files\Foraging Simulator\house.png')
pygame.display.set_icon(icon)
market = pygame.transform.scale(market, (100, 100))
house = pygame.transform.scale(house, (100, 100))
root = pygame.display.set_mode((603, 573))
pygame.display.set_caption("Foraging Simulator")
window_is_open = True
white = (255, 255, 255)
black = (0, 0, 0)
width = 10
leaves_width = 30
height = 20
leaves_height = 10
x = 0
tree_trunk_x = 10
y = 0
tree_trunk_y = 10
vel = 5
brown = (150, 75, 0)
green = (58, 95, 11)
grass = (124, 252, 0)
score = 0
chop_wood = 'C:\Program Files\Foraging Simulator\chopping.mp3'
clock = pygame.time.Clock()
time = 0
happiness = 10
def test(string):
    print(string)
def reset_trees():
    for tree in trees:
        tree.tree_property = True
def open_house():
    reset_trees()
    score = 0
def open_market():
    happiness = score / 2
class Tree: # The class for the trees
    def __init__(self, tree_x, tree_y):
        self.tree_x = tree_x
        self.tree_y = tree_y
        self.tree_property = True # Creating instance tree_property
        self.trunk = None
        self.leaves = None
    def destroy(self):
        self.tree_property = False
    def create_tree(self):
        if self.tree_property:
            trunk_x = self.tree_x + 10
            trunk_y = self.tree_y + 10
            self.trunk = pygame.draw.rect(root, brown, (trunk_x, trunk_y, width, height))
            self.leaves = pygame.draw.rect(root, green, (self.tree_x, self.tree_y, leaves_width, leaves_height))
    def redraw(self):
        self.create_tree()
trees = []
for x in range(5):
    for y in range (5):
        trees.append(Tree(x*50, y*50))
root.fill(grass)
destroy_tree = None
countdown = 3
clock.tick(60)
say = True
print('Loading and attributes finsihed! Using mainloop...')
while window_is_open:
    if say:
        print('Mainloop loaded! Ready to go.')
        say = False
    time = pygame.time.delay(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            window_is_open = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = event.pos
            if market.get_rect().collidepoint(mouse_x, mouse_y):
                dx = mouse_x - x
                dy = mouse_y - y
                if abs(dx) <= 50 and abs(dy) <= 50:
                    open_market()
            mouse_x, mouse_y = event.pos
            if house.get_rect().collidepoint(mouse_x, mouse_y):
                dx = mouse_x - x
                dy = mouse_y - y
                if abs(dx) <= 50 and abs(dy) <= 50:
                    open_house()
            for tree in trees: # Clicking detection
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if tree.trunk.collidepoint(mouse_x, mouse_y):
                    dx = mouse_x - x
                    dy = mouse_y - y
                    if abs(dx) <= 50 and abs(dy) <= 50:
                        countdown = 3
                        destroy_tree = tree

    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        x += vel
    if keys[pygame.K_LEFT]:
        x -= vel
    if keys[pygame.K_UP]:
        y -= vel
    if keys[pygame.K_DOWN]:
        y += vel
    if destroy_tree != None:
        if countdown == 3:
            pygame.time.delay(950)
            countdown = countdown - 1
            pygame.mixer.music.load(chop_wood)
            pygame.mixer.music.play()
            while pygame.mixer.music.get_busy(): 
                pygame.time.Clock().tick(1)
        elif countdown > 0:
            pygame.mixer.music.load(chop_wood)
            pygame.mixer.music.play()
            while pygame.mixer.music.get_busy(): 
                pygame.time.Clock().tick(1)
            pygame.time.delay(950)
            countdown = countdown - 1
        else:
            destroy_tree.destroy()
            destroy_tree = None
            countdown = 3
            score = score + 1
    font = pygame.font.SysFont('Tahoma', 18, True, False)
    count = font.render(str(countdown), True, (0, 0, 0))
    screen_score = font.render("Score: " + str(score), True, (0, 0, 0))
    rendered_happiness = font.render("Happines: " + str(happiness), True, (0, 0, 0))
    root.blit(rendered_happiness, (410, 40))
    root.blit(count, (410, 0))
    root.blit(screen_score, (410, 20))
    rectangle = pygame.draw.rect(root, (0, 0, 0), (x, y, width, 10)) 
    for tree in trees:
        tree.redraw()
    root.blit(market, (400, 300))
    seconds = clock.tick()
    pre = time + seconds / 1000
    time = int(pre)
    root.blit(house, (400, 100))
    pygame.display.update()
    root.fill(grass)
pygame.quit()




Thanks!

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
BoxifyMC
  • 43
  • 10
  • This is a lot of completely undocumented code. Please [edit] your question to make the code understandable, ideally by reducing it to a minimal example that reproduces your problem. See [ask] page for details. – MisterMiyagi May 20 '20 at 19:47
  • When you click on the first tree, the actually click on `house` (`house.get_rect().collidepoint(mouse_x, mouse_y)`), thus `open_house` is invoked. – Rabbid76 May 20 '20 at 19:49
  • @Rabbid76 But why though? – BoxifyMC May 20 '20 at 20:32

1 Answers1

2

A pygame.Surface object has no location. The position of the rectangle which is returned by get_rect() is always (0, 0).
Note, you specify a location when the surface is blit:

root.blit(house, (400, 100))

You have to set the same location, when you retrieve the rectangle for the collision. You can pass keyword argument values to this function, which are set to the pygame.Rect object:

house.get_rect(topleft = (400, 100))

For instance:

while window_is_open:
    # [...]

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            window_is_open = False

        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = event.pos
            dx = mouse_x - x
            dy = mouse_y - y

            if market.get_rect(topleft = (400, 300)).collidepoint(mouse_x, mouse_y):
                if abs(dx) <= 50 and abs(dy) <= 50:
                    open_market()

            if house.get_rect(topleft = (400, 100)).collidepoint(mouse_x, mouse_y):
                if abs(dx) <= 50 and abs(dy) <= 50:
                    open_house()

            for tree in trees:
                if tree.trunk.collidepoint(mouse_x, mouse_y):
                    if abs(dx) <= 50 and abs(dy) <= 50:
                        countdown = 3
                        destroy_tree = tree
    # [...]

Furthermore in you have to use the global statement to treat happiness as a variable in global namespace in the function open_market:

def open_market():
    global happiness
    happiness = score / 2
Rabbid76
  • 202,892
  • 27
  • 131
  • 174