# HG changeset patch # User Thibaut Girka # Date 1314120444 -7200 # Node ID 694f25881d0f642af8908d6810de9a40aaa788d4 # Parent 299de3a9b69fdea37fdda799bd0dc2fb4df915c4 Fix move_to interpolation, add support for a few ANM and ECL instructions diff --git a/pytouhou/formats/anm0.py b/pytouhou/formats/anm0.py --- a/pytouhou/formats/anm0.py +++ b/pytouhou/formats/anm0.py @@ -79,6 +79,8 @@ class Animations(object): args = unpack(' self.frame: break else: self.instruction_pointer += 1 + + + #TODO: skip bad ranks + if not rank_mask & (0x100 << self._game_state.rank): + continue + + if frame == self.frame: try: callback = self._handlers[instr_type] @@ -126,7 +131,9 @@ class ECLRunner(object): return self._enemy.frame elif value == -10024: return self._enemy.life - raise NotImplementedError #TODO + elif value == -10025: + return self._enemy.select_player(self._game_state.players).character + raise NotImplementedError(value) #TODO else: return value @@ -302,6 +309,11 @@ class ECLRunner(object): self._enemy.move_to(duration, x, y, z) + @instruction(61) + def stop_in(self, duration): + self._enemy.stop_in(duration) + + @instruction(65) def set_screen_box(self, xmin, ymin, xmax, ymax): self._enemy.screen_box = xmin, ymin, xmax, ymax @@ -312,6 +324,16 @@ class ECLRunner(object): self._enemy.screen_box = None + @instruction(67) + def set_bullet_attributes1(self, bullet_anim, launch_anim, bullets_per_shot, + number_of_shots, speed, unknown, launch_angle, + angle, flags): + self._enemy.set_bullet_attributes(1, bullet_anim, launch_anim, + bullets_per_shot, number_of_shots, + speed, unknown, launch_angle, angle, + flags) + + @instruction(77) def set_bullet_interval(self, value): self._enemy.bullet_launch_interval = value @@ -348,6 +370,12 @@ class ECLRunner(object): self._enemy.death_anim = sprite_index % 256 #TODO + @instruction(101) + def set_boss_mode(self, unknown): + #TODO: unknown + self._game_state.boss = self._enemy + + @instruction(103) def set_hitbox(self, width, height, depth): self._enemy.hitbox = (width, height) diff --git a/pytouhou/game/enemymanager.py b/pytouhou/game/enemymanager.py --- a/pytouhou/game/enemymanager.py +++ b/pytouhou/game/enemymanager.py @@ -54,6 +54,7 @@ class Enemy(object): self.movement_dependant_sprites = None self.direction = None self.interpolator = None #TODO + self.speed_interpolator = None self.angle = 0. self.speed = 0. self.rotation_speed = 0. @@ -63,15 +64,19 @@ class Enemy(object): self.screen_box = None - def set_bullet_attributes(self, bullet_anim, launch_anim, bullets_per_shot, - number_of_shots, speed, unknown, launch_angle, - angle, flags): - self.bullet_attributes = (1, bullet_anim, launch_anim, bullets_per_shot, + def set_bullet_attributes(self, type_, bullet_anim, launch_anim, + bullets_per_shot, number_of_shots, speed, unknown, + launch_angle, angle, flags): + self.bullet_attributes = (type_, bullet_anim, launch_anim, bullets_per_shot, number_of_shots, speed, unknown, launch_angle, angle, flags) if not self.delay_attack: - pass - #TODO: actually fire + self.fire() + + + def fire(self): + #TODO + pass def select_player(self, players): @@ -93,9 +98,19 @@ class Enemy(object): def move_to(self, duration, x, y, z): + if not self.interpolator: + self.interpolator = Interpolator((self.x, self.y)) + self.interpolator.set_interpolation_start(self.frame, (self.x, self.y)) self.interpolator.set_interpolation_end(self.frame + duration, (x, y)) + def stop_in(self, duration): + if not self.speed_interpolator: + self.speed_interpolator = Interpolator((self.speed,)) + self.speed_interpolator.set_interpolation_start(self.frame, (self.speed,)) + self.speed_interpolator.set_interpolation_end(self.frame + duration, (0.,)) + + def is_visible(self, screen_width, screen_height): if not self._sprite: return False @@ -132,12 +147,19 @@ class Enemy(object): def update(self, frame): x, y = self.x, self.y - if self.interpolator and self.interpolator.update(self.frame): + if self.interpolator: + self.interpolator.update(self.frame) x, y = self.interpolator.values self.speed += self.acceleration #TODO: units? Execution order? self.angle += self.rotation_speed #TODO: units? Execution order? + + if self.speed_interpolator: + self.speed_interpolator.update(self.frame) + self.speed, = self.speed_interpolator.values + + dx, dy = cos(self.angle) * self.speed, sin(self.angle) * self.speed if self._type & 2: x -= dx @@ -146,7 +168,9 @@ class Enemy(object): y += dy if self.movement_dependant_sprites: - #TODO: is that really how it works? + #TODO: is that really how it works? Almost. + # Sprite determination is done only once per changement, and is + # superseeded by ins_97. if x < self.x: self.set_anim(self.movement_dependant_sprites[2]) self.direction = -1 @@ -235,6 +259,12 @@ class EnemyManager(object): enemy._removed = True self.enemies.remove(enemy) + + #TODO: disable boss mode if it is dead/it has timeout + if self._game_state.boss and self._game_state.boss._removed: + self._game_state.boss = None + + # Add enemies to vertices/uvs self.objects_by_texture = {} for enemy in visible_enemies: diff --git a/pytouhou/game/player.py b/pytouhou/game/player.py --- a/pytouhou/game/player.py +++ b/pytouhou/game/player.py @@ -22,4 +22,4 @@ class Player(object): self.power = 0 self.lives = 0 self.bombs = 0 - self.type = 0 # ReimuA/ReimuB/MarisaA/MarisaB/... + self.character = 0 # ReimuA/ReimuB/MarisaA/MarisaB/... diff --git a/pytouhou/game/sprite.py b/pytouhou/game/sprite.py --- a/pytouhou/game/sprite.py +++ b/pytouhou/game/sprite.py @@ -13,6 +13,7 @@ ## +from random import randrange from struct import unpack from pytouhou.utils.matrix import Matrix @@ -37,6 +38,7 @@ class Sprite(object): self.texoffsets = (0., 0.) self.mirrored = False self.rescale = (1., 1.) + self.scale_speed = (0., 0.) self.rotations_3d = (0., 0., 0.) self.rotations_speed_3d = (0., 0., 0.) self.corner_relative_placement = False @@ -44,6 +46,7 @@ class Sprite(object): self.keep_still = False self.playing = True self.frame = 0 + self.color = (255, 255, 255) self.alpha = 255 self._uvs = [] self._vertices = [] @@ -85,7 +88,7 @@ class Sprite(object): d = vertmat.data assert (d[3][0], d[3][1], d[3][2], d[3][3]) == (1., 1., 1., 1.) - self._colors = [(255, 255, 255, self.alpha)] * 4 + self._colors = [(self.color[0], self.color[1], self.color[2], self.alpha)] * 4 self._uvs, self._vertices = uvs, zip(d[0], d[1], d[2]) @@ -115,11 +118,15 @@ class Sprite(object): 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] @@ -129,6 +136,12 @@ class Sprite(object): 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: @@ -137,7 +150,7 @@ class Sprite(object): elif instr_type == 28: tox, toy = self.texoffsets self.texoffsets = tox, toy + args[0] - elif instr_type == 15: + elif instr_type in (15, 21): self.keep_still = True break else: @@ -147,8 +160,9 @@ class Sprite(object): 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.): + if self.rotations_speed_3d != (0., 0., 0.) or self.scale_speed != (0., 0.): return True return changed diff --git a/pytouhou/utils/interpolator.py b/pytouhou/utils/interpolator.py --- a/pytouhou/utils/interpolator.py +++ b/pytouhou/utils/interpolator.py @@ -23,6 +23,10 @@ class Interpolator(object): self._frame = 0 + def __nonzero__(self): + return self._frame <= self.end_frame + + def set_interpolation_start(self, frame, values): self.start_values = tuple(values) self.start_frame = frame @@ -47,10 +51,8 @@ class Interpolator(object): 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