changeset 166:dcf32488a2c9

Better enemy death, with animation and (hopefully) correct flags handling.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 18 Oct 2011 13:17:23 -0700
parents c8c60291c56f
children e483b5a7e84b
files pytouhou/game/enemy.py pytouhou/game/game.py pytouhou/opengl/gamerenderer.pyx pytouhou/vm/eclrunner.py
diffstat 4 files changed, 56 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- 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
 
--- 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()
--- 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)
--- 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)