def _init_continue_btn(self): box_size = (200, 50) ctn_btn = RectButton( 550, 500, box_size[0], box_size[1], WOOD, 0, Text(pygame.font.SysFont('Agency FB', 20), "Continue", Color.GREEN2)) ctn_btn.add_frame(FRAME) ctn_btn.on_click(self._continue) self.sprite_grp.add(ctn_btn)
def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: color, color_text: color): box_size = (130, 48) button_back = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) button_back.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.HOSTMENUSCENE)) button_back.change_bg_image(WOOD) button_back.add_frame(FRAME) self.sprite_grp.add(button_back)
def _init_end_turn_button(self): btn = RectButton(1130, 500, 150, 50, background=Color.ORANGE, txt_obj=Text(pygame.font.SysFont('Arial', 23), "END TURN", Color.GREEN2)) btn.change_bg_image(WOOD) btn.add_frame(FRAME) btn.on_click(self._end_turn) return btn
class HostJoinScene(Scene): def __init__(self, screen: pygame.Surface, current_player: PlayerModel): Scene.__init__(self, screen) self._current_player = current_player self._init_background() self._init_btn_host(575, 481, "Host", Color.STANDARDBTN, Color.GREEN2) self._init_btn_join(575, 371, "Join", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) self.buttonJoin.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.JOINSCENE)) self.buttonBack.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.STARTSCENE)) self.buttonHost.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.HOSTMENUSCENE)) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_log_box(self): box_size = (self.resolution[0] / 2, self.resolution[1] / 2) x_pos = self.resolution[0] / 2 - box_size[0] / 2 y_pos = self.resolution[1] / 2 - box_size[1] / 2 log_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], Color.GREEN) self.sprite_grp.add(log_box) def _init_btn_join(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonJoin = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonJoin.change_bg_image(WOOD) self.buttonJoin.add_frame(FRAME) self.sprite_grp.add(self.buttonJoin) def _init_btn_host(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonHost = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonHost.change_bg_image(WOOD) self.buttonHost.add_frame(FRAME) self.sprite_grp.add(self.buttonHost) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.sprite_grp.add(self.buttonBack)
class JoinScene(object): def __init__(self, screen, current_player: PlayerModel): self._current_player = current_player self.resolution = (1280, 700) self.sprite_grp = pygame.sprite.Group() self._init_background() self._init_text_box(342, 350, "Enter IP:", Color.STANDARDBTN, Color.GREEN2) self._init_text_bar(500, 350, 400, 32) self._init_btn(625, 536, "Connect", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) self._text_bar = self._init_text_bar(500, 350, 400, 32) self.error_msg = "" self.buttonBack.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.HOSTJOINSCENE, player=self._current_player)) self.buttonConnect.on_click(self.join) def join(self): """ Start the join host process in Networking :param ip_addr: ip address to connect :param next_scene: next scene to be called after the process completes :param args: extra arguments for the next scene :return: """ ip_addr = self.text_bar_msg try: self._current_player.status = PlayerStatusEnum.NOT_READY Networking.get_instance().join_host(ip_addr, player=self._current_player) reply = Networking.wait_for_reply() # Connection error will be raised if no reply if reply: reply = JSONSerializer.deserialize(reply) if isinstance(reply, TooManyPlayersEvent): raise TooManyPlayersException(self._current_player) # GameStateModel.set_game(JSONSerializer.deserialize(reply)) EventQueue.post(CustomEvent(ChangeSceneEnum.LOBBYSCENE)) except TimeoutError: msg = "Host not found." print(msg) self.init_error_message(msg) except TooManyPlayersException: msg = "Lobby is full. Cannot join the game." print(msg) self.init_error_message(msg) # Disconnect client that's trying to connect if not Networking.get_instance().is_host: Networking.get_instance().client.disconnect() except Networking.Client.SocketError: msg = "Failed to establish connection." print(msg) self.init_error_message(msg) except OSError: msg = "Invalid IP address" print(msg) self.init_error_message(msg) def _init_text_box(self, x_pos, y_pos, text, color: Color, color_text: Color): box_size = (136, 32) user_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) user_box.change_bg_image(WOOD) user_box.add_frame(FRAME) self.sprite_grp.add(user_box) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_btn(self, x_pos, y_pos, text, color: Color, color_text: Color): box_size = (130, 48) self.buttonConnect = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonConnect.change_bg_image(WOOD) self.buttonConnect.add_frame(FRAME) self.sprite_grp.add(self.buttonConnect) def _init_text_bar(self, x_pos, y_pos, width, height): inputbox = InputBox(x=x_pos, y=y_pos, w=width, h=height) inputbox.disable_enter() return inputbox def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.sprite_grp.add(self.buttonBack) def init_error_message(self, msg): label_width = 400 label_left = (pygame.display.get_surface().get_size()[0] / 2) - (label_width / 2) label_top = (pygame.display.get_surface().get_size()[1] / 6) * 2 error_msg_label = RectLabel(label_left, label_top, label_width, label_width, (255, 255, 255), txt_obj=(Text(pygame.font.SysFont('Agency FB', 24), msg, Color.RED))) error_msg_label.set_transparent_background(True) self.error_msg = error_msg_label def draw(self, screen): self.sprite_grp.draw(screen) self._text_bar.draw(screen) if self.error_msg: self.error_msg.draw(screen) def update(self, event_queue): self.sprite_grp.update(event_queue) self._text_bar.update(event_queue) if self.error_msg: self.error_msg.update(event_queue) @property def text_bar_msg(self): return self._text_bar.text
class CreateGameMenuScene(Scene): def __init__(self, screen: pygame.Surface, current_player: PlayerModel): Scene.__init__(self, screen) self._current_player = current_player self._init_background() self._init_text_box(344, 387, 200, 32, "Regular Mode:", Color.STANDARDBTN, Color.GREEN2) self._init_text_box(344, 506, 200, 32, "Advanced Modes:", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) self._init_btn_family(575, 381, "Family", Color.STANDARDBTN, Color.GREEN2) self._init_btn_rec(575, 500, "Recruit", Color.GREEN, Color.GREEN) self._init_btn_veteran(710, 500, "Veteran", Color.YELLOW, Color.YELLOW) self._init_btn_heroic(845, 500, "Heroic", Color.RED, Color.RED) self.buttonRecruit.on_click(self.create_new_game, GameKindEnum.EXPERIENCED, DifficultyLevelEnum.RECRUIT) self.buttonVeteran.on_click(self.create_new_game, GameKindEnum.EXPERIENCED, DifficultyLevelEnum.VETERAN) self.buttonHeroic.on_click(self.create_new_game, GameKindEnum.EXPERIENCED, DifficultyLevelEnum.HEROIC) self.buttonFamily.on_click(self.create_new_game, GameKindEnum.FAMILY) self.buttonBack.on_click(self._disconnect_and_back) @staticmethod def _disconnect_and_back(): Networking.get_instance().disconnect() EventQueue.post(CustomEvent(ChangeSceneEnum.HOSTMENUSCENE)) # ------------- GAME CREATE/LOAD STUFF ---------- # def create_new_game(self, game_kind: GameKindEnum, difficulty_level: DifficultyLevelEnum = None): """Instantiate a new family game and move to the lobby scene.""" GameStateModel(self._current_player, 6, game_kind, GameBoardTypeEnum.ORIGINAL, difficulty_level) EventQueue.post(CustomEvent(ChangeSceneEnum.CHOOSEBOARDSCENE)) # ----------------------------------------------- # def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_text_box(self, x_pos, y_pos, w, h, text, color, color_text): box_size = (w, h) user_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) user_box.change_bg_image(WOOD) user_box.add_frame(FRAME) self.sprite_grp.add(user_box) def _init_btn_family(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonFamily = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonFamily.change_bg_image(WOOD) pygame.draw.rect(self.buttonFamily.image, Color.GREEN2, [0, 0, box_size[0], box_size[1]], 7) self.sprite_grp.add(self.buttonFamily) def _init_btn_rec(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonRecruit = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonRecruit.change_bg_image(WOOD) pygame.draw.rect(self.buttonRecruit.image, Color.GREEN, [0, 0, box_size[0], box_size[1]], 7) self.sprite_grp.add(self.buttonRecruit) def _init_btn_veteran(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonVeteran = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonVeteran.change_bg_image(WOOD) pygame.draw.rect(self.buttonVeteran.image, Color.YELLOW, [0, 0, box_size[0], box_size[1]], 7) self.sprite_grp.add(self.buttonVeteran) def _init_btn_heroic(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonHeroic = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonHeroic.change_bg_image(WOOD) pygame.draw.rect(self.buttonHeroic.image, Color.RED, [0, 0, box_size[0], box_size[1]], 7) self.sprite_grp.add(self.buttonHeroic) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.sprite_grp.add(self.buttonBack)
class LobbyScene(GameStateObserver): def __init__(self, screen, current_player: PlayerModel): super().__init__() self._current_player = current_player self._game = GameStateModel.instance() self.player_boxes = [] if Networking.get_instance().is_host: self._current_player.color = Color.BLUE self._game.host.color = Color.BLUE self._current_player.status = PlayerStatusEnum.READY self._player_count = len(self._game.players) self.isReady = False self.resolution = (1280, 700) self.sprite_grp = pygame.sprite.Group() self.players_not_ready_prompt = None self._init_all() if self._game.rules == GameKindEnum.EXPERIENCED: self.buttonSelChar.on_click( EventQueue.post, CustomEvent(ChangeSceneEnum.CHARACTERSCENE)) if Networking.get_instance().is_host: self._current_player.status = PlayerStatusEnum.READY self.isReady = True self.start_button.on_click(self.start_game) self.start_button.disable() if self._game.rules == GameKindEnum.EXPERIENCED and self._current_player.role == PlayerRoleEnum.FAMILY: self._current_player.status = PlayerStatusEnum.NOT_READY else: self._current_player.status = PlayerStatusEnum.NOT_READY self.buttonReady.on_click(self.set_ready) self.buttonBack.on_click(self.go_back) @staticmethod def go_back(): if Networking.get_instance().is_host: next_scene = ChangeSceneEnum.SETMAXPLAYERSCENE else: next_scene = ChangeSceneEnum.JOINSCENE Networking.get_instance().disconnect() EventQueue.post(CustomEvent(next_scene)) def start_game(self): """Callback for when the host tries to start the game.""" game = GameStateModel.instance() players_ready = len([ player for player in game.players if player.status == PlayerStatusEnum.READY ]) if not self._game.rules == GameKindEnum.FAMILY: if any(player.role == PlayerRoleEnum.FAMILY for player in game.players): return if not players_ready == game.max_players: self.not_enough_players_ready_prompt() return # Perform the start game hook in Networking (ie. stop accepting new connections and kill broadcast) if Networking.get_instance().is_host: # Kill the broadcast for box in self.player_boxes: box.delete_class() Networking.get_instance().send_to_all_client(StartGameEvent()) def set_ready(self): """Set the status of the current player to ready.""" if not self.isReady: if not (self._game.rules == GameKindEnum.FAMILY and self._current_player.role == PlayerRoleEnum.FAMILY) \ or self._game.rules == GameKindEnum.FAMILY: self.isReady = True self.sprite_grp.remove(self.buttonReady) box_size = (130, 48) self.buttonReady = RectButton( 1050, 575, box_size[0], box_size[1], Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Ready", Color.GREEN)) self.buttonReady.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonReady.add_frame(MEDIA_CONSTS.FRAME) self.buttonReady.on_click(self.set_ready) self.sprite_grp.add(self.buttonReady) self._current_player.status = PlayerStatusEnum.READY event = ReadyEvent(self._current_player, True) if self._current_player.ip == GameStateModel.instance( ).host.ip: event.execute() Networking.get_instance().send_to_all_client(event) else: Networking.get_instance().send_to_server(event) else: self.isReady = False self.sprite_grp.remove(self.buttonReady) box_size = (130, 48) self.buttonReady = RectButton( 1050, 575, box_size[0], box_size[1], Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Not Ready", Color.GREY)) self.buttonReady.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonReady.add_frame(MEDIA_CONSTS.FRAME) self.buttonReady.on_click(self.set_ready) self.sprite_grp.add(self.buttonReady) self._current_player.status = PlayerStatusEnum.NOT_READY event = ReadyEvent(self._current_player, False) if self._current_player.ip == GameStateModel.instance().host.ip: event.execute() Networking.get_instance().send_to_all_client(event) else: Networking.get_instance().send_to_server(event) def _init_all(self, reuse=False): self._init_background() self._init_ip_addr() self.chat_box = ChatBox(self._current_player) if not reuse: self._init_btn_back(20, 20, "Exit", MEDIA_CONSTS.WOOD, Color.GREEN2) if self._current_player.ip == GameStateModel.instance().host.ip: self._init_start_game_button() else: # Ready button is grey at first self._init_ready(1050, 575, "Not Ready", Color.GREY, Color.BLACK) if not self._game.rules == GameKindEnum.FAMILY: self._init_selec_char(1050, 475, "Select Character", Color.STANDARDBTN, Color.GREEN2) else: if not self._game.rules == GameKindEnum.FAMILY: self.sprite_grp.add(self.buttonSelChar) if self._current_player.ip == GameStateModel.instance().host.ip: self.sprite_grp.add(self.start_button) else: self.sprite_grp.add(self.buttonReady, self.buttonBack) self.sprite_grp.add(self.buttonBack) self._init_sprites() def _init_start_game_button(self): """Button for starting the game once all players have clicked ready.""" box_size = (130, 48) self.start_button = RectButton( 1050, 575, box_size[0], box_size[1], Color.GREY, 0, Text(pygame.font.SysFont('Agency FB', 25), "Start", Color.RED)) self.start_button.change_bg_image(MEDIA_CONSTS.WOOD) self.start_button.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.start_button) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], MEDIA_CONSTS.FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def create_butn_img(self, x, y, width, height, path): box_size = (width, height) self.this_img = RectButton(x, y, box_size[0], box_size[1], path) self.sprite_grp.add(self.this_img) def _init_background_player(self, rect): if not self._game.rules == GameKindEnum.FAMILY and self._current_player.role: role_path = self.get_path_from_character_enum( self._current_player.role) user_box = RectLabel(rect[0], rect[1], rect[2], rect[3], role_path) else: user_box = RectLabel(rect[0], rect[1], rect[2], rect[3], MEDIA_CONSTS.FAMILY) return user_box def get_path_from_character_enum(self, enum: PlayerRoleEnum): if enum == PlayerRoleEnum.CAFS: return MEDIA_CONSTS.CAFS_FIREFIGHTER elif enum == PlayerRoleEnum.CAPTAIN: return MEDIA_CONSTS.FIRE_CAPTAIN elif enum == PlayerRoleEnum.GENERALIST: return MEDIA_CONSTS.GENERALIST elif enum == PlayerRoleEnum.DRIVER: return MEDIA_CONSTS.DRIVER_OPERATOR elif enum == PlayerRoleEnum.HAZMAT: return MEDIA_CONSTS.HAZMAT_TECHNICIAN elif enum == PlayerRoleEnum.IMAGING: return MEDIA_CONSTS.IMAGING_TECHNICIAN elif enum == PlayerRoleEnum.PARAMEDIC: return MEDIA_CONSTS.PARAMEDIC elif enum == PlayerRoleEnum.RESCUE: return MEDIA_CONSTS.RESCUE_SPECIALIST elif enum == PlayerRoleEnum.FAMILY: return MEDIA_CONSTS.FAMILY def _init_text_box(self, position, text, color): box_size = (position[2], position[3]) user_box = RectLabel( position[0], position[1], box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 20), text, (0, 255, 0, 0))) return user_box def _init_selec_char(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (170, 48) self.buttonSelChar = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonSelChar.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonSelChar.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.buttonSelChar) def _init_ready(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.isReady = False self.buttonReady = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, Color.GREY)) self.buttonReady.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonReady.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.buttonReady) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, bg: str, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton( x_pos, y_pos, box_size[0], box_size[1], bg, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.buttonBack) def _init_ip_addr(self): if Networking.get_instance().is_host: ip_addr = f"Your IP address: {Networking.get_instance().get_ip()}" label_width = 400 label_left = (pygame.display.get_surface().get_size()[0] / 2) - (label_width / 2) ip_addr_label = RectLabel(label_left, 20, label_width, 50, (255, 255, 255), txt_obj=(Text( pygame.font.SysFont('Agency FB', 26), ip_addr, Color.GREEN2))) ip_addr_label.change_bg_image(MEDIA_CONSTS.WOOD) ip_addr_label.add_frame(MEDIA_CONSTS.FRAME) #ip_addr_label.set_transparent_background(True) self.sprite_grp.add(ip_addr_label) def _init_sprites(self): text_pos = [(565, 625, 200, 32), (100, 364, 150, 32), (400, 289, 150, 32), (780, 289, 150, 32), (1080, 364, 150, 32)] background_pos = [(565, 375, 200, 250), (100, 164, 150, 200), (400, 89, 150, 200), (780, 89, 150, 200), (1080, 164, 150, 200)] self.player_boxes = [] current_player = [ player for player in GameStateModel.instance().players if player.ip == self._current_player.ip ][0] self.player_boxes.append( PlayerBox(text_pos[0], background_pos[0], self._current_player.nickname, current_player, current_player.color)) players = [ x for x in GameStateModel.instance().players if x.ip != self._current_player.ip ] i = 1 for player in players: self.player_boxes.append( PlayerBox(text_pos[i], background_pos[i], player.nickname, player, player.color)) # self.sprite_grp.add(self._init_text_box(text_pos[i], player.nickname, player.color)) # self.sprite_grp.add(self._init_background_player(background_pos[i])) i += 1 def not_enough_players_ready_prompt(self): """Prompt to the host that there are not enough players to join the game.""" label_width = 400 label_height = 30 label_left = 1050 - 100 label_top = (pygame.display.get_surface().get_size()[1] - 50) - (label_height / 2) message = f"Not all players are ready!" prompt_label = RectLabel(label_left, label_top, label_width, label_height, Color.WHITE, txt_obj=Text( pygame.font.SysFont('Agency FB', 24), message)) prompt_label.set_transparent_background(True) self.players_not_ready_prompt = prompt_label def draw(self, screen): self.sprite_grp.draw(screen) for box in self.player_boxes: box.draw(screen) self.chat_box.draw(screen) if self.players_not_ready_prompt: self.players_not_ready_prompt.draw(screen) def update(self, event_queue): if Networking.get_instance().is_host: game = GameStateModel.instance() players_ready = len([ player for player in game.players if player.status == PlayerStatusEnum.READY ]) if players_ready == game.max_players: self.sprite_grp.remove(self.start_button) self.start_button = RectButton( 1050, 575, 130, 48, Color.RED, 0, Text(pygame.font.SysFont('Agency FB', 25), "Start", Color.GREEN)) self.start_button.on_click(self.start_game) self.start_button.change_bg_image(MEDIA_CONSTS.WOOD) self.start_button.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.start_button) # self.start_button.txt_obj.set_color(Color.GREEN) else: self.sprite_grp.remove(self.start_button) self.start_button = RectButton( 1050, 575, 130, 48, Color.GREEN, 0, Text(pygame.font.SysFont('Agency FB', 25), "Start", Color.RED)) self.start_button.disable() self.start_button.change_bg_image(MEDIA_CONSTS.WOOD) self.start_button.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.start_button) self.chat_box.update(event_queue) # game is mutated by reference, BE CAREFUL!!! if len(GameStateModel.instance().players) != self._player_count: self._player_count = len(GameStateModel.instance().players) self.sprite_grp.empty() self._init_all(reuse=True) self.sprite_grp.update(event_queue) def notify_player_index(self, player_index: int): pass def notify_game_state(self, game_state: GameStateEnum): pass def damage_changed(self, new_damage: int): pass def saved_victims(self, victims_saved: int): pass def dead_victims(self, victims_dead: int): pass def player_added(self, player: PlayerModel): pass def player_removed(self, player: PlayerModel): pass def player_command(self, source: PlayerModel, target: PlayerModel): pass
class DodgePrompt(object): """Prompt for the player deciding whether to dodge or not.""" def __init__(self): self.bg = pygame.image.load(WOOD) self.frame = pygame.image.load(FRAME) self.frame = pygame.transform.scale(self.frame, (400, 100)) self.accept_button = RectButton( 550 - 75, 310, 75, 50, Color.ORANGE, 0, Text(pygame.font.SysFont('Agency FB', 25), "Accept", Color.GREEN2)) self.accept_button.change_bg_image(WOOD) self.accept_button.add_frame(FRAME) self.deny_button = RectButton( 550 + 200, 310, 75, 50, Color.ORANGE, 0, Text(pygame.font.SysFont('Agency FB', 25), "Deny", Color.GREEN2)) self.deny_button.change_bg_image(WOOD) self.deny_button.add_frame(FRAME) self.accept_button.on_click(self._accept_on_click) self.deny_button.on_click(self._deny_on_click) self.background = RectLabel( 550, 300, 200, 75, Color.GREY, 0, Text(pygame.font.SysFont('Agency FB', 25), "Dodge?", Color.GREEN2)) self.background.change_bg_image(WOOD) self.background.add_frame(FRAME) self.enabled = False self.disable() def enable(self): self.deny_button.enable() self.accept_button.enable() self.accept_button.on_click(self._accept_on_click) self.deny_button.on_click(self._deny_on_click) self.enabled = True def disable(self): self.deny_button.disable() self.accept_button.disable() self.accept_button.on_click(None) self.deny_button.on_click(None) self.enabled = False def _send_reply_event(self, reply: bool): if Networking.get_instance().is_host: Networking.get_instance().send_to_all_client( DodgeReplyEvent(reply)) else: Networking.get_instance().send_to_server(DodgeReplyEvent(reply)) def _deny_on_click(self): self.disable() self._send_reply_event(False) def _accept_on_click(self): self.disable() self._send_reply_event(True) def update(self, event_queue: EventQueue): self.background.update(event_queue) self.accept_button.update(event_queue) self.deny_button.update(event_queue) def draw(self, screen): if self.enabled: self.background.draw(screen) self.accept_button.draw(screen) self.deny_button.draw(screen)
class FireDeckGunController(Controller): _instance = None def __init__(self, current_player: PlayerModel): super().__init__(current_player) if FireDeckGunController._instance: self._current_player = current_player # raise Exception("Victim Controller is a singleton") self._game: GameStateModel = GameStateModel.instance() self.player = current_player self.board: GameBoardModel = self._game.game_board self.engine = self.board.engine FireDeckGunController._instance = self self.label = None self.input1 = None self.input2 = None self.input3 = None self.input4 = None self.max_input = 0 @classmethod def instance(cls): return cls._instance def process_input(self, tile_sprite: TileSprite): assoc_model = self.board.get_tile_at(tile_sprite.row, tile_sprite.column) button = None if self.run_checks(assoc_model): button = tile_sprite.fire_deck_gun_button if button: tile_sprite.fire_deck_gun_button.enable() if self._current_player.role == PlayerRoleEnum.DRIVER: button.on_click(self.driver_menu_popup, assoc_model) else: button.on_click(self.send_event_and_close_menu, assoc_model, button) else: tile_sprite.fire_deck_gun_button.disable() def run_checks(self, tile_model: TileModel) -> bool: """ Determines whether or not it is possible to perform this event. :return: True if it possible to perform this event. False otherwise. """ # Doge cannot fire the deck gun if self.player.role == PlayerRoleEnum.DOGE: return False if not self.player == GameStateModel.instance().players_turn: return False ap_deduct = 2 if self.player.role == PlayerRoleEnum.DRIVER else 4 if not TurnEvent.has_required_AP(self.player.ap, ap_deduct): return False # If the player is not located in the # same space as the engine, they cannot # fire the deck gun. engine_orient = self.engine.orientation if engine_orient == VehicleOrientationEnum.HORIZONTAL: on_first_spot = self.player.row == self.engine.row and self.player.column == self.engine.column on_second_spot = self.player.row == self.engine.row and self.player.column == self.engine.column + 1 if not on_first_spot and not on_second_spot: return False elif engine_orient == VehicleOrientationEnum.VERTICAL: on_first_spot = self.player.row == self.engine.row and self.player.column == self.engine.column on_second_spot = self.player.row == self.engine.row + 1 and self.player.column == self.engine.column if not on_first_spot and not on_second_spot: return False engine_quadrant = self._determine_quadrant(self.engine.row, self.engine.column) tile_input_quadrant = self._determine_quadrant(tile_model.row, tile_model.column) # If there are players present in the # quadrant, the deck gun cannot be fired. # tile input gotta be on quadrant adjacent to engine if self._are_players_in_quadrant( engine_quadrant) or tile_input_quadrant != engine_quadrant: return False return True @staticmethod def _determine_quadrant(row, column) -> QuadrantEnum: """ Determines the quadrant to which the row and column belong to. :param row: :param column: :return: Quadrant in which the row and column are located. """ if row < 4 and column < 5: return QuadrantEnum.TOP_LEFT elif row < 4 and column >= 5: return QuadrantEnum.TOP_RIGHT elif row >= 4 and column < 5: return QuadrantEnum.BOTTOM_LEFT else: return QuadrantEnum.BOTTOM_RIGHT @staticmethod def _determine_quadrant_player(row, column) -> QuadrantEnum: """ Determines the quadrant to which the row and column belong to. :param row: :param column: :return: Quadrant in which the row and column are located. """ if 4 > row > 0 and 5 > column > 0: return QuadrantEnum.TOP_LEFT elif 4 > row > 0 and 5 <= column < 9: return QuadrantEnum.TOP_RIGHT elif 4 <= row < 7 and 5 > column > 0: return QuadrantEnum.BOTTOM_LEFT elif 4 <= row < 7 and 5 <= column < 9: return QuadrantEnum.BOTTOM_RIGHT def _are_players_in_quadrant(self, quadrant: QuadrantEnum) -> bool: """ Determines whether there are any players in the given quadrant. :param quadrant: Quadrant that we are interested in. :return: True if there are players in the quadrant, False otherwise. """ for player in self._game.players: logger.info( f"Player assoc_quadrant: {self._determine_quadrant_player(player.row, player.column)}" ) logger.info( f"Player row: {player.row}, Player column: {player.column}") logger.info(f"Quadrant to compare: {quadrant}") if quadrant == self._determine_quadrant_player( player.row, player.column): return True return False def send_event_and_close_menu(self, tile_model: TileModel, menu_to_close: Interactable, row: int = -1, column: int = -1): event = FireDeckGunEvent(row=row, column=column) if not self.run_checks(tile_model): menu_to_close.disable() return if Networking.get_instance().is_host: Networking.get_instance().send_to_all_client(event) else: Networking.get_instance().client.send(event) if menu_to_close: menu_to_close.disable() def _set_target_tile(self, row: int = -1, column: int = -1): """ Set the tile which will be the target for the firing of the deck gun. :return: """ engine_quadrant = self._determine_quadrant(self.engine.row, self.engine.column) if row == -1: target_row = GameStateModel.instance().roll_red_dice() else: target_row = row if column == -1: target_column = GameStateModel.instance().roll_black_dice() else: target_column = column target_quadrant = self._determine_quadrant(target_row, target_column) # If the roll gives a tile in the engine's # quadrant, that will become the target tile. if target_quadrant == engine_quadrant: target_tile = GameStateModel.instance().game_board.get_tile_at( target_row, target_column) return target_tile else: # Flipping the red dice involves # subtracting the roll value from 7. flipped_row = 7 - target_row # Try out the following combinations # and see if any of them are in the # engine's quadrant: # 1. flipping the row, same column # 2. same row, flipping the column # 3. flipping the row, flipping the column new_target_quadrant = self._determine_quadrant( flipped_row, target_column) if new_target_quadrant == engine_quadrant: target_tile = GameStateModel.instance().game_board.get_tile_at( flipped_row, target_column) return target_tile flipped_column = GameStateModel.instance( ).determine_black_dice_opposite_face(target_column) new_target_quadrant = self._determine_quadrant( target_row, flipped_column) if new_target_quadrant == engine_quadrant: target_tile = GameStateModel.instance().game_board.get_tile_at( target_row, flipped_column) return target_tile new_target_quadrant = self._determine_quadrant( flipped_row, flipped_column) if new_target_quadrant == engine_quadrant: target_tile = GameStateModel.instance().game_board.get_tile_at( flipped_row, flipped_column) return target_tile # $$$$$$$$$$$$$$$$$ # Shouldn't be able to reach this point!! # One of the cases above should have worked. # $$$$$$$$$$$$$$$$$ logger.error("Possible issue with dice flipping! Stop!!") raise FlippingDiceProblemException() def driver_menu_popup(self, tile_model: TileModel): decision: int = 0 targetTile: TileModel = self._set_target_tile() red_dice = targetTile.row black_dice = targetTile.column boardSprite: GameBoard = GameBoard.instance().top_ui self.label = RectLabel( 200, 400, 600, 200, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), f"Roll: {red_dice}, {black_dice}", Color.GREEN2)) self.label.change_bg_image(WOOD) self.label.add_frame(FRAME) self.input1 = RectButton( 200, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Accept Roll", Color.GREEN2)) self.input1.change_bg_image(WOOD) self.input1.add_frame(FRAME) self.input2 = RectButton( 350, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Re-Roll Black Dice", Color.GREEN2)) self.input2.change_bg_image(WOOD) self.input2.add_frame(FRAME) self.input3 = RectButton( 500, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Re-Roll Red Dice", Color.GREEN2)) self.input3.change_bg_image(WOOD) self.input3.add_frame(FRAME) self.input4 = RectButton( 650, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Re-Roll Both Die", Color.GREEN2)) self.input4.change_bg_image(WOOD) self.input4.add_frame(FRAME) self.input1.on_click(self.input1_process, tile_model, red_dice, black_dice) self.input2.on_click(self.input2_process, tile_model, red_dice, black_dice) self.input3.on_click(self.input3_process, tile_model, red_dice, black_dice) self.input4.on_click(self.input4_process, tile_model, red_dice, black_dice) boardSprite.add(self.label) boardSprite.add(self.input1) boardSprite.add(self.input2) boardSprite.add(self.input3) boardSprite.add(self.input4) def input1_process(self, tile: TileModel, red_dice: int, black_dice: int): self.kill_all() self.max_input = 0 self.send_event_and_close_menu(tile, self.input1, red_dice, black_dice) def input2_process(self, tile: TileModel, red_dice: int, black_dice: int): self.max_input += 1 board_sprite: GameBoard = GameBoard.instance().top_ui self.kill_all() new_tile: TileModel = self._set_target_tile(red_dice) if self.max_input == 2: self.max_input = 0 self.send_event_and_close_menu(tile, self.input1, new_tile.row, new_tile.column) else: black_dice = new_tile.column self.label = RectLabel( 200, 400, 600, 200, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), f"Roll: {red_dice}, {black_dice}", Color.GREEN2)) self.label.change_bg_image(WOOD) self.label.add_frame(FRAME) self.input1 = RectButton( 200, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Accept Roll", Color.GREEN2)) self.input1.change_bg_image(WOOD) self.input1.add_frame(FRAME) self.input3 = RectButton( 350, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Re-Roll Red Dice", Color.GREEN2)) self.input3.change_bg_image(WOOD) self.input3.add_frame(FRAME) self.input1.on_click(self.input1_process, tile, red_dice, new_tile.column) self.input3.on_click(self.input3_process, tile, red_dice, new_tile.column) board_sprite.add(self.label) board_sprite.add(self.input1) board_sprite.add(self.input3) def input3_process(self, tile: TileModel, red_dice: int, black_dice: int): self.max_input += 1 board_sprite: GameBoard = GameBoard.instance().top_ui self.kill_all() new_tile: TileModel = self._set_target_tile(-1, black_dice) if self.max_input == 2: self.max_input = 0 self.send_event_and_close_menu(tile, self.input1, new_tile.row, new_tile.column) else: red_dice = new_tile.row self.label = RectLabel( 200, 400, 600, 200, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), f"Roll: {red_dice}, {black_dice}", Color.GREEN2)) self.label.change_bg_image(WOOD) self.label.add_frame(FRAME) self.input1 = RectButton( 200, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Accept Roll", Color.GREEN2)) self.input1.change_bg_image(WOOD) self.input1.add_frame(FRAME) self.input2 = RectButton( 350, 350, 150, 50, Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 25), "Re-Roll Black Dice", Color.GREEN2)) self.input2.change_bg_image(WOOD) self.input2.add_frame(FRAME) self.input1.on_click(self.input1_process, tile, new_tile.row, black_dice) self.input2.on_click(self.input2_process, tile, new_tile.row, black_dice) board_sprite.add(self.label) board_sprite.add(self.input1) board_sprite.add(self.input2) def input4_process(self, tile: TileModel, red_dice: int, black_dice: int): self.kill_all() new_tile: TileModel = self._set_target_tile() self.send_event_and_close_menu(tile, self.input1, new_tile.row, new_tile.column) def kill_all(self): self.input1.kill() self.input2.kill() self.input3.kill() self.input4.kill() self.label.kill()
class CommandNotification(object): """Displays the command status to the targeted players (source and target)""" def __init__(self): self._source = None self._target = None self._notification = RectLabel( 500, 0, 350, 75, Color.GREY, 0, Text(pygame.font.SysFont('Agency FB', 30), f"Commanding: None", Color.GREEN2)) self._notification.change_bg_image(WOOD) self._notification.add_frame(FRAME) self._wait_command = RectLabel( 500, 400, 300, 50, Color.GREY, 0, Text(pygame.font.SysFont('Agency FB', 30), f"Commanded by: None", Color.GREEN2)) self._wait_command.change_bg_image(WOOD) self._wait_command.add_frame(FRAME) self._init_end_command_btn() self._is_source = False self._is_target = False def _init_end_command_btn(self): # End command button self._end_command_btn = RectButton( 1080, 450, 200, 50, background=Color.ORANGE, txt_obj=Text(pygame.font.SysFont('Agency FB', 23), "END COMMAND", Color.GREEN2)) self._end_command_btn.change_bg_image(WOOD) self._end_command_btn.add_frame(FRAME) self._end_command_btn.on_click(self.end_command) def end_command(self): event = StopCommandEvent(self._source) if Networking.get_instance().is_host: Networking.get_instance().send_to_all_client(event) else: Networking.get_instance().send_to_server(event) @property def command(self) -> Tuple[PlayerModel, PlayerModel]: return self._source, self._target @command.setter def command(self, command: Tuple[PlayerModel, PlayerModel]): (self._source, self._target) = command source_msg = "Commanding: None" target_msg = "Commanded by: None" if self._target: source_msg = f"Commanding: {self._target.nickname}" if self._source: target_msg = f"Commanded by: {self._source.nickname}" self._notification.change_text( Text(pygame.font.SysFont('Agency FB', 30), source_msg, Color.ORANGE)) self._notification.change_bg_image(WOOD) self._notification.add_frame(FRAME) self._wait_command.change_text( Text(pygame.font.SysFont('Agency FB', 30), target_msg, Color.ORANGE)) self._wait_command.change_bg_image(WOOD) self._wait_command.add_frame(FRAME) @property def is_source(self) -> bool: return self._is_source @is_source.setter def is_source(self, source: bool): self._is_source = source @property def is_target(self) -> bool: return self._is_target @is_target.setter def is_target(self, target: bool): self._is_target = target def update(self, event_queue: EventQueue): if self.is_source: self._notification.update(event_queue) self._end_command_btn.update(event_queue) elif self.is_target: self._wait_command.update(event_queue) def draw(self, screen): if self.is_source: self._notification.draw(screen) self._end_command_btn.draw(screen) elif self.is_target: self._wait_command.draw(screen)
class StartScene(object): def __init__(self, screen): self.profiles = PROFILES self.resolution = (1280, 700) self.sprite_grp = pygame.sprite.Group() self._init_background() self._init_profile_selector(((self.resolution[0]/2)-(500/2)), 330, color.GREY) self.text_bar1 = self._init_text_bar(((self.resolution[0]/2)-(500/2))-20, 600, 400, 32) self.text_bar1.disable_enter() self._init_btn_register(((self.resolution[0]/2)-(500/2))+400, 592, "Create Profile", color.STANDARDBTN, color.GREEN2) self.update_profiles() def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_log_box(self, clr): box_size = (self.resolution[0] / 2, self.resolution[1] / 2) x_pos = self.resolution[0] / 2 - box_size[0] / 2 y_pos = self.resolution[1] / 2 - box_size[1] / 2 log_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], clr) log_box.change_bg_image(WOOD) log_box.add_frame(FRAME) self.sprite_grp.add(log_box) def _init_text_box(self, x_pos, y_pos, text, clr, color_text): box_size = (136, 32) user_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], clr, 0, Text(pygame.font.SysFont('Agency FB', 20), text, color_text)) self.sprite_grp.add(user_box) def _init_btn_register(self, x_pos, y_pos, text, clr, color_text): box_size = (130, 48) self.buttonRegister = RectButton(x_pos, y_pos, box_size[0], box_size[1], clr, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonRegister.change_bg_image(WOOD) self.buttonRegister.add_frame(FRAME) self.buttonRegister.on_click(self.register_profile) self.sprite_grp.add(self.buttonRegister) def register_profile(self): # Gets the text in the text bar and send the create profile event to the event queue (scene manager) self.create_profile(self.text_bar1) self.update_profiles() @staticmethod def _init_text_bar(x_pos, y_pos, width, height): return InputBox(x=x_pos, y=y_pos, w=width, h=height) def _init_profile_selector(self, x_pos, y_pos, clr): box_size = (500, 250) self.profile = ProfileList(x_pos, y_pos, box_size[0], box_size[1], 3, clr) def draw(self, screen): self.sprite_grp.draw(screen) # self._text_bar2.draw(screen) self.text_bar1.draw(screen) self.profile.draw(screen) def update(self, event_queue): self.sprite_grp.update(event_queue) self.text_bar1.update(event_queue) self.profile.update(event_queue) # self._text_bar2.update(event_queue) def init_error_message(self, msg): label_width = 400 label_left = (pygame.display.get_surface().get_size()[0] / 2) - (label_width / 2) label_top = (pygame.display.get_surface().get_size()[1] / 6) * 2 error_msg_label = RectLabel(label_left, label_top, label_width, label_width, (255, 255, 255), txt_obj=(Text(pygame.font.SysFont('Agency FB', 24), msg, color.RED))) error_msg_label.set_transparent_background(True) self.error_msg = error_msg_label # ------------ Stuff for profiles and start scene ------------ # def update_profiles(self): if not os.path.exists(self.profiles): with open(self.profiles, mode="w+", encoding='utf-8') as myFile: myFile.write("[]") with open(self.profiles, mode='r', encoding='utf-8') as myFile: temp = json.load(myFile) for i, user in enumerate(temp): player: PlayerModel = JSONSerializer.deserialize(user) player.ip = Networking.get_instance().get_ip() player.set_pos(-1, -1) player.ap = 0 player.special_ap = 0 player.carrying_victim = NullModel() self.profile.set_profile( i, player.nickname, player.wins, player.losses, EventQueue.post, CustomEvent(ChangeSceneEnum.HOSTJOINSCENE, player=player) ) self.profile.remove_profile_callback(i, self.remove_profile, player.nickname) def create_profile(self, text_bar: InputBox): temp = {} with open(self.profiles, mode='r+', encoding='utf-8') as myFile: temp = json.load(myFile) size = len(temp) if size >= 3: return if not text_bar.text.strip(): return size = len(text_bar.text.strip()) if size <= 12: player_model = PlayerModel( ip=Networking.get_instance().get_ip(), nickname=text_bar.text.strip() ) player = JSONSerializer.serialize(player_model) temp.append(player) else: msg = "Nickname must have less than 12 letters" self.init_error_message(msg) with open(self.profiles, mode='w', encoding='utf-8') as myFile: json.dump(temp, myFile) self.update_profiles() def remove_profile(self, removename: str): temp = {} with open(self.profiles, mode='r+', encoding='utf-8') as myFile: temp = json.load(myFile) for perm in temp: for name in perm.values(): if name == removename: temp.remove(perm) else: continue with open(self.profiles, mode='w', encoding='utf-8') as myFile: json.dump(temp, myFile) self.update_profiles()
class CharacterScene(Scene): def __init__(self, screen, current_player: PlayerModel): self.label_grp = pygame.sprite.Group() self._current_player = current_player Scene.__init__(self, screen) self._init_background() self._game = GameStateModel.instance() self.create_label(0, 0, 100, 150, 1) self.create_butn_img(150, 150, 99, 150, MEDIA_CONSTS.CAFS_FIREFIGHTER, 1) self.create_butn_img(350, 150, 100, 150, MEDIA_CONSTS.DRIVER_OPERATOR, 2) self.create_butn_img(550, 150, 100, 150, MEDIA_CONSTS.FIRE_CAPTAIN, 3) self.create_butn_img(750, 150, 99, 150, MEDIA_CONSTS.GENERALIST, 4) self.create_butn_img(150, 450, 100, 150, MEDIA_CONSTS.HAZMAT_TECHNICIAN, 5) self.create_butn_img(350, 450, 99, 150, MEDIA_CONSTS.IMAGING_TECHNICIAN, 6) self.create_butn_img(550, 450, 99, 150, MEDIA_CONSTS.PARAMEDIC, 7) self.create_butn_img(750, 450, 98, 150, MEDIA_CONSTS.RESCUE_SPECIALIST, 8) self.create_butn_img(950, 150, 98, 150, MEDIA_CONSTS.DOGE, 9) self.create_butn_img(950, 450, 99, 150, MEDIA_CONSTS.VETERAN, 10) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.BLACK) self._init_btn_confirm(1100, 575, "Confirm", Color.STANDARDBTN, Color.BLACK) self._init_title_text() self.character_enum: PlayerRoleEnum = None self.buttonBack.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.LOBBYSCENE)) self.buttonConfirm.on_click(self.confirm) def confirm(self): if self.character_enum: accept = True # This is a boolean flag if any([player.role == self.character_enum for player in self._game.players]): accept = False if accept: # means no one took this character EventQueue.post(CustomEvent(ChangeSceneEnum.LOBBYSCENE)) event = ChooseCharacterEvent(self.character_enum, self._game.players.index(self._current_player)) if Networking.get_instance().is_host: Networking.get_instance().send_to_all_client(event) else: Networking.get_instance().send_to_server(event) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], MEDIA_CONSTS.FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def create_butn_img(self, x, y, width, height, path: str, count: int): label = self.create_label(x, y, width, height, count) self.label_grp.add(label) self.sprite_grp.add(label) box_size = (width, height) self.this_img = RectButton(x, y, box_size[0], box_size[1], path) role: PlayerRoleEnum = self.decide_enum(count) self.this_img.on_click(self.click_img, label, role) self.sprite_grp.add(self.this_img) @staticmethod def decide_enum(count: int): if count == 1: return PlayerRoleEnum.CAFS elif count == 2: return PlayerRoleEnum.DRIVER elif count == 3: return PlayerRoleEnum.CAPTAIN elif count == 4: return PlayerRoleEnum.GENERALIST elif count == 5: return PlayerRoleEnum.HAZMAT elif count == 6: return PlayerRoleEnum.IMAGING elif count == 7: return PlayerRoleEnum.PARAMEDIC elif count == 8: return PlayerRoleEnum.RESCUE elif count == 9: return PlayerRoleEnum.DOGE elif count == 10: return PlayerRoleEnum.VETERAN def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, Color.GREEN2)) self.buttonBack.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonBack.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.buttonBack) def _init_title_text(self): box_size = (400, 50) self.text_title = RectButton(400, 60, box_size[0], box_size[1], Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 35), "Character Selection", Color.GREEN2)) self.text_title.change_bg_image(MEDIA_CONSTS.WOOD) self.text_title.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.text_title) def _init_btn_confirm(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonConfirm = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, Color.GREEN2)) self.buttonConfirm.change_bg_image(MEDIA_CONSTS.WOOD) self.buttonConfirm.add_frame(MEDIA_CONSTS.FRAME) self.sprite_grp.add(self.buttonConfirm) def create_label(self, x_pos: int, y_pos: int, width: int, height: int, count: int): role: PlayerRoleEnum = self.decide_enum(count) accept = True if any([player.role == role for player in self._game.players]): accept = False if not accept: label = RectLabel(x_pos - 15, y_pos - 15, width + 30, height + 30, Color.RED) label.change_bg_image(MEDIA_CONSTS.WOOD) return label else: label = RectLabel(x_pos - 15, y_pos - 15, width + 30, height + 30, Color.GREEN) label.change_bg_image(MEDIA_CONSTS.WOOD) return label def set_color(self, sprite: pygame.sprite.Sprite, i: int=0, enum:PlayerRoleEnum=None): accept = True role = self.decide_enum(i) if i else enum if any([player.role == role for player in self._game.players]): accept = False if isinstance(sprite, RectLabel): if accept: #sprite.change_color(Color.GREEN) pygame.draw.rect(sprite.image, Color.GREEN, [0, 0, 130, 180], 7) else: #sprite.change_color(Color.RED) pygame.draw.rect(sprite.image, Color.RED, [0, 0, 130, 180], 7) if self.character_enum == role: #sprite.change_color(Color.WHITE) pygame.draw.rect(sprite.image, Color.WHITE, [0, 0, 130, 180], 7) def click_img(self, btn, enum: PlayerRoleEnum): self.character_enum = enum self.set_color(btn, enum=enum) def update(self, event_queue: EventQueue): for i, sprite in enumerate(self.label_grp, 1): self.set_color(sprite, i) self.sprite_grp.update(event_queue)
class ChooseBoardScene(object): def __init__(self, screen, current_player: PlayerModel): self.game_kind = GameStateModel.instance().rules self.sprite_grp = pygame.sprite.Group() self._current_player = current_player self.resolution = (1280, 700) self._init_background() self._init_title_text() self._init_board1(410, 400, "Original", Color.STANDARDBTN, Color.GREEN2) self._init_board2(575, 400, "Alternative", Color.STANDARDBTN, Color.GREEN2) self._init_board3(740, 400, "Random", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) def _init_back_box(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): user_box = RectLabel( x_pos, y_pos, 500, 500, color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.sprite_grp.add(user_box) def _init_title_text(self): box_size = (500, 50) self.text_title = RectButton((int)(1280 / 2 - 250), 300, box_size[0], box_size[1], Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 35), "Choose Board", Color.GREEN2)) self.text_title.change_bg_image(WOOD) self.text_title.add_frame(FRAME) self.sprite_grp.add(self.text_title) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_board1(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_board1 = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_board1.change_bg_image(WOOD) self.button_board1.add_frame(FRAME) self.button_board1.on_click(self.set_and_continue, GameBoardTypeEnum.ORIGINAL) self.sprite_grp.add(self.button_board1) def _init_board2(self, x_pos, y_pos, text, color, color_text): box_size = (130, 48) self.button_board2 = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_board2.on_click(self.set_and_continue, GameBoardTypeEnum.ALTERNATIVE) self.button_board2.change_bg_image(WOOD) self.button_board2.add_frame(FRAME) self.sprite_grp.add(self.button_board2) def _init_board3(self, x_pos, y_pos, text, color, color_text): box_size = (130, 48) self.button_board3 = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_board3.on_click(self.set_and_continue, GameBoardTypeEnum.RANDOM) self.button_board3.change_bg_image(WOOD) self.button_board3.add_frame(FRAME) self.sprite_grp.add(self.button_board3) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.buttonBack.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.CREATEGAMEMENU)) self.sprite_grp.add(self.buttonBack) @staticmethod def set_and_continue(board_type: GameBoardTypeEnum): GameStateModel.instance().board_type = board_type EventQueue.post(CustomEvent(ChangeSceneEnum.SETMAXPLAYERSCENE)) def draw(self, screen): self.sprite_grp.draw(screen) def update(self, event_queue): self.sprite_grp.update(event_queue)
class SetMaxPlayers(object): def __init__(self, screen, current_player: PlayerModel): self.game_kind = GameStateModel.instance().rules self.sprite_grp = pygame.sprite.Group() self._current_player = current_player self.resolution = (1280, 700) self._init_background() self._init_title_text() #self._init_back_box((int)(1280/2-250), 130, "", Color.GREY, Color.GREEN) # COMMENT THIS OUT LATER self._init_solo(410, 400, "I'm alone :'(", Color.STANDARDBTN, Color.GREEN2) self._init_duo(740, 400, "Duo :'(", Color.STANDARDBTN, Color.GREEN2) self._init_button3(410, 200, "3", Color.STANDARDBTN, Color.GREEN2) self._init_button4(740, 200, "4", Color.STANDARDBTN, Color.GREEN2) self._init_button5(410, 300, "5", Color.STANDARDBTN, Color.GREEN2) self._init_button6(740, 300, "6", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) def _init_back_box(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): user_box = RectLabel(x_pos, y_pos, 500, 500, color, 0, Text(pygame.font.SysFont('Agency FB', 20), text, color_text)) self.sprite_grp.add(user_box) def _init_title_text(self): box_size = (500, 50) self.text_title = RectButton((int)(1280/2-250), 60, box_size[0], box_size[1], Color.BLACK, 0, Text(pygame.font.SysFont('Agency FB', 35), "Set Number of Max Players", Color.GREEN2)) self.text_title.change_bg_image(WOOD) self.text_title.add_frame(FRAME) self.sprite_grp.add(self.text_title) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_solo(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_solo = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_solo.change_bg_image(WOOD) self.button_solo.add_frame(FRAME) self.button_solo.on_click(self.set_and_continue, 1) self.sprite_grp.add(self.button_solo) def _init_button3(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_players3 = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_players3.on_click(self.set_and_continue, 3) self.button_players3.change_bg_image(WOOD) self.button_players3.add_frame(FRAME) self.sprite_grp.add(self.button_players3) def _init_button4(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_players4 = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_players4.change_bg_image(WOOD) self.button_players4.add_frame(FRAME) self.button_players4.on_click(self.set_and_continue, 4) self.sprite_grp.add(self.button_players4) def _init_button5(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_players5 = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_players5.change_bg_image(WOOD) self.button_players5.add_frame(FRAME) self.button_players5.on_click(self.set_and_continue, 5) self.sprite_grp.add(self.button_players5) def _init_button6(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.button_players6 = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_players6.on_click(self.set_and_continue, 6) self.button_players6.change_bg_image(WOOD) self.button_players6.add_frame(FRAME) self.sprite_grp.add(self.button_players6) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.CHOOSEBOARDSCENE)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.sprite_grp.add(self.buttonBack) @staticmethod def set_and_continue(desired_players: int): GameStateModel.instance().max_players = desired_players Networking.get_instance().create_host() EventQueue.post(CustomEvent(ChangeSceneEnum.LOBBYSCENE)) def draw(self, screen): self.sprite_grp.draw(screen) def update(self, event_queue): self.sprite_grp.update(event_queue) def _init_duo(self, x_pos, y_pos, text,color, color_text): box_size = (130, 48) self.button_duo= RectButton(x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.button_duo.change_bg_image(WOOD) self.button_duo.add_frame(FRAME) self.button_duo.on_click(self.set_and_continue, 2) self.sprite_grp.add(self.button_duo)
class HostMenuScene(Scene): def __init__(self, screen: pygame.Surface, host_player: PlayerModel): Scene.__init__(self, screen) self._host = host_player self._init_background() self._init_btn_new_game(575, 481, "New Game", Color.STANDARDBTN, Color.GREEN2) self._init_btn_login(575, 371, "Load Game", Color.STANDARDBTN, Color.GREEN2) self._init_btn_back(20, 20, "Back", Color.STANDARDBTN, Color.GREEN2) self.buttonNewGame.on_click( EventQueue.post, CustomEvent(ChangeSceneEnum.CREATEGAMEMENU)) self.buttonLogin.on_click(EventQueue.post, CustomEvent(ChangeSceneEnum.LOADGAME)) self.buttonBack.on_click( EventQueue.post, CustomEvent(ChangeSceneEnum.HOSTJOINSCENE, player=self._host)) def _init_background(self): box_size = (self.resolution[0], self.resolution[1]) background_box = RectLabel(0, 0, box_size[0], box_size[1], FLASHPOINT_BACKGROUND) self.sprite_grp.add(background_box) def _init_log_box(self): box_size = (self.resolution[0] / 2, self.resolution[1] / 2) x_pos = self.resolution[0] / 2 - box_size[0] / 2 y_pos = self.resolution[1] / 2 - box_size[1] / 2 log_box = RectLabel(x_pos, y_pos, box_size[0], box_size[1], Color.GREEN) self.sprite_grp.add(log_box) def _init_btn_new_game(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonNewGame = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonNewGame.change_bg_image(WOOD) self.buttonNewGame.add_frame(FRAME) self.sprite_grp.add(self.buttonNewGame) def _init_btn_login(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonLogin = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonLogin.change_bg_image(WOOD) self.buttonLogin.add_frame(FRAME) self.sprite_grp.add(self.buttonLogin) def _init_btn_back(self, x_pos: int, y_pos: int, text: str, color: Color, color_text: Color): box_size = (130, 48) self.buttonBack = RectButton( x_pos, y_pos, box_size[0], box_size[1], color, 0, Text(pygame.font.SysFont('Agency FB', 25), text, color_text)) self.buttonBack.change_bg_image(WOOD) self.buttonBack.add_frame(FRAME) self.sprite_grp.add(self.buttonBack)
class PermissionPrompt(object): """Prompt for the player deciding whether to allow to be commanded or not.""" def __init__(self): self.accept_button = RectButton( 500 - 75, 310, 75, 50, Color.ORANGE, 0, Text(pygame.font.SysFont('Agency FB', 20), "Accept", Color.GREEN2)) self.accept_button.change_bg_image(WOOD) self.accept_button.add_frame(FRAME) self.deny_button = RectButton( 500 + 300, 310, 75, 50, Color.ORANGE, 0, Text(pygame.font.SysFont('Agency FB', 20), "Deny", Color.GREEN2)) self.deny_button.change_bg_image(WOOD) self.deny_button.add_frame(FRAME) self.accept_button.on_click(self._accept_on_click) self.deny_button.on_click(self._deny_on_click) self._source = None self._target = None self.background = RectLabel( 500, 300, 300, 75, Color.GREY, 0, Text(pygame.font.SysFont('Agency FB', 20), "Permission?", Color.GREEN2)) self.background.change_bg_image(WOOD) self.background.add_frame(FRAME) self._enabled = False def _send_reply_event(self, reply: bool): if Networking.get_instance().is_host: Networking.get_instance().send_to_all_client( PermissionReplyEvent(reply, self._source, self._target)) else: Networking.get_instance().send_to_server( PermissionReplyEvent(reply, self._source, self._target)) def _deny_on_click(self): self.enabled = False self._send_reply_event(False) def _accept_on_click(self): self.enabled = False self._send_reply_event(True) def update(self, event_queue: EventQueue): if self._enabled: self.background.update(event_queue) self.accept_button.update(event_queue) self.deny_button.update(event_queue) def draw(self, screen): if self._enabled: self.background.draw(screen) self.accept_button.draw(screen) self.deny_button.draw(screen) @property def enabled(self) -> bool: return self._enabled @enabled.setter def enabled(self, enable: bool): self._enabled = enable @property def command(self) -> Tuple[PlayerModel, PlayerModel]: return self._source, self._target @command.setter def command(self, command: Tuple[PlayerModel, PlayerModel]): self._source = command[0] self._target = command[1] self.background.change_text( Text(pygame.font.SysFont('Agency FB', 20), f"{self._source.nickname} wants to command you", Color.GREEN2)) self.background.change_bg_image(WOOD) self.background.add_frame(FRAME)