Пример #1
0
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"
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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!")
Пример #5
0
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)