class Bullet(Entity): """Base bullet class which handles movement and removal of bullets. Arguments: groups (pygame.sprite.Group): All the groups this entity will be in. Attributes: dirty (int): Wether or not the sprite should be drawn. """ explosion = Animation(SpriteSheet.animation(BULLET_COLLISION, 1), 0.3) def __init__(self, *groups): super().__init__(*groups) self.dirty = 2 self._collision = copy(self.explosion) def update(self, seconds_elapsed): """Update the bullets position on the display.""" super().update(seconds_elapsed) self.rect.y += int(self._seconds_elapsed * self._velocity.y) def take_damage(self, bullets, *groups): """Check to see if there are any bullets in contact with each other. If any are destroy them both and create a collision explosion. Arguments: bullets (pygame.sprite.Group): The group of bullets. groups (pygame.sprite.Group): The groups the explosion will be in. """ for bullet in bullets: if bullet is not self and pygame.sprite.collide_mask(self, bullet): bullet.kill() self.kill() Explosion(self._collision, (self.rect.x - BULLET_COLLISION.width / 2, self.rect.y - BULLET_COLLISION.height / 2), *groups)
class Tank(Entity): """The tank which the user controls to destroy the oncoming alien horde. Arguments: groups (pygame.sprite.Group): All the groups this entity will be in. Attributes: tank (pygame.Surface): The default sprite for the tank. bullet (pygame.Surface): The tanks bullet sprite. explosion (pygame.Surface): The tanks explosion animation. image (pygame.Surface): The current image which represents the sprite. rect (pygame.Rect): The rect used for placing the sprite. mask (pygame.mask.Mask): The mast for the image. _velocity (pygame.math.Vector2): The x, y velocities for the sprite. _last_shot (float): The last time that the tank fired a shot. _reload_speed (float): The amount of time it takes to reload. _current_time (float): Time in seconds. (Used for time based actions) """ tank = SpriteSheet.sprite(TANK) bullet = SpriteSheet.sprite(TANK_BULLET) explosion = Animation(SpriteSheet.animation(TANK_EXPLOSION, 1), 0.3) bullet_explosion = Animation( SpriteSheet.animation(TANK_BULLET_EXPLOSION, 1), 0.3) def __init__(self, position, *groups): super().__init__(*groups) self.image = copy(self.tank).convert_alpha() self.rect = self.image.get_rect() self.mask = pygame.mask.from_surface(self.image) self._velocity = pygame.math.Vector2(250, 0) self._last_shot = 0 self._reload_speed = 0.5 self._current_time = self._reload_speed self.rect.topleft = position def move(self, direction): """Move the tank according the users input. Arguments: The direction to move the tank. left < 0 > right. """ velocity = int(self._seconds_elapsed * self._velocity.x) if direction != 0: self.dirty = 1 if direction < 0 and self.rect.left > velocity: self.rect.x -= velocity elif direction > 0 and self.rect.right < DISPLAY.height - velocity: self.rect.x += velocity def shoot(self, *groups): """If the tank isn't reloading then fire a shot. Arguments: groups (pygame.sprite.Group): The groups the bullet will be in. """ if abs(self._last_shot - self._current_time) >= self._reload_speed: TankBullet(self, *groups) self._last_shot = self._current_time def take_damage(self, bullets, *groups): """Tank damage from any of the bullets on the display. Arguments: bullets (pygame.sprite.Group): The bullet group. groups (pygame.sprite.Group): The groups the explosion will be in. """ for bullet in bullets: if pygame.sprite.collide_mask(self, bullet): Explosion(copy(self.explosion), (self.rect.x - 4, self.rect.y), *groups) bullet.kill() self.kill()
class Mystery(Entity): """The mystery ship which flys accross the screen. Arguments: direction (int): The direction the ship will move across the screen. groups (pygame.sprite.Group): All the groups this entity will be in. Attributes: ship (Ship): The default image for the ship. explosion (Animation): The explosion animation. dirty (int): Wether or not to draw the entity. image (pygame.Surface): The sprites image. rect (pygame.Rect): The rect used to place the sprite. _explosion (Animation): The explosion animation. """ ship = SpriteSheet.sprite(MYSTERY) explosion = Animation(SpriteSheet.animation(MYSTERY_EXPLOSION, 1), 0.3) def __init__(self, direction, *groups): super().__init__(*groups) self.dirty = 2 self.image = copy(self.ship).convert_alpha() self.rect = self.image.get_rect() self.mask = pygame.mask.from_surface(self.image) self._explosion = copy(self.explosion) self.rect.y = 0 + self.rect.height if direction: self._velocity = pygame.math.Vector2(250, 0) self.rect.x = 0 else: self._velocity = pygame.math.Vector2(-250, 0) self.rect.x = DISPLAY.width def update(self, seconds_elapsed): """Update the entities time based variables and update the sprites position. Arguments: seconds_elapsed (float): The time since the last frame was drawn. """ super().update(seconds_elapsed) self.rect.x += int(self._seconds_elapsed * self._velocity.x) if not self.rect.colliderect(DISPLAY): self.kill() def take_damage(self, bullets, *groups): """Take any damage from the bullets on the screen. Create an explosion animation when the entities died. bullets (pygame.sprite.Group): The group containing the bullet sprites. groups (pygame.sprite.Group): The groups the explosion will be in. """ for bullet in bullets: if pygame.sprite.collide_mask(self, bullet): Explosion(self._explosion, (self.rect.x + 6, self.rect.y), *groups) bullet.kill() self.kill()
class Ship(Entity): """The different types of ships that the user has to fight against. Arguments: ship_type (int): Which type of ship to create. position (tuple {int, int}): The positon to place the ship. groups (pygame.sprite.Group): The groups the ship sprite will be in. Attributes: num_ships (int): The number of ship objects. type_one (dict): The images and animations for ship type one. type_two (dict): The images and animations for ship type two. type_three (dict): The images and animations for ship type three. _ship_type (int): The type of the ship sprite. _animation (Animation): The sprites default animation. _explosion (Animation): The sprites explosion animation. image (pygame.Surface): The image which represents the sprite. rect (pygame.Rect): The rect used to place the sprite. mask (pygame.mask.Mask): The mask used for collision detection. _last_frame (float): The last time the animation was updated. _last_shot (float): The last time a shot was fired. """ num_ships = 0 type_one = { 'ship': Animation(SpriteSheet.animation(TYPE_ONE, 2), 1, loop=True), 'bullet': Animation(SpriteSheet.animation(TYPE_ONE_BULLET, 2), 0.2, loop=True), 'explosion': Animation(SpriteSheet.animation(TYPE_ONE_EXPLOSION, 1), 0.3), 'bullet_explosion': Animation(SpriteSheet.animation(SHIP_BULLET_EXPLOSION, 1), 0.3) } type_two = { 'ship': Animation(SpriteSheet.animation(TYPE_TWO, 2), 1, loop=True), 'bullet': Animation(SpriteSheet.animation(TYPE_TWO_BULLET, 10), 0.05, loop=True), 'explosion': Animation(SpriteSheet.animation(TYPE_TWO_EXPLOSION, 1), 0.3), 'bullet_explosion': Animation(SpriteSheet.animation(SHIP_BULLET_EXPLOSION, 1), 0.3) } type_three = { 'ship': Animation(SpriteSheet.animation(TYPE_THREE, 2), 1, loop=True), 'bullet': Animation(SpriteSheet.animation(TYPE_THREE_BULLET, 7), 0.05, loop=True), 'explosion': Animation(SpriteSheet.animation(TYPE_THREE_EXPLOSION, 1), 0.3), 'bullet_explosion': Animation(SpriteSheet.animation(SHIP_BULLET_EXPLOSION, 1), 0.3) } def __init__(self, ship_type, position, *groups): super().__init__(*groups) self.__class__.num_ships += 1 self._ship_type = ship_type if self.type == 1: self._animation = copy(self.type_one['ship']) self._explosion = copy(self.type_one['explosion']) elif self.type == 2: self._animation = copy(self.type_two['ship']) self._explosion = copy(self.type_two['explosion']) elif self.type == 3: self._animation = copy(self.type_three['ship']) self._explosion = copy(self.type_three['explosion']) self.image = self._animation.next().convert_alpha() self.rect = self.image.get_rect() self.mask = pygame.mask.from_surface(self.image) self._last_frame = 0 self._reload_speed = 5 self._last_shot = -(self._reload_speed / 2) self.rect.topleft = position @property def type(self): """Get which ship type this sprite is.""" return self._ship_type def update(self, seconds_elapsed): """Update the sprites time based variables and if the time is right update the sprites animation. Arguments: seconds_elapsed (float): The time in seconds since the last frame. """ super().update(seconds_elapsed) if abs(self._last_frame - self._current_time) >= self._animation.delay: self.image = self._animation.next().convert_alpha() self.dirty = 1 self._last_frame = self._current_time def shoot(self, tank, *groups): """If the ship is not reloading then fire a shot at the tank. Arguments: tank (Tank): The tank the ships are shooting at. groups (pygame.sprite.Group): The groups the bullet will be in. """ if self.rect.x >= tank.rect.x - 50 and self.rect.x <= tank.rect.x + 50: if abs(self._last_shot - self._current_time) >= self._reload_speed: ShipBullet(self, *groups) self._last_shot = self._current_time def take_damage(self, bullets, *groups): """Check if the ship should be destroyed. Arguments: bullets (pygame.sprite.Group): The group of bullets. groups (pygame.sprite.Group): The groups the explosion will be in. """ for bullet in bullets: if pygame.sprite.collide_mask(self, bullet): if self._ship_type == 1: Explosion(self._explosion, (self.rect.x - 10, self.rect.y), *groups) elif self._ship_type == 2: Explosion(self._explosion, (self.rect.x - 4, self.rect.y), *groups) elif self._ship_type == 3: Explosion(self._explosion, (self.rect.x - 2, self.rect.y), *groups) bullet.kill() self.kill() def kill(self): """Remove the sprite from all groups and decriment the ship counter.""" super().kill() self.__class__.num_ships -= 1