Exemplo n.º 1
0
class Stage:
    def __init__(self, window_size: Size, window_title: str, stage_size: Size,
                 stage_colors: Tuple[Color, Color], clock_font: Font,
                 clock_font_color: Color, text_box_font: Font):
        Rectangle.set_window(window_size, window_title)
        self.__width = stage_size.width
        self.__height = stage_size.height
        self.__stage_color = stage_colors[0]
        self.__walls_color = stage_colors[1]

        self.__window_width = Constants.WINDOW_WIDTH
        self.__window_height = Constants.WINDOW_HEIGHT
        self.__wall_width = (self.__window_width - self.__width) / 2
        self.__wall_height = (self.__window_height - self.__height) / 2

        self.__walls, self.__stage = self.__initialize_stage()
        self.__text_boxes = self.__initialize_text_boxes(text_box_font)

        clock_pos = Point(self.__width + self.__wall_width + 1,
                          self.__height + self.__wall_height)
        self.__clock = Clock(clock_pos, self.__stage_color, self.__walls_color,
                             clock_font, clock_font_color)

    # Initializes the stage and returns its walls and stage.
    def __initialize_stage(self) -> Tuple[List[Rectangle], Rectangle]:
        wall_rects = [
            PointSize(0, 0, self.__wall_width, self.__window_height),
            PointSize(0, 0, self.__window_width, self.__wall_height),
            PointSize(self.__wall_width + self.__width, 0, self.__wall_width,
                      self.__window_height),
            PointSize(0, self.__wall_height + self.__height,
                      self.__window_width, self.__wall_height)
        ]
        stage_rect = PointSize(self.__wall_width, self.__wall_height,
                               self.__width, self.__height)

        walls = list()
        for wall in wall_rects:
            walls.append(
                Rectangle(wall, self.__walls_color, self.__stage_color))
        stage = Rectangle(stage_rect, self.__stage_color, self.__walls_color)
        return walls, stage

    # Initializes the text boxes. This part is partly hard_coded.
    def __initialize_text_boxes(self, font: Font) -> List[TextBox.TextBox]:
        text_boxes = list()

        colors = (self.__stage_color, self.__walls_color)
        position = Point(self.__width + (self.__wall_width) + 10,
                         self.__wall_height)
        separations = (5, 10)
        per_row = 2
        is_input = Constants.TEXTBOX_MATRIX_IS_INPUT
        data = Constants.TEXTBOX_MATRIX
        text_boxes = TextBox.create_matrix(position, colors, separations,
                                           per_row, is_input, data, font)
        position = Point(5, self.__wall_height)
        is_input = Constants.INSTRUCTIONS_INPUT
        data = Constants.INSTRUCTIONS_TEXTBOXES
        text_boxes += TextBox.create_matrix(position, colors, separations,
                                            per_row, is_input, data, font)
        return text_boxes

    # Returns the index of the box with the desired name.
    def __box_index(self, name: str) -> int:
        for i in range(len(self.__text_boxes)):
            if self.__text_boxes[i].get_name() == name:
                return i
        # Should never reach this case.
        return -1

    # Returns the walls.
    def get_walls(self) -> List[Rectangle]:
        return self.__walls

    # Returns the stage.
    def get_stage(self) -> Rectangle:
        return self.__stage

    # Returns the stage color.
    def get_stage_color(self) -> Color:
        return self.__stage_color

    # Returns the wall color.
    def get_walls_color(self) -> Color:
        return self.__walls_color

    # Returns the Stage limits as in: x_min, y_min, x_max, y_max
    def get_stage_limits(self) -> Limits:
        return self.__stage.get_limits()

    # Gets the TTL in seconds.
    def get_ttl_seconds(self) -> int:
        return self.__clock.get_ttl() / 1000

    def get_fps(self) -> int:
        return self.__clock.get_fps()

    # Draws all the text boxes.
    def draw_input_boxes(self):
        for box in self.__text_boxes:
            if box.is_input():
                box.draw()

    # Returns True if it's under its Time To Live, otherwise False.
    def update_clock(self):
        self.__clock.update_clock()
        self.__clock.draw()
        return self.__clock.still_valid()

    # Return the value of each text_box on a list.
    def get_text_values(self) -> Dict[str, int]:
        return_values = dict()
        for text_box in self.__text_boxes:
            if text_box.is_input():
                key_value = text_box.get_name_value()
                return_values[key_value[0]] = key_value[1]
        return return_values

    # Returns the closest wall to the object, its direction towards it,
    # and the distance to travel.
    def closest_wall_to(self,
                        a: Rectangle) -> Tuple[Rectangle, Direction, int]:
        selected_wall, distance = Distances.closest_of_all_Linf(
            a, self.__walls)
        direction = Distances.cardinal_system_direction(a, selected_wall)
        return selected_wall, direction, distance

    # Resets the clock back to 0.
    def reset_clock(self):
        self.__clock.reset()

    # Sets the new TTL.
    def set_ttl_seconds(self, ttl: int):
        self.__clock.set_ttl(ttl * 1000)

    # Sets the new FPS.
    def set_fps(self, fps: int):
        self.__clock.set_fps(fps)

    # Handle the events for each text box.
    def handle_event(self, event: pygame.event):
        for text_box in self.__text_boxes:
            text_box.handle_event(event)

    # Handles the in-game updates.
    def handle_in_game(self, key_value: Dict[str, int]) -> bool:
        for key, value in key_value.items():
            self.__text_boxes[self.__box_index(key)].write(str(value))
        self.draw_input_boxes()
        return self.update_clock()

    # Handles the updates necessary for the new round.
    def new_round_stage(self, key_value: Dict[str, int]):
        self.reset_clock()
        self.handle_in_game(key_value)

    # Changes the boxes that won't be updated anymore to output only, and those
    # that will be to input. The naming is weird, but input are the only ones
    # that get updated.
    def initialize_game(self):
        for box in self.__text_boxes:
            if box.has_name():
                if box.get_name() != Constants.INITIAL_CHARACTERS \
                        and box.get_name() != Constants.INITIAL_FOODS:
                    box.change_type()

    # Changes the boxes that won't be updated anymore to output only, and those
    # that will be to input. The naming is weird, but input are the only ones
    # that get updated.
    def continue_game(self):
        for box in self.__text_boxes:
            if box.has_name():
                if box.get_name() != Constants.TTL \
                        and box.get_name() != Constants.FPS:
                    box.change_type()
        self.__clock.reset()
Exemplo n.º 2
0
class Game:
    WINDOW_WIDTH = 1500
    WINDOW_HEIGHT = 750
    GROUND = WINDOW_HEIGHT
    DT = 0.01

    def __init__(self,
                 c,
                 is_last_level=False,
                 time_this=False,
                 start_time=time.time()):
        self.time_this = time_this
        self.start_time = start_time
        self.jump_sound = None
        self.is_last_level = is_last_level
        self.SPEED_OF_LIGHT = c  # 80 -> 8 -> 5.5
        pygame.init()
        pygame.font.init()
        pygame.display.set_caption("Relativity Man")
        self.screen = pygame.display.set_mode(
            (self.WINDOW_WIDTH, self.WINDOW_HEIGHT), pygame.FULLSCREEN)

        self.player = Player(150, Game.GROUND - 300)
        thickness = 25
        self.platforms = (
            Platform(self.GROUND - 100, 100, 1600, 2 * thickness),
            Platform(self.GROUND - 200, 200, 200, thickness),
            Platform(self.GROUND - 500, 500, 200, thickness),
            Platform(self.GROUND - 400, 300, 200, thickness),
            Platform(self.GROUND - 300, 700, 200, thickness),
        )
        self.clock_rel = Clock(50, 100, 200, 100, self.SPEED_OF_LIGHT)
        self.clock = Clock(160, 100, 200, 100, self.SPEED_OF_LIGHT)
        self.keys = {}  # Automatically initialized in handle user input.

        self.ball = Ball(50, 50, 0)

    def main_loop(self):
        clock = pygame.time.Clock()

        prize = Nobel_Prize(550, 100)
        myfont = pygame.font.SysFont('Comic Sans MS', 30)
        if not self.jump_sound:
            self.jump_sound = pygame.mixer.Sound('assets/jump.wav')
        while True:
            try:
                self.clear_scene()

                prize.draw(self.screen)

                if not prize.hidden:
                    if prize.in_prize(self.player.x, self.player.y):
                        prize.hidden = True
                        prize_sound = pygame.mixer.Sound('assets/powerup.wav')
                        prize_sound.play()
                else:
                    textsurface = myfont.render(
                        "Congratulations! You got Einstein a Nobel Prize!",
                        False, (100, 100, 100))
                    self.screen.blit(textsurface,
                                     (self.WINDOW_WIDTH / 2.0 - 200, 50))
                    prize.time_held_for += 1
                    if prize.time_held_for > 200:
                        if not self.is_last_level:
                            return 'Next Level'
                        else:
                            prize.hidden = False
                            prize.time_held_for = -1

                textsurface = myfont.render("Einstein", False, (100, 100, 100))
                self.screen.blit(textsurface, (0, 200))
                textsurface = myfont.render("Lab", False, (100, 100, 100))
                self.screen.blit(textsurface, (130, 200))

                if self.time_this:
                    textsurface = myfont.render(
                        "Time: {0:.2f}".format(
                            (time.time() - self.start_time)), False,
                        (100, 100, 100))
                    self.screen.blit(
                        textsurface,
                        (Game.WINDOW_WIDTH * 0.9, Game.WINDOW_HEIGHT * 0.8))

                self.player.draw(self.screen)
                for platform in self.platforms:
                    platform.draw(self.screen)
                self.clock.draw(self.screen)
                self.clock_rel.draw(self.screen)

                clock_rel_copy = copy.deepcopy(self.clock_rel)
                clock_rel_copy.x = self.player.x
                clock_rel_copy.y = self.player.y - self.player.height - 50
                clock_rel_copy.draw(self.screen)

                vx2_c2 = self.player.vx**2 / self.SPEED_OF_LIGHT**2
                vy2_c2 = self.player.vy**2 / self.SPEED_OF_LIGHT**2
                v2_c2 = vx2_c2 + vy2_c2
                theta = atan2(self.player.vy, self.player.vx)
                gamma = 1.0 / sqrt(1 - v2_c2)
                lorentz_y = gamma**1 * cos(theta)**2 + gamma**3 * sin(theta)**2
                lorentz_x = gamma**3 * cos(theta)**2 + gamma**1 * sin(theta)**2

                speed = (self.player.vx**2 +
                         self.player.vy**2)**0.5 / self.SPEED_OF_LIGHT * 100

                textsurface = myfont.render('v  = {:.2f}% c'.format(speed),
                                            False, (100, 100, 100))
                self.screen.blit(textsurface, (self.WINDOW_WIDTH - 200, 50))

                textsurface = myfont.render('m_x = {:.2f}'.format(lorentz_x),
                                            False, (100, 100, 100))
                self.screen.blit(textsurface, (self.WINDOW_WIDTH - 200, 100))

                textsurface = myfont.render('m_y = {:.2f}'.format(lorentz_y),
                                            False, (100, 100, 100))
                self.screen.blit(textsurface, (self.WINDOW_WIDTH - 200, 150))

                textsurface = myfont.render(
                    'Walking Speed = {:.1f}%c'.format(
                        self.player.walking_speed / self.SPEED_OF_LIGHT),
                    False, (100, 100, 100))
                self.screen.blit(textsurface, (300, 0))

                self.show_scene()

                clock.tick(60)
                for frames in range(300):
                    try:
                        if self.keys['space'].state:
                            if self.player.onGround:
                                # self.jump_sound.play()
                                pass
                    except:
                        pass
                    player_wants_to_return = self.update_key_states()
                    if player_wants_to_return:
                        return 'Menu'
                    self.handle_user_input()

                    self.player.update(self.DT, self.SPEED_OF_LIGHT)
                    self.player.handle_collisions(self.platforms)
                    self.clock.update(self.DT, 0, 0, self.SPEED_OF_LIGHT)
                    self.clock_rel.update(self.DT, self.player.vx,
                                          self.player.vy, self.SPEED_OF_LIGHT)

                    if self.player.is_dead(self.GROUND):
                        self.player = Player(150, Game.GROUND - 300)
            except ValueError:
                self.player.x = 150
                self.player.y = Game.GROUND - 300
                self.player.vx = 0
                self.player.vy = 0
                print(
                    "Crashed: [Slightly] Exceeded the speed of light (its a bug)."
                )

    def update_key_states(self):
        if using_joy_stick() and pygame.joystick.get_count():
            joystick = pygame.joystick.Joystick(0)
            joystick.init()
            jump_button = joystick.get_button(0)
            d_pressed = joystick.get_hat(0)[0] > 0
            a_pressed = joystick.get_hat(0)[0] < 0

            for char, key_object in self.keys.items():
                if char == 'space':
                    self.keys[char] = Key(key_object.py_key, jump_button)
                if char == 'a':
                    self.keys[char] = Key(key_object.py_key, a_pressed)
                if char == 'd':
                    self.keys[char] = Key(key_object.py_key, d_pressed)
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                pygame.quit()
                exit(0)
            if e.type == pygame.KEYDOWN:
                for char, key_object in self.keys.items():
                    if e.key == 8:
                        return True
                    if e.key == key_object.py_key:
                        self.keys[char] = Key(key_object.py_key, True)
            if e.type == pygame.KEYUP:
                for char, key_object in self.keys.items():
                    if e.key == key_object.py_key:
                        self.keys[char] = Key(key_object.py_key, False)

    def handle_user_input(self):
        """ To add a new key function, just add it in this if else block. It will
            be automatically added in the exception. Keys should be named according to:
            https://www.pygame.org/docs/ref/key.html under the 'Common Name' column. """
        try:
            if self.keys['a'].state:
                self.player.fx = -(self.player.walking_speed +
                                   self.player.vx / self.DT) * self.player.mass
            if self.keys['d'].state:
                self.player.fx = -(-self.player.walking_speed +
                                   self.player.vx / self.DT) * self.player.mass
            if self.keys['space'].state:
                if self.player.onGround:
                    self.jump_sound.play()
                    # self.player.vy = -self.player.jumping_speed
                    self.player.fy = -self.player.jumping_speed / 0.01
                    self.keys['space'].state = False

            if self.keys['escape'].state:
                pygame.quit()
                exit(0)
        except KeyError as e:
            ''' If it can't access a key (exception thrown), it will add the key automatically. '''
            invalid_key = str(e).strip("'")
            num_keys_possible = 133
            for i in range(num_keys_possible):
                if pygame.key.name(i) == invalid_key:
                    self.keys[invalid_key] = Key(i, False)
                    break

    @staticmethod
    def show_scene():
        pygame.display.flip()

    def clear_scene(self):
        self.screen.fill(0)