class Interface(object): def __init__(self, world): self.world = world self.load_image = self.world.load_image self.display = world.display self.menu = MainMenu(self) self.dialog = Dialog(self, self.display) self.state = "" def show_dialog(self, owner, player, handler): self.dialog.init_dialog(owner, player, handler) self.state = "dialog" self.world.state = "itf" def show_menu(self, state="game"): self.menu.set_state(state) self.state = "menu" self.world.state = "itf" def return_event(self): for event in pygame.event.get(): if event.type == KEYDOWN: return event else: return EmptyEvent() def draw(self): if self.state == "menu": self.menu.key_loop(self.return_event()) self.menu.draw(self.display) elif self.state == "dialog": self.dialog.key_loop(self.return_event()) self.dialog.draw() else: self.world.state = "game"
class Game(arcade.Window): """main game class""" def __init__(self, width, height, name): """Initialize the main game class. Parameters ---------- width : int Window width height : int Window height name : str Window name """ super().__init__(width, height, name) # init variable holding the state self.state = None # init variable holding the background self.background = None # init variable holding the menu self.main_menu = None self.pause_menu = None self.game_over_screen = None self.score_board = None # init variable holding the player self.player = None # list for all creature sprites self.all_creature_sprites_list = None # list for all the obstacle sprites self.all_obstacle_sprite_list = None # list for highscores self.high_scores = [] # variable to check if state has changed self.state_change = None # variable for the new state, after state change self.new_state = None # previous state self.old_state = None # pause variable self.is_game_paused = True self.game_update_counter = 0 self.current_creature_amount = MAX_CREATURE_COUNT self.obstacle_mess_up_counter = 0 def setup(self): """Set up the game""" # set the default state self.state_change = False self.state = GameStates.MAIN_MENU # setup the background self.background = Background() self.background.center_x = SCREEN_WIDTH / 2 self.background.center_y = SCREEN_HEIGHT / 2 # setup the main menu self.main_menu = MainMenu() self.main_menu.center_x = SCREEN_WIDTH / 2 self.main_menu.center_y = SCREEN_HEIGHT / 2 # setup the pause menu self.pause_menu = PauseMenu() self.pause_menu.center_x = SCREEN_WIDTH / 2 self.pause_menu.center_y = SCREEN_HEIGHT / 2 # setup the game over screen self.game_over_screen = GameOverMenu() self.game_over_screen.center_x = SCREEN_WIDTH / 2 self.game_over_screen.center_y = SCREEN_HEIGHT / 2 # setup the highscores self.load_scores() # setup the scoreboard self.score_board = ScoreBoard() self.score_board.center_x = SCREEN_WIDTH / 2 self.score_board.center_y = SCREEN_HEIGHT / 2 # setup the player self.player = Player() self.player.center_x = SCREEN_WIDTH / 2 self.player.center_y = SCREEN_HEIGHT / 2 # setup the creatures self.all_creature_sprites_list = arcade.SpriteList() for i in range(self.current_creature_amount): rand = random.randint(1, 2) if rand == 1: creature = Moth() elif rand == 2: creature = Bug() creature.setup() self.all_creature_sprites_list.append(creature) # setup the obstacles self.all_obstacle_sprite_list = arcade.SpriteList() for i in range(MAX_OBSTACLE_COUNT): obstacle = Obstacle() obstacle.setup() self.all_obstacle_sprite_list.append(obstacle) def load_scores(self, file_path="scores.txt"): try: with open(file_path, "r") as file: for line in file: self.high_scores.append(int(line.rstrip())) except FileNotFoundError: print("Error, couldn't find the score file.") def save_scores(self, file_path=None): if file_path is None: file_path = "scores.txt" with open(file_path, "w") as file: for value in self.high_scores: file.write(str(value) + "\n") def display_scores(self): score_pos_y = SCREEN_HEIGHT / 3 * 2 font_size = 20 for score in self.high_scores: score = str(score) arcade.draw_text(text=score, start_x=SCREEN_WIDTH / 2, start_y=score_pos_y, font_name="arial", font_size=40, color=arcade.color.BLACK, align="center", anchor_x="center", anchor_y="center", bold=True) score_pos_y += -font_size * 2 def on_draw(self): """execute this code whenever window gets drawn""" arcade.start_render() self.background.draw() # when the game is in main_menu if self.state == GameStates.MAIN_MENU: self.main_menu.draw() # when the game is in the pause menu if self.state == GameStates.PAUSE_MENU: self.player.display_score(start_x=0, start_y=SCREEN_HEIGHT - 60) self.player.display_health() self.player.display_attack_counts() self.pause_menu.draw() if self.state == GameStates.GAME_OVER: self.game_over_screen.draw() self.player.display_score( start_x=SCREEN_WIDTH / 2 - 100, start_y=SCREEN_HEIGHT / 2, font_size=50, bold=True, ) if self.state == GameStates.SCORES: self.score_board.draw() self.high_scores = sorted(self.high_scores, reverse=True) self.display_scores() # when the game playing if self.state == GameStates.PLAYING: self.all_creature_sprites_list.draw() self.player.draw() self.all_obstacle_sprite_list.draw() self.player.display_health() self.player.display_score(start_x=0, start_y=SCREEN_HEIGHT - 60) self.player.display_attack_counts() if self.player.freeze_bullet is not None: self.player.freeze_bullet.draw() def on_update(self, delta_time): """Update the game""" self.check_collision_off_all_dynamic_elements() # make sure the obstacles are not overlapping if self.obstacle_mess_up_counter < 3: self.all_obstacle_sprite_list.update() for obstacle in self.all_obstacle_sprite_list: if (len( arcade.check_for_collision_with_list( obstacle, self.all_obstacle_sprite_list)) > 0): obstacle.randomize_position() if not self.is_game_paused: # scale max creature amount with game time self.game_update_counter += 1 if self.game_update_counter % 600 == 0: if self.current_creature_amount > 0: self.current_creature_amount += -1 self.background.update() self.player.update() self.all_creature_sprites_list.update() if self.state == GameStates.PLAYING: # check if all creatures are gone, so new ones can be spawned if len(self.all_creature_sprites_list) <= 0: self.reset_all_creatures() if self.player.check_if_dead(): self.set_state_change(True) self.set_new_state(GameStates.GAME_OVER) if self.player.has_hit_edge is True: self.player.has_hit_edge = False print(self.player.has_hit_edge) if self.player.is_attacking is False: self.background.set_texture_change_flag(True) self.reset_all_creatures() self.reset_all_obstacles() if self.get_state_change() is True: self.set_state_change(False) self.set_old_state() self.update_state() # check if game is back to playing, if so, unpause game if self.state == GameStates.PLAYING: self.unpause_game() else: self.pause_game() if self.state == GameStates.MAIN_MENU: self.background.reset() self.high_scores.append(self.player.current_score) self.player.reset() self.reset_all_creatures() self.reset_all_obstacles() for creature in self.all_creature_sprites_list: creature.reset_to_random_position() if self.state == GameStates.GAME_OVER: self.display_scores() if self.state == GameStates.SCORES: self.score_board.update() def on_key_press(self, symbol, modifiers): if self.state == GameStates.MAIN_MENU: if symbol == arcade.key.ENTER: self.set_state_change(True) self.set_new_state(GameStates.PLAYING) if symbol == arcade.key.TAB: self.set_state_change(True) self.set_new_state(GameStates.SCORES) if symbol == arcade.key.ESCAPE: self.quit() arcade.close_window() if self.state == GameStates.PLAYING: if symbol == arcade.key.ESCAPE: self.set_state_change(True) self.set_new_state(GameStates.PAUSE_MENU) if symbol == arcade.key.UP: self.player.move_up(PLAYER_MOVEMENT_SPEED) if symbol == arcade.key.DOWN: self.player.move_down(PLAYER_MOVEMENT_SPEED) if symbol == arcade.key.RIGHT: self.player.move_right(PLAYER_MOVEMENT_SPEED) if symbol == arcade.key.LEFT: self.player.move_left(PLAYER_MOVEMENT_SPEED) if symbol == arcade.key.SPACE: self.player.attack(self.all_creature_sprites_list) if symbol == arcade.key.A: self.player.select_attack_type(0) if symbol == arcade.key.S: self.player.select_attack_type(1) if symbol == arcade.key.D: self.player.select_attack_type(2) if symbol == arcade.key.F: self.player.select_attack_type(3) if self.state == GameStates.PAUSE_MENU: if symbol == arcade.key.ESCAPE: self.set_state_change(True) self.set_new_state(GameStates.MAIN_MENU) if symbol == arcade.key.ENTER: self.set_state_change(True) self.set_new_state(GameStates.PLAYING) if symbol == arcade.key.S: self.player.buy_attack(SPIN_ATTACK_COST, SPIN_ATTACK) print("spin attack count: {0}".format( self.player.spin_attack_amount) ) # TODO: DEBUG LINE, REMOVE if symbol == arcade.key.D: self.player.buy_attack(FREEZE_ATTACK_COST, FREEZE_ATTACK) print("freeze attack count: {0}".format( self.player.freeze_attack_amount) ) # TODO: DEBUG LINE, REMOVE if symbol == arcade.key.F: self.player.buy_attack(TELEPORT_COST, TELEPORT) print("teleport count: {0}".format( self.player.teleport_amount)) # TODO: DEBUG LINE, REMOVE if self.state == GameStates.GAME_OVER: if symbol == arcade.key.ESCAPE: self.set_state_change(True) self.set_new_state(GameStates.MAIN_MENU) if self.state == GameStates.SCORES: if symbol == arcade.key.ESCAPE: self.set_state_change(True) self.set_new_state(self.old_state) def on_key_release(self, symbol, modifiers): if self.state == GameStates.PLAYING: # make the player stop moving in a certain direction when the # corresponding key is released if symbol == arcade.key.UP: self.player.move_up(0) if symbol == arcade.key.DOWN: self.player.move_down(0) if symbol == arcade.key.RIGHT: self.player.move_right(0) if symbol == arcade.key.LEFT: self.player.move_left(0) if symbol == arcade.key.SPACE: self.player.reset_after_attack() def set_state_change(self, boolean): """Set wether or not the state has changed. Parameters ---------- boolean : Bool Boolean, wether the game state has changed. """ self.state_change = boolean def get_state_change(self): """Get if the state of the game has changed. Returns ------- bool Boolean, True when the game state has changed, else False """ return self.state_change def set_new_state(self, new_state): """Set the next state the game should change to when updated. Parameters ---------- new_state : GameState A state from the enum class GameState. """ self.new_state = new_state def get_new_state(self): """Get the state the game will change to when updated. Returns ------- GameState A state from the enum class GameState. """ return self.new_state def set_old_state(self): """Set the old state to fall back to after state change""" self.old_state = self.state def update_state(self): """Update the game state to the new state.""" self.state = self.new_state def reset_all_creatures(self): for creature in self.all_creature_sprites_list: creature.kill() for i in range(self.current_creature_amount): rand = random.randint(1, 2) if rand == 1: creature = Moth() elif rand == 2: creature = Bug() creature.setup() self.all_creature_sprites_list.append(creature) if (len(self.all_creature_sprites_list) >= self.current_creature_amount): break def reset_all_obstacles(self): self.obstacle_mess_up_counter = 0 for obstacle in self.all_obstacle_sprite_list: obstacle.kill() for i in range(MAX_OBSTACLE_COUNT): obstacle = Obstacle() self.all_obstacle_sprite_list.append(obstacle) if len(self.all_obstacle_sprite_list) >= MAX_OBSTACLE_COUNT: break def pause_game(self): if not self.is_game_paused: self.is_game_paused = True for creature in self.all_creature_sprites_list: creature.pause_movement() def unpause_game(self): if self.is_game_paused: self.is_game_paused = False for creature in self.all_creature_sprites_list: creature.unpause_movement() def quit(self): self.save_scores() def check_collision_off_all_dynamic_elements(self): for creature in self.all_creature_sprites_list: collision_list = arcade.check_for_collision_with_list( creature, self.all_obstacle_sprite_list) if len(collision_list) > 0: if not creature.frozen: creature.center_x += creature.collision_radius * 0.1 creature.reverse_movement() if self.player.is_attacking is False: if (len( arcade.check_for_collision_with_list( self.player, self.all_obstacle_sprite_list)) > 0): if self.player.angle == RIGHT: self.player.stop() self.player.center_x += -self.player.collision_radius * 0.1 if self.player.angle == LEFT: self.player.stop() self.player.center_x += self.player.collision_radius * 0.1 if self.player.angle == UP: self.player.stop() self.player.center_y += -self.player.collision_radius * 0.1 if self.player.angle == DOWN: self.player.stop() self.player.center_y += self.player.collision_radius * 0.1
class Application: def __init__(self, title="", sWidth=1280, sHeight=720, bgC=color_rgb(25, 25, 25)): self.window = GraphWin(title, sWidth, sHeight) self.window.setBackground(bgC) self.gameState = GameState.MENU self.menu = MainMenu(self.window) self.game = Game(self.window, False) self.dt = 0.0 #DeltaTime self.lastFrameTime = 0.0 self.setState(self.gameState) def quit(self): self.window.flush() self.window.close() def undraw(self): if self.gameState == GameState.MENU: self.menu.undraw() elif self.gameState == GameState.GAME: self.game.undraw() else: self.menu.undraw() self.game.undraw() def run(self): while not self.window.isClosed(): #Calculate deltatime currentTime = time.time() self.dt = currentTime - self.lastFrameTime self.lastFrameTime = currentTime self.update(self.dt) if not self.window.isClosed(): self.window.flush() #If the close key is pressed, close window, for debugging if input.kClose(): quit() def update(self, dt): state = GameState.NONE if self.gameState == GameState.MENU: state = self.menu.update(self.window) elif self.gameState == GameState.GAME: state = self.game.update(dt, self.window) if not state == GameState.NONE: if state == GameState.QUIT: quit() else: self.setState(state) def setState(self, state=GameState.NONE): if state == GameState.NONE: self.setState(GameState.MENU) else: if state == GameState.GAMEONLINE: #Failsafe since its not yet implemented print("Game Online Unavailable") print("Reverting to GameState.GAME") self.setState(GameState.GAME) else: self.undraw() if state == GameState.GAME: self.game.draw(self.window) elif state == GameState.MENU: self.menu.draw(self.window) self.gameState = state
class Game: def __init__(self): # initialize the pygame module pygame.init() logo_name = os.path.join('assets', "logo.png") logo = pygame.image.load(logo_name) pygame.display.set_icon(logo) pygame.display.set_caption("Escape") self.screen = pygame.display.set_mode(SCREEN_SIZE) self.black_surface = self.screen.copy() self.black_surface.fill(pygame.Color("black")) # default font used for the timer self.font = pygame.font.Font(None, 100) self.subtext_font = pygame.font.Font(None, 25) self.notification_font = pygame.font.Font(None, 25) # specifies the middle of the screen self.character = Character("avatar/MC-front.png", 0, 0) self.camera = Coords(CHARACTER_START[X], CHARACTER_START[Y]) # saves the camera for when enering the building self.camera_save = None self.user_input = UserInput() # sound self.ambient = pygame.mixer.Sound("assets/ambient.wav") self.ambient_channel = pygame.mixer.Channel(1) self.wind = pygame.mixer.Sound("assets/wind.wav") self.wind.play(-1) # a shit ton of time related stuff self.clock = pygame.time.Clock() self.countdown = Countdown() self.ticker = Ticker() self.fade_in_timer = Timer() self.display_timer = Timer() self.notification_timer = Timer() self.fade_out_timer = Timer() # variables for when you're in some building self.current_building = None self.buildings = create_buildings() self.well_area = ScaledRect(208, 354, 60, 51) # define a variable to control the main loop # where it is really only set to false if the player exits or the X button is pressed self.running = True self.state = None self.fade_into_main_menu() self.wins = create_wins() self.subtext_value = "" self.notification_value = "" # self.ended = False # self.show_ending = False self.end_image = None self.begin_image = load_image("beginlol.png", use_scale=False, return_rect=False) self.begin_image = pygame.transform.scale(self.begin_image, SCREEN_SIZE) self.default_background = load_image('background.png', return_rect=False) self.building_wall_mask = load_image('background_outline.png', convert_alpha=True, return_rect=False) # background can change to default background or house background self.background = self.default_background # creates the start menus self.pause_menu = PauseMenu(self.screen) self.main_menu = MainMenu(self.screen) self.temp = [] @property def in_building(self): return self.current_building is not None def play(self): while self.running: if DEBUG: print(STATE_DICT[self.state]) self.handle_event() self.update() self.draw() self.clock.tick(60) if DEBUG: print("debug: temp =", self.temp) print("collected {}".format(self.character.items)) def handle_event(self): self.user_input.update() self.ticker.update() # can only pause when you're in the f*****g game matey lmao if self.state == GAME_PLAY and self.user_input.clicked_pause( self.ticker.tick): self.pause() elif self.state == GAME_PAUSE: if self.user_input.clicked_mouse(): pos = pygame.mouse.get_pos() if self.pause_menu.resume_button.border.collidepoint(pos): self.unpause() if self.pause_menu.exit_button.border.collidepoint(pos): self.running = False if self.user_input.clicked_pause(self.ticker.tick): self.unpause() # checks for main menu clicks if self.state == MAIN_MENU: if self.user_input.clicked_mouse(): pos = pygame.mouse.get_pos() if self.main_menu.play_button.border.collidepoint(pos): self.fade_outof_main_menu() if self.main_menu.exit_button.border.collidepoint(pos): self.running = False return if self.user_input.clicked_interact(self.ticker.tick): self.fade_outof_main_menu() # only do something if the event is of type QUIT if self.user_input.clicked_quit(): # change the value to False, to exit the main loop self.running = False return # if the player presses interact and it's during one of the display screens if self.user_input.clicked_interact(self.ticker.tick): if BEGIN_FADE_IN <= self.state <= BEGIN_FADE_OUT: self.start_game_fade_in() if END_FADE_IN <= self.state <= END_FADE_OUT: self.fade_into_main_menu() if self.state in STATES_DISPLAY: if self.display_timer.ended: if self.state == BEGIN_DISPLAY: # begin display -> fade out begin display self.state = BEGIN_FADE_OUT self.fade_out_timer.start(BEGIN_FADE_OUT_TIME) elif self.state == END_DISPLAY: # end display -> fade out end display self.state = END_FADE_OUT self.fade_out_timer.start(END_FADE_OUT_TIME) if self.state in STATES_FADE_IN: if self.fade_in_timer.ended: if self.state == MAIN_MENU_FADE_IN: # menu fade in to main menu self.to_main_menu() elif self.state == BEGIN_FADE_IN: # end instruction fade in self.state = BEGIN_DISPLAY self.display_timer.start(BEGIN_DISPLAY_TIME) elif self.state == GAME_FADE_IN: # starts the game here (end fade in) self.countdown.start() self.ambient_channel.play(self.ambient) self.state = GAME_PLAY elif self.state == END_FADE_IN: # end the ending fade in self.state = END_DISPLAY self.display_timer.start(END_DISPLAY_TIME) if self.state in STATES_FADE_OUT: if self.fade_out_timer.ended: if self.state == MAIN_MENU_FADE_OUT: # menu fade out -> begin fade in self.state = BEGIN_FADE_IN self.fade_in_timer.start(BEGIN_FADE_IN_TIME) elif self.state == BEGIN_FADE_OUT: # begin fade out -> game fade in self.start_game_fade_in() elif self.state == GAME_FADE_OUT: # already ended the game, shows display self.state = END_FADE_IN self.fade_in_timer.start(END_FADE_IN_TIME) elif self.state == END_FADE_OUT: self.fade_into_main_menu() # countdown ends: game ends if self.state == GAME_PLAY and self.countdown.ended: self.end() def update(self): if self.state == GAME_PLAY: self.countdown.update() velocity = self.user_input.get_velocity() self.camera.store_previous() self.camera += velocity if DEBUG: coords = self.character.get_pixel_at_feet(self.camera) print(coords) if self.user_input.clicked_debug(self.ticker.tick): self.temp.append(coords) self.character.update(velocity, self.ticker.tick) if not self.in_building: # detects collision with walls and buildings pixel = (self.building_wall_mask.get_at( tuple(self.character.get_pixel_at_feet(self.camera)))) if pixel[3] > ALPHA_THRESHOLD: # TODO make less sticky if possible if COLLIDES: # checks for each x and y if they can work current_x = self.camera.x current_y = self.camera.y self.camera.x = self.camera.previous_x self.camera.y = self.camera.previous_y coords = Coords(current_x, current_y) coords.x = self.camera.previous_x # gets the new y coordinate and keeps x constant pixel = (self.building_wall_mask.get_at( tuple(self.character.get_pixel_at_feet(coords)))) if not (pixel[3] > ALPHA_THRESHOLD ): # if the new y coordinate is free self.camera.y = coords.y coords = Coords(current_x, current_y) coords.y = self.camera.previous_y pixel = (self.building_wall_mask.get_at( tuple(self.character.get_pixel_at_feet(coords)))) if not pixel[3] > ALPHA_THRESHOLD: self.camera.x = coords.x # checks for collecting water at the well if (self.well_area.collide(self.camera, self.character.proper_size) and self.character.items["water_skin"] >= 1): self.subtext_value = "Press {} to fill your water skin".format( INTERACT_KEY) if self.user_input.clicked_interact(self.ticker.tick): self.character.items["water_skin"] -= 1 self.character.items["filled_water_skin"] += 1 self.notification_value = "Filled your water skin" self.notification_timer.start(NOTIFICATION_TIME) # checks for the building shit for building in self.buildings: if building.enters(self.character, self.camera): self.subtext_value = "Press {} to enter ".format( INTERACT_KEY) + building.name if self.user_input.clicked_interact(self.ticker.tick): self.notification_value = "Entered " + building.name self.notification_timer.start(NOTIFICATION_TIME) self.current_building = building self.background = building.background self.camera_save = self.camera.copy() # resets the camera building_exit_coords = building.camera_exit_coords building_exit_coords.y -= self.character.rect.height // 2 self.camera = building_exit_coords # collides with any win for win in self.wins: if win.collide(self.camera, self.character.get_rect_at_feet()): # set player to escaped self.character.escaped = True # ambient fades out only here self.ambient_channel.fadeout(AMBIENT_FADE_OUT) self.end() else: # detects collision with the building walls by checking # if the player leaves the given rect coords = tuple(self.character.get_pixel_at_feet(self.camera)) if not self.current_building.walls.rect.collidepoint(coords): # TODO make less sticky if possible if COLLIDES: # checks for each x and y if they can work current_x = self.camera.x current_y = self.camera.y self.camera.x = self.camera.previous_x self.camera.y = self.camera.previous_y coords2 = Coords(current_x, current_y) coords2.x = self.camera.previous_x # gets the new y coordinate and keeps x constant if self.current_building.walls.rect.collidepoint( tuple(self.character.get_pixel_at_feet( coords2))): self.camera.y = coords2.y coords2 = Coords(current_x, current_y) coords2.y = self.camera.previous_y if self.current_building.walls.rect.collidepoint( tuple(self.character.get_pixel_at_feet( coords2))): self.camera.x = coords2.x # detects collision for furniture for furniture in self.current_building.furniture.values(): if furniture.rect.collidepoint(coords): # TODO make less sticky if possible if COLLIDES: # checks for each x and y if they can work current_x = self.camera.x current_y = self.camera.y self.camera.x = self.camera.previous_x self.camera.y = self.camera.previous_y coords2 = Coords(current_x, current_y) coords2.x = self.camera.previous_x # gets the new y coordinate and keeps x constant if not furniture.rect.collidepoint( tuple( self.character.get_pixel_at_feet( coords2))): self.camera.y = coords2.y coords2 = Coords(current_x, current_y) coords2.y = self.camera.previous_y if not furniture.rect.collidepoint( tuple( self.character.get_pixel_at_feet( coords2))): self.camera.x = coords2.x # checks for collectibles for collectible in self.current_building.collectibles: if collectible.collides(self.character, self.camera): self.subtext_value = "Collect " + collectible.display_name if self.user_input.clicked_interact(self.ticker.tick): collectible.pick_up() self.notification_value = "Collected " + collectible.display_name self.notification_timer.start(NOTIFICATION_TIME) self.character.items[collectible.type] += 1 #self.character.points += collectible.points #self.character.weight += collectible.weight # checks whether they have left the building if self.current_building.exit_area.collide( self.camera, self.character.get_rect_at_feet()): self.subtext_value = "Press {} to leave ".format( INTERACT_KEY) + self.current_building.name if self.user_input.clicked_interact(self.ticker.tick): self.notification_value = "Left " + self.current_building.name self.notification_timer.start(NOTIFICATION_TIME) self.camera = self.camera_save self.current_building = None self.background = self.default_background def draw(self): self.screen.fill((0, 0, 0)) # draws the background relative to the character and countdown if GAME_FADE_IN <= self.state <= GAME_FADE_OUT: self.screen.blit(self.background, (SCREEN_SIZE[X] // 2 - self.camera.x, SCREEN_SIZE[Y] // 2 - self.camera.y)) self.countdown.draw(self.screen, self.font) if not self.in_building: if DEBUG: for building in self.buildings: building.debug_draw_position(self.screen, self.camera) for win in self.wins: win.debug_draw(self.camera, self.screen) self.well_area.debug_draw(self.camera, self.screen, "pink") else: # in building if DEBUG: self.current_building.debug_draw_inner( self.screen, self.camera) # draws all collectibles within a building self.current_building.collectibles.draw( self.screen, self.camera) # draws character at the last lmao self.character.draw(self.screen) if DEBUG: self.character.draw_pixel(self.screen, self.camera) if self.state == GAME_PAUSE: self.pause_menu.draw(self.screen) # renders the display name on the bottom right of the screen if self.subtext_value: text_render = self.subtext_font.render(self.subtext_value, True, pygame.Color("orange")) # text_render = pygame.transform.scale(text_render, (text_render.get_width()*3, text_render.get_height()*3)) text_pos = text_render.get_rect() text_pos.bottomleft = self.screen.get_rect().bottomleft self.screen.blit(text_render, text_pos) self.subtext_value = "" if self.notification_value: if self.notification_timer.ended: self.notification_value = "" else: text_render = self.notification_font.render( self.notification_value, True, pygame.Color("orange")) # text_render = pygame.transform.scale(text_render, (text_render.get_width()*3, text_render.get_height()*3)) text_pos = text_render.get_rect() text_pos.bottomleft = self.screen.get_rect().bottomleft text_pos.y -= 25 self.screen.blit(text_render, text_pos) # displays the beginning image if BEGIN_FADE_IN <= self.state <= BEGIN_FADE_OUT: self.screen.blit(self.begin_image, (0, 0)) # displays the ending image if END_FADE_IN <= self.state <= END_FADE_OUT: self.screen.blit(self.end_image, (0, 0)) # displays the main menu if self.state in (MAIN_MENU_FADE_IN, MAIN_MENU, MAIN_MENU_FADE_OUT): self.main_menu.draw(self.screen) if self.state in STATES_FADE_IN or self.state in STATES_FADE_OUT: if self.state in STATES_FADE_IN: # goes from black to normal alpha_value = int(255 * (self.fade_in_timer.get())) else: # goes from normal to black alpha_value = int(255 - 255 * (self.fade_out_timer.get())) self.black_surface.set_alpha(alpha_value) self.screen.blit(self.black_surface, (0, 0)) pygame.display.flip() def start_game_fade_in(self): self.state = GAME_FADE_IN self.fade_in_timer.start(GAME_FADE_IN_TIME) def pause(self): #pygame.mixer.pause() self.ambient_channel.pause() self.countdown.pause() self.state = GAME_PAUSE def unpause(self): #pygame.mixer.unpause() self.ambient_channel.unpause() self.countdown.unpause() self.state = GAME_PLAY def fade_into_main_menu(self): # does the majority of resetting here if necessary # fades out if there's still sound for some reason self.countdown.start() self.countdown.pause() self.ambient_channel.fadeout(2) self.character.reset() for building in self.buildings: for collectible in building.collectibles: collectible.reset() self.camera = Coords(CHARACTER_START[X], CHARACTER_START[Y]) self.fade_in_timer.start(MAIN_MENU_FADE_IN_TIME) self.state = MAIN_MENU_FADE_IN def to_main_menu(self): self.state = MAIN_MENU def fade_outof_main_menu(self): self.state = MAIN_MENU_FADE_OUT self.fade_out_timer.start(MAIN_MENU_FADE_OUT_TIME) def end(self): if not self.countdown.ended: self.countdown.pause() self.state = GAME_FADE_OUT self.fade_out_timer.start(GAME_FADE_OUT_TIME) # gets the end image if not self.character.escaped: self.end_image = load_image("endings/stay_death.png", return_rect=False) elif sum(self.character.items.values()) <= 2: self.end_image = load_image("endings/missing_lots_death.png", return_rect=False) elif self.character.has_no_items("filled_water_skin"): self.end_image = load_image("endings/thirst_death.png", return_rect=False) elif self.character.has_no_items("ugly_green_scarf"): self.end_image = load_image("endings/cold_death.png", return_rect=False) elif self.character.has_no_items("bread", "cheese", "jerky"): self.end_image = load_image("endings/starvation_death.png", return_rect=False) elif self.character.has_no_items("bandages", "bow_arrow", "cheese"): self.end_image = load_image("endings/bandits_death.png", return_rect=False) elif self.character.has_no_items("pileogold"): self.end_image = load_image("endings/no_money_death.png", return_rect=False) else: self.end_image = load_image("endings/win_happy.png", return_rect=False) self.end_image = pygame.transform.scale(self.end_image, SCREEN_SIZE) if DEBUG: print("ended!")
class GameLoop: def __init__(self, option_handler): self.option_handler = option_handler self.state = State.MENU self.level = Level() self.players = dict() self.colliders = [] self.time_scale = 1.0 self.camera = Camera([0, 0], self.option_handler.resolution) self.respawn_time = 50.0 self.menu = MainMenu() self.player_menus = [ PlayerMenu((6 * i - 9) * basis(0)) for i in range(4) ] self.options_menu = OptionsMenu() self.options_menu.set_values(self.option_handler) self.network = None self.network_id = -1 self.obj_id = -1 self.controller_id = 0 def load_level(self): self.level = Level('lvl') size = [ int(self.camera.zoom * self.level.width), int(self.camera.zoom * self.level.height) ] self.level.background = pygame.Surface(size) self.level.background.fill((150, 150, 150)) self.colliders.clear() self.colliders = [[[] for _ in range(int(self.level.height))] for _ in range(int(self.level.width))] for wall in self.level.walls: wall.collider.update_occupied_squares(self.colliders) for obj in self.level.objects.values(): obj.collider.update_occupied_squares(self.colliders) def reset_game(self): for w in self.level.walls: if type(w) is Basket: self.level.scoreboard.scores[w.team] = w.score for o in self.level.objects.values(): if o.collider: o.collider.clear_occupied_squares(self.colliders) self.level.reset() for o in self.level.objects.values(): o.collider.update_occupied_squares(self.colliders) for p in self.players.values(): p.set_spawn(self.level, self.players) p.reset(self.colliders) def add_player(self, controller_id, network_id=-1): if network_id == -1: network_id = controller_id player = Player([0, 0], controller_id, network_id) self.players[network_id] = player def update(self, time_step): if self.state is State.PLAY: alive = 0 for player in self.players.values(): if player.active: alive += 1 player.update(self.level.gravity, self.time_scale * time_step, self.colliders) if len(self.players) > 1 >= alive: self.reset_game() self.level.update(self.time_scale * time_step, self.colliders) for o in list(self.level.objects.values()): if type(o) is Ball and o.scored and o.speed < 0.1: self.reset_game() self.camera.update(self.time_scale * time_step, self.players, self.level) elif self.state is State.MENU: self.menu.update(time_step) self.state = self.menu.target_state self.menu.target_state = State.MENU if self.state is State.OPTIONS: self.option_handler.load() self.options_menu.set_values(self.option_handler) elif self.state is State.PLAYER_SELECT: if not self.players: return for pm in self.player_menus: if pm.controller_id is not None: # FIXME: hardcoded index self.players[pm.controller_id].body_type = pm.buttons[ 1].get_value() self.players[pm.controller_id].head_type = pm.buttons[ 0].get_value() for pm in self.player_menus: if pm.controller_id is None: if pm.controller_id in self.players: del self.players[pm.controller_id] if pm.target_state is State.MENU: pm.target_state = State.PLAYER_SELECT self.state = State.MENU self.players.clear() return if pm.controller_id is not None and pm.target_state is State.PLAYER_SELECT: return self.state = State.PLAY self.load_level() for p in self.players.values(): p.set_spawn(self.level, self.players) p.reset(self.colliders) elif self.state is State.LAN: if self.network is None: self.network = Network() data = self.network.data if data is None: print('Server can not be reached') self.network = None self.state = State.MENU return self.network_id = data[0][0] self.add_player(self.controller_id, data[0][0]) self.players[data[0][0]].apply_data(data[0]) self.level.clear() self.level.apply_data(data[1]) self.colliders = [[[] for _ in range(int(self.level.height + 1))] for _ in range(int(self.level.width + 1))] for wall in self.level.walls: wall.collider.update_occupied_squares(self.colliders) for obj in self.level.objects.values(): obj.collider.update_occupied_squares(self.colliders) start_new_thread(self.network_thread, ()) player = self.players[self.network_id] player.update(self.level.gravity, self.time_scale * time_step, self.colliders) obj = self.players[self.network_id].object if obj is not None: obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) self.camera.update(time_step, self.players, self.level) for i in list(self.level.objects): obj = self.level.objects[i] if isinstance(obj, Destroyable): obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) if obj.destroyed and not obj.debris: del self.level.objects[i] elif isinstance(obj, Bullet): obj.update(self.level.gravity, self.time_scale * time_step, self.colliders) if obj.destroyed and not obj.particle_clouds: del self.level.objects[i] elif self.state is State.OPTIONS: self.state = self.options_menu.target_state self.options_menu.target_state = State.OPTIONS if self.options_menu.options_changed: if self.options_menu.buttons[0].get_value() == 'windowed': pygame.display.set_mode( self.options_menu.buttons[1].get_value()) self.option_handler.fullscreen = False else: pygame.display.set_mode( self.options_menu.buttons[1].get_value(), pygame.FULLSCREEN) self.option_handler.fullscreen = True self.camera.set_resolution( self.options_menu.buttons[1].get_value()) self.option_handler.resolution = self.options_menu.buttons[ 1].get_value() self.option_handler.shadows = self.options_menu.buttons[ 4].get_value() == 'ON' self.option_handler.save() self.options_menu.options_changed = False def network_thread(self): while True: data = [self.players[self.network_id].get_data()] if self.obj_id != -1: data.append(self.level.objects[self.obj_id].get_data()) self.level.objects[self.obj_id].attacked = False data = self.network.send(data) for p in data[0]: if p[0] == self.network_id: player = self.players[self.network_id] if player.health <= 0 < p[9]: player.set_spawn(self.level, self.players) player.reset(self.colliders) player.health = p[9] else: if p[0] not in self.players: self.add_player(-1, p[0]) self.players[p[0]].apply_data(p) # kinda purkka ids = [p[0] for p in data[0]] for k in list(self.players.keys()): if k != self.network_id and k not in ids: del self.players[k] for d in data[1]: if d[0] == self.obj_id: continue if d[0] in self.level.objects: self.level.objects[d[0]].apply_data(d) else: obj = d[1]([d[2], d[3]]) obj.apply_data(d) self.level.objects[d[0]] = obj self.colliders[obj.collider.group].append(obj.collider) ids = [o[0] for o in data[1]] for i in list(self.level.objects): obj = self.level.objects[i] obj.collider.update_occupied_squares(self.colliders) if i not in ids: if isinstance(obj, Destroyable): obj.destroy(self.colliders) elif isinstance(obj, Bullet): obj.destroy() def input(self, input_handler): input_handler.update(self.camera) if input_handler.quit: self.state = State.QUIT if self.state is State.PLAY: if 0 in self.players: input_handler.relative_mouse[:] = input_handler.mouse_position - self.players[ 0].shoulder for i, player in enumerate(self.players.values()): player.input(input_handler) if input_handler.keys_pressed[pygame.K_r]: self.reset_game() elif self.state is State.MENU: self.menu.input(input_handler) for i in range(len(input_handler.controllers)): if self.menu.selection_moved[i]: self.controller_id = i break elif self.state is State.PLAYER_SELECT: for i, controller in enumerate(input_handler.controllers): for pm in self.player_menus: if pm.controller_id == i: pm.input(input_handler, i) break else: for pm in self.player_menus: if pm.controller_id is None: pm.input(input_handler, i) if pm.controller_id == i: self.add_player(i) break if all(pm.target_state is State.PLAYER_SELECT for pm in self.player_menus): for i, controller in enumerate(input_handler.controllers): if controller.button_down['B']: self.state = State.MENU elif self.state is State.LAN: if self.network is not None: player = self.players[self.network_id] input_handler.relative_mouse[:] = input_handler.mouse_position - player.shoulder self.obj_id = player.object.id if player.object is not None else -1 player.input(input_handler) elif self.state is State.OPTIONS: self.options_menu.input(input_handler) def draw(self, screen, image_handler): if self.state in [State.PLAY, State.LAN]: screen.fill((0, 0, 0)) screen.blit( self.level.background, self.camera.world_to_screen(np.array([0, self.level.height]))) if self.option_handler.shadows: self.level.draw_shadow(screen, self.camera, image_handler) for p in self.players.values(): p.draw_shadow(screen, self.camera, image_handler, self.level.light) self.level.draw(screen, self.camera, image_handler) for player in self.players.values(): player.draw(screen, self.camera, image_handler) if self.option_handler.debug_draw: self.debug_draw(screen, image_handler) elif self.state is State.MENU: screen.fill((50, 50, 50)) self.menu.draw(screen, self.camera, image_handler) elif self.state is State.PLAYER_SELECT: screen.fill((50, 50, 50)) for pm in self.player_menus: pm.draw(screen, self.camera, image_handler) if pm.controller_id is not None: self.players[pm.controller_id].set_position(pm.position + 3 * basis(1)) self.players[pm.controller_id].on_ground = True self.players[pm.controller_id].animate(0.0) self.players[pm.controller_id].draw( screen, self.camera, image_handler) elif self.state is State.OPTIONS: screen.fill((50, 50, 50)) self.options_menu.draw(screen, self.camera, image_handler) def debug_draw(self, screen, image_handler): for player in self.players.values(): player.debug_draw(screen, self.camera, image_handler) self.level.debug_draw(screen, self.camera, image_handler) def play_sounds(self, sound_handler): if self.state in [State.PLAY, State.LAN]: for p in self.players.values(): p.play_sounds(sound_handler) self.level.play_sounds(sound_handler) elif self.state is State.OPTIONS: sound_handler.set_volume(self.options_menu.buttons[2].get_value()) sound_handler.set_music_volume( self.options_menu.buttons[3].get_value()) self.option_handler.sfx_volume = self.options_menu.buttons[ 2].get_value() self.option_handler.music_volume = self.options_menu.buttons[ 3].get_value() self.option_handler.save() self.menu.play_sounds(sound_handler) self.options_menu.play_sounds(sound_handler) else: self.menu.play_sounds(sound_handler) self.options_menu.play_sounds(sound_handler) for pm in self.player_menus: pm.play_sounds(sound_handler)