# HG changeset patch # User Emmanuel Gil Peyrot # Date 1318969043 25200 # Node ID dcf32488a2c90575f2122f3b7ae7c90c9103c97a # Parent c8c60291c56fdf7d1751cdfe574c987d799fa555 Better enemy death, with animation and (hopefully) correct flags handling. diff --git a/pytouhou/game/enemy.py b/pytouhou/game/enemy.py --- a/pytouhou/game/enemy.py +++ b/pytouhou/game/enemy.py @@ -21,6 +21,24 @@ from pytouhou.game.item import Item from math import cos, sin, atan2, pi +class Effect(object): + def __init__(self, pos, index, anm_wrapper): + self._sprite = Sprite() + self._anmrunner = ANMRunner(anm_wrapper, index, self._sprite) + self._anmrunner.run_frame() + self._removed = False + + self.x, self.y = pos + + def update(self): + if self._anmrunner and not self._anmrunner.run_frame(): + self._anmrunner = None + + if self._sprite: + if self._sprite._removed: + self._sprite = None + + class Enemy(object): def __init__(self, pos, life, _type, bonus_dropped, anm_wrapper, game): self._game = game @@ -144,21 +162,18 @@ class Enemy(object): self._anmrunner.run_frame() - def collide(self): - #TODO: animation - #TODO: doesn’t always kill herself (a boss for example), search how - self._removed = True + def on_attack(self, bullet): + if self.damageable: + self.life -= bullet._bullet_type.damage - def killed(self): - if self.touchable: - if 0 <= self._bonus_dropped < 256: - self._game.drop_bonus(self.x, self.y, 0) - elif -256 <= self._bonus_dropped < 0: - pass #TODO: should be random, search how it is done. + def on_collide(self): + self.life -= 80 # found experimentally + - #TODO: use self.death_flags - self._removed = True + def die_anim(self): + eff00 = self._game.resource_loader.get_anm_wrapper(('eff00.anm',)) + self._game.effects.append(Effect((self.x, self.y), self.death_anim, eff00)) def set_pos(self, x, y, z): @@ -271,8 +286,5 @@ class Enemy(object): if self.bullet_launch_timer == self.bullet_launch_interval: self.fire() - if self.life <= 0: - self.killed() - self.frame += 1 diff --git a/pytouhou/game/game.py b/pytouhou/game/game.py --- a/pytouhou/game/game.py +++ b/pytouhou/game/game.py @@ -35,6 +35,7 @@ class Game(object): self.players = [Player(player_state, characters[player_state.character]) for player_state in player_states] self.enemies = [] + self.effects = [] self.bullets = [] self.cancelled_bullets = [] self.players_bullets = [] @@ -81,6 +82,7 @@ class Game(object): # 2. Filter out destroyed enemies self.enemies = [enemy for enemy in self.enemies if not enemy._removed] + self.effects = [enemy for enemy in self.effects if not enemy._removed] self.bullets = [bullet for bullet in self.bullets if not bullet._removed] self.cancelled_bullets = [bullet for bullet in self.cancelled_bullets if not bullet._removed] self.items = [item for item in self.items if not item._removed] @@ -101,6 +103,9 @@ class Game(object): for enemy in self.enemies: enemy.update() + for enemy in self.effects: + enemy.update() + for bullet in self.bullets: bullet.update() @@ -140,7 +145,7 @@ class Game(object): if enemy.touchable and not (bx2 < px1 or bx1 > px2 or by2 < py1 or by1 > py2): - enemy.collide() + enemy.on_collide() player.collide() for item in self.items: @@ -168,7 +173,7 @@ class Game(object): if not (bx2 < ex1 or bx1 > ex2 or by2 < ey1 or by1 > ey2): bullet.collide() - enemy.life -= bullet._bullet_type.damage + enemy.on_attack(bullet) # 5. Cleaning self.cleanup() diff --git a/pytouhou/opengl/gamerenderer.pyx b/pytouhou/opengl/gamerenderer.pyx --- a/pytouhou/opengl/gamerenderer.pyx +++ b/pytouhou/opengl/gamerenderer.pyx @@ -150,6 +150,7 @@ cdef class GameRenderer: glDisable(GL_FOG) self.render_elements(game.enemies) + self.render_elements(game.effects) self.render_elements(game.players) self.render_elements(game.bullets) self.render_elements(game.cancelled_bullets) diff --git a/pytouhou/vm/eclrunner.py b/pytouhou/vm/eclrunner.py --- a/pytouhou/vm/eclrunner.py +++ b/pytouhou/vm/eclrunner.py @@ -118,16 +118,33 @@ class ECLRunner(object): self.sub = enm.boss_callback self.instruction_pointer = 0 enm.boss_callback = None - if enm.life <= 0: + if enm.life <= 0 and enm.touchable: death_flags = enm.death_flags & 7 + enm.die_anim() + if death_flags < 4: if enm._bonus_dropped >= 0: self._game.drop_bonus(enm.x, enm.y, enm._bonus_dropped) elif enm._bonus_dropped == -1: self._game.drop_bonus(enm.x, enm.y, self._game.prng.rand_uint16() % 2) #TODO: find the formula in the binary. Can be big power sometimes. - if enm.death_callback is not None: + if death_flags == 0: + enm._removed = True + return + + if death_flags == 1: + enm.touchable = False + elif death_flags == 2: + pass # Just that? + elif death_flags == 3: + enm.damageable = False + enm.life = 1 + enm.death_flags = 0 + else: + pass #TODO: sparks + + if death_flags != 0 and enm.death_callback is not None: self.frame = 0 self.sub = enm.death_callback self.instruction_pointer = 0 @@ -702,7 +719,8 @@ class ECLRunner(object): @instruction(96) def kill_enemies(self): for enemy in self._game.enemies: - enemy.killed() + if enemy.touchable: + enemy.life = 0 @instruction(97)