changeset 181:184196480f59

Don’t use the useless eff00.anm and implement particles (grazing, death, and more).
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sun, 23 Oct 2011 14:50:44 -0700
parents 5a1533677a9a
children 20843875ad8f
files pytouhou/game/effect.py pytouhou/game/enemy.py pytouhou/game/game.py pytouhou/game/player.py pytouhou/vm/eclrunner.py
diffstat 5 files changed, 67 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/game/effect.py
+++ b/pytouhou/game/effect.py
@@ -15,6 +15,8 @@
 
 from pytouhou.game.sprite import Sprite
 from pytouhou.vm.anmrunner import ANMRunner
+from pytouhou.utils.interpolator import Interpolator
+from math import pi
 
 
 
@@ -27,6 +29,7 @@ class Effect(object):
 
         self.x, self.y = pos
 
+
     def update(self):
         if self._anmrunner and not self._anmrunner.run_frame():
             self._anmrunner = None
@@ -34,3 +37,41 @@ class Effect(object):
         if self._sprite:
             if self._sprite._removed:
                 self._sprite = None
+
+
+class Particle(object):
+    def __init__(self, start_pos, index, anm_wrapper, size, end_pos):
+        self._sprite = Sprite()
+        self._sprite.anm, self._sprite.texcoords = anm_wrapper.get_sprite(index)
+        self._removed = False
+
+        self.x, self.y = start_pos
+        self.frame = 0
+        self._sprite.alpha = 128
+        self._sprite.blendfunc = 1
+
+        self.pos_interpolator = Interpolator(start_pos, 0,
+                                             end_pos, 24, formula=(lambda x: 2. * x - x ** 2))
+        self.scale_interpolator = Interpolator((size, size), 0,
+                                               (0., 0.), 24)
+        self.rotations_interpolator = Interpolator((0., 0., 0.), 0,
+                                                   (0., 0., 2*pi), 24)
+        self._sprite._changed = True
+
+
+    def update(self):
+        self.pos_interpolator.update(self.frame)
+        self.x, self.y = self.pos_interpolator.values
+
+        self.scale_interpolator.update(self.frame)
+        self._sprite.rescale = self.scale_interpolator.values
+
+        self.rotations_interpolator.update(self.frame)
+        self._sprite.rotations_3d = self.rotations_interpolator.values
+
+        self._sprite._changed = True
+
+        if self.frame == 24:
+            self._removed = True
+
+        self.frame += 1
--- a/pytouhou/game/enemy.py
+++ b/pytouhou/game/enemy.py
@@ -156,7 +156,11 @@ class Enemy(object):
 
 
     def die_anim(self):
-        self._game.new_effect((self.x, self.y), self.death_anim)
+        self._game.new_death((self.x, self.y), self.death_anim)
+        #TODO: 8 white particles are used only in stage 3 to 6,
+        # in other stages they are 2 red and 6 blue.
+        for i in range(8):
+            self._game.new_particle((self.x, self.y), 0, 3., 192)
 
 
     def set_pos(self, x, y, z):
--- a/pytouhou/game/game.py
+++ b/pytouhou/game/game.py
@@ -21,6 +21,7 @@ from pytouhou.game.player import Player
 from pytouhou.game.enemy import Enemy
 from pytouhou.game.item import Item
 from pytouhou.game.effect import Effect
+from pytouhou.game.effect import Particle
 
 
 
@@ -52,7 +53,7 @@ class Game(object):
 
         self.enm_anm_wrapper = resource_loader.get_anm_wrapper2(('stg%denm.anm' % stage,
                                                                  'stg%denm2.anm' % stage))
-        self.eff00 = resource_loader.get_anm_wrapper(('eff00.anm',))
+        self.etama4 = resource_loader.get_anm_wrapper(('etama4.anm',))
         ecl = resource_loader.get_ecl('ecldata%d.ecl' % stage)
         self.ecl_runner = ECLMainRunner(ecl, self)
 
@@ -73,8 +74,15 @@ class Game(object):
         self.bullets = []
 
 
-    def new_effect(self, pos, anim):
-        self.effects.append(Effect(pos, anim, self.eff00))
+    def new_death(self, pos, index):
+        anim = {0: 3, 1: 4, 2: 5}[index % 256] # The TB is wanted, if index isn’t in these values the original game crashs.
+        self.effects.append(Effect(pos, anim, self.etama4))
+
+
+    def new_particle(self, pos, color, size, amp):
+        self.effects.append(Particle(pos, 7 + 4 * color + self.prng.rand_uint16() % 4, self.etama4, size,
+                                     (pos[0] + amp * self.prng.rand_double() - amp/2,
+                                      pos[1] + amp * self.prng.rand_double() - amp/2)))
 
 
     def new_enemy(self, pos, life, instr_type, bonus_dropped, die_score):
@@ -152,9 +160,13 @@ class Game(object):
                     if player.state.invulnerable_time == 0:
                         player.collide()
 
-                elif not (bx2 < gx1 or bx1 > gx2
+                elif not bullet.grazed and not (bx2 < gx1 or bx1 > gx2
                         or by2 < gy1 or by1 > gy2):
-                    pass#TODO: graze
+                    bullet.grazed = True
+                    player.state.score += 500 # found experimentally
+                    self.new_particle((px, py), 0, .8, 192)
+                    #TODO: display a static particle during one frame at
+                    # 12 pixels of the player, in the axis of the “collision”.
 
             for enemy in self.enemies:
                 half_size_x, half_size_y = enemy.hitbox_half_size
--- a/pytouhou/game/player.py
+++ b/pytouhou/game/player.py
@@ -77,7 +77,7 @@ class Player(object):
     def collide(self):
         if not self.state.invulnerable_time and not self.death_time and self.state.touchable: # Border Between Life and Death
             self.death_time = self._game.frame
-            self._game.new_effect((self.state.x, self.state.y), 2)
+            self._game.new_death((self.state.x, self.state.y), 2)
 
 
     def collect(self, item):
@@ -131,6 +131,8 @@ class Player(object):
                 for i in range(5):
                     self._game.drop_bonus(self.state.x, self.state.y, 0, end_pos=None) #TODO: find the formula
                 self.state.lives -= 1
+                for i in range(16):
+                    self._game.new_particle((self.state.x, self.state.y), 0, 4., 256)
 
             elif time == 7:
                 self._sprite.mirrored = False
--- a/pytouhou/vm/eclrunner.py
+++ b/pytouhou/vm/eclrunner.py
@@ -743,7 +743,7 @@ class ECLRunner(object):
 
     @instruction(100)
     def set_death_anim(self, sprite_index):
-        self._enemy.death_anim = sprite_index % 256 #TODO
+        self._enemy.death_anim = sprite_index
 
 
     @instruction(101)