# HG changeset patch # User Thibaut Girka # Date 1314040607 -7200 # Node ID 811cefefb5c891b12341660299f4eb2cc503b4d2 # Parent cbe1cb50f2fda3a5d8d4d8684b7074c42fe32a39 Fix a few bugs and add support for a few instructions diff --git a/eclviewer.py b/eclviewer.py --- 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))) diff --git a/pytouhou/formats/ecl.py b/pytouhou/formats/ecl.py --- 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'), diff --git a/pytouhou/game/eclrunner.py b/pytouhou/game/eclrunner.py --- 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 diff --git a/pytouhou/game/enemymanager.py b/pytouhou/game/enemymanager.py --- 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)) diff --git a/pytouhou/game/game.py b/pytouhou/game/game.py --- 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 diff --git a/pytouhou/game/player.py b/pytouhou/game/player.py 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/... diff --git a/pytouhou/utils/interpolator.py b/pytouhou/utils/interpolator.py --- 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