How to detect a second pygame.MOUSEBUTTONDOWN inside a loop that starts by checking a first pygame.MOUSEBUTTONDOWN

Viewed 42

The question is a bit confusing but to make it better understandable I will first post the block of code interested:

 # Event loop (inner)
for event in pygame.event.get():
    if event.type == pygame.QUIT:
        sys.exit()

    # mouse handling
    if event.type == pygame.MOUSEBUTTONDOWN:
        mouse_position = pygame.mouse.get_pos()
        if selected_unit:
            selected_unit = None
        else:
            for unit in white_army:
                if unit.tile_area.collidepoint(mouse_position):
                    selected_unit = unit
                # highlight the selected unit
    if selected_unit:
        highlight_tile(selected_unit.tile_area)
        mouse_position = pygame.mouse.get_pos()
        for unit in white_army:
            current_position = selected_unit.position
            if on_target():
                print('ON TARGET -------------------------------------------')
                if event.type == pygame.MOUSEBUTTONDOWN:
                    print('MOUSE BUTTON PRESSED++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')

Basically I am selecting a tile on a chessboard (to select a unit), highlighting it, and then trying to select the tile right on top of the selected one, to then redraw the unit on it.

User Rabbid76 gave me a nice way to select (highlight) a tile, and deselect (tile back to normal), but unfortunately involves a check about MOUSEBUTTONDOWN.

So what happens is that every time I select a tile with a white unit on it (at the moment it works only for white units, better if pikemen), the tile will be highlighted as wanted, but when I try to select the next tile up on the chessboard, the loop will take it as if I am deselecting the first tile.

I have literally spent the whole morning trying to figure out a solution, creating new variables, and switching the order of the lines inside the loop.

Also, I have managed to actually check if, once a tile is selected, the mouse hovers on the right next tile up. Unfortunately what I try to next, for now, is to check if the mouse button has been pressed again, and if so, to print 'MOUSE BUTTON PRESSED', but I can't make it work.

Here is the full code

import pygame
import sys
from coordinator import coordinator

# set up the display
pygame.init()
window_size = (800, 800)
game_window = pygame.display.set_mode(size=window_size)
pygame.display.set_caption('My Game')


# defines classes and related methods

class WhiteSquare:
    def __init__(self):
        self.height = int(window_size[0] / 8)
        self.width = int(window_size[1] / 8)
        self.white_square = pygame.Surface((self.height, self.width))
        self.white_square.fill((255, 255, 255))


class BlackSquare:
    def __init__(self):
        self.height = int(window_size[0] / 8)
        self.width = int(window_size[1] / 8)
        self.black_square = pygame.Surface((self.height, self.width))
        self.black_square.fill((0, 0, 0))


class ChessBoard:
    def __init__(self):
        self.ws = ws
        self.bs = bs
        self.white_columns = white_columns
        self.black_columns = black_columns

    def draw(self):
        for w_columns in self.white_columns:
            game_window.blit(self.ws.white_square, w_columns)

        for b_columns in self.black_columns:
            game_window.blit(self.bs.black_square, b_columns)


# declare letters and numbers
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
numbers = ['1', '2', '3', '4', '5', '6', '7', '8']

# create coordinates
coordinates = []
for item_letter in letters:
    letter = item_letter
    for item_number in numbers:
        number = item_number
        coordinates.append(letter + number)

# create coordinates values components
x_values = []
for number in range(0, 800, 100):
    x = number
    x_values.append(x)

y_values = []
for number in range(0, 800, 100):
    y = number
    y_values.append(y)

# create coordinate values
coordinate_values = []
for x in x_values:
    for y in y_values:
        coordinate_values.append((x, y))

# assign values to coordinates
squares_coordinates = dict(zip(coordinates, coordinate_values))


# Background for units
class CircleSurface:
    def __init__(self):
        self.circle_surface = pygame.Surface((100, 100), flags=pygame.SRCALPHA)
        pygame.draw.circle(self.circle_surface, (255, 0, 0), (50, 50), 45)


# define colours
black = (0, 0, 0)
white = (255, 255, 255)
gold = (153, 153, 0)
green = (0, 255, 0)
dark_green = (0, 200, 0)


class Unit:
    def __init__(self, colour, position):

        # define Unit colour
        self.colour = colour

        # define Unit position
        self.position = position


class Knight(Unit):
    def __init__(self, colour, position):
        # draw circle, inline, and outline
        super().__init__(colour, position)
        self.center_x = position[0]
        self.center_y = position[1]
        self.colour = colour
        self.position = position
        # define tile position
        self.tile_area = pygame.Rect(position[0] - 50, position[1] - 50, 100, 100)
        self.circle_radius = 40

    def draw_unit(self):
        pygame.draw.circle(game_window, self.colour, self.position, self.circle_radius)
        pygame.draw.circle(game_window, gold, self.position, self.circle_radius, 5)
        pygame.draw.circle(game_window, gold, self.position, (self.circle_radius - 7), 2)

        # draw letter
        pygame.font.init()
        my_font_size = 50
        my_font = pygame.font.SysFont('Time New Roman', my_font_size)
        text_surface = my_font.render('K', 1, gold)
        center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
        game_window.blit(text_surface, center_text)


class Archer(Unit):
    def __init__(self, colour, first_point, second_point, third_point):
        self.colour = colour
        self.first_point = first_point
        self.second_point = second_point
        self.third_point = third_point
        self.position = self.first_point, self.second_point, self.third_point
        super().__init__(colour, self.position)
        self.center_x = self.second_point[0]
        self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1]) / 2)) + 10
        self.inline_position = [(self.first_point[0] + 10, self.first_point[1] - 5),
                                (self.second_point[0], self.second_point[1] + 10),
                                (self.third_point[0] - 10, self.third_point[1] - 5)]
        # define_tile_centre
        self.tile_area = pygame.Rect(self.first_point[0] - 10, self.second_point[1] - 10, 100, 100)

        if self.colour == black:
            self.first_point = self.first_point[0], self.first_point[1] - 80
            self.second_point = self.second_point[0], self.second_point[1] + 80
            self.third_point = self.third_point[0], self.third_point[1] - 80
            self.position = [self.first_point, self.second_point, self.third_point]

            self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1])
                                                     / 2)) - 10
            self.inline_position = [(self.first_point[0] + 10, self.first_point[1] + 5),
                                    (self.second_point[0], self.second_point[1] - 10),
                                    (self.third_point[0] - 10, self.third_point[1] + 5)]

    def draw_unit(self):
        pygame.draw.polygon(game_window, self.colour, self.position)
        pygame.draw.polygon(game_window, gold, self.position, 5)
        pygame.draw.polygon(game_window, gold, self.inline_position, 2)

        # draw letter
        pygame.font.init()
        my_font_size = 50
        my_font = pygame.font.SysFont('Time New Roman', my_font_size)
        text_surface = my_font.render('A', 1, gold)
        center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
        game_window.blit(text_surface, center_text)


class Pikeman(Unit):
    def __init__(self, colour, position):

        # draw circle, inline, and outline
        super().__init__(colour, position)
        self.dimension = (80, 80)
        self.center_x = position[0] + self.dimension[0] / 2
        self.center_y = position[1] + self.dimension[1] / 2
        self.colour = colour
        self.position = position

        # define_tile_centre
        self.tile_area = pygame.Rect(position[0] - 10, position[1] - 10, 100, 100)

        self.position_and_dimension = self.position + self.dimension
        self.inline_position_and_dimension = (self.position[0] + 5, self.position[1] + 5),\
                                             (self.dimension[0] - 10, self.dimension[1] - 10)

    def draw_unit(self):
        pygame.draw.rect(game_window, self.colour, self.position_and_dimension, )
        pygame.draw.rect(game_window, gold, self.position_and_dimension, 5)
        pygame.draw.rect(game_window, gold, self.inline_position_and_dimension, 2)

        # draw letter
        pygame.font.init()
        my_font_size = 50
        my_font = pygame.font.SysFont('Time New Roman', my_font_size)
        text_surface = my_font.render('P', 1, gold)
        center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
        game_window.blit(text_surface, center_text)


def highlight_tile(tile_area):
    pygame.draw.rect(game_window, dark_green, tile_area, 5)


# Sets and gets the coordinates for black and white squares
coordinator = coordinator()

black_columns = coordinator[2] + coordinator[3]
white_columns = coordinator[0] + coordinator[1]

# Creates needed objects
ws = WhiteSquare()
bs = BlackSquare()
cb = ChessBoard()
cs = CircleSurface()

# Draws the chessboard
cb.draw()

# set up white units
white_knight_1 = Knight(white, (150, 650))
white_knight_2 = Knight(white, (650, 650))
white_archer_1 = Archer(white, (210, 790), (250, 710), (290, 790))
white_archer_2 = Archer(white, (310, 790), (350, 710), (390, 790))
white_archer_3 = Archer(white, (410, 790), (450, 710), (490, 790))
white_archer_4 = Archer(white, (510, 790), (550, 710), (590, 790))
white_pikeman_1 = Pikeman(white, (210, 610))
white_pikeman_2 = Pikeman(white, (310, 610))
white_pikeman_3 = Pikeman(white, (410, 610))
white_pikeman_4 = Pikeman(white, (510, 610))

# set up black units
black_knight_1 = Knight(black, (150, 150))
black_knight_1.draw_unit()
black_knight_2 = Knight(black, (650, 150))
black_knight_2.draw_unit()
black_archer_1 = Archer(black, (210, 90), (250, 10), (290, 90))
black_archer_2 = Archer(black, (310, 90), (350, 10), (390, 90))
black_archer_3 = Archer(black, (410, 90), (450, 10), (490, 90))
black_archer_4 = Archer(black, (510, 90), (550, 10), (590, 90))
black_pikeman_1 = Pikeman(black, (210, 110))
black_pikeman_2 = Pikeman(black, (310, 110))
black_pikeman_3 = Pikeman(black, (410, 110))
black_pikeman_4 = Pikeman(black, (510, 110))

black_army = [black_knight_1, black_knight_2,
              black_archer_1, black_archer_2, black_archer_3, black_archer_4,
              black_pikeman_1, black_pikeman_2, black_pikeman_3, black_pikeman_4]

white_army = [white_knight_1, white_knight_2,
              white_archer_1, white_archer_2, white_archer_3, white_archer_4,
              white_pikeman_1, white_pikeman_2, white_pikeman_3, white_pikeman_4]
army = [white_army, black_army]

print(squares_coordinates.get(letter))

# draw units
for white_unit in white_army:
    for black_unit in black_army:
        white_unit.draw_unit()
        black_unit.draw_unit()


def redraw_units():
    Knight(white, white_knight_1.position)
    white_knight_1.draw_unit()
    Knight(white, white_knight_2.position)
    white_knight_2.draw_unit()
    Archer(white, white_archer_1.position[0], white_archer_1.position[1], white_archer_1.position[2])
    white_archer_1.draw_unit()
    Archer(white, white_archer_2.position[0], white_archer_2.position[1], white_archer_2.position[2])
    white_archer_2.draw_unit()
    Archer(white, white_archer_3.position[0], white_archer_3.position[1], white_archer_3.position[2])
    white_archer_3.draw_unit()
    Archer(white, white_archer_4.position[0], white_archer_4.position[1], white_archer_4.position[2])
    white_archer_4.draw_unit()
    Pikeman(white, white_pikeman_1.position)
    white_pikeman_1.draw_unit()
    Pikeman(white, white_pikeman_2.position)
    white_pikeman_2.draw_unit()
    Pikeman(white, white_pikeman_3.position)
    white_pikeman_3.draw_unit()
    Pikeman(white, white_pikeman_4.position)
    white_pikeman_4.draw_unit()
    pygame.display.update()


selected_unit = None
destination_tile = None


def on_target():
    if (current_position[0] < mouse_position[0] < current_position[0] + 100) and \
    (current_position[1] - 100) < mouse_position[1] < (current_position[1]):
        return True

# Event loop (outer)
while 1:
    cb.draw()

    # set up white units
    white_knight_1 = Knight(white, (150, 650))
    white_knight_1.draw_unit()
    white_knight_2 = Knight(white, (650, 650))
    white_knight_2.draw_unit()
    white_archer_1 = Archer(white, (210, 790), (250, 710), (290, 790))
    white_archer_1.draw_unit()
    white_archer_2 = Archer(white, (310, 790), (350, 710), (390, 790))
    white_archer_2.draw_unit()
    white_archer_3 = Archer(white, (410, 790), (450, 710), (490, 790))
    white_archer_3.draw_unit()
    white_archer_4 = Archer(white, (510, 790), (550, 710), (590, 790))
    white_archer_4.draw_unit()
    white_pikeman_1 = Pikeman(white, (210, 610))
    white_pikeman_1.draw_unit()
    white_pikeman_2 = Pikeman(white, (310, 610))
    white_pikeman_2.draw_unit()
    white_pikeman_3 = Pikeman(white, (410, 610))
    white_pikeman_3.draw_unit()
    white_pikeman_4 = Pikeman(white, (510, 610))
    white_pikeman_4.draw_unit()

    # set up black units
    black_knight_1 = Knight(black, (150, 150))
    black_knight_1.draw_unit()
    black_knight_2 = Knight(black, (650, 150))
    black_knight_2.draw_unit()
    black_archer_1 = Archer(black, (210, 90), (250, 10), (290, 90))
    black_archer_1.draw_unit()
    black_archer_2 = Archer(black, (310, 90), (350, 10), (390, 90))
    black_archer_2.draw_unit()
    black_archer_3 = Archer(black, (410, 90), (450, 10), (490, 90))
    black_archer_3.draw_unit()
    black_archer_4 = Archer(black, (510, 90), (550, 10), (590, 90))
    black_archer_4.draw_unit()
    black_pikeman_1 = Pikeman(black, (210, 110))
    black_pikeman_1.draw_unit()
    black_pikeman_2 = Pikeman(black, (310, 110))
    black_pikeman_2.draw_unit()
    black_pikeman_3 = Pikeman(black, (410, 110))
    black_pikeman_3.draw_unit()
    black_pikeman_4 = Pikeman(black, (510, 110))
    black_pikeman_4.draw_unit()

    # create armies
    black_army = [black_knight_1, black_knight_2,
                  black_archer_1, black_archer_2, black_archer_3, black_archer_4,
                  black_pikeman_1, black_pikeman_2, black_pikeman_3, black_pikeman_4]

    white_army = [white_knight_1, white_knight_2,
                  white_archer_1, white_archer_2, white_archer_3, white_archer_4,
                  white_pikeman_1, white_pikeman_2, white_pikeman_3, white_pikeman_4]
    army = [white_army, black_army]

    # Event loop (inner)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        # mouse handling
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_position = pygame.mouse.get_pos()
            if selected_unit:
                selected_unit = None
            else:
                for unit in white_army:
                    if unit.tile_area.collidepoint(mouse_position):
                        selected_unit = unit
                    # highlight the selected unit
        if selected_unit:
            highlight_tile(selected_unit.tile_area)
            mouse_position = pygame.mouse.get_pos()
            for unit in white_army:
                current_position = selected_unit.position
                if on_target():
                    print('ON TARGET -------------------------------------------')
                    if event.type == pygame.MOUSEBUTTONDOWN:
                        print('MOUSE BUTTON PRESSED++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')

        pygame.display.update()

Any help would be most appreciated.

1 Answers

That is not the proper way to do this. If you want to handle a dynamic game process, then you've to handle the events in the one and only event loop and to do all the drawing in the main application loop.
Note, you've set a state variable (selected_unit). Dependent on this state you can handle different actions in the event loop and you can do conditional drawing in the main application loop:

while 1:

    for event in pygame.event.get():

        if selected_unit:
            # do something
        else:
            # do something else

    if selected_unit: 
        # draw something
    else:
        # draw something different

    pygame.display.update()

Apply this algorithm to your code. e.g.:

while 1:

    # [...]

    # Event loop (inner)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        # mouse handling
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_position = pygame.mouse.get_pos()

            if selected_unit:    
                for unit in white_army:
                    current_position = selected_unit.position
                    if on_target():
                        print('ON TARGET -------------------------------------------')    
                selected_unit = None            
            else:
                for unit in white_army:
                    if unit.tile_area.collidepoint(mouse_position):
                        selected_unit = unit

    # <--
    # highlight the selected unit
    if selected_unit:
        highlight_tile(selected_unit.tile_area)

    pygame.display.update()  

Please note, highlight_tile is not in the event loop, it is in the application loop.

Related