# HG changeset patch # User Emmanuel Gil Peyrot # Date 1381746055 -7200 # Node ID 3da7395f39e31474b47bb063cea60f2166d41878 # Parent 104c737ce8b322fb85319cd8e400560427fb45e9 Make enemy callbacks programmables. diff --git a/pytouhou/game/enemy.pxd b/pytouhou/game/enemy.pxd --- a/pytouhou/game/enemy.pxd +++ b/pytouhou/game/enemy.pxd @@ -3,11 +3,20 @@ from pytouhou.game.game cimport Game from pytouhou.game.player cimport Player from pytouhou.utils.interpolator cimport Interpolator +cdef class Callback: + cdef function + cdef public tuple args # XXX: public only for ECL’s copy_callbacks. + + cpdef enable(self, function, tuple args) + cpdef disable(self) + cpdef fire(self) + cdef class Enemy(Element): cdef public double z, angle, speed, rotation_speed, acceleration - cdef public long _type, bonus_dropped, die_score, frame, life, death_flags, current_laser_id, death_callback, boss_callback, low_life_callback, low_life_trigger, timeout, timeout_callback, remaining_lives, bullet_launch_interval, bullet_launch_timer, death_anim, direction, update_mode + cdef public long _type, bonus_dropped, die_score, frame, life, death_flags, current_laser_id, low_life_trigger, timeout, remaining_lives, bullet_launch_interval, bullet_launch_timer, death_anim, direction, update_mode cdef public bint visible, was_visible, touchable, collidable, damageable, boss, automatic_orientation, delay_attack cdef public tuple difficulty_coeffs, extended_bullet_attributes, bullet_attributes, bullet_launch_offset, movement_dependant_sprites, screen_box + cdef public Callback death_callback, boss_callback, low_life_callback, timeout_callback cdef public dict laser_by_id cdef public list aux_anm cdef public Interpolator interpolator, speed_interpolator diff --git a/pytouhou/game/enemy.pyx b/pytouhou/game/enemy.pyx --- a/pytouhou/game/enemy.pyx +++ b/pytouhou/game/enemy.pyx @@ -21,6 +21,27 @@ from pytouhou.game.laser cimport Laser, from pytouhou.game.effect cimport Effect +cdef class Callback: + def __init__(self, function=None, args=()): + self.function = function + self.args = args + + def __nonzero__(self): + return self.function is not None + + cpdef enable(self, function, tuple args): + self.function = function + self.args = args + + cpdef disable(self): + self.function = None + + cpdef fire(self): + if self.function is not None: + self.function(*self.args) + self.function = None + + cdef class Enemy(Element): def __init__(self, pos, long life, long _type, long bonus_dropped, long die_score, anms, Game game): Element.__init__(self) @@ -50,14 +71,15 @@ cdef class Enemy(Element): self.laser_by_id = {} self.bullet_attributes = None self.bullet_launch_offset = (0, 0) - self.death_callback = -1 - self.boss_callback = -1 - self.low_life_callback = -1 self.low_life_trigger = -1 self.timeout = -1 - self.timeout_callback = -1 self.remaining_lives = 0 + self.death_callback = Callback() + self.boss_callback = Callback() + self.low_life_callback = Callback() + self.timeout_callback = Callback() + self.automatic_orientation = False self.bullet_launch_interval = 0 @@ -382,7 +404,7 @@ cdef class Enemy(Element): #TODO: implement missing callbacks and clean up! if self.life <= 0 and self.touchable: self.timeout = -1 #TODO: not really true, the timeout is frozen - self.timeout_callback = -1 + self.timeout_callback.disable() death_flags = self.death_flags & 7 self.die_anim() @@ -428,14 +450,12 @@ cdef class Enemy(Element): self.life = 1 self.death_flags = 0 - if death_flags != 0 and self.death_callback > -1: - self.process.switch_to_sub(self.death_callback) - self.death_callback = -1 - elif self.life <= self.low_life_trigger and self.low_life_callback > -1: - self.process.switch_to_sub(self.low_life_callback) - self.low_life_callback = -1 + if death_flags != 0: + self.death_callback.fire() + elif self.life <= self.low_life_trigger and self.low_life_callback: + self.low_life_callback.fire() self.low_life_trigger = -1 - self.timeout_callback = -1 + self.timeout_callback.disable() elif self.timeout != -1 and self.frame == self.timeout: self.frame = 0 self.timeout = -1 @@ -446,12 +466,11 @@ cdef class Enemy(Element): self.life = self.low_life_trigger self.low_life_trigger = -1 - if self.timeout_callback > -1: - self.process.switch_to_sub(self.timeout_callback) - self.timeout_callback = -1 + if self.timeout_callback: + self.timeout_callback.fire() #TODO: this is only done under certain (unknown) conditions! # but it shouldn't hurt anyway, as the only option left is to crash! - elif self.death_callback > -1: + elif self.death_callback: self.life = 0 #TODO: do this next frame? Bypass self.touchable? else: raise Exception('What the hell, man!') diff --git a/pytouhou/game/game.pyx b/pytouhou/game/game.pyx --- a/pytouhou/game/game.pyx +++ b/pytouhou/game/game.pyx @@ -203,10 +203,9 @@ cdef class Game: pass # Bosses are immune to 96 elif enemy.touchable: enemy.life = 0 - elif enemy.death_callback > 0: + else: #TODO: check - enemy.process.switch_to_sub(enemy.death_callback) - enemy.death_callback = -1 + enemy.death_callback.fire() cpdef new_effect(self, pos, long anim, anm=None, long number=1): diff --git a/pytouhou/vm/eclrunner.py b/pytouhou/vm/eclrunner.py --- a/pytouhou/vm/eclrunner.py +++ b/pytouhou/vm/eclrunner.py @@ -103,13 +103,11 @@ class ECLMainRunner(object): @instruction(10) def resume_ecl(self, sub, instr_type, unk1, unk2): - boss = self._game.boss + boss = self._game.boss._enemy self._game.msg_wait = False - if boss._enemy.boss_callback > -1: - boss.switch_to_sub(boss._enemy.boss_callback) - boss._enemy.boss_callback = -1 - else: + if not boss.boss_callback: raise Exception #TODO + boss.boss_callback.fire() @instruction(12) @@ -843,13 +841,13 @@ class ECLRunner(object): @instruction(108) def set_death_callback(self, sub): - self._enemy.death_callback = sub + self._enemy.death_callback.enable(self.switch_to_sub, (sub,)) @instruction(109) def memory_write(self, value, index): if index == 0: - self._enemy.boss_callback = value + self._enemy.boss_callback.enable(self.switch_to_sub, (value,)) else: raise Exception #TODO @@ -880,7 +878,7 @@ class ECLRunner(object): @instruction(114) def set_low_life_callback(self, sub): - self._enemy.low_life_callback = sub + self._enemy.low_life_callback.enable(self.switch_to_sub, (sub,)) @instruction(115) @@ -891,7 +889,7 @@ class ECLRunner(object): @instruction(116) def set_timeout_callback(self, sub): - self._enemy.timeout_callback = sub + self._enemy.timeout_callback.enable(self.switch_to_sub, (sub,)) @instruction(117) @@ -1144,5 +1142,5 @@ class ECLRunner(object): @instruction(133) def copy_callbacks(self): - self._enemy.timeout_callback = self._enemy.death_callback + self._enemy.timeout_callback.enable(self.switch_to_sub, (self._enemy.death_callback.args[0],))