# HG changeset patch # User Thibaut Girka # Date 1314435534 -7200 # Node ID a142e57218a0fcea524c9053a997c58b789c45ba # Parent a2459defd4b649b0bfae7ea1ae6be5b4426cf9ce Refactor. Move VMs to pytouhou.vm. diff --git a/pytouhou/formats/anm0.py b/pytouhou/formats/anm0.py --- a/pytouhou/formats/anm0.py +++ b/pytouhou/formats/anm0.py @@ -13,12 +13,34 @@ ## from struct import pack, unpack -from pytouhou.utils.helpers import read_string +from pytouhou.utils.helpers import read_string, get_logger + + +logger = get_logger(__name__) #TODO: refactor/clean up class Animations(object): + _instructions = {0: ('', 'delete'), + 1: ('I', 'set_sprite'), + 2: ('ff', 'set_scale'), + 3: ('I', 'set_alpha'), + 4: ('BBBx', 'set_color'), + 5: ('I', 'jump'), + 7: ('', 'toggle_mirrored'), + 9: ('fff', 'set_3d_rotations'), + 10: ('fff', 'set_3d_rotations_speed'), + 11: ('ff', 'set_scale_speed'), + 12: ('ii', 'fade'), + 15: ('', 'keep_still'), + 16: ('i', 'set_random_sprite'), + 23: ('', 'set_corner_relative_placement'), + 27: ('f', 'shift_texture_x'), + 28: ('f', 'shift_texture_y'), + 30: ('ffi', 'scale_in')} + + def __init__(self): self.size = (0, 0) self.first_name = None @@ -71,40 +93,24 @@ class Animations(object): while True: #TODO instruction_offsets.append(file.tell() - offset) - time, instr_type, length = unpack(' self.frame: - break - else: - self.instruction_pointer += 1 - if frame == self.frame: - changed = True - if instr_type == 0: - self.playing = False - return False - if instr_type == 1: - #TODO: handle out-of-anm sprites - self.texcoords = self.anm.sprites[args[0]] - elif instr_type == 2: - self.rescale = args - elif instr_type == 3: - self.alpha = args[0] % 256 #TODO - elif instr_type == 4: - b, g, r = args - self.color = (r, g, b) - elif instr_type == 5: - self.instruction_pointer, = args - self.frame = script[self.instruction_pointer][0] - elif instr_type == 7: - self.mirrored = not self.mirrored - elif instr_type == 9: - self.rotations_3d = args - elif instr_type == 10: - self.rotations_speed_3d = args - elif instr_type == 11: - self.scale_speed = args - elif instr_type == 16: - #TODO: handle out-of-anm sprites - #TODO: use the game's PRNG? - self.texcoords = self.anm.sprites[args[0] + randrange(args[1])] - elif instr_type == 23: - self.corner_relative_placement = True #TODO - elif instr_type == 27: - tox, toy = self.texoffsets - self.texoffsets = tox + args[0], toy - elif instr_type == 28: - tox, toy = self.texoffsets - self.texoffsets = tox, toy + args[0] - elif instr_type in (15, 21): - self.keep_still = True - break - else: - logger.warn('unhandled instruction %d (args: %r)', instr_type, args) - self.frame += 1 - - ax, ay, az = self.rotations_3d - sax, say, saz = self.rotations_speed_3d - self.rotations_3d = ax + sax, ay + say, az + saz - self.rescale = self.rescale[0] + self.scale_speed[0], self.rescale[1] + self.scale_speed[1] - - if self.rotations_speed_3d != (0., 0., 0.) or self.scale_speed != (0., 0.): - return True - - return changed - diff --git a/pytouhou/vm/__init__.py b/pytouhou/vm/__init__.py new file mode 100644 diff --git a/pytouhou/vm/anmrunner.py b/pytouhou/vm/anmrunner.py new file mode 100644 --- /dev/null +++ b/pytouhou/vm/anmrunner.py @@ -0,0 +1,148 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + + +from random import randrange + +from pytouhou.utils.helpers import get_logger +from pytouhou.vm.common import MetaRegistry, instruction + +logger = get_logger(__name__) + + +class ANMRunner(object): + __metaclass__ = MetaRegistry + __slots__ = ('_anm_wrapper', '_sprite', '_running', + 'script', 'instruction_pointer', 'frame') + + + def __init__(self, anm_wrapper, script_id, sprite): + self._anm_wrapper = anm_wrapper + self._sprite = sprite + self._running = True + + anm, self.script = anm_wrapper.get_script(script_id) + self.frame = 0 + self.instruction_pointer = 0 + pass + + + def run_frame(self): + if self._sprite._removed: + return False + + while self._running: + try: + frame, instr_type, args = self.script[self.instruction_pointer] + except IndexError: + return False + + if frame > self.frame: + break + else: + self.instruction_pointer += 1 + + if frame == self.frame: + try: + callback = self._handlers[instr_type] + except KeyError: + logger.warn('unhandled opcode %d (args: %r)', instr_type, args) + else: + callback(self, *args) + self._sprite._changed = True + self.frame += 1 + return self._running + + + @instruction(0) + def remove(self): + self._sprite._removed = True + self._running = True + + + @instruction(1) + def load_sprite(self, sprite_index): + self._sprite.anm, self._sprite.texcoords = self._anm_wrapper.get_sprite(sprite_index) + + + @instruction(2) + def set_scale(self, sx, sy): + self._sprite.rescale = sx, sy + + + @instruction(3) + def set_alpha(self, alpha): + self._sprite.alpha = alpha % 256 #TODO + + + @instruction(4) + def set_color(self, b, g, r): + self._sprite.color = (r, g, b) + + + @instruction(5) + def jump(self, instruction_pointer): + #TODO: is that really how it works? + self.instruction_pointer = instruction_pointer + self.frame = self.script[self.instruction_pointer][0] + + + @instruction(7) + def toggle_mirrored(self): + self._sprite.mirrored = not self._sprite.mirrored + + + @instruction(9) + def set_rotations_3d(self, rx, ry, rz): + self._sprite.rotations_3d = rx, ry, rz + + + @instruction(10) + def set_rotations_speed_3d(self, srx, sry, srz): + self._sprite.rotations_speed_3d = srx, sry, srz + + + @instruction(11) + def set_scale_speed(self, ssx, ssy): + self._sprite.scale_speed = ssx, ssy + + + @instruction(16) + def load_random_sprite(self, min_idx, amp): + #TODO: use the game's PRNG? + self.load_sprite(min_idx + randrange(amp)) + + + @instruction(23) + def set_corner_relative_placement(self): + self._sprite.corner_relative_placement = True #TODO + + + @instruction(27) + def shift_texture_x(self, dx): + tox, toy = self._sprite.texoffsets + self._sprite.texoffsets = tox + dx, toy + + + @instruction(28) + def shift_texture_y(self, dy): + tox, toy = self._sprite.texoffsets + self._sprite.texoffsets = tox, toy + dy + + + @instruction(15) + @instruction(21) #TODO + def keep_still(self): + self._running = False + diff --git a/pytouhou/vm/common.py b/pytouhou/vm/common.py new file mode 100644 --- /dev/null +++ b/pytouhou/vm/common.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + + +class MetaRegistry(type): + def __new__(mcs, name, bases, classdict): + instruction_handlers = {} + for item in classdict.itervalues(): + try: + instruction_ids = item._instruction_ids + except AttributeError: + pass + else: + for id_ in instruction_ids: + instruction_handlers[id_] = item + classdict['_handlers'] = instruction_handlers + return type.__new__(mcs, name, bases, classdict) + + + +def instruction(instruction_id): + def _decorator(func): + if not hasattr(func, '_instruction_ids'): + func._instruction_ids = set() + func._instruction_ids.add(instruction_id) + return func + return _decorator diff --git a/pytouhou/game/eclrunner.py b/pytouhou/vm/eclrunner.py rename from pytouhou/game/eclrunner.py rename to pytouhou/vm/eclrunner.py --- a/pytouhou/game/eclrunner.py +++ b/pytouhou/vm/eclrunner.py @@ -17,33 +17,9 @@ from math import atan2, cos, sin from pytouhou.utils.helpers import get_logger -logger = get_logger(__name__) - - +from pytouhou.vm.common import MetaRegistry, instruction -class MetaRegistry(type): - def __new__(mcs, name, bases, classdict): - instruction_handlers = {} - for item in classdict.itervalues(): - try: - instruction_ids = item._instruction_ids - except AttributeError: - pass - else: - for id_ in instruction_ids: - instruction_handlers[id_] = item - classdict['_handlers'] = instruction_handlers - return type.__new__(mcs, name, bases, classdict) - - - -def instruction(instruction_id): - def _decorator(func): - if not hasattr(func, '_instruction_ids'): - func._instruction_ids = set() - func._instruction_ids.add(instruction_id) - return func - return _decorator +logger = get_logger(__name__) @@ -79,7 +55,6 @@ class ECLRunner(object): #TODO # Now, process script - frame = self.frame while True: try: frame, instr_type, rank_mask, param_mask, args = self._ecl.subs[self.sub][self.instruction_pointer]