Example #1
0
 def __FaceRight(self):
     image_bounding_rectangle = self.Sprite.image.get_rect()
     # The handle should be in the center in order to support easy rotation.
     self.HandleLocalImagePosition = Vector2(
         image_bounding_rectangle.centerx, image_bounding_rectangle.centery)
     # The tip should be at the right of the image.
     self.TipLocalImagePosition = Vector2(image_bounding_rectangle.right,
                                          image_bounding_rectangle.centery)
Example #2
0
    def __init__(self):
        # CREATE THE SPRITE FOR THE SWORD.
        ## The sprite (image) for the sword.
        self.Sprite = pygame.sprite.Sprite()

        # In order to handle transparency and rotation easily, a separate, larger surface
        # is needed onto which the actual sword image file is blitted to.
        self.Sprite.image = pygame.Surface(
            (Sword.IMAGE_DIMENSION_IN_PIXELS, Sword.IMAGE_DIMENSION_IN_PIXELS))
        # Color-keying is currently used for transparency due to its simplicity.
        self.Sprite.image.fill(Sword.TRANSPARENT_COLOR.value)
        self.Sprite.image.set_colorkey(Sword.TRANSPARENT_COLOR.value)
        # Since the sword image is smaller than the full surface, it's position when
        # blitted to the surface needs to be adjusted so that it will appear in
        # the center when swinging.  This exact offset is dependent on the exact
        # size and position of the sword within its image.
        SWORD_IMAGE_X_OFFSET_IN_PIXELS = 32
        SWORD_IMAGE_Y_OFFSET_IN_PIXELS = 16
        sword_image = pygame.image.load('../Images/Sword.gif').convert()
        self.Sprite.image.blit(
            sword_image,
            (SWORD_IMAGE_X_OFFSET_IN_PIXELS, SWORD_IMAGE_Y_OFFSET_IN_PIXELS))

        # INITIALIZE REMAINING MEMBER VARIABLES.
        ## The screen position of the sword's handle.
        ## Expected to be set to near the player's position anytime
        ## the sword starts being swung.
        self.__HandleScreenPosition = Vector2(0, 0)
        ## The local position of the handle within the image.
        ## Should be properly initialized whenever the sword starts being swung.
        self.HandleLocalImagePosition = Vector2(0, 0)
        ## The tip position of the handle within the image.
        ## Should be properly initialized whenever the sword starts being swung.
        self.TipLocalImagePosition = Vector2(0, 0)
        ## The current amount of rotation of the sword from its initial
        ## position when being swung.
        self.CurrentRotationAngleInDegrees = 0.0
        ## The destination rotation angle for the sword when it has finished
        ## being completely swung.
        self.DestinationRotationAngleInDegrees = 0.0
        ## True if the sword is currently being swung; false if not.
        self.IsSwinging = False
        ## The bounding rectangle for the sword.
        ## \todo    This is currently being set to a fix value each
        ## time the sword starts being swung.  It would be better to
        ## have this dynamically calculated to account for rotation
        ## (or perform more advanced line or rotated bounding box
        ## collision, but there's likely not time to get that working
        ## before the game's deadline).
        self.BoundingScreenRectangle = pygame.Rect(0, 0, 0, 0)
        ## The width of the sword in pixels.
        self.WidthInPixels = 1
        ## The color of the sword.
        self.Color = Color.Gray
Example #3
0
    def Render(self, screen):
        # ONLY RENDER THE SWORD IF IT'S BEING SWUNG.
        if not self.IsSwinging:
            return

        # DRAW THE A DEBUG LINE FOR THE SWORD IF ENABLED.
        DEBUG_DRAWING_ENABLED = False
        if DEBUG_DRAWING_ENABLED:
            pygame.draw.line(self.Sprite.image, self.Color.value,
                             self.HandleLocalImagePosition.AsXYTuple(),
                             self.TipLocalImagePosition.AsXYTuple(),
                             self.WidthInPixels)

        # ROTATE THE SWORD BASED ON HOW IT'S BEING SWUNG.
        rotated_sword_image = pygame.transform.rotate(
            self.Sprite.image, self.CurrentRotationAngleInDegrees)

        # ADJUST THE SWORD'S POSITION BASED ON ROTATION.
        # When an image is rotated, the actual size of the bounding rectangle increases to encompass
        # an axis-aligned box that encompasses all of the image.  To avoid having the sword appear
        # to change positions, the handle's position must be adjusted to account for the changing
        # size of the bounding rectangle.
        rotated_image_rect = rotated_sword_image.get_rect()
        original_image_rect = self.Sprite.image.get_rect()
        image_width_increase_in_pixels = rotated_image_rect.width - original_image_rect.width
        image_height_increase_in_pixels = rotated_image_rect.height - original_image_rect.height
        # A copy of the screen position is made to avoid altering the original position.
        handle_screen_position = Vector2(self.HandleScreenPosition.X,
                                         self.HandleScreenPosition.Y)
        # Due to this position being the center of rotation, it only needs to be adjusted by
        # half of the change in dimensions of the image's bounding box.
        handle_screen_position.X -= image_width_increase_in_pixels / 2
        handle_screen_position.Y -= image_height_increase_in_pixels / 2

        # DRAW THE SWORD ON THE SCREEN.
        screen.blit(rotated_sword_image, handle_screen_position.AsXYTuple())

        # DRAW DEBUG RECTANGLES IF DEBUGGING IS ENABLED.
        if DEBUG_DRAWING_ENABLED:
            # DRAW A DEBUG RECTANGLE FOR THE UNADJUSTED POSITION.
            DEBUG_RECTANGLE_OUTLINE_WIDTH_IN_PIXELS = 1
            unadjusted_debug_image_rect = rotated_image_rect.move(
                self.HandleScreenPosition.X, self.HandleScreenPosition.Y)
            pygame.draw.rect(screen, Color.FullGreen.value,
                             unadjusted_debug_image_rect,
                             DEBUG_RECTANGLE_OUTLINE_WIDTH_IN_PIXELS)

            # DRAW A DEBUG RECTANGLE FOR THE ADJUSTED POSITION.
            adjusted_debug_image_rect = rotated_image_rect.move(
                handle_screen_position.AsXYTuple())
            pygame.draw.rect(screen, Color.Magenta.value,
                             adjusted_debug_image_rect,
                             DEBUG_RECTANGLE_OUTLINE_WIDTH_IN_PIXELS)

            # DRAW A DEBUG RECTANGLE FOR THE BOUNDING BOX FOR COLLISIONS.
            pygame.draw.rect(screen, Color.Red.value,
                             self.BoundingScreenRectangle,
                             DEBUG_RECTANGLE_OUTLINE_WIDTH_IN_PIXELS)
Example #4
0
    def neighbors(self, grid_position):
        # Get the possible neighbors of the position.
        neighbors = [
            Vector2(grid_position.X, grid_position.Y - 1),
            Vector2(grid_position.X, grid_position.Y + 1),
            Vector2(grid_position.X - 1, grid_position.Y),
            Vector2(grid_position.X + 1, grid_position.Y)
        ]
        accessible_neighbors = []
        for neighbor in neighbors:
            # Check whether this position is within map bounds and unoccupied.
            in_x_bounds = (0 <= neighbor.X < self.Map.MapWidth)
            in_y_bounds = (0 <= neighbor.Y < self.Map.MapHeight)
            unoccupied = (neighbor.X, neighbor.Y) not in self.Map.Map
            is_destination = (neighbor == self.Destination)
            if in_x_bounds and in_y_bounds and (unoccupied or is_destination):
                accessible_neighbors.append(neighbor)

        return accessible_neighbors
Example #5
0
    def Shoot(self, player):
        # CALCULATE THE TRAJECTORY TO THE PLAYER.
        enemy_position = Vector2(self.Coordinates.centerx,
                                 self.Coordinates.centery)
        player_position = Vector2(player.Coordinates.centerx,
                                  player.Coordinates.centery)
        trajectory_to_player = Vector2.Subtract(player_position,
                                                enemy_position)

        # The trajectory should be normalized to have it just represent the direction.
        # The laser can independently control the speed at which it moves.
        trajectory_to_player = Vector2.Normalize(trajectory_to_player)

        # GENERATE A LASER AND FIRE IT TOWARDS THE PLAYER.
        laser = Laser(self.Coordinates.centerx, self.Coordinates.centery,
                      Laser.Color.Red, trajectory_to_player)

        # Move the laser closer to the player so that it doesn't hit the wall when it spawns.
        laser.Update(0.2)

        # INDICATE THAT A SHOT WAS JUST FIRED.
        self.TimeElapsedSinceLastShotInSeconds = 0
        return laser
Example #6
0
    def UpdateEnemies(self, time_since_last_update_in_seconds):
        # UPDATE EACH ENEMY.
        player = self.Map.GetPlayer()
        player_position = self.Map.GetGridPosition(player.Coordinates.center)
        player_position_vector = Vector2(player_position[0], player_position[1])
        enemies = self.Map.GetEnemies()
        for enemy in enemies:
            # CHECK IF THE ENEMY WAS HIT BY A REFLECTED LASER.
            lasers_that_kill_enemy = [laser for laser in self.Map.Lasers
                if laser.HasBeenReflected and enemy.Coordinates.colliderect(laser.Coordinates)]
            enemy_was_hit_by_laser = (len(lasers_that_kill_enemy) > 0)
            if enemy_was_hit_by_laser:
                # Remove this enemy from the map.
                self.Map.RemoveObject(enemy)
                
                # No further updates are necessary for this enemy.
                continue

            # TARGET THE PLAYER.
            enemy.TargetPlayer(player)
                        
            # TRY TO SHOOT AT THE PLAYER.
            laser = enemy.TryShooting(time_since_last_update_in_seconds, player, self.Map)
            if laser:
                self.Map.Lasers.append(laser)

            # MOVE IF NON-STATIONARY.
            enemy_is_stationary = isinstance(enemy, Turret)
            if enemy_is_stationary:
                continue
            
            # CHECK IF THE ENEMY IS ALREADY CLOSE ENOUGH TO THE PLAYER.
            enemy_position = self.Map.GetGridPosition(enemy.Coordinates.center)
            enemy_position_vector = Vector2(enemy_position[0], enemy_position[1])
            distance_to_player = self.Pathing.GetDirectDistanceBetweenGridPositions(player_position_vector, enemy_position_vector)
            MINIMUM_DISTANCE = 3.5
            too_close_to_player = (distance_to_player < MINIMUM_DISTANCE)
            DESIRED_DISTANCE = 4.5
            within_desired_distance_of_player = (distance_to_player < DESIRED_DISTANCE)
            if too_close_to_player:
                # BACK AWAY FROM THE PLAYER.
                directions_to_move = LevelHandler.GetDirectionTowardPosition(player_position_vector, enemy_position_vector)
                pass
            elif within_desired_distance_of_player:
                # DON'T MOVE.
                continue
            else:
                # MOVE TOWARD THE PLAYER.
                # Get the shortest path to the player from the enemy.
                path_to_player = self.Pathing.GetPath(enemy_position_vector, player_position_vector)
                if path_to_player is None:
                    # This enemy cannot currently reach the player.
                    continue

                # Get the cardinal direction in which the enemy should move in order to reach
                # the player (or directions, if the true direction is diagonal).
                next_grid_position_in_path_to_player = next(islice(path_to_player, 1, None), None)
                next_grid_position_center_x = (next_grid_position_in_path_to_player.X + 0.5) * GameObject.WidthPixels
                next_grid_position_center_y = (next_grid_position_in_path_to_player.Y + 0.5) * GameObject.HeightPixels
                directions_to_move = LevelHandler.GetDirectionTowardPosition(
                    Vector2(enemy.Coordinates.centerx, enemy.Coordinates.centery),
                    Vector2(next_grid_position_center_x, next_grid_position_center_y))

            # MOVE.
            # If the true direction is diagonal, one of the directions to move may be blocked
            # by an obstacle, but trying to move in the other direction should succeed.
            for direction in directions_to_move:
                if direction == MoveDirection.Up:
                    enemy.MoveUp(self.Map)
                elif direction == MoveDirection.Down:
                    enemy.MoveDown(self.Map)
                elif direction == MoveDirection.Left:
                    enemy.MoveLeft(self.Map)
                elif direction == MoveDirection.Right:
                    enemy.MoveRight(self.Map)
Example #7
0
    def UpdateEnemies(self, time_since_last_update_in_seconds):
        # UPDATE EACH ENEMY.
        player = self.Map.GetPlayer()
        player_position = self.Map.GetGridPosition(player.Coordinates.center)
        player_position_vector = Vector2(player_position[0], player_position[1])
        enemies = self.Map.GetEnemies()
        for enemy in enemies:
            # CHECK IF THE ENEMY WAS HIT BY A REFLECTED LASER.
            for laser in self.Map.Lasers:
                if laser.HasBeenReflected:
                    # Check collision.
                    enemy_was_hit_by_laser = enemy.Coordinates.colliderect(laser.Coordinates)
                    if enemy_was_hit_by_laser:
                        # Remove this enemy from the map.
                        self.Map.RemoveObject(enemy)
                        
                        # No further updates are necessary for this enemy.
                        continue

            # TARGET THE PLAYER.
            enemy.TargetPlayer(player)
                        
            # RANDOMLY SHOOT AT THE PLAYER.
            laser = enemy.TryShooting(time_since_last_update_in_seconds, player, self.Map)
            if laser:
                self.Map.Lasers.append(laser)

            # MOVE IF NON-STATIONARY.
            enemy_is_stationary = isinstance(enemy, Turret)
            if enemy_is_stationary:
                continue
            
            # CHECK IF THE ENEMY IS ALREADY CLOSE ENOUGH TO THE PLAYER.
            enemy_position = self.Map.GetGridPosition(enemy.Coordinates.center)
            enemy_position_vector = Vector2(enemy_position[0], enemy_position[1])
            distance_to_player = self.Pathing.GetDirectDistanceBetweenGridPositions(player_position_vector, enemy_position_vector)
            MINIMUM_DISTANCE = 3.5
            too_close_to_player = (distance_to_player < MINIMUM_DISTANCE)
            DESIRED_DISTANCE = 4.5
            within_desired_distance_of_player = (distance_to_player < DESIRED_DISTANCE)
            if too_close_to_player:
                # BACK AWAY FROM THE PLAYER.
                direction_to_move = LevelHandler.GetDirectionTowardPosition(player_position_vector, enemy_position_vector)
                pass
            elif within_desired_distance_of_player:
                # DON'T MOVE.
                continue
            else:
                # MOVE TOWARD THE PLAYER.
                # Get the shortest path to the player from the enemy.
                path_to_player = self.Pathing.GetPath(enemy_position_vector, player_position_vector)
                if path_to_player is None:
                    # This enemy cannot currently reach the player.
                    continue

                # Get the next grid position that the enemy should move to in
                # order to reach the player.
                next_grid_position_in_path_to_player = next(islice(path_to_player, 1, None), None)
                direction_to_move = LevelHandler.GetDirectionTowardPosition(
                    enemy_position_vector,
                    next_grid_position_in_path_to_player)

            # MOVE.
            if direction_to_move is None:
                continue
            elif direction_to_move == MoveDirection.Up:
                enemy.MoveUp(self.Map)
            elif direction_to_move == MoveDirection.Down:
                enemy.MoveDown(self.Map)
            elif direction_to_move == MoveDirection.Left:
                enemy.MoveLeft(self.Map)
            elif direction_to_move == MoveDirection.Right:
                enemy.MoveRight(self.Map)
Example #8
0
 def Update(self, time_since_last_update_in_seconds):
     MOVE_SPEED_IN_PIXELS_PER_SECOND = 200
     movement_distance_in_pixels = MOVE_SPEED_IN_PIXELS_PER_SECOND * time_since_last_update_in_seconds
     movement_amount_in_pixels = Vector2.Scale(movement_distance_in_pixels, self.Trajectory)
     self.Coordinates = self.Coordinates.move(movement_amount_in_pixels.X, movement_amount_in_pixels.Y)