class Coin(Sprite): """Represents a coin with an associated point value""" def __init__(self, x, y, screen, points=100): super(Coin, self).__init__() images = [ 'map/Coin-1.png', 'map/Coin-2.png', 'map/Coin-3.png', 'map/Coin-4.png' ] self.animator = Animator(images) self.image = self.animator.get_image() self.rect = self.image.get_rect() self.rect.left, self.rect.top = x, y self.screen = screen self.points = points def update(self): """Update the coin image""" self.image = self.animator.get_image() def blit(self): """Blit the coin to the screen""" self.screen.blit(self.image, self.rect)
class Koopa(Enemy): def __init__(self, screen, x, y, player, floor, block, goombas, koopas): self.name_1, self.name_2 = None, None self.name_1 = Enemy.img_file('KoopaWalkLeft_1', 25, 40) self.name_2 = Enemy.img_file('KoopaWalkLeft_2', 25, 40) self.left_images = [self.name_1, self.name_2] self.name_1 = Enemy.img_file('KoopaWalkRight_1', 25, 40) self.name_2 = Enemy.img_file('KoopaWalkRight_2', 25, 40) self.right_images = [self.name_1, self.name_2] self.name_1 = Enemy.img_file('KoopaShell', 35, 30) self.death_images = [self.name_1] self.name_1 = Enemy.img_file('KoopaShellUD', 35, 30) self.UD_death_images = [self.name_1] self.name_1 = Enemy.img_file('KoopaLegs', 35, 30) self.feet_images = [self.name_1] self.animator = Animator(self.left_images) image = self.animator.get_image() super().__init__(screen, image, x, y, player, floor, block, goombas, koopas) self.collision_flag = False self.feet_frame = 0 self.counter = 0 def upside_down_death_animation(self): time = pygame.time.get_ticks() # Animate getting hit (Go up for two seconds) if self.death_animation_frame == 0: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_SPEED) else: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_SPEED * -1) # After two seconds fall down while upside down if self.death_animation_frame == 0 and abs(self.last_frame - time) > 2000: self.animator = Animator(self.UD_death_images) self.death_animation_frame += 1 """MIGHT BE REDUNDANT WITH CHECK BOUNDARY""" # Kill off after 10 seconds (Enough to be off screen) if abs(self.last_frame - time) > 10000: self.player.score += 100 self.kill() def update(self): self.koopa_physics() self.image = self.animator.get_image() def check_player_shell_collision(self): # Check player collision when in shell if self.rect.colliderect(self.player.rect): return True def koopa_physics(self): """USE MARIO CURRENT POSITION TO GET LEFT OF SCREEN""" self.check_boundary() if not self.check_floor() and self.start_movement: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_GRAVITY) self.rect.x = self.rect.x + (self.ENEMY_DIRECTION * (self.ENEMY_SPEED - 1)) if self.check_floor() and self.start_movement: self.rect.x = self.rect.x + (self.ENEMY_DIRECTION * self.ENEMY_SPEED) # If collision if self.check_collisions(): # Gets stomped on -> stop # Collides with player when in shell -> Movement if self.enemy_player_collide_flag and self.shell_mode: time = pygame.time.get_ticks() # Only put in shell if needed if self.death_animation_frame == 0: self.animator = Animator(self.death_images) self.image = self.animator.get_image() tempx, tempy = self.rect.x, self.rect.y self.rect = self.image.get_rect() self.rect.x = tempx self.rect.y = tempy self.death_animation_frame += 1 # Collide with player in shell mode causes movement if self.check_player_shell_collision(): self.shell_movement = True # Move shell depending on which side was hit if self.shell_movement: if self.death_animation_frame == 1: # Left side was hit if self.rect.x >= self.player.rect.x: self.ENEMY_DIRECTION = abs(self.ENEMY_DIRECTION) # Right side hit else: self.ENEMY_DIRECTION = abs( self.ENEMY_DIRECTION) * -1 self.death_animation_frame += 1 if self.check_block_collision(): pass self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) # Not being hit by player again makes koopa pop out of shell if not self.check_player_shell_collision() and abs(self.last_frame - time) > 8000 and not\ self.shell_movement: if self.counter == 0: self.animator = Animator(self.feet_images) self.feet_frame = pygame.time.get_ticks() self.counter += 1 if abs(self.feet_frame - time) > 3000: self.counter = 0 self.ENEMY_DIRECTION = abs(self.ENEMY_DIRECTION) * -1 self.animator = Animator(self.left_images) self.enemy_player_collide_flag = False self.shell_mode = False # Collision with map or block elif self.enemy_block_collide_flag: # Killed by player hitting block if self.block_enemy_kill: self.dead = True self.upside_down_death_animation() # If colliding with map (i.e. Pipe) change direction else: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_block_collide_flag = False # If colliding with goomba change direction elif self.enemy_goomba_collide_flag: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_goomba_collide_flag = False elif self.enemy_koopa_collide_flag: # Colliding with koopa shell thats moving if self.shell_enemy_kill: self.dead = True self.upside_down_death_animation() # Colliding with koopa enemy or shell else: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_koopa_collide_flag = False
class Goomba(Enemy): def __init__(self, screen, x, y, player, floor, block, goombas, koopas): self.walk_images = [ 'images/enemies/goomba/GoombaLeftBoot.png', 'images/enemies/goomba/GoombaRightBoot.png' ] self.upside_down_images = [ 'images/enemies/goomba/GoombaUD1.png', 'images/enemies/goomba/GoombaUD2.png' ] self.crushed_images = ['images/enemies/goomba/GoombaCrushed.png'] self.animator = Animator(self.walk_images) image = self.animator.get_image() super().__init__(screen, image, x, y, player, floor, block, goombas, koopas) def crushed_death_animation(self): print('ENEMY CRUSHED') time = pygame.time.get_ticks() print(str(time)) print(str(self.last_frame)) # Animate and keep on screen for half a second before killing sprite self.animator = Animator(self.crushed_images) if abs(time - self.last_frame) > 1000: self.player.score += 100 self.kill() def upside_down_death_animation(self): time = pygame.time.get_ticks() # Animate getting hit (Go up for two seconds) if self.death_animation_frame == 0: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_SPEED) else: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_SPEED * -1) # After two seconds fall down while upside down if self.death_animation_frame == 0 and abs(self.last_frame - time) > 2000: self.animator = Animator(self.upside_down_images) self.death_animation_frame += 1 """MIGHT BE REDUNDANT WITH CHECK BOUNDARY""" # Kill off after 10 seconds (Enough to be off screen) if abs(self.last_frame - time) > 10000: self.player.score += 100 self.kill() def update(self): if not self.dead: self.goomba_physics() else: if self.player_enemy_kill is True: self.crushed_death_animation() elif self.block_enemy_kill is True: self.upside_down_death_animation() elif self.shell_enemy_kill is True: self.upside_down_death_animation() self.image = self.animator.get_image() def goomba_physics(self): self.check_boundary() # If no blocks are touching enemy -> Fall Down if not self.check_floor() and self.start_movement: self.rect.y += (abs(self.ENEMY_DIRECTION) * self.ENEMY_GRAVITY) self.rect.x = self.rect.x + (self.ENEMY_DIRECTION * (self.ENEMY_SPEED - 1)) if self.check_floor() and self.start_movement: self.rect.x = self.rect.x + (self.ENEMY_DIRECTION * self.ENEMY_SPEED) # print('Player ' + str(self.check_player_collision())) # print('Block ' + str(self.check_block_collision())) # print('Enemy' + str(self.check_friendly_collision())) if self.check_collisions(): # Collides with player if self.enemy_player_collide_flag: # Enemy dead if self.player_enemy_kill: self.dead = True self.last_frame = pygame.time.get_ticks() self.crushed_death_animation() else: self.enemy_player_collide_flag = False # Collision with map or block elif self.enemy_block_collide_flag: # Killed by player hitting block if self.block_enemy_kill: self.dead = True self.upside_down_death_animation() # If colliding with map (i.e. Pipe) change direction else: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_block_collide_flag = False # If colliding with goomba change direction elif self.enemy_goomba_collide_flag: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_goomba_collide_flag = False # If colliding with koopa elif self.enemy_koopa_collide_flag: # Colliding with koopa shell thats moving if self.shell_enemy_kill: self.dead = True self.upside_down_death_animation() # Colliding with koopa enemy or shell else: self.rect.x += (self.ENEMY_DIRECTION * self.ENEMY_SPEED) self.enemy_koopa_collide_flag = False
class Item(Sprite): """Represents a generic item object in the mario game""" MUSHROOM = 'mushroom' ONE_UP = '1-up' FIRE_FLOWER = 'fire-flower' STARMAN = 'starman' def __init__(self, x, y, image, speed, obstacles, floor, item_type, rise_from=None, animated=False): super(Item, self).__init__() if animated: self.animator = Animator(image) self.image = self.animator.get_image() else: self.animator = None self.image = image self.item_type = item_type self.rect = self.image.get_rect() self.rect.left, self.rect.top = x, y self.speed = speed self.jump_speed = 0 self.obstacles = obstacles # objects that the item may collide with self.floor = floor # rects for the floor self.rise_from = rise_from def rise(self): """Have the item rise up from another object""" if not self.rise_from: raise ValueError( 'Cannot rise from an object when that object is None') if self.rect.bottom <= self.rise_from.rect.top: self.rise_from = None else: self.rect.bottom -= 2 def jump(self): """Have the item jump into the air""" if self.speed >= 0: self.jump_speed = -(self.speed * 5) else: self.jump_speed = (self.speed * 5) def flip_direction(self): """Flip the direction the item is moving on the x-axis""" self.speed = -self.speed self.rect.left += self.speed def bounce_off_obstacles(self): """Check if the item has hit any obstacles which cause it to bounce the other direction""" for obs in self.obstacles: pts = [ obs.rect.bottomleft, obs.rect.midleft, obs.rect.bottomright, obs.rect.midright ] for pt in pts: if self.rect.collidepoint(pt): self.flip_direction() return for rect in self.floor: pts = [ rect.midleft, rect.midright, rect.bottomleft, rect.bottomright ] y_cap = rect.top for pt in pts: if self.rect.collidepoint(pt) or \ ((self.rect.left == rect.right or self.rect.right == rect.left) and self.rect.top > y_cap): self.flip_direction() return def fall(self): """If the item is not supported by any floor rects, then fall down""" falling = True for rect in self.floor: # check if bottom is at the top of the floor rect and that the x pos is within floor area if self.rect.bottom == rect.top and ( rect.left < self.rect.center[0] < rect.right): self.rect.bottom = rect.top falling = False break if falling: for obj in self.obstacles: pts = [obj.rect.topleft, obj.rect.midtop, obj.rect.topright] for pt in pts: if self.rect.collidepoint(pt): falling = False break if not falling: break if falling: self.rect.bottom += abs(self.speed) def update(self): """Update the item position based on its speed variables""" if self.animator: self.image = self.animator.get_image() if not self.rise_from: if abs(self.jump_speed) > 0: self.rect.top += self.jump_speed self.jump_speed += 1 # simulate gravity reducing speed self.rect.left += self.speed self.bounce_off_obstacles() self.fall() else: self.rise()
class FireBall(Sprite): """A fireball which can be thrown from Mario when he is in his fire flower state""" def __init__(self, x, y, norm_images, explode_images, obstacles, floor, goomba, koopa, speed=5): self.norm_animator = Animator(norm_images) self.explode_animator = Animator(explode_images, repeat=False) self.image = self.norm_animator.get_image() self.rect = self.image.get_rect() self.rect.x, self.rect.y = x, y self.obstacles = obstacles self.floor = floor self.goomba, self.koopa = goomba, koopa self.speed_x = speed self.speed_y = speed self.active = True super(FireBall, self).__init__() def check_hit_wall(self): """Check if the fireball has hit any walls""" for obs in self.obstacles: pts = [ obs.rect.midleft, obs.rect.midright, obs.rect.bottomleft, obs.rect.bottomright ] for pt in pts: if self.rect.collidepoint(pt): self.active = False return for flr_rect in self.floor: pts = [ flr_rect.midleft, flr_rect.midright, flr_rect.bottomleft, flr_rect.bottomright ] for pt in pts: if self.rect.collidepoint(pt): self.active = False return def check_hit_enemies(self): """Check if the fireball has hit any enemies""" for g_enemy in self.goomba: if collide_rect( self, g_enemy): # FIXME: change kill() when animation works g_enemy.kill() self.active = False return for k_enemy in self.koopa: if collide_rect(self, k_enemy): k_enemy.kill() self.active = False return def apply_gravity(self): """Apply gravity to the fireball, bounce off of horizontal side of surfaces""" bounce = False for obs in self.obstacles: pts = [obs.rect.topleft, obs.rect.midtop, obs.rect.topright] for pt in pts: if self.rect.collidepoint(pt): bounce = True break if bounce: break if not bounce: for flr_rect in self.floor: # check if bottom is at the top of the floor rect and that the x pos is within floor area if self.rect.bottom >= flr_rect.top and ( flr_rect.left < self.rect.center[0] < flr_rect.right): bounce = True break if bounce: self.speed_y = -abs( self.speed_y) # ensure speed in y-direction is negative else: self.speed_y += 2 # apply gravity self.rect.y += self.speed_y def update(self): """Update the position of the fireball""" if self.active: self.rect.x += self.speed_x self.apply_gravity() self.image = self.norm_animator.get_image() self.check_hit_wall() self.check_hit_enemies() elif self.explode_animator.is_animation_done(): self.kill() else: self.image = self.explode_animator.get_image()
class QuestionBlock(CoinBlock): """Represents a question block which can be hit to release an item""" MUSHROOM = 'mushroom' ONE_UP = '1-up' FIRE_FLOWER = 'fire-flower' STARMAN = 'starman' def __init__(self, x, y, screen, map_group, game_objects, item=MUSHROOM, static_img=None): if not static_img: images = [ 'map/Question-Block-1.png', 'map/Question-Block-2.png', 'map/Question-Block-3.png' ] self.animator = Animator(images) initial_image = self.animator.get_image() else: initial_image = static_img self.animator = None self.game_objects = game_objects if item in (QuestionBlock.MUSHROOM, QuestionBlock.FIRE_FLOWER, QuestionBlock.STARMAN, QuestionBlock.ONE_UP): self.item = item # TODO: items coins = None else: self.item = None coins = 1 super(QuestionBlock, self).__init__(x, y, initial_image, screen, map_group, coins=coins if coins else 0) if self.item: self.sound = mixer.Sound('audio/Powerup-Appear.wav') self.blank_img = image.load( 'map/super-mario-empty-block.png') # force blank image self.state['blank'] = False @classmethod def q_block_from_tmx_obj(cls, obj, screen, map_group, game_objects): """Create a question block using tmx data""" item_type = obj.properties.get('item', None) if obj.properties.get('invisible', None): return cls(obj.x, obj.y, screen, map_group, game_objects, item_type, static_img=obj.image) return cls(obj.x, obj.y, screen, map_group, game_objects, item_type) def check_hit(self, other): points = super(QuestionBlock, self).check_hit(other) if self.item and self.state['meta'] == CoinBlock.HIT_STATE: obstacles, floor = self.game_objects[ 'collide_objs'], self.game_objects['floors'] if self.item == QuestionBlock.MUSHROOM and not other.state_info[ 'big']: n_item = Mushroom(self.rect.x, self.rect.y, obstacles, floor, rise_from=self) elif self.item == QuestionBlock.ONE_UP: n_item = OneUp(self.rect.x, self.rect.y, obstacles, floor, rise_from=self) elif self.item == QuestionBlock.FIRE_FLOWER or self.item == QuestionBlock.MUSHROOM: n_item = FireFlower(self.rect.x, self.rect.y, obstacles, floor, rise_from=self) else: n_item = StarMan(self.rect.x, self.rect.y, obstacles, floor, rise_from=self) self.game_objects['items'].add(n_item) self.map_group.add(n_item) self.item = None self.state['blank'] = True self.sound.play() elif points: return points def update(self): """Update the question block to its next animated image""" if not self.state['blank'] and self.animator: self.image = self.animator.get_image() elif self.state['blank']: self.image = self.blank_img super(QuestionBlock, self).update()