view pytouhou/vm/anmrunner.py @ 182:20843875ad8f

(Hopefully) use difficulty as it should. The difficulty[0] (also called rank) varies from 0 to 32 and affects various parts of the game. The difficulty now impact those parts, but how it is modified during the gameplay is not clear. Such changes to the difficulty are not handled yet. [0] http://en.touhouwiki.net/wiki/Embodiment_of_Scarlet_Devil/Gameplay#Rank
author Thibaut Girka <thib@sitedethib.com>
date Tue, 25 Oct 2011 01:29:40 +0200
parents e7902309305c
children 741860192b56
line wrap: on
line source

# -*- encoding: utf-8 -*-
##
## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
##
## 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',
                 'sprite_index_offset',
                 'script', 'instruction_pointer', 'frame')


    def __init__(self, anm_wrapper, script_id, sprite, sprite_index_offset=0):
        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

        self.sprite_index_offset = sprite_index_offset


    def run_frame(self):
        if not self._running:
            return False

        sprite = self._sprite

        while self._running:
            frame, opcode, args = self.script[self.instruction_pointer]

            if frame > self.frame:
                break
            else:
                self.instruction_pointer += 1

            if frame == self.frame:
                try:
                    callback = self._handlers[opcode]
                except KeyError:
                    logger.warn('unhandled opcode %d (args: %r)', opcode, args)
                else:
                    callback(self, *args)
                    sprite._changed = True
        self.frame += 1

        # Update sprite
        sprite.frame += 1

        if sprite.rotations_speed_3d != (0., 0., 0.):
            ax, ay, az = sprite.rotations_3d
            sax, say, saz = sprite.rotations_speed_3d
            sprite.rotations_3d = ax + sax, ay + say, az + saz
            sprite._changed = True

        if sprite.scale_speed != (0., 0.):
            rx, ry = sprite.rescale
            rsx, rsy = sprite.scale_speed
            sprite.rescale = rx + rsx, ry + rsy
            sprite._changed = True

        if sprite.fade_interpolator:
            sprite.fade_interpolator.update(sprite.frame)
            sprite.alpha = int(sprite.fade_interpolator.values[0])
            sprite._changed = True

        if sprite.scale_interpolator:
            sprite.scale_interpolator.update(sprite.frame)
            sprite.rescale = sprite.scale_interpolator.values
            sprite._changed = True

        if sprite.offset_interpolator:
            sprite.offset_interpolator.update(sprite.frame)
            sprite.dest_offset = sprite.offset_interpolator.values
            sprite._changed = True

        return self._running


    @instruction(0)
    def remove(self):
        self._sprite._removed = True
        self._running = False


    @instruction(1)
    def load_sprite(self, sprite_index):
        self._sprite.anm, self._sprite.texcoords = self._anm_wrapper.get_sprite(sprite_index + self.sprite_index_offset)


    @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(12)
    def fade(self, new_alpha, duration):
        self._sprite.fade(duration, new_alpha, lambda x: x) #TODO: formula


    @instruction(13)
    def set_blendfunc_alphablend(self):
        self._sprite.blendfunc = 1


    @instruction(14)
    def set_blendfunc_add(self):
        self._sprite.blendfunc = 0 #TODO


    @instruction(15)
    @instruction(21) #TODO
    def keep_still(self):
        self._running = False

    @instruction(16)
    def load_random_sprite(self, min_idx, amp):
        #TODO: use the game's PRNG?
        self.load_sprite(min_idx + randrange(amp))


    @instruction(17)
    def move(self, x, y, z):
        self._sprite.dest_offset = (x, y, z)


    @instruction(18)
    def move_in_linear(self, x, y, z, duration):
        self._sprite.move_in(duration, x, y, z, lambda x: x)


    @instruction(19)
    def move_in_decel(self, x, y, z, duration):
        self._sprite.move_in(duration, x, y, z, lambda x: 2. * x - x ** 2)


    @instruction(20)
    def move_in_accel(self, x, y, z, duration):
        self._sprite.move_in(duration, x, y, z, lambda x: x ** 2)


    @instruction(23)
    def set_corner_relative_placement(self):
        self._sprite.corner_relative_placement = True #TODO


    @instruction(25)
    def set_allow_dest_offset(self, value):
        self._sprite.allow_dest_offset = bool(value)


    @instruction(26)
    def set_automatic_orientation(self, value):
        """If true, rotate by pi-angle around the z axis.
        """
        self._sprite.automatic_orientation = bool(value)


    @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(30)
    def scale_in(self, sx, sy, duration):
        self._sprite.scale_in(duration, sx, sy, lambda x: x) #TODO: formula