changeset 50:811cefefb5c8

Fix a few bugs and add support for a few instructions
author Thibaut Girka <thib@sitedethib.com>
date Mon, 22 Aug 2011 21:16:47 +0200
parents cbe1cb50f2fd
children 0707ff53e7b5
files eclviewer.py pytouhou/formats/ecl.py pytouhou/game/eclrunner.py pytouhou/game/enemymanager.py pytouhou/game/game.py pytouhou/game/player.py pytouhou/utils/interpolator.py
diffstat 7 files changed, 141 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/eclviewer.py
+++ b/eclviewer.py
@@ -19,6 +19,7 @@ from pytouhou.game.background import Bac
 from pytouhou.game.enemymanager import EnemyManager
 from pytouhou.opengl.texture import TextureManager
 from pytouhou.game.game import GameState
+from pytouhou.game.player import Player
 
 import OpenGL
 OpenGL.FORWARD_COMPATIBLE_ONLY = True
@@ -62,7 +63,7 @@ def main(path, stage_num):
             pass
         else:
             anims.append(enemies2_anim)
-        enemy_manager = EnemyManager(stage, AnmWrapper(anims), ecl, GameState([], stage_num, 0, 16))
+        enemy_manager = EnemyManager(stage, AnmWrapper(anims), ecl, GameState([Player()], stage_num, 0, 16))
         texture_manager.preload(anims)
 
         background_anim = Animations.read(BytesIO(archive.extract('stg%dbg.anm' % stage_num)))
--- a/pytouhou/formats/ecl.py
+++ b/pytouhou/formats/ecl.py
@@ -51,8 +51,8 @@ class ECL(object):
                      59: ('iffi', 'move_to2'),
                      61: ('i', 'stop_in'),
                      63: ('i', None),
-                     65: ('ffff', None),
-                     66: ('', None),
+                     65: ('ffff', 'set_screen_box'),
+                     66: ('', 'clear_screen_box'),
                      67: ('hhiiffffi', 'set_bullet_attributes'),
                      68: ('hhiiffffi', 'set_bullet_attributes2'),
                      69: ('hhiiffffi', 'set_bullet_attributes3'),
--- a/pytouhou/game/eclrunner.py
+++ b/pytouhou/game/eclrunner.py
@@ -56,24 +56,25 @@ class ECLRunner(object):
 
         # Now, process script
         frame = self.frame
-        try:
-            while frame <= self.frame:
+        while True:
+            try:
                 frame, instr_type, rank_mask, param_mask, args = self._ecl.subs[self.sub][self.instruction_pointer]
+            except IndexError:
+                return False
 
-                #TODO: skip bad ranks
+            #TODO: skip bad ranks
 
-                if frame == self.frame:
-                    try:
-                        callback = self._handlers[instr_type]
-                    except KeyError:
-                        print('Warning: unhandled opcode %d!' % instr_type) #TODO
-                    else:
-                        callback(self, *args)
-                        frame, instr_type, rank_mask, param_mask, args = self._ecl.subs[self.sub][self.instruction_pointer]
-                if frame <= self.frame:
-                    self.instruction_pointer += 1
-        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:
+                    print('Warning: unhandled opcode %d!' % instr_type) #TODO
+                else:
+                    callback(self, *args)
 
         self.frame += 1
         return True
@@ -83,18 +84,51 @@ class ECLRunner(object):
         if -10012 <= value <= -10001:
             return self.variables[int(-10001-value)]
         elif -10025 <= value <= -10013:
+            if value == -10013:
+                return self._game_state.rank
+            elif value == -10014:
+                return self._game_state.difficulty
+            elif value == -10015:
+                return self._enemy.x
+            elif value == -10016:
+                return self._enemy.y
+            elif value == -10017:
+                return self._enemy.z
+            elif value == -10018:
+                player = self._enemy.select_player(self._game_state.players)
+                return player.x
+            elif value == -10019:
+                player = self._enemy.select_player(self._game_state.players)
+                return player.y
+            elif value == -10021:
+                player = self._enemy.select_player(self._game_state.players)
+                return self._enemy.get_player_angle(player)
+            elif value == -10022:
+                return self._enemy.frame
+            elif value == -10024:
+                return self._enemy.life
             raise NotImplementedError #TODO
         else:
             return value
 
 
     def _setval(self, variable_id, value):
-        if -10012 <= value <= -10001:
+        if -10012 <= variable_id <= -10001:
             self.variables[int(-10001-variable_id)] = value
-        elif -10025 <= value <= -10013:
-            raise NotImplementedError #TODO
+        elif -10025 <= variable_id <= -10013:
+            if value == -10015:
+                self._enemy.x = value
+            elif value == -10016:
+                self._enemy.y = value
+            elif value == -10017:
+                self._enemy.z = value
+            elif value == -10022:
+                self._enemy.frame = value
+            elif value == -10024:
+                self._enemy.life = value
+            raise IndexError #TODO: proper exception
         else:
-            raise IndexError #TODO
+            raise IndexError #TODO: proper exception
 
 
     @instruction(1)
@@ -110,16 +144,38 @@ class ECLRunner(object):
 
     @instruction(3)
     def relative_jump_ex(self, frame, instruction_pointer, variable_id):
-        if self.variables[-10001-variable_id]:
-            self.variables[-10001-variable_id] -= 1
+        counter_value = self._getval(variable_id)
+        if counter_value:
+            self._setval(variable_id, counter_value - 1)
             self.frame, self.instruction_pointer = frame, instruction_pointer
 
 
     @instruction(4)
     @instruction(5)
     def set_variable(self, variable_id, value):
-        #TODO: -10013 and beyond!
-        self.variables[-10001-variable_id] = self._getval(value)
+        self._setval(variable_id, self._getval(value))
+
+
+    @instruction(6)
+    def set_random_int(self, variable_id, maxval):
+        self._setval(variable_id, int(self._getval(maxval) * self._game_state.prng.rand_double()))
+
+
+    @instruction(8)
+    def set_random_float(self, variable_id, maxval):
+        self._setval(variable_id, self._getval(maxval) * self._game_state.prng.rand_double())
+
+
+    @instruction(20)
+    def add(self, variable_id, a, b):
+        #TODO: int vs float thing
+        self._setval(variable_id, self._getval(a) + self._getval(b))
+
+
+    @instruction(21)
+    def substract(self, variable_id, a, b):
+        #TODO: int vs float thing
+        self._setval(variable_id, self._getval(a) - self._getval(b))
 
 
     @instruction(35)
@@ -139,18 +195,10 @@ class ECLRunner(object):
         self.sub, self.frame, self.instruction_pointer, self.variables = self.stack.pop()
 
 
-    @instruction(20)
-    def add(self, variable_id, a, b):
-        #TODO: proper variable handling
-        #TODO: int vs float thing
-        self.variables[-10001-variable_id] = self._getval(a) + self._getval(b)
-
-
-    @instruction(21)
-    def substract(self, variable_id, a, b):
-        #TODO: proper variable handling
-        #TODO: int vs float thing
-        self.variables[-10001-variable_id] = self._getval(a) - self._getval(b)
+    @instruction(39)
+    def call_if_equal(self, sub, param1, param2, a, b):
+        if self._getval(a) == self._getval(b):
+            self.call(sub, param1, param2)
 
 
     @instruction(43)
@@ -180,9 +228,9 @@ class ECLRunner(object):
 
     @instruction(51)
     def target_player(self, unknown, speed):
-        self._enemy.speed = speed #TODO: unknown
-        player_x, player_y = 192., 384.#TODO
-        self._enemy.angle = atan2(player_y - self._enemy.y, player_x - self._enemy.x) #TODO
+        #TODO: unknown
+        self._enemy.speed = speed
+        self._enemy.angle = self._enemy.get_player_angle(self._enemy.select_player(self._game_state.players))
 
 
     @instruction(57)
@@ -190,6 +238,16 @@ class ECLRunner(object):
         self._enemy.move_to(duration, x, y, z)
 
 
+    @instruction(65)
+    def set_screen_box(self, xmin, ymin, xmax, ymax):
+        self._enemy.screen_box = xmin, ymin, xmax, ymax
+
+
+    @instruction(66)
+    def clear_screen_box(self):
+        self._enemy.screen_box = None
+
+
     @instruction(77)
     def set_bullet_interval(self, value):
         self._enemy.bullet_launch_interval = value
--- a/pytouhou/game/enemymanager.py
+++ b/pytouhou/game/enemymanager.py
@@ -8,10 +8,6 @@ from pytouhou.game.sprite import Sprite
 from math import cos, sin, atan2
 
 
-from pytouhou.utils.random import Random
-random = Random(0x39f4)
-
-
 class Enemy(object):
     def __init__(self, pos, life, _type, anm_wrapper):
         self._anm_wrapper = anm_wrapper
@@ -48,6 +44,7 @@ class Enemy(object):
         self.acceleration = 0.
 
         self.hitbox = (0, 0)
+        self.screen_box = None
 
 
     def set_bullet_attributes(self, bullet_anim, launch_anim, bullets_per_shot,
@@ -61,6 +58,14 @@ class Enemy(object):
             #TODO: actually fire
 
 
+    def select_player(self, players):
+        return players[0] #TODO
+
+
+    def get_player_angle(self, player):
+        return atan2(player.y - self.y, player.x - self.x)
+
+
     def set_anim(self, index):
         self._anm, self._sprite = self._anm_wrapper.get_sprite(index)
 
@@ -111,8 +116,7 @@ class Enemy(object):
 
     def update(self, frame):
         x, y = self.x, self.y
-        if self.interpolator:
-            self.interpolator.update(self.frame)
+        if self.interpolator and self.interpolator.update(self.frame):
             x, y = self.interpolator.values
 
         self.speed += self.acceleration #TODO: units? Execution order?
@@ -137,6 +141,13 @@ class Enemy(object):
                 self.set_anim(self.movement_dependant_sprites[{-1: 0, +1:1}[self.direction]])
                 self.direction = None
 
+
+        if self.screen_box:
+            xmin, ymin, xmax, ymax = self.screen_box
+            x = max(xmin, min(x, xmax))
+            y = max(ymin, min(y, ymax))
+
+
         self.x, self.y = x, y
         if self._sprite:
             changed = self._sprite.update()
@@ -180,12 +191,12 @@ class EnemyManager(object):
                 if instr_type in (0, 2, 4, 6): # Normal/mirrored enemy
                     x, y, z, life, unknown1, unknown2, unknown3 = args
                     if instr_type & 4:
-                        if x < -990:
-                            x = random.rand_double() * 368 #102h.exe@0x411820
-                        if y < -990:
-                            y = random.rand_double() * 416 #102h.exe@0x41184b
-                        if z < -990:
-                            y = random.rand_double() * 800 #102h.exe@0x411881
+                        if x < -990: #102h.exe@0x411820
+                            x = self._game_state.prng.rand_double() * 368
+                        if y < -990: #102h.exe@0x41184b
+                            y = self._game_state.prng.rand_double() * 416
+                        if z < -990: #102h.exe@0x411881
+                            y = self._game_state.prng.rand_double() * 800
                     enemy = Enemy((x, y), life, instr_type, self.anm_wrapper)
                     self.enemies.append(enemy)
                     self.processes.append(ECLRunner(self.ecl, sub, enemy, self._game_state))
--- a/pytouhou/game/game.py
+++ b/pytouhou/game/game.py
@@ -1,9 +1,12 @@
+from pytouhou.utils.random import Random
+
 class GameState(object):
-    __slots__ = ('players', 'rank', 'difficulty', 'frame', 'stage', 'boss')
+    __slots__ = ('players', 'rank', 'difficulty', 'frame', 'stage', 'boss', 'prng')
     def __init__(self, players, stage, rank, difficulty):
         self.stage = stage
         self.players = players
         self.rank = rank
         self.difficulty = difficulty
         self.boss = None
+        self.prng = Random()
         self.frame = 0
new file mode 100644
--- /dev/null
+++ b/pytouhou/game/player.py
@@ -0,0 +1,10 @@
+class Player(object):
+    def __init__(self):
+        self.x = 192.0
+        self.y = 384.0
+        self.score = 0
+        self.graze = 0
+        self.power = 0
+        self.lives = 0
+        self.bombs = 0
+        self.type = 0 # ReimuA/ReimuB/MarisaA/MarisaB/...
--- a/pytouhou/utils/interpolator.py
+++ b/pytouhou/utils/interpolator.py
@@ -5,6 +5,7 @@ class Interpolator(object):
         self.end_values = tuple(values)
         self.start_frame = 0
         self.end_frame = 0
+        self._frame = 0
 
 
     def set_interpolation_start(self, frame, values):
@@ -26,12 +27,15 @@ class Interpolator(object):
 
 
     def update(self, frame):
+        self._frame = frame
         if frame >= self.end_frame:
             self.values = tuple(self.end_values)
             self.start_values = tuple(self.end_values)
             self.start_frame = frame
+            return frame == self.end_frame
         else:
             truc = float(frame - self.start_frame) / float(self.end_frame - self.start_frame)
             self.values = tuple(start_value + truc * (end_value - start_value)
                                 for (start_value, end_value) in zip(self.start_values, self.end_values))
+            return True