Пример #1
0
 def __init__(self, master, **kwargs):
     Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
     self.start_game = master.start_game
     self.text_title = Text("Waiting for Player 2", font=("calibri", 50))
     self.text_ip_address = Text(font=("calibri", 40))
     self.text_port_of_connection = Text(font=("calibri", 40))
     self.button_cancel = Button(self, "Return to menu", theme="option", callback=self.stop)
     self.lets_play_countdown = CountDown(self, 3, "Player 2 connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")
Пример #2
0
class PlayerClient(Dialog):

    def __init__(self, master, **kwargs):
        Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
        params_for_all_buttons = {
            "font": ("calibri", 30),
            "bg": GREEN,
            "hover_bg": GREEN_LIGHT,
            "active_bg": GREEN_DARK,
            "highlight_color": YELLOW,
            "outline": 3
        }
        self.start_game = NavySetup(2)
        self.text_title = Text("Connect to Player 1", ("calibri", 50))
        self.ip = Entry(self, width=15, font=("calibri", 40), bg=GREEN, highlight_color=YELLOW, outline=2)
        self.text_ip_address = Text("IP address", ("calibri", 40), YELLOW)
        self.port = Entry(self, width=15, font=("calibri", 40), bg=GREEN, highlight_color=YELLOW, outline=2)
        self.text_port_of_connection = Text("Port", ("calibri", 40), YELLOW)
        self.text_connection = Text(font=("calibri", 25), color=YELLOW)
        self.text_connection.hide()
        self.button_connect = Button(self, "Connection", callback=self.connection, **params_for_all_buttons)
        self.button_cancel = Button(self, "Return to menu", callback=self.stop, **params_for_all_buttons)
        self.lets_play_countdown = CountDown(self, 3, "Connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")

    def on_dialog_quit(self):
        self.disable_text_input()
        self.stop_connection()

    def place_objects(self):
        self.frame.move(center=self.center)
        self.text_title.move(centerx=self.frame.centerx, top=self.frame.top + 50)
        self.lets_play_countdown.move(center=self.text_title.center)
        self.ip.move(centerx=self.frame.centerx + self.frame.w // 10, bottom=self.frame.centery - 10)
        self.text_ip_address.move(centery=self.ip.centery, right=self.ip.left - 10)
        self.port.move(left=self.ip.left, top=self.ip.bottom + 20)
        self.text_port_of_connection.move(centery=self.port.centery, right=self.port.left - 10)
        self.text_connection.move(centerx=self.frame.centerx, top=self.port.bottom + 5)
        self.button_connect.move(centerx=self.frame.centerx - (self.frame.width // 4), bottom=self.frame.bottom - 10)
        self.button_cancel.move(centerx=self.frame.centerx + (self.frame.width // 4), bottom=self.frame.bottom - 10)

    def connection(self):
        self.text_connection.show()
        self.text_connection.message = "Connection..."
        self.draw_and_refresh()
        if not self.connect_to_server(self.ip.get(), int(self.port.get()), 3):
            self.text_connection.message = "Connection failed. Try again."
        else:
            self.text_connection.hide()
            self.text_title.hide()
            self.button_connect.state = self.button_cancel.state = Button.DISABLED
            self.button_connect.focus_leave()
            self.lets_play_countdown.start(at_end=self.play)

    def play(self):
        self.start_game.mainloop()
        self.stop()
Пример #3
0
 def __init__(self, master, **kwargs):
     Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
     self.start_game = master.start_game
     self.text_title = Text("Connect to Player 1", font=("calibri", 50))
     self.form = Form(self)
     self.form.add_entry("IP", Text("IP address", font=("calibri", 40), color=YELLOW), Entry(self, width=15, font=("calibri", 30), bg=GREEN, highlight_color=YELLOW, outline=2))
     self.form.add_entry("Port", Text("Port", font=("calibri", 40), color=YELLOW), Entry(self, width=15, font=("calibri", 30), bg=GREEN, highlight_color=YELLOW, outline=2))
     self.text_connection = Text(font=("calibri", 25), color=YELLOW)
     self.text_connection.hide()
     self.button_connect = Button(self, "Connection", theme="option", callback=self.connection)
     self.button_cancel = Button(self, "Return to menu", theme="option", callback=self.stop)
     self.lets_play_countdown = CountDown(self, 3, "Connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")
Пример #4
0
 def __init__(self, player_id: int):
     Window.__init__(self,
                     bg_color=(0, 200, 255),
                     bg_music=RESOURCES.MUSIC["setup"])
     self.gameplay = Gameplay(player_id)
     self.count_down = CountDown(self,
                                 60,
                                 "Time left: {seconds}",
                                 font=(None, 70),
                                 color=WHITE)
     self.start_count_down = lambda: self.count_down.start(
         at_end=self.timeout) if self.client_socket.connected() else None
     params_for_all_buttons = {
         "bg": GREEN,
         "hover_bg": GREEN_LIGHT,
         "active_bg": GREEN_DARK,
         "highlight_color": YELLOW
     }
     self.button_back = ImageButton(self,
                                    RESOURCES.IMG["arrow_blue"],
                                    **params_for_all_buttons,
                                    rotate=180,
                                    size=50,
                                    callback=self.stop)
     self.navy_grid = DrawableListVertical(offset=0, bg_color=(0, 157, 255))
     for i in range(NB_LINES_BOXES):
         box_line = DrawableListHorizontal(offset=0)
         for j in range(NB_COLUMNS_BOXES):
             box_line.add(BoxSetup(self, size=BOX_SIZE, pos=(i, j)))
         self.navy_grid.add(box_line)
     self.ships_list = DrawableListVertical(offset=70, justify="left")
     for ship_name, ship_infos in SHIPS.items():
         ship_line = DrawableListHorizontal(offset=ship_infos["offset"])
         for _ in range(ship_infos["nb"]):
             ship_line.add(ShipSetup(self, ship_name, ship_infos["size"]))
         self.ships_list.add(ship_line)
     option_size = 50
     self.button_restart = Button.withImageOnly(
         self,
         Image(RESOURCES.IMG["reload_blue"], size=option_size),
         callback=self.reinit_all_ships,
         **params_for_all_buttons)
     self.button_random = Button.withImageOnly(self,
                                               Image(
                                                   RESOURCES.IMG["random"],
                                                   size=option_size),
                                               callback=self.shuffle,
                                               **params_for_all_buttons)
     self.button_play = Button(self,
                               "Play",
                               font=(None, 40),
                               callback=self.play,
                               **params_for_all_buttons)
Пример #5
0
class PlayerClient(Dialog):

    def __init__(self, master, **kwargs):
        Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
        self.start_game = master.start_game
        self.text_title = Text("Connect to Player 1", font=("calibri", 50))
        self.form = Form(self)
        self.form.add_entry("IP", Text("IP address", font=("calibri", 40), color=YELLOW), Entry(self, width=15, font=("calibri", 30), bg=GREEN, highlight_color=YELLOW, outline=2))
        self.form.add_entry("Port", Text("Port", font=("calibri", 40), color=YELLOW), Entry(self, width=15, font=("calibri", 30), bg=GREEN, highlight_color=YELLOW, outline=2))
        self.text_connection = Text(font=("calibri", 25), color=YELLOW)
        self.text_connection.hide()
        self.button_connect = Button(self, "Connection", theme="option", callback=self.connection)
        self.button_cancel = Button(self, "Return to menu", theme="option", callback=self.stop)
        self.lets_play_countdown = CountDown(self, 3, "Connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")

    def on_quit(self):
        self.stop_connection()

    def place_objects(self):
        self.frame.move(center=self.center)
        self.text_title.move(centerx=self.frame.centerx, top=self.frame.top + 50)
        self.lets_play_countdown.move(center=self.text_title.center)
        self.form.move(center=self.frame.center)
        self.text_connection.move(centerx=self.frame.centerx, top=self.form.bottom + 5)
        self.button_connect.move(centerx=self.frame.centerx - (self.frame.width // 4), bottom=self.frame.bottom - 10)
        self.button_cancel.move(centerx=self.frame.centerx + (self.frame.width // 4), bottom=self.frame.bottom - 10)

    def connection(self):
        self.text_connection.show()
        self.text_connection.message = "Connection..."
        self.draw_and_refresh()
        try:
            address = self.form.get("IP")
            port = int(self.form.get("Port"))
        except ValueError:
            self.text_connection.message = "The port of connection must be a number."
            return
        if not self.connect_to_server(address, port, 3):
            self.text_connection.message = "Connection failed. Try again."
        else:
            self.text_connection.hide()
            self.text_title.hide()
            self.button_connect.state = self.button_cancel.state = Button.DISABLED
            self.button_connect.focus_leave()
            self.lets_play_countdown.start(at_end=self.play)

    def play(self):
        self.start_game.start(2)
        self.stop()
Пример #6
0
class PlayerServer(Dialog):

    def __init__(self, master, **kwargs):
        Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
        params_for_all_buttons = {
            "font": ("calibri", 30),
            "bg": GREEN,
            "hover_bg": GREEN_LIGHT,
            "active_bg": GREEN_DARK,
            "outline": 3,
            "highlight_color": YELLOW
        }
        self.start_game = master.start_game
        self.text_title = Text("Waiting for Player 2", ("calibri", 50))
        self.text_ip_address = Text(font=("calibri", 40))
        self.text_port_of_connection = Text(font=("calibri", 40))
        self.button_cancel = Button(self, "Return to menu", callback=self.stop, **params_for_all_buttons)
        self.lets_play_countdown = CountDown(self, 3, "Player 2 connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")

    def on_dialog_start_loop(self):
        try:
            ip, port = self.create_server(12800, 1)
            self.text_ip_address.message = f"IP address: {ip}"
            self.text_port_of_connection.message = f"Port: {port}"
        except OSError:
            self.stop()

    def on_dialog_quit(self):
        self.stop_connection()

    def place_objects(self):
        self.frame.move(center=self.center)
        self.text_title.move(centerx=self.frame.centerx, top=self.frame.top + 50)
        self.lets_play_countdown.move(center=self.text_title.center)
        self.text_ip_address.move(centerx=self.centerx, bottom=self.frame.centery - 10)
        self.text_port_of_connection.move(centerx=self.text_ip_address.centerx, top=self.text_ip_address.bottom + 20)
        self.button_cancel.move(centerx=self.frame.centerx, bottom=self.frame.bottom - 10)

    def update(self) -> None:
        if self.get_server_clients_count() > 1 and not self.lets_play_countdown.started():
            self.set_server_listen(0)
            self.text_title.hide()
            self.button_cancel.state = Button.DISABLED
            self.lets_play_countdown.start(at_end=self.play)

    def play(self):
        self.start_game.mainloop()
        self.stop()
Пример #7
0
 def __init__(self, master, **kwargs):
     Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
     params_for_all_buttons = {
         "font": ("calibri", 30),
         "bg": GREEN,
         "hover_bg": GREEN_LIGHT,
         "active_bg": GREEN_DARK,
         "outline": 3,
         "highlight_color": YELLOW
     }
     self.start_game = master.start_game
     self.text_title = Text("Waiting for Player 2", ("calibri", 50))
     self.text_ip_address = Text(font=("calibri", 40))
     self.text_port_of_connection = Text(font=("calibri", 40))
     self.button_cancel = Button(self, "Return to menu", callback=self.stop, **params_for_all_buttons)
     self.lets_play_countdown = CountDown(self, 3, "Player 2 connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")
Пример #8
0
 def __init__(self):
     Window.__init__(self, bg_color=(0, 200, 255))
     self.gameplay = Gameplay()
     self.enemy_quit_window = EnemyQuitGame(self)
     self.transition = GameSetupTransition()
     self.count_down = CountDown(self,
                                 60,
                                 "Time left: {seconds}",
                                 font=(None, 70),
                                 color=WHITE)
     self.start_count_down = lambda: self.count_down.start(
         at_end=self.timeout) if self.client_socket.connected() else None
     self.button_back = ImageButton(self,
                                    RESOURCES.IMG["arrow_blue"],
                                    rotate=180,
                                    size=50,
                                    callback=self.stop)
     self.navy_grid = Grid(self, bg_color=(0, 157, 255))
     self.__boxes_dict = {(i, j): BoxSetup(self, size=BOX_SIZE, pos=(i, j))
                          for i in range(NB_LINES_BOXES)
                          for j in range(NB_COLUMNS_BOXES)}
     self.__boxes_list = list(self.__boxes_dict.values())
     self.navy_grid.place_multiple(self.__boxes_dict)
     self.ships_list = DrawableListVertical(offset=70, justify="left")
     for ship_name, ship_infos in SHIPS.items():
         ship_line = DrawableListHorizontal(offset=ship_infos["offset"])
         for _ in range(ship_infos["nb"]):
             ship_line.add(ShipSetup(self, ship_name, ship_infos["size"]))
         self.ships_list.add(ship_line)
     option_size = 50
     self.button_restart = Button(self,
                                  img=Image(RESOURCES.IMG["reload_blue"],
                                            size=option_size),
                                  callback=self.reinit_all_ships)
     self.button_random = Button(self,
                                 img=Image(RESOURCES.IMG["random"],
                                           size=option_size),
                                 callback=self.shuffle)
     self.button_play = Button(self,
                               "Play",
                               font=(None, 40),
                               callback=self.play)
Пример #9
0
 def __init__(self, master, **kwargs):
     Dialog.__init__(self, master=master, bg_color=GREEN_DARK, **kwargs)
     params_for_all_buttons = {
         "font": ("calibri", 30),
         "bg": GREEN,
         "hover_bg": GREEN_LIGHT,
         "active_bg": GREEN_DARK,
         "highlight_color": YELLOW,
         "outline": 3
     }
     self.start_game = NavySetup(2)
     self.text_title = Text("Connect to Player 1", ("calibri", 50))
     self.ip = Entry(self, width=15, font=("calibri", 40), bg=GREEN, highlight_color=YELLOW, outline=2)
     self.text_ip_address = Text("IP address", ("calibri", 40), YELLOW)
     self.port = Entry(self, width=15, font=("calibri", 40), bg=GREEN, highlight_color=YELLOW, outline=2)
     self.text_port_of_connection = Text("Port", ("calibri", 40), YELLOW)
     self.text_connection = Text(font=("calibri", 25), color=YELLOW)
     self.text_connection.hide()
     self.button_connect = Button(self, "Connection", callback=self.connection, **params_for_all_buttons)
     self.button_cancel = Button(self, "Return to menu", callback=self.stop, **params_for_all_buttons)
     self.lets_play_countdown = CountDown(self, 3, "Connected.\nGame start in {seconds} seconds", font=("calibri", 35), color=YELLOW, justify="center")
Пример #10
0
class NavySetup(Window):
    def __init__(self):
        Window.__init__(self, bg_color=(0, 200, 255))
        self.gameplay = Gameplay()
        self.enemy_quit_window = EnemyQuitGame(self)
        self.transition = GameSetupTransition()
        self.count_down = CountDown(self,
                                    60,
                                    "Time left: {seconds}",
                                    font=(None, 70),
                                    color=WHITE)
        self.start_count_down = lambda: self.count_down.start(
            at_end=self.timeout) if self.client_socket.connected() else None
        self.button_back = ImageButton(self,
                                       RESOURCES.IMG["arrow_blue"],
                                       rotate=180,
                                       size=50,
                                       callback=self.stop)
        self.navy_grid = Grid(self, bg_color=(0, 157, 255))
        self.__boxes_dict = {(i, j): BoxSetup(self, size=BOX_SIZE, pos=(i, j))
                             for i in range(NB_LINES_BOXES)
                             for j in range(NB_COLUMNS_BOXES)}
        self.__boxes_list = list(self.__boxes_dict.values())
        self.navy_grid.place_multiple(self.__boxes_dict)
        self.ships_list = DrawableListVertical(offset=70, justify="left")
        for ship_name, ship_infos in SHIPS.items():
            ship_line = DrawableListHorizontal(offset=ship_infos["offset"])
            for _ in range(ship_infos["nb"]):
                ship_line.add(ShipSetup(self, ship_name, ship_infos["size"]))
            self.ships_list.add(ship_line)
        option_size = 50
        self.button_restart = Button(self,
                                     img=Image(RESOURCES.IMG["reload_blue"],
                                               size=option_size),
                                     callback=self.reinit_all_ships)
        self.button_random = Button(self,
                                    img=Image(RESOURCES.IMG["random"],
                                              size=option_size),
                                    callback=self.shuffle)
        self.button_play = Button(self,
                                  "Play",
                                  font=(None, 40),
                                  callback=self.play)

    @property
    def ships(self) -> Sequence[ShipSetup]:
        return self.ships_list.drawable

    @property
    def boxes(self) -> Sequence[BoxSetup]:
        return self.__boxes_list

    def start(self, player_id: int) -> None:
        self.gameplay.player_id = player_id
        self.mainloop(transition=self.transition)

    def on_start_loop(self) -> None:
        self.start_count_down()

    def on_quit(self) -> None:
        self.reinit_all_ships()

    def place_objects(self) -> None:
        self.button_back.move(x=20, y=20)
        self.count_down.move(top=20, right=self.right - 20)
        self.navy_grid.move(x=20, centery=self.centery)
        self.ships_list.move(left=self.navy_grid.right + 100,
                             top=self.navy_grid.top + 30)
        self.button_restart.move(left=self.navy_grid.right + 20,
                                 bottom=self.navy_grid.bottom)
        self.button_random.move(left=self.button_restart.right + 20,
                                bottom=self.navy_grid.bottom)
        self.button_play.move(right=self.right - 20, bottom=self.bottom - 20)
        for ship in self.ships:
            ship.default_center = ship.center

    def update(self):
        self.button_play.state = Button.NORMAL if all(
            ship.on_map for ship in self.ships) else Button.DISABLED
        if self.client_socket.recv("quit"):
            self.count_down.stop()
            self.enemy_quit_window.mainloop()
            self.stop()

    def create_setup(self) -> Sequence[dict[str, dict[str, Any]]]:
        setup = list()
        for ship in self.ships:
            setup.append({
                "name": ship.name,
                "orient": ship.orient,
                "boxes": [box.pos for box in ship.boxes_covered]
            })
        return setup

    def timeout(self):
        for ship in filter(lambda ship: not ship.on_map, self.ships):
            self.set_random_position_for_ship(ship)
        self.play()

    def play(self):
        if not all(ship.on_map for ship in self.ships):
            return
        if not self.client_socket.connected():
            ai_navy_setup = NavySetup()
            ai_navy_setup.shuffle()
            ai_setup = ai_navy_setup.create_setup()
        else:
            ai_setup = None
            self.count_down.stop()
            self.client_socket.send("ready")
            WaitEnemy(self).mainloop()
        self.gameplay.start(self.create_setup(), ai_setup=ai_setup)
        self.reinit_all_ships()
        if self.gameplay.restart:
            self.start_count_down()
        else:
            self.stop()

    def reinit_all_ships(self):
        for ship in self.ships:
            ship.center = ship.default_center
            ship.orient = ShipSetup.HORIZONTAL
            ship.clear()

    def get_box(self, line: int, column: float) -> BoxSetup:
        return self.__boxes_dict.get((line, column))

    def remove_boxes_highlight(self):
        for box in self.boxes:
            box.hover = False
            box.state = Button.NORMAL

    def get_valid_highlighted_boxes(self) -> Sequence[BoxSetup]:
        return list(
            filter(lambda box: box.hover and box.state == Button.NORMAL,
                   self.boxes))

    def highlight_boxes(self, ship: ShipSetup) -> None:
        boxes = list()
        for box in self.boxes:
            self.highlight_one_box(ship, box, len(boxes))
            if box.hover is True:
                boxes.append(box)
        if len(boxes) != ship.ship_size or any(not self.valid_box(ship, box)
                                               for box in boxes):
            for box in boxes:
                box.state = Button.DISABLED

    def highlight_one_box(self, ship: ShipSetup, box: BoxSetup,
                          nb_boxes_covered: int) -> None:
        box.hover = False
        box.state = Button.NORMAL
        if nb_boxes_covered == ship.ship_size:
            return
        if ship.orient == ShipSetup.HORIZONTAL:
            if (box.top <= ship.centery <= box.bottom) is False:
                return
            if ship.left > box.centerx or ship.right < box.centerx:
                return
        else:
            if (box.left <= ship.centerx <= box.right) is False:
                return
            if ship.top > box.centery or ship.bottom < box.centery:
                return
        box.hover = True

    def valid_box(self, ship: ShipSetup, box: BoxSetup) -> bool:
        line, column = box.pos
        offsets = [(-1, -1), (0, -1), (1, -1), (-1, 0), (0, 0), (1, 0),
                   (-1, 1), (0, 1), (1, 1)]
        for u, v in offsets:
            box = self.get_box(line + u, column + v)
            if box is None:
                continue
            if box.ship is not None and box.ship != ship:
                return False
        return True

    def shuffle(self) -> None:
        self.reinit_all_ships()
        for ship in self.ships:
            self.set_random_position_for_ship(ship)

    def set_random_position_for_ship(self, ship: ShipSetup) -> None:
        ship.orient = random.choice([ShipSetup.HORIZONTAL, ShipSetup.VERTICAL])
        first_box = random.choice(self.get_available_boxes(ship))
        boxes = [first_box]
        for i in range(1, ship.ship_size):
            u = first_box.pos[
                0] + i if ship.orient == ShipSetup.VERTICAL else first_box.pos[
                    0]
            v = first_box.pos[
                1] + i if ship.orient == ShipSetup.HORIZONTAL else first_box.pos[
                    1]
            boxes.append(self.get_box(u, v))
        ship.place_ship_on_map(boxes)

    def get_available_boxes(self, ship: ShipSetup):
        available_boxes = list()
        for box in self.boxes:
            if not self.valid_box(ship, box):
                continue
            line, column = box.pos
            valid = True
            for i in range(1, ship.ship_size):
                u = line + i if ship.orient == ShipSetup.VERTICAL else line
                v = column + i if ship.orient == ShipSetup.HORIZONTAL else column
                b = self.get_box(u, v)
                if b is None or (not self.valid_box(ship, b)):
                    valid = False
                    break
            if valid:
                available_boxes.append(box)
        return available_boxes
    def __init__(self, car_id: int, env: str):
        Window.__init__(self,
                        bg_color=ENVIRONMENT[env],
                        bg_music=RESOURCES.MUSIC["gameplay"])
        self.bind_key(pygame.K_ESCAPE, lambda event: self.pause())
        self.bind_joystick(0, "START", lambda event: self.pause())

        font = RESOURCES.FONT["cooperblack"]

        # Demaraction lines
        self.road = DrawableListVertical(bg_color=GRAY, offset=70)
        self.white_bands = list()
        white_bands_width = 50  #px
        white_lines_height = 10  #px
        for i in range(5):
            if i % 2 == 0:
                self.road.add(
                    RectangleShape(self.width, white_lines_height, WHITE))
            else:
                white_bands = DrawableListHorizontal(offset=20)
                while white_bands.width < self.width:
                    white_bands.add(
                        RectangleShape(white_bands_width, white_lines_height,
                                       WHITE))
                self.road.add(white_bands)
                self.white_bands.append(white_bands)

        # Environment
        self.env_top = DrawableListHorizontal(offset=400)
        self.env_bottom = DrawableListHorizontal(offset=400)
        while self.env_top.width < self.width:
            self.env_top.add(Image(RESOURCES.IMG[env], height=110))
        while self.env_bottom.width < self.width:
            self.env_bottom.add(Image(RESOURCES.IMG[env], height=110))

        #Infos
        params_for_infos = {
            "font": (font, 45),
            "color": YELLOW,
            "shadow": True,
            "shadow_x": 3,
            "shadow_y": 3
        }
        self.infos_score = Info("Score", round_n=0, **params_for_infos)
        self.infos_speed = Info("Speed",
                                extension="km/h",
                                **params_for_infos,
                                justify="right")
        self.infos_distance = Info("Distance",
                                   round_n=2,
                                   extension="km",
                                   **params_for_infos,
                                   justify="right")
        self.infos_time_100 = Info("High speed", **params_for_infos)
        self.infos_time_opposite = Info("Opposite side", **params_for_infos)
        self.clock_time_100 = Clock()
        self.clock_time_opposite = Clock()
        self.total_time_100 = self.total_time_opposite = 0

        self.car = PlayerCar(car_id)
        self.speed = 0
        self.traffic = TrafficCarList(nb_ways=4, max_nb_car=6)
        self.clock_traffic = Clock()
        self.img_crash = Image(RESOURCES.IMG["crash"], size=150)
        self.count_down = CountDown(self,
                                    3,
                                    font=(font, 90),
                                    color=YELLOW,
                                    shadow=True,
                                    shadow_x=5,
                                    shadow_y=5)
        self.last_car_way = 0

        # Background
        self.background = DrawableList(draw=False)
        self.background.add(self.env_top, self.env_bottom, *self.white_bands,
                            self.traffic)

        # Default values
        self.update_clock = Clock()
        self.update_time = 15  #ms
        self.pixel_per_sec = 6  # For 1km/h
        self.pixel_per_ms = self.pixel_per_sec * self.update_time / 1000
        self.paused = False
        self.go_to_garage = self.restart = False
        self.crashed_car = None

        self.disable_key_joy_focus()
        self.init_game()
class Gameplay(Window):
    def __init__(self, car_id: int, env: str):
        Window.__init__(self,
                        bg_color=ENVIRONMENT[env],
                        bg_music=RESOURCES.MUSIC["gameplay"])
        self.bind_key(pygame.K_ESCAPE, lambda event: self.pause())
        self.bind_joystick(0, "START", lambda event: self.pause())

        font = RESOURCES.FONT["cooperblack"]

        # Demaraction lines
        self.road = DrawableListVertical(bg_color=GRAY, offset=70)
        self.white_bands = list()
        white_bands_width = 50  #px
        white_lines_height = 10  #px
        for i in range(5):
            if i % 2 == 0:
                self.road.add(
                    RectangleShape(self.width, white_lines_height, WHITE))
            else:
                white_bands = DrawableListHorizontal(offset=20)
                while white_bands.width < self.width:
                    white_bands.add(
                        RectangleShape(white_bands_width, white_lines_height,
                                       WHITE))
                self.road.add(white_bands)
                self.white_bands.append(white_bands)

        # Environment
        self.env_top = DrawableListHorizontal(offset=400)
        self.env_bottom = DrawableListHorizontal(offset=400)
        while self.env_top.width < self.width:
            self.env_top.add(Image(RESOURCES.IMG[env], height=110))
        while self.env_bottom.width < self.width:
            self.env_bottom.add(Image(RESOURCES.IMG[env], height=110))

        #Infos
        params_for_infos = {
            "font": (font, 45),
            "color": YELLOW,
            "shadow": True,
            "shadow_x": 3,
            "shadow_y": 3
        }
        self.infos_score = Info("Score", round_n=0, **params_for_infos)
        self.infos_speed = Info("Speed",
                                extension="km/h",
                                **params_for_infos,
                                justify="right")
        self.infos_distance = Info("Distance",
                                   round_n=2,
                                   extension="km",
                                   **params_for_infos,
                                   justify="right")
        self.infos_time_100 = Info("High speed", **params_for_infos)
        self.infos_time_opposite = Info("Opposite side", **params_for_infos)
        self.clock_time_100 = Clock()
        self.clock_time_opposite = Clock()
        self.total_time_100 = self.total_time_opposite = 0

        self.car = PlayerCar(car_id)
        self.speed = 0
        self.traffic = TrafficCarList(nb_ways=4, max_nb_car=6)
        self.clock_traffic = Clock()
        self.img_crash = Image(RESOURCES.IMG["crash"], size=150)
        self.count_down = CountDown(self,
                                    3,
                                    font=(font, 90),
                                    color=YELLOW,
                                    shadow=True,
                                    shadow_x=5,
                                    shadow_y=5)
        self.last_car_way = 0

        # Background
        self.background = DrawableList(draw=False)
        self.background.add(self.env_top, self.env_bottom, *self.white_bands,
                            self.traffic)

        # Default values
        self.update_clock = Clock()
        self.update_time = 15  #ms
        self.pixel_per_sec = 6  # For 1km/h
        self.pixel_per_ms = self.pixel_per_sec * self.update_time / 1000
        self.paused = False
        self.go_to_garage = self.restart = False
        self.crashed_car = None

        self.disable_key_joy_focus()
        self.init_game()

    def pause(self):
        if not self.count_down.is_shown():
            self.paused = True
            self.car.stop_animation()
            for car in self.traffic:
                car.stop_animation()
        pause = Pause(self)
        pause.mainloop()
        if self.loop and not self.count_down.is_shown():
            self.count_down.start(at_end=self.return_to_game)
            self.objects.set_priority(self.count_down, self.objects.end)

    def return_to_game(self):
        self.paused = False
        self.car.restart_animation()
        for car in self.traffic:
            car.restart_animation()
        self.clock_traffic.tick()
        self.clock_time_100.tick()
        self.clock_time_opposite.tick()
        self.update_clock.tick()

    def init_game(self):
        self.go_to_garage = False
        self.paused = False
        self.crashed_car = None
        for info in (self.infos_speed, self.infos_score, self.infos_distance,
                     self.infos_time_100, self.infos_time_opposite):
            info.value = 0
            if info in (self.infos_time_100, self.infos_time_opposite):
                info.hide()
        self.total_time_100 = self.total_time_opposite = 0
        self.count_down.start()
        self.img_crash.hide()

    def place_objects(self):
        self.count_down.center = self.road.center = self.center
        self.infos_score.move(topleft=(10, 10))
        self.infos_speed.move(right=self.right - 10, top=10)
        self.infos_distance.move(right=self.right - 10,
                                 bottom=self.road.top - 10)
        self.infos_time_100.move(left=10, bottom=self.road.top - 10)
        self.infos_time_opposite.move(left=10, top=self.road.bottom + 10)
        self.car.move(centery=self.road.centery, left=50)
        self.env_top.move(centerx=self.centerx,
                          centery=(self.top + self.road[0].top) / 2)
        self.env_bottom.move(centerx=self.centerx,
                             centery=(self.road[-1].bottom + self.bottom) / 2)

    def update(self):
        if self.paused:
            return
        self.update_player_car()
        if self.update_clock.elapsed_time(self.update_time):
            self.update_infos()
            self.update_background()
            self.update_traffic()
            self.rect_to_update = (self.road.rect, self.env_top.rect,
                                   self.env_bottom.rect, self.infos_score.rect,
                                   self.infos_speed.rect,
                                   self.infos_distance.rect,
                                   self.infos_time_100.rect,
                                   self.infos_time_opposite.rect)

    def update_player_car(self):
        if not self.count_down.is_shown():
            joystick = self.joystick[0]
            controls = SAVE["controls"]
            car_handling = (
                (controls["speed_up"], self.car.speed_up),
                (controls["brake"], self.car.brake),
                (controls["up"], self.car.moveUp),
                (controls["down"], self.car.moveDown),
            )
            for control, car_function in car_handling:
                car_function(self.keyboard.is_pressed(control["key"]))
                car_function(joystick.get_value(control["joy"]))
            if SAVE["auto_acceleration"] is True:
                self.car.speed_up()
        self.car.update(self.pixel_per_ms)
        if self.car.top < self.road[0].bottom + 5:
            self.car.top = self.road[0].bottom + 5
        elif self.car.bottom > self.road[-1].top - 5:
            self.car.bottom = self.road[-1].top - 5
        if not self.car.is_crashed():
            self.speed = self.car.speed
            for car in self.traffic:
                collision = pygame.sprite.collide_mask(self.car, car)
                if collision:
                    self.car.crash(car)
                    self.crashed_car = car
                    self.play_sound(RESOURCES.SFX["crash"])
                    self.img_crash.show()
                    self.img_crash.move(centerx=collision[0] + self.car.left,
                                        centery=collision[1] + self.car.top)
                    self.objects.set_priority(self.img_crash, self.objects.end)
        elif self.car.right <= 0 and self.crashed_car.right <= 0:
            self.end_game()

    def car_in_opposite_side(self) -> bool:
        return bool(self.car.bottom < self.road.centery)

    def update_infos(self):
        min_speed = 30
        score_to_add = (self.car.speed - min_speed) / 5
        bonus = False
        if self.car.speed > 30 and self.car_in_opposite_side():
            self.infos_time_opposite.show()
            self.infos_time_opposite.value = self.clock_time_opposite.get_elapsed_time(
            ) / 1000
            score_to_add += 120
            bonus = True
        else:
            self.total_time_opposite += self.infos_time_opposite.value
            self.infos_time_opposite.value = 0
            self.clock_time_opposite.restart()
            self.infos_time_opposite.hide()
        if self.car.speed >= 100:
            self.infos_time_100.show()
            self.infos_time_100.value = self.clock_time_100.get_elapsed_time(
            ) / 1000
            score_to_add += 150
            bonus = True
        else:
            self.total_time_100 += self.infos_time_100.value
            self.infos_time_100.value = 0
            self.clock_time_100.restart()
            self.infos_time_100.hide()
        if bonus:
            self.infos_score.color = GREEN_DARK
            self.infos_score.shadow_color = YELLOW
        else:
            self.infos_score.color = YELLOW
            self.infos_score.shadow_color = BLACK
        self.infos_score.value += score_to_add * self.update_time / 1000
        self.infos_speed.value = self.car.speed
        self.infos_distance.value += self.car.speed * self.pixel_per_ms / (
            1000 * 3.6)

    def update_background(self):
        offset = self.speed * self.pixel_per_ms
        self.background.move_ip(-offset, 0)
        for white_bands_list in self.white_bands:
            if white_bands_list[0].right <= 0:
                white_bands_list.remove_from_index(0)
            if white_bands_list[-1].right < self.right:
                white_bands_list.add(
                    RectangleShape(*white_bands_list[-1].size, WHITE))
        for env in (self.env_top, self.env_bottom):
            img = env[0]
            if img.right <= 0:
                img.move(left=env[-1].right + env.offset)
                env.set_priority(img, env.end)
        if self.img_crash.is_shown():
            self.img_crash.move_ip(-offset, 0)

    def update_traffic(self):
        for car in self.traffic.drawable:
            car.update(self.pixel_per_ms)
            if car.right < 0:
                self.traffic.remove(car)
        for way, car_list in enumerate(self.traffic.ways, 1):
            for i in range(1, len(car_list)):
                car_1 = car_list[i - 1]
                car_2 = car_list[i]
                if car_2.left - car_1.right < 20:
                    if (way in [1, 2]) and (car_1.speed < car_2.speed):
                        car_2.speed = car_1.speed
                    elif (way in [3, 4]) and (car_1.speed > car_2.speed):
                        car_1.speed = car_2.speed
        ratio = (2 - (round(self.infos_score.value) / 20000)) * 1000
        if self.car.speed > 30 and self.clock_traffic.elapsed_time(ratio):
            if self.traffic.empty(
            ) or self.traffic.last.right < self.right - 20:
                self.traffic.add_cars(self, self.road,
                                      round(self.infos_score.value))

    def end_game(self):
        for car in self.traffic:
            car.stop_animation()
        self.crashed_car = None
        score = round(self.infos_score.value)
        distance = round(self.infos_distance.value, 1)
        time_100 = round(self.total_time_100, 1)
        time_opposite = round(self.total_time_opposite, 1)
        window = EndGame(self, score, distance, time_100, time_opposite)
        window.mainloop()
        if self.restart:
            self.traffic.clear()
            self.car.move(left=50, centery=self.road.centery)
            self.car.restart()
            self.init_game()