Ejemplo n.º 1
0
class Weapon(pygame.sprite.Sprite):
    def __init__(self, owner, sprite_sheet_location):
        pygame.sprite.Sprite.__init__(self)
        self.owner = owner
        self.screen = owner.screen
        self.owned = True

        # Loading Sprite Sheet
        image_types = owner.character_data['actions']
        image_directions = owner.character_data['directions']
        char_size = [32, 32]
        scaled_size = owner.scaled_size
        coords = owner.character_data

        self.loadSpriteSheets(image_types, image_directions, scaled_size,
                              char_size, coords, sprite_sheet_location)

        # Setting up initial image
        self.state = owner.state
        self.image = self.images[self.state[0]][self.state[1]]
        self.index = owner.image_index
        self.rect = self.image[self.index].get_rect()
        self.rect.centerx = self.owner.rect.centerx
        self.rect.centery = self.owner.rect.centery

# Assinging animation images to self

    def loadSpriteSheets(self,
                         image_types,
                         image_directions,
                         scaled_size,
                         char_size,
                         coords,
                         sprite_sheet_location,
                         background_colour=(0, 255, 0)):

        self.spritesheet = SpriteSheet(sprite_sheet_location)
        self.images = {}
        for image_type in image_types:
            self.images[image_type] = {}
            for image_direction in image_directions:
                self.images[image_type][image_direction] = []
                for coord in coords[image_type][image_direction]:
                    specific_image = pygame.transform.scale(
                        self.spritesheet.image_at(coord, char_size),
                        scaled_size)
                    specific_image.set_colorkey(background_colour)

                    self.images[image_type][image_direction] += \
                                                             [specific_image]

    def addCharacterGroup(self, char_group):
        self.characters = char_group

    def display(self):
        ''' display function

        state takes form [action, direction], images[action][direction]
        gives a list of images
        '''

        # Get owner variables
        self.state = self.owner.state
        self.index = self.owner.image_index
        action, direction = self.state[0], self.state[1]

        # Select Image
        self.image = self.images[action][direction]

        self.rect.center = self.owner.rect.center

        # Rect position alive
        if self.owner.alive:

            self.screen.blit(self.image[self.index], self.rect)
        else:
            self.owned = False

    # Attack function for weapon
    def attack(self, direction, target):
        if target.health < self.strength:
            self.owner.score += target.health
        else:
            self.owner.score += self.strength
        self.sound()
        target.recoil(self.strength, direction)
Ejemplo n.º 2
0
class Character(pygame.sprite.Sprite):
    ''' Character Class - Used to display and animate sprites from
    sprite sheets on screen.  Usually won't be initialised directly, 
    rather its two child classes (Player and NPC) will be called.
    '''
    def __init__(self,
                 character_data,
                 background,
                 screen,
                 x_position,
                 y_position,
                 arm_type='arms'):
        ''' Init Character
        Function takes and unpacks relevat information from the 
        characters JSON dictionary
        '''
        # Initi for sprite
        pygame.sprite.Sprite.__init__(self)

        # Assigning character data to self.charactar_data
        self.character_data = character_data
        self.addSpritesheetJSON()

        # Putting object to screen
        self.screen = screen

        ### Unpacking some important JSON dictionary into variables
        self.speed = character_data['speed']
        self.gravity = character_data['gravity']
        self.jump_speed = character_data['jump_speed']
        self.state = character_data['initialstate']
        self.refresh_rate = character_data['refresh']

        ### Health and stats data
        self.alive = True
        self.__max_health = character_data['initial_health_points']
        self.__health = self.__max_health
        self.strength = character_data['initial_strength']

        # Character Position
        self.position = [x_position, y_position]

        # Load sprite sheet and extract frames to dictionary
        self.loadSpriteSheets(character_data)

        # Adding screen to object
        self.image = self.images[self.state[0]][self.state[1]]
        self.image_index = 0
        self.plot_rect = self.image[self.image_index].get_rect()
        self.plot_rect.center = self.position

        self.rect = pygame.Rect((0, 0, self.width, self.height))
        self.rect.center = self.plot_rect.center

        self.feet_rect = pygame.Rect((0, 0, self.width, self.height // 10))
        self.feet_rect.bottomleft = self.rect.bottomleft

        # setup score
        self.score = 0

        # Get Character Arms TODO MAY need updating to reflect some
        # enemies having own arms/other arms
        self.arms = WEAPON_TYPES[arm_type](self)
        self.healthbar = HealthBar(self)

        # Important move variables
        self.refresh_counter = 0
        self.x_y_moving = False
        self.recoil_status = (False, 0)

        # Storing dimension variables to self
        self.screen_dims = (screen.get_width(), screen.get_height())

        # Referencing important background variables
        self.changeMap(background)

        ##### TO GO TO JSON
        self.is_falling = False
        self.is_jumping = False
        self.attacking = False
        self.init_attacking = False
        self.jumps_in_action = 0
        self.max_jumps_in_action = 2
        self.attack_frame_counter = 0

        self.thrown_projectiles = pygame.sprite.Group()

    def changeMap(self, background):
        ''' changeMap(background) - used to update to new map

        Function to update player with new background.  Call this on 
        player when new map produced, map refers to class containing 
        sprite group of tiles, and map_matrix
        '''
        self.background = background
        self.map_matrix = background.map_matrix
        self.tiles_group = background.map_group
        self.tile_rect = []
        for tile in self.tiles_group:
            self.tile_rect.append(tile.rect)

    def addSpritesheetJSON(self):
        ''' addSpritesheetJSON

        Loads spritesheet interpretation data from SPRITESHEET_JSON
        '''
        for key in SPRITESHEET_JSON.keys():
            self.character_data[key] = SPRITESHEET_JSON[key]

    def loadSpriteSheets(self, character_data):
        ''' loadSpriteSheets(self, character_data)

        Procedure which loads spritesheet from path given, and extracts 
        each frame of the sprite and stores to dictionary self.images
        These can then be updated depending on this instances state
        '''
        self.spritesheet = SpriteSheet(character_data['path'])
        char_size = character_data['charsize']
        scale_factor = character_data['scale_factor']
        scaled_size = [
            char_size[0] * scale_factor, char_size[1] * scale_factor
        ]
        self.scaled_size = scaled_size
        background_colour = character_data['background']

        image_types = character_data['actions']
        image_directions = character_data['directions']

        graphic_dims = character_data['graphic_dims']
        self.width = graphic_dims[0] * scale_factor
        self.height = graphic_dims[1] * scale_factor

        self.images = {}

        # Importing images into self.images dictionary
        # This interacts with spritesheet code from https://ehmatthes.gi
        # thub.io/pcc_2e/beyond_pcc/pygame_sprite_sheets/#a-simple-sprit
        # e-sheet
        # to load sprites into a dictinoary
        for image_type in image_types:
            self.images[image_type] = {}
            for image_direction in image_directions:
                self.images[image_type][image_direction] = []
                for coords in character_data[image_type][image_direction]:
                    specific_image = pygame.transform.scale(
                        self.spritesheet.image_at(coords, char_size),
                        scaled_size)
                    specific_image.set_colorkey(background_colour)

                    self.images[image_type][image_direction] += \
                                                            [specific_image]

    def changeArms(self, arm_type):
        self.arms = WEAPON_TYPES[arm_type](self)

    def addTarget(self, target_group):
        ''' Adds group of enemies to player
        '''
        self.target_group = target_group

    def spriteCollision(self, other):
        if pygame.sprite.collide_rect(self, other):
            print('COLLISION')
        else:
            print('NO COLLISION')

    def attack(self, target, type=1):
        ''' Attack function - Attacks player assigned to it 

        Causes player being attacked to recoil in opposite direction, 
        and lose health.
        '''
        if self.rect[0] < target.rect[0]:
            direction = 1
        else:
            direction = -1
        self.arms.attack(direction, target)

    def isFacingTarget(self, target):
        '''
        Function to check whether self is facing a particular enemy
        '''
        if self.state[1] == 'left' and \
            (self.rect.centerx > target.rect.centerx):
            return True
        elif self.state[1] == 'right' and \
            (self.rect.centerx < target.rect.centerx):
            return True
        return False

    def recoil(self, force, direction):
        ''' Recoil function - Loses health from attack and sets recoil 
        counter

        Recoil counter processed in display function.  Each frame pushes 
        character back while recoiling.
        '''
        self.health -= force
        self.score -= force // 5
        self.recoil_status = (True, direction)
        self.recoil_counter = 5

    def update(self):
        ''' Update function

        Updates position of characters subject to state.
        '''

        # Check if attacking, if attacking change state to attacking for
        # one frame
        if (self.attacking) and (self.init_attacking == False):
            self.pre_attack_action, self.pre_action_direction = self.state
            self.updateState('attack', self.state[1])
            self.init_attacking = True
        elif self.init_attacking:
            self.attack_frame_counter += 1
        if self.attack_frame_counter == self.refresh_rate:
            self.attack_frame_counter = 0
            self.attacking = False
            self.init_attacking = False
            self.updateState(self.pre_attack_action, self.state[1])

        # Update verticle subject to jumping
        #if self.state[0] == 'jumping':
        if self.is_jumping:
            self.applyJump()
        else:
            if not self.collisionWithGround():
                self.is_falling = True

            # Updating position subject to gravity
            if self.is_falling:
                self.applyGravity()

        # Updating subject to recoil.  If character is recoiling, move
        # in recoil direction
        if self.recoil_status[0]:
            if self.recoil_counter == 0:
                self.recoil_status = (False, 0)
            self.moveX(15 * self.recoil_status[1])
            self.recoil_counter = self.recoil_counter - 1

        # Update x/y subject to status
        if self.x_y_moving:

            if self.state[1] == 'right':
                self.moveX(self.speed)

            if self.state[1] == 'left':

                move_speed = -1 * self.speed
                self.moveX(move_speed)

    def syncRects(self):
        self.feet_rect.bottomleft = self.rect.bottomleft
        self.plot_rect.center = self.rect.center

    def display(self):
        ''' Display function

        Updates image if required, and displays image(s) to screen
        '''
        # Update state image TODO CHANGE image code
        self.image = self.images[self.state[0]][self.state[1]]

        # Updating counter, and if necessary incrementing image
        self.refresh_counter += 1
        if self.refresh_counter == self.refresh_rate:
            self.incrementImage()

        # Catch frames changed mid refresh
        if self.image_index >= len(self.image):
            self.incrementImage()

        self.syncRects()

        # ###################################################
        # # TODO DELETE THE FOLLOWING CODE - FOR TESTING ONLY
        # surf = pygame.Surface((self.rect.width, self.rect.height))
        # surf.fill((100, 100, 0))
        # self.screen.blit(surf, self.rect)

        # surf = pygame.Surface((self.feet_rect.width, self.feet_rect.he
        # ight))
        # surf.fill((0, 100, 100))
        # self.screen.blit(surf, self.feet_rect)
        # ###################################################

        # Displaying current image at current position
        self.screen.blit(self.image[self.image_index], self.plot_rect)

        # Display arms and health bar
        #print(self.arms)
        self.arms.display()
        self.healthbar.display()

    def collisionWithGround(self):
        ''' Collision Detection
        Detects collision with the ground - if colliding with ground,
        returns True

        Based on code from Python Basics YouTube series
        https://www.youtube.com/watch?v=bQnEQvyS1Ns - Approx 4 minutes 
        in, and the pygame documentation.

        Initially this was implemented with pygame.sprite.spritecollide
        with the tiles_group, however this caused issues with any point 
        of a sprite colliding with a tile causing it to cease to fall.
        This means you could get characters awkwardly suspended by their
        heads.  Instead we used pygame.Rect.collidelist
        '''

        if self.feet_rect.collidelist(self.tile_rect) != -1:
            self.is_falling = False
            self.is_jumping = False
            self.jumps_in_action = 0
            self.stopMoveY()
            return True
        else:
            return False

    def applyGravity(self):
        ''' applyGravity
        Updates position subject to gravity.
        If self is falling, then move
        down by gravity.  Then checks for collisions with tiles to 
        update falling status.
        '''
        # Updating positions subject to gravity
        self.moveY(self.gravity)
        self.collisionWithGround()

    def applyJump(self):
        ''' Applys Jump to player after jump has been pressed
        '''
        jump = self.jump_speed // self.jumpcount
        self.moveY(-1 * jump)
        self.jumpcount = self.jumpcount * 2
        if (jump == 1) or (jump == 0):
            self.is_falling = True
            self.is_jumping = False
            #self.state[0] = 'falling'

    def incrementImage(self):
        ''' Increment Image function
        Increments image by 1, and resets counting variable
        '''
        # Resetting refresh counter
        self.refresh_counter = 0

        # Incrementing image index
        self.image_index += 1

        # Returning to 0 when image index > length
        n_images = len(self.image)
        if self.image_index >= n_images:
            self.image_index = 0

    def updateState(self, action, direction):
        ''' updateState(action, direction)
        function to update state of character
        '''
        self.state = [action, direction]
        self.refresh_counter = 0
        self.image_index = 0

    def moveX(self, step):
        ''' moveX(step)
        Function to move character step pixels in the X direction
        '''
        self.rect.centerx += step

    def moveY(self, step):
        ''' moveY(step)
        Function to move character step pixels in the Y direction.
        - Note: the y axis is flipped from what we might naturally 
                assume, 0 is at the top and not the bottom
        '''
        self.rect.centery += step

    def startMove(self, direction):
        ''' startMove(direction)
        Input 'l' or 'r' for horizontal movement, 'u' for jump
        '''
        if self.state[0] == 'idle':
            self.state[0] = 'running'
        if direction == 'l':
            # Moving one speed step left
            self.x_y_moving = True
            self.state[1] = 'left'
        elif direction == 'r':
            # Moving right
            self.x_y_moving = True
            self.state[1] = 'right'
        elif direction == 'u':
            #self.state[0] = 'jumping'
            if (self.jumps_in_action < self.max_jumps_in_action):
                self.is_jumping = True
                self.jumpcount = 1
                self.jumps_in_action += 1

    def stopMoveX(self, direction='none'):
        ''' stopMove()
        Returns state to idle when no longer moving.  Purpose of 
        function is to stop running animation, and prevent piece from 
        continueing to move after a jump if player ahs taken hand off
        '''
        if self.state == ['running', direction]:
            self.updateState('idle', direction)
            self.x_y_moving = False
        elif (self.state[0] == 'attack') and direction == self.state[1]:
            self.pre_attack_action == 'idle'
            self.x_y_moving = False

    def stopMoveY(self):
        ''' Sets state to idle to ensure animation matches with lack of
        moving
        '''
        if (not self.x_y_moving) and (self.state[0] != 'attack'):
            self.state[0] = 'idle'

    def setArms(self, new_arms):
        self.arms = new_arms

    @property
    def health(self):
        return self.__health

    @health.setter
    def health(self, new):
        self.__health = new
        if self.health <= 0:
            self.alive = False
            #self.kill()
            return
        self.healthbar.updateHealth()

    @property
    def max_health(self):
        return self.__max_health

    @max_health.setter
    def max_health(self, new):
        self.healthbar.max_health = new
        self.health += new - self.max_health
        self.__max_health = new
Ejemplo n.º 3
0
class BoomerangAmmo(pygame.sprite.Sprite):
    def __init__(self, owner, characters, strength, direction):
        pygame.sprite.Sprite.__init__(self)
        self.loadFrames()
        self.group = characters
        self.owner = owner
        self.strength = strength
        self.screen = self.owner.screen

        if direction == 'left':
            self.speed = -3
        else:
            self.speed = 3

        self.frame_rate = 10
        self.frame_counter = 0
        self.period = 1
        self.period_counter = 0

        self.rect.center = self.owner.rect.center

    def loadFrames(self):
        self.frames = []
        self.spritesheet = SpriteSheet(BOOMERANG_ANIMATION_LOCATION)
        for i in range(4):
            frame = self.spritesheet.image_at((i, 0), (32, 32), (0, 255, 0))
            self.frames.append(frame)
        self.current_frame = 0
        self.num_frames = len(self.frames)
        self.image = self.frames[self.current_frame]
        self.rect = self.image.get_rect()

    def update(self):

        self.period_counter += 1
        if self.period_counter == self.period:
            self.rect.centerx += self.speed
            self.period_counter = 0
        self.frame_counter += 1

        if self.frame_counter == self.frame_rate:
            self.current_frame += 1
            self.frame_counter = 0

        if self.current_frame >= len(self.frames):
            self.current_frame = 0

        collisions = pygame.sprite.spritecollide(self, self.group, False)

        if collisions != None:
            for sprite in collisions:
                if self.rect.centerx < sprite.rect.centerx:
                    direction = 1
                else:
                    direction = -1
                sprite.recoil(self.strength, direction)
                self.owner.score += self.strength
                self.kill()

    def display(self):
        self.image = self.frames[self.current_frame]
        self.screen.blit(self.image, self.rect)