changeset 236:741860192b56

Implement ANM0 interrupts “Instruction” 22 is used as a label for interrupts. If the normal animation is interrupted, it goes straight to the matched instruction. Interrupt -1 matches all interrupts.
author Thibaut Girka <thib@sitedethib.com>
date Sun, 01 Jan 2012 19:47:34 +0100
parents e59bd7979ddc
children cbe9dbd80dfb
files pytouhou/formats/anm0.py pytouhou/formats/exe.py pytouhou/game/bullet.py pytouhou/game/player.py pytouhou/ui/sprite.pyx pytouhou/vm/anmrunner.py
diffstat 6 files changed, 50 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/formats/anm0.py
+++ b/pytouhou/formats/anm0.py
@@ -29,6 +29,13 @@ logger = get_logger(__name__)
 #TODO: refactor/clean up
 
 
+class Script(list):
+    def __init__(self):
+        list.__init__(self)
+        self.interrupts = {}
+
+
+
 class Animations(object):
     _instructions = {0: ('', 'delete'),
                      1: ('I', 'set_sprite'),
@@ -49,8 +56,8 @@ class Animations(object):
                      18: ('fffi', 'move_to_linear'),
                      19: ('fffi', 'move_to_decel'),
                      20: ('fffi', 'move_to_accel'),
-                     21: ('', None),
-                     22: ('i', None),
+                     21: ('', 'wait'),
+                     22: ('i', 'interrupt_label'),
                      23: ('', 'set_corner_relative_placement'),
                      24: ('', None),
                      25: ('i', 'set_allow_offset'), #TODO: better name
@@ -107,7 +114,7 @@ class Animations(object):
         # Scripts
         anm.scripts = {}
         for i, offset in script_offsets:
-            anm.scripts[i] = []
+            anm.scripts[i] = Script()
             instruction_offsets = []
             file.seek(offset)
             while True:
@@ -125,11 +132,14 @@ class Animations(object):
                 if opcode == 0:
                     break
 
-            # Translate offsets to instruction pointers
+            # Translate offsets to instruction pointers and register interrupts
             for instr_offset, (j, instr) in zip(instruction_offsets, enumerate(anm.scripts[i])):
                 time, opcode, args = instr
                 if opcode == 5:
                     args = (instruction_offsets.index(args[0]),)
+                if opcode == 22:
+                    interrupt = args[0]
+                    anm.scripts[i].interrupts[interrupt] = j + 1
                 anm.scripts[i][j] = time, opcode, args
         #TODO
 
--- a/pytouhou/formats/exe.py
+++ b/pytouhou/formats/exe.py
@@ -155,7 +155,6 @@ class SHT(object):
                     shots_offsets[offset] = []
                 shots_offsets[offset].append(sht)
 
-        character = 0
         for shots_offset, shts in shots_offsets.iteritems():
             pe_file.seek_to_va(shots_offset)
 
@@ -187,8 +186,6 @@ class SHT(object):
             for sht in shts:
                 sht.shots = shots
 
-            character += 1
-
 
         return characters
 
--- a/pytouhou/game/bullet.py
+++ b/pytouhou/game/bullet.py
@@ -19,6 +19,7 @@ from pytouhou.vm.anmrunner import ANMRun
 from pytouhou.game.sprite import Sprite
 
 
+
 class Bullet(object):
     def __init__(self, pos, bullet_type, sprite_idx_offset,
                        angle, speed, attributes, flags, target, game,
@@ -186,6 +187,7 @@ class Bullet(object):
             length, angle = self.attributes[4:6]
             angle = self.angle if angle < -900.0 else angle #TODO: is that right?
             dx, dy = dx + cos(angle) * length, dy + sin(angle) * length
+            self.speed = (dx ** 2 + dy ** 2) ** 0.5
             self.angle = sprite.angle = atan2(dy, dx)
             if sprite.automatic_orientation:
                 sprite._changed = True
--- a/pytouhou/game/player.py
+++ b/pytouhou/game/player.py
@@ -130,6 +130,7 @@ class Player(object):
             bullet_type = BulletType(self.anm_wrapper, shot.sprite % 256,
                                      shot.sprite % 256 + 32, #TODO: find the real cancel anim
                                      0, 0, 0, 0.)
+            #TODO: Type 1 (homing bullets) and type 3 (laser)
             if shot.type == 2:
                 #TODO: triple-check acceleration!
                 bullets.append(Bullet((x, y), bullet_type, 0,
@@ -137,7 +138,6 @@ class Player(object):
                                       (-1, 0, 0, 0, 0.15, -pi/2., 0., 0.),
                                       16, self, self._game, player_bullet=True,
                                       damage=shot.damage, hitbox=shot.hitbox))
-            #TODO: types 1 and 4
             else:
                 bullets.append(Bullet((x, y), bullet_type, 0,
                                       shot.angle, shot.speed,
--- a/pytouhou/ui/sprite.pyx
+++ b/pytouhou/ui/sprite.pyx
@@ -44,6 +44,8 @@ cpdef object get_sprite_rendering_data(o
     elif sprite.force_rotation:
         rz += sprite.angle
 
+    if sprite.allow_dest_offset:
+        vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2])
     if (rx, ry, rz) != (0., 0., 0.):
         if rx:
             vertmat.rotate_x(-rx)
@@ -53,8 +55,6 @@ cpdef object get_sprite_rendering_data(o
             vertmat.rotate_z(-rz) #TODO: minus, really?
     if sprite.corner_relative_placement: # Reposition
         vertmat.translate(width / 2., height / 2., 0.)
-    if sprite.allow_dest_offset:
-        vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2])
 
     x_1 = 1. / sprite.anm.size[0]
     y_1 = 1. / sprite.anm.size[1]
--- a/pytouhou/vm/anmrunner.py
+++ b/pytouhou/vm/anmrunner.py
@@ -25,13 +25,15 @@ class ANMRunner(object):
     __metaclass__ = MetaRegistry
     __slots__ = ('_anm_wrapper', '_sprite', '_running',
                  'sprite_index_offset',
-                 'script', 'instruction_pointer', 'frame')
+                 'script', 'instruction_pointer', 'frame',
+                 'waiting')
 
 
     def __init__(self, anm_wrapper, script_id, sprite, sprite_index_offset=0):
         self._anm_wrapper = anm_wrapper
         self._sprite = sprite
         self._running = True
+        self.waiting = False
 
         anm, self.script = anm_wrapper.get_script(script_id)
         self.frame = 0
@@ -40,10 +42,25 @@ class ANMRunner(object):
         self.sprite_index_offset = sprite_index_offset
 
 
+    def interrupt(self, interrupt):
+        new_ip = self.script.interrupts.get(interrupt, None)
+        if new_ip is None:
+            new_ip = self.script.interrupts.get(-1, None)
+        if new_ip is None:
+            return False
+        else:
+            self.instruction_pointer = new_ip
+            self.frame, opcode, args = self.script[self.instruction_pointer]
+            return True
+
+
     def run_frame(self):
         if not self._running:
             return False
 
+        if self.waiting:
+            return True
+
         sprite = self._sprite
 
         while self._running:
@@ -166,7 +183,6 @@ class ANMRunner(object):
 
 
     @instruction(15)
-    @instruction(21) #TODO
     def keep_still(self):
         self._running = False
 
@@ -196,6 +212,19 @@ class ANMRunner(object):
         self._sprite.move_in(duration, x, y, z, lambda x: x ** 2)
 
 
+    @instruction(21)
+    def wait(self):
+        """Wait for an interrupt.
+        """
+        self.waiting = True
+
+
+    @instruction(22)
+    def interrupt_label(self, interrupt):
+        """Noop"""
+        pass
+
+
     @instruction(23)
     def set_corner_relative_placement(self):
         self._sprite.corner_relative_placement = True #TODO