class CapeGuy(Character): def __init__(self, game): super().__init__(game) self.sprite = Sprite(3, colorkey=c.BLACK) idle = SpriteSheet(c.image_path("evil_guy_placeholder.png"), (1, 1), 1) self.sprite.add_animation({"Idle": idle}) self.sprite.start_animation("Idle") self.name = "Parity" self.x = c.WINDOW_WIDTH//2 self.y = 125 self.yoff = 0 self.age = 0 self.alpha = 0 self.target_alpha = 255 def update(self, dt, events): self.sprite.update(dt) self.age += dt self.yoff = math.sin(self.age * 1) * 12 da = self.target_alpha - self.alpha if da >= 0: self.alpha = min(self.alpha + da * dt, self.target_alpha) else: self.alpha = max(self.alpha + da * dt, self.target_alpha) def draw(self, surface): if self.alpha <= 0: return size = self.sprite.size() x, y = self.game.xy_transform(self.x, self.y) self.sprite.set_position((x - size[0]//2, y + self.yoff - size[1]//2)) self.sprite.draw(surface, self.alpha)
def main(self): inst = SpriteSheet("images/controls.png", (2, 1), 2) sprite = Sprite(4) sprite.add_animation({"Idle": inst}) sprite.start_animation("Idle") clock = pygame.time.Clock() age = 0 shade = pygame.Surface(c.WINDOW_SIZE) shade.fill(c.BLACK) shade.set_alpha(255) while True: end = 4 if age < 0.25: shade.set_alpha((0.25 - age) * 255 / 0.25) elif age > end - 0.25: shade.set_alpha((age - end + 0.25) * 255 / 0.25) else: shade.set_alpha(0) sprite.set_position((c.WINDOW_WIDTH // 2, c.WINDOW_HEIGHT // 2)) dt = clock.tick(60) / 1000 age += dt events = self.game.update_globals() sprite.update(dt) sprite.draw(self.game.screen) self.game.screen.blit(shade, (0, 0)) pygame.display.flip() if age >= end: self.game.bus_ride.fadeout(500) break
class BulletHit(Particle): def __init__(self, game, position=(0, 0)): super().__init__(game, None, position=position) self.sprite = Sprite(16) anim = SpriteSheet(c.image_path("bullet_hit.png"), (8, 1), 8) self.sprite.add_animation({"Default": anim}) self.sprite.start_animation("Default") self.game.top_particles.add(self) surface = pygame.Surface((100, 100)) surface.fill((0, 0, 0)) pygame.draw.circle(surface, (255, 255, 255), (50, 50), 50) self.boom = surface self.boom.set_colorkey((0, 0, 0)) self.boom_alpha = 200 self.boom_size = 40 def update(self, dt, events): self.age += dt self.boom_alpha -= 1600 * dt self.boom_size += 600 * dt self.sprite.update(dt) if self.age > 0.4: self.destroy() def draw(self, surface, offset=(0, 0)): x = self.x + offset[0] y = self.y + offset[1] self.sprite.set_position((x, y)) self.sprite.draw(surface) boom = pygame.transform.scale( self.boom, (int(self.boom_size), int(self.boom_size))) boom.set_alpha(self.boom_alpha) surface.blit(boom, (x - self.boom_size // 2, y - self.boom_size // 2)) def destroy(self): super().destroy() self.game.top_particles.remove(self)
class Warden(Character): def __init__(self, game): super().__init__(game) self.sprite = Sprite(3, colorkey=c.BLACK) self.halberd = Sprite(3, colorkey=c.BLACK) idle = SpriteSheet(c.image_path("warden.png"), (1, 1), 1) self.sprite.add_animation({"Idle": idle}) self.sprite.start_animation("Idle") idle = SpriteSheet(c.image_path("halberd.png"), (1, 1), 1) self.halberd.add_animation({"Idle": idle}) self.halberd.start_animation("Idle") self.x = c.WINDOW_WIDTH // 2 self.y = 125 self.age = 0 self.alpha = -200 self.target_alpha = 255 self.name = "Warden" self.slashing = False self.slash_timer = 0 self.left = pygame.image.load(c.image_path("warden_left.png")).convert() self.left.set_colorkey(c.BLACK) self.left.set_alpha(255) self.right = pygame.image.load(c.image_path("warden_right.png")).convert() self.right.set_colorkey(c.BLACK) self.right.set_alpha(255) def draw(self, surface): if self.alpha <= -200: return size = self.sprite.size() x, y = self.game.xy_transform(self.x, self.y) self.sprite.set_position((x - size[0]//2, y - size[1]//2)) yoff = math.sin(self.age * 1.6) * 5 self.halberd.set_position((x - size[0]//2, y - size[1]//2 + yoff)) if not self.slashing: self.sprite.draw(surface, self.alpha) self.halberd.draw(surface, self.alpha + 200) else: width = 4 yoff = max(0, ((self.slash_timer - 2.0) * 40)) ** 2 alpha = min(255, 255 - (self.slash_timer - 2.0) * 800) lx = x - width//2 - size[0]//2 rx = lx + self.left.get_width() + width ly = y - size[1]//2 + yoff ry = y - size[1]//2 - yoff self.left.set_alpha(alpha) self.right.set_alpha(alpha) if alpha > 0: surface.blit(self.left, (lx, ly)) surface.blit(self.right, (rx, ry)) else: self.game.characters.remove(self) def update(self, dt, events): self.sprite.update(dt) if not self.slashing: self.age += dt else: self.slash_timer += dt da = self.target_alpha - self.alpha if da >= 0: self.alpha = min(self.alpha + da * dt, self.target_alpha) else: self.alpha = max(self.alpha + da * dt, self.target_alpha) def slash(self): self.game.slash_sound.play() self.game.shake(15) self.slashing = True self.game.set_flash(0.5)
class Character(OverWorldObject): name = None def __init__(self, scene, pos=(3, 3)): super().__init__(scene) self.priority = 3 self.blocking = True self.interactive = True self.dialogue = None tag_dict = {"Player": "player", "Emilia": "captain", None: "player"} tag = tag_dict[self.name] down = SpriteSheet(scene.load_image(f"{tag}_walk_down"), (6, 1), 6) up = SpriteSheet(scene.load_image(f"{tag}_walk_up"), (6, 1), 6) down_idle = SpriteSheet(scene.load_image(f"{tag}_idle_down"), (4, 1), 4) up_idle = SpriteSheet(scene.load_image(f"{tag}_idle_up"), (4, 1), 4) right = SpriteSheet(scene.load_image(f"{tag}_walk_right"), (6, 1), 6) left = SpriteSheet(scene.load_image(f"{tag}_walk_right"), (6, 1), 6) left.reverse(1, 0) right_idle = SpriteSheet(scene.load_image(f"{tag}_idle_right"), (4, 1), 4) left_idle = SpriteSheet(scene.load_image(f"{tag}_idle_right"), (4, 1), 4) left_idle.reverse(1, 0) self.sprite = Sprite(8) self.sprite.add_animation({ "down": down, "up": up, "down_idle": down_idle, "up_idle": up_idle, "right": right, "left": left, "right_idle": right_idle, "left_idle": left_idle }) self.sprite.start_animation("down_idle") self.idles = { c.DOWN: "down_idle", c.UP: "up_idle", c.RIGHT: "right_idle", c.LEFT: "left_idle" } self.idle_playing = True self.x = pos[0] self.y = pos[1] self.scene.map.add_to_cell(self, self.x, self.y) self.draw_x = self.x self.draw_y = self.y self.prev_x = self.x self.prev_y = self.y self.shadow = CharacterShadow(scene, self) self.scene.map.add_to_cell(self.shadow, self.x, self.y) self.since_move = 0 self.move_speed = 3 self.in_motion = False self.next_direction = c.NO_DIRECTION self.face_direction = c.DOWN self.last_face_direction = c.DOWN self.since_arrive = 999 self.shadow_surf = pygame.Surface(c.TILE_SIZE) self.shadow_surf.fill(c.WHITE) width = int(c.TILE_WIDTH * 0.7) height = int(c.TILE_HEIGHT * 0.5) pygame.draw.ellipse( self.shadow_surf, c.BLACK, (c.TILE_WIDTH // 2 - width // 2, 3.2 * c.TILE_HEIGHT // 5 - height // 2, width, height)) self.shadow_surf.set_alpha(30) self.shadow_surf.set_colorkey(c.WHITE) def draw(self, surface): draw_x, draw_y = self.scene.camera.grid_to_screen( self.draw_x, self.draw_y) yoff = c.TILE_HEIGHT // 1.5 self.sprite.set_position((draw_x, draw_y - yoff)) surface.blit(self.shadow_surf, (draw_x, draw_y)) self.sprite.draw(surface) def touch(self): pass def setDialogue(self, dialogueTag): self.dialogue = Dialogue(dialogueTag, self.name) def update(self, dt, events): self.check_events(events) self.since_move += dt self.since_arrive += dt self.draw_x = h.approach(self.draw_x, self.x, self.move_speed * dt) self.draw_y = h.approach(self.draw_y, self.y, self.move_speed * dt) if (self.draw_x, self.draw_y) == (self.x, self.y) and (self.x, self.y) != ( self.prev_x, self.prev_y) and self.in_motion: self.arrive() self.update_move(self.direction()) self.sprite.update(dt) if self.since_arrive > 0.1 and not self.idle_playing and not self.in_motion: self.idle_playing = True if self.face_direction in self.idles: self.sprite.start_animation(self.idles[self.face_direction]) def check_events(self, events): pass def direction(self): new_direction = self.next_direction self.next_direction = c.NO_DIRECTION return new_direction def arrive(self): """ Called when the player arrives at the target location each step. """ self.in_motion = False self.scene.map.remove_from_cell(self.shadow, self.prev_x, self.prev_y) self.scene.map.add_to_cell(self.shadow, self.x, self.y) self.prev_x, self.prev_y = self.x, self.y self.since_arrive = 0 def move(self, direction): """ Queues a movement in the direction for the character.""" self.next_direction = direction def update_move(self, direction): """ Move the character one square in a direction. """ self.scene.map.remove_from_cell(self, self.x, self.y) if not self.in_motion and direction is not c.NO_DIRECTION: new_x = self.x + direction[0] new_y = self.y + direction[1] self.face_direction = direction if not self.scene.map.cell_is_blocking(new_x, new_y): self.prev_x = self.x self.prev_y = self.y self.x += direction[0] self.y += direction[1] self.since_move = 0 self.in_motion = True self.turn(direction) self.scene.map.add_to_cell(self, self.x, self.y) def turn(self, direction): if direction != self.last_face_direction or self.idle_playing: self.idle_playing = False if direction == c.UP: self.sprite.start_animation("up") elif direction == c.DOWN: self.sprite.start_animation("down") elif direction == c.LEFT: self.sprite.start_animation("left") elif direction == c.RIGHT: self.sprite.start_animation("right") self.last_face_direction = direction
class Player: def __init__(self, game): self.game = game self.width = 64 self.height = 64 self.x = c.WINDOW_WIDTH / 3 self.y = c.WINDOW_HEIGHT // 2 + self.game.corridor.width // 2 - self.height // 2 self.y_velocity = 0 self.x_velocity = 0 self.extra_jumps = 1 self.bullets = set() self.bullet_cooldown = 0 self.bullet_period = 0.10 self.gunfire = pygame.mixer.Sound("sounds/gunfire.wav") self.hurt_noise = pygame.mixer.Sound("sounds/hurt.wav") self.death_noise = pygame.mixer.Sound("sounds/death.wav") self.hurt_noise.set_volume(0.5) self.jump_noise = pygame.mixer.Sound("sounds/jump.wav") self.sprite = Sprite(7) self.sprite.add_animation({ "Running": player_running, "Dead": player_dead, "Hit": player_hit }) self.sprite.start_animation("Running") self.gun = pygame.image.load("images/player_gun.png") self.gun = pygame.transform.scale( self.gun, (self.gun.get_width() * 2, self.gun.get_height() * 2)) self.since_hit = 10 self.invincibility_period = 1 self.movement_enabled = True self.in_bus = False self.cash = 0 self.cash_this_level = 0 self.dead = False self.collision_radius = 24 self.hit_circle_radius = 1000 self.hp = 3 def update(self, dt, events): if pygame.mouse.get_pressed()[0]: self.shoot() if self.hit_circle_radius < 1000: self.hit_circle_radius += 2500 * dt if self.movement_enabled: self.y_velocity += 2500 * dt self.y += self.y_velocity * dt self.x_velocity *= 0.0005**dt self.x += self.x_velocity * dt self.bullet_cooldown += dt self.since_hit += dt if self.since_hit >= 0.05 and not self.dead: self.sprite.start_animation("Running") self.collide_corridor(self.game.corridor) self.update_movement(dt, events) to_destroy = set() for bullet in self.bullets: bullet.update(dt, events) margin = 30 if bullet.x > c.WINDOW_WIDTH + margin or bullet.y > c.WINDOW_HEIGHT + margin or \ bullet.x < -margin or bullet.y < -margin: to_destroy.add(bullet) self.bullets -= to_destroy self.sprite.update(dt) mindist = 800 for enemy in self.game.enemies: if enemy.dead or self.dead: continue dist = c.distance_between_points(self.x, self.y, enemy.x, enemy.y) dist -= enemy.radius + 32 if dist < mindist: mindist = dist if mindist < 0: mindist = 0 self.game.slowdown = min(0.4 + 0.6 * mindist / 130, 1) def die(self): self.game.scroll_speed = 0 self.dead = True self.x_velocity = 500 self.y_velocity = -1000 self.sprite.start_animation("Dead") self.death_noise.play() def update_movement(self, dt, events): if not self.movement_enabled: return pressed = pygame.key.get_pressed() if self.on_floor(): acceleration = 6000 else: acceleration = 3000 max_speed = 500 if not self.dead: if pressed[pygame.K_a]: self.x_velocity -= dt * acceleration if pressed[pygame.K_d]: self.x_velocity += dt * acceleration if pressed[pygame.K_w]: if not self.on_floor(): self.y_velocity -= 1400 * dt for event in events: if event.type == pygame.KEYDOWN: if event.key == pygame.K_w: if not self.dead: self.jump() invisible_force = 40 if not self.on_floor(): invisible_force = 20 if self.x > c.WINDOW_WIDTH * 0.5: diff = self.x - c.WINDOW_WIDTH * 0.4 self.x_velocity -= invisible_force * diff * dt elif self.x < c.WINDOW_WIDTH * 0.25: diff = c.WINDOW_WIDTH * 0.25 - self.x self.x_velocity += invisible_force * diff * dt if self.x_velocity > max_speed: self.x_velocity = max_speed if self.x_velocity < -max_speed: self.x_velocity = -max_speed def jump(self): if self.on_floor() or self.extra_jumps > 0: self.jump_noise.play() self.y_velocity = -660 if not self.on_floor(): self.extra_jumps -= 1 def draw(self, surface): sx, sy = self.game.get_shake_offset() x, y = int(self.x + sx), int(self.y + sy) self.sprite.set_position((x, y)) self.sprite.draw(surface) mpos = pygame.mouse.get_pos() dx = mpos[0] - self.x dy = mpos[1] - self.y angle = -math.atan2(dy, dx) * 180 / math.pi if self.dead: angle = 0 gun = pygame.transform.rotate(self.gun, angle) gx = x - gun.get_width() // 2 gy = y - gun.get_height() // 2 - 14 surface.blit(gun, (gx, gy)) if self.hit_circle_radius < 1000: pass #pygame.draw.circle(surface, c.WHITE, (int(self.x), int(self.y)), int(self.hit_circle_radius+5), 5) for bullet in self.bullets: bullet.draw(surface) def shoot(self): if not self.bullet_cooldown > self.bullet_period: return if self.dead: return if not self.movement_enabled: return self.bullet_cooldown = 0 self.game.particles.append(Shell(self.game, self.x, self.y)) mpos = pygame.mouse.get_pos() dx = mpos[0] - self.x dy = mpos[1] - self.y mag = (dx**2 + dy**2)**0.5 dir = (dx / mag, dy / mag) new_bullet = Bullet(self.game, self.x, self.y, dir) self.bullets.add(new_bullet) recoil = 80 self.game.shake(3) self.x_velocity -= recoil * dir[0] if not self.on_floor(): self.y_velocity -= recoil * dir[1] self.gunfire.play() def on_floor(self): off = self.height // 3 if self.y >= c.WINDOW_HEIGHT // 2 + self.game.corridor.width // 2 - off: self.extra_jumps = 1 return True return False def collide_corridor(self, corridor): off = self.height // 3 if self.y + off > c.WINDOW_HEIGHT // 2 + corridor.width // 2: self.y = c.WINDOW_HEIGHT // 2 + corridor.width // 2 - off self.y_velocity = 0 elif self.y - off < c.WINDOW_HEIGHT // 2 - corridor.width // 2: self.y = c.WINDOW_HEIGHT // 2 - corridor.width // 2 + off self.y_velocity = 0 def get_hit_by(self, enemy): if self.in_bus == True: return if self.since_hit < self.invincibility_period: return self.game.shake(25) self.since_hit = 0 self.hp -= 1 self.game.flash_alpha = 100 self.game.slowdown = 0.1 if self.hp == 0: self.game.flash_alpha = 255 self.die() else: self.hurt_noise.play() self.hit_circle_radius = 0 pickups = self.game.pickups[:] for enemy in self.game.enemies: enemy.die() self.game.pickups = pickups dx = self.x - enemy.x dy = self.y - enemy.y dx, dy = dx / (abs(dx) + abs(dy)), dy / (abs(dx) + abs(dy)) self.x_velocity += dx * 1000 self.y_velocity += dy * 1000 enemy.velocity[0] -= dx * 1000 enemy.velocity[1] -= dy * 1000 self.sprite.start_animation("Hit")
class Blocker(Enemy): def __init__(self, game): self.radius = 50 self.game = game super().__init__(game, c.WINDOW_WIDTH + self.radius, self.game.corridor.floor_y() - self.radius, self.radius) self.hp = 4 self.value = 6 self.velocity = [-self.game.scroll_speed, 0] self.tink_sound = pygame.mixer.Sound("sounds/tink.wav") self.sprite = Sprite(10) self.sprite.add_animation({"Idle": blocker, "Hurt": blocker_hurt, "Dead": blocker_dead}) self.sprite.start_animation("Idle") self.shield_surf = pygame.image.load("images/blocker_shield.png") self.shield_surf = pygame.transform.scale(self.shield_surf, (self.shield_surf.get_width()*2, self.shield_surf.get_height()*2)) self.shield_offset = 0 self.since_hit = 10 self.playing_idle = True self.remove_on_death = False def update(self, dt, events): super().update(dt, events) self.sprite.update(dt) self.x += self.velocity[0] * dt self.y += self.velocity[1] * dt self.shield_offset *= 0.003**dt self.since_hit += dt if self.since_hit > 0.04 and self.playing_idle == False and not self.dead: self.playing_idle = True self.sprite.start_animation("Idle") if self.dead: self.sprite.start_animation("Dead") if self.dead: self.velocity[1] += 2500*dt if self.y >= self.game.corridor.floor_y() - self.radius and self.velocity[1] >= 0: self.y = self.game.corridor.floor_y() - self.radius self.velocity = [-self.game.scroll_speed, 0] def draw(self, surface): sx, sy = self.game.get_shake_offset() self.sprite.set_position((sx + self.x, sy + self.y)) self.sprite.draw(surface) x = self.x - self.radius + sx - 20 + self.shield_offset y = self.y - self.radius + sy if not self.dead: surface.blit(self.shield_surf, (x, y)) def get_hit_by(self, bullet): if self.dead: return if bullet.x > self.x - self.radius * 0.75 and bullet.dir[1]*2 > bullet.dir[0]: super().get_hit_by(bullet) self.sprite.start_animation("Hurt") self.playing_idle = False self.since_hit = 0 else: self.tink_sound.play() self.shield_offset = 15 pass def die(self): super().die() self.velocity = [random.random()*200 - 100 - self.game.scroll_speed, -300]
class Crawler(Enemy): CRAWLING = 0 LAUNCHING = 1 DEAD = 2 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.speed = -400 self.velocity = [0, 0] self.launch_speed = 1000 self.state = self.CRAWLING self.radius = 24 self.y = c.WINDOW_HEIGHT//2 + self.game.corridor.width//2 - self.radius self.hp = 2 self.remove_on_death = False self.sprite = Sprite(12) self.sprite.add_animation({"Running":crawler_running, "Jumping":crawler_jump, "Hurt":crawler_hurt, "Dead":crawler_dead}) self.sprite.start_animation("Running") self.detect_range = 200 self.since_hurt = 10 self.hurt_playing = False self.since_land = 10 self.value = 3 def get_hit_by(self, bullet): super().get_hit_by(bullet) if not self.state == self.DEAD: self.since_hurt = 0 self.hurt_playing = True self.sprite.start_animation("Hurt") if self.state == self.LAUNCHING: self.velocity[0] += bullet.dir[0] * 300 self.velocity[1] += bullet.dir[1] * 300 elif self.state == self.CRAWLING: self.x += 5 def update(self, dt, events): super().update(dt, events) dx = self.x - self.game.player.x dy = self.y - self.game.player.y if dx < self.detect_range and dx > 0 and self.state == self.CRAWLING and self.since_land > 2: self.state = self.LAUNCHING self.velocity = self.launch_velocity() self.sprite.start_animation("Jumping") if self.state == self.CRAWLING: self.x += self.speed * dt elif self.state == self.LAUNCHING: self.velocity[1] += 2000*dt self.x += self.velocity[0] * dt self.y += self.velocity[1] * dt if self.y >= c.WINDOW_HEIGHT//2 + self.game.corridor.width//2 - self.radius and self.velocity[1] > 0: self.reset_as_floor_crawler() self.since_land = 0 return elif self.state == self.DEAD: self.velocity[1] += 2000*dt self.x += self.velocity[0] * dt self.y += self.velocity[1] * dt if self.y > c.WINDOW_HEIGHT//2 + self.game.corridor.width//2 - self.radius: self.y = c.WINDOW_HEIGHT//2 + self.game.corridor.width//2 - self.radius self.velocity[0] = -self.game.scroll_speed if self.since_hurt > 0.05 and self.hurt_playing and not self.state == self.DEAD: if self.state == self.CRAWLING: self.sprite.start_animation("Running") elif self.state == self.LAUNCHING: self.sprite.start_animation("Jumping") if self.y <= c.WINDOW_HEIGHT//2 - self.game.corridor.width//2 + self.radius: self.y = c.WINDOW_HEIGHT//2 - self.game.corridor.width//2 + self.radius self.since_hurt += dt self.since_land += dt self.sprite.update(dt) def launch_velocity(self): angle = math.pi * 3/4 angle += (random.random() * math.pi/6) - math.pi/12 x = math.cos(angle) * self.launch_speed y = -math.sin(angle) * self.launch_speed return [x, y] def draw(self, surface): if not self.is_colliding_with_player(self.game.player): color = c.RED else: color = c.BLUE sx, sy = self.game.get_shake_offset() x, y = int(self.x + sx), int(self.y + sy) self.sprite.set_position((x, y)) self.sprite.draw(surface) def die(self): super().die() self.state = self.DEAD self.sprite.start_animation("Dead") new_y = -300 if self.y < c.WINDOW_HEIGHT//2 - self.game.corridor.width//2 + 200 else -150 self.velocity = [-random.random() * 200, new_y] def reset_as_floor_crawler(self): Crawler.__init__(self, self.game, self.x, self.y) self.y = c.WINDOW_HEIGHT//2 + self.game.corridor.width//2 - self.radius
class Dasher(Enemy): HOVERING = 0 DASHING = 1 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.hover_x = c.WINDOW_WIDTH * 0.8 self.hover_length = 2 self.x_velocity = 0 self.velocity = [-1, -1] self.hp = 7 self.value = 6 self.shiver_x = 0 self.shiver_y = 0 self.shiver_target = [1, 0] self.shiver_intensity = 0 self.since_hit = 10 self.since_particle = 0 self.state = self.HOVERING self.sprite = Sprite(12) self.sprite.add_animation({"Hovering": dasher_hovering, "Dashing": dasher_dashing, "Damage": dasher_damage, "DashDamage": dasher_dashing_damage}) self.sprite.start_animation("Hovering") def update(self, dt, events): super().update(dt, events) if abs(self.shiver_x - self.shiver_target[0]) < 10 and abs(self.shiver_y - self.shiver_target[1]) < 10: self.shiver_target[0] = random.random() * 2 * self.shiver_intensity - self.shiver_intensity self.shiver_target[1] = random.random() * 2 * self.shiver_intensity - self.shiver_intensity if self.age < self.hover_length: if self.x > self.hover_x: self.x -= (self.x - self.hover_x) * 7 * dt self.shiver_intensity = self.age * 10 else: self.state = self.DASHING self.sprite.start_animation("Dashing") self.x_velocity = -1600 self.x += self.x_velocity * dt self.shiver_target = [0, 0] self.shiver_x += (self.shiver_target[0] - self.shiver_x) * self.shiver_intensity * dt * 2.5 self.shiver_y += (self.shiver_target[1] - self.shiver_y) * self.shiver_intensity * dt * 2.5 self.check_player_bullets() if self.hp <= 0 and not self.dead: self.die() self.since_hit += dt self.since_particle += dt if self.since_hit > 0.04 and self.state == self.HOVERING: self.sprite.start_animation("Hovering") elif self.since_hit > 0.04 and self.state == self.DASHING: self.sprite.start_animation("Dashing") if self.since_particle >= 0.02: self.game.particles.insert(0, DasherSmokeRed(self.game, self.x, self.y)) self.game.particles.append(DasherSmoke(self.game, self.x, self.y)) self.since_particle = 0 self.sprite.update(dt) def draw(self, surface): xoff = math.sin(self.age * 5) * 15 yoff = math.sin(self.age * 7) * 15 sx, sy = self.game.get_shake_offset() x, y = int(self.x + self.shiver_x + sx), int(self.y + self.shiver_y + sy) self.sprite.set_position((x + xoff, y + yoff)) self.sprite.draw(surface) def get_hit_by(self, bullet): super().get_hit_by(bullet) if self.state == self.HOVERING: self.sprite.start_animation("Damage") self.since_hit = 0 if self.state == self.DASHING: self.sprite.start_animation("DashDamage") self.since_hit = 0 def die(self): super().die() self.game.shake(6)