# HG changeset patch # User Thibaut Girka # Date 1325443654 -3600 # Node ID 741860192b56dc75b93575097a8d935d1270ead2 # Parent e59bd7979ddccee921a943a53943c1efc401681b 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. diff --git a/pytouhou/formats/anm0.py b/pytouhou/formats/anm0.py --- 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 diff --git a/pytouhou/formats/exe.py b/pytouhou/formats/exe.py --- 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 diff --git a/pytouhou/game/bullet.py b/pytouhou/game/bullet.py --- 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 diff --git a/pytouhou/game/player.py b/pytouhou/game/player.py --- 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, diff --git a/pytouhou/ui/sprite.pyx b/pytouhou/ui/sprite.pyx --- 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] diff --git a/pytouhou/vm/anmrunner.py b/pytouhou/vm/anmrunner.py --- 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