comparison pytouhou/vm/eclrunner.py @ 316:f0be7ea62330

Fix a bug with ECL instruction 96, and fix overall ECL handling. The issue with instruction 96 was about death callbacks, being executed on the caller of instruction 96 instead of the dying enemies. This was introduced by changeset 5930b33a0370. Additionnaly, ECL processes are now an attribute of the Enemy, and death/timeout conditions are checked right after the ECL frame, even if the ECL script has already ended, just like in the original game.
author Thibaut Girka <thib@sitedethib.com>
date Thu, 29 Mar 2012 21:18:35 +0200
parents 2ba2462afc70
children 1366cefd0334
comparison
equal deleted inserted replaced
315:e935ed8dc5e6 316:f0be7ea62330
23 23
24 24
25 25
26 class ECLMainRunner(object): 26 class ECLMainRunner(object):
27 __metaclass__ = MetaRegistry 27 __metaclass__ = MetaRegistry
28 __slots__ = ('_ecl', '_game', 'processes', 'frame', 28 __slots__ = ('_ecl', '_game', 'frame',
29 'instruction_pointer', 29 'instruction_pointer',
30 'boss_wait') 30 'boss_wait')
31 31
32 def __init__(self, ecl, game): 32 def __init__(self, ecl, game):
33 self._ecl = ecl 33 self._ecl = ecl
34 self._game = game 34 self._game = game
35 self.frame = 0 35 self.frame = 0
36 self.boss_wait = False 36 self.boss_wait = False
37
38 self.processes = []
39 37
40 self.instruction_pointer = 0 38 self.instruction_pointer = 0
41 39
42 40
43 def run_iter(self): 41 def run_iter(self):
61 callback = self._handlers[instr_type] 59 callback = self._handlers[instr_type]
62 except KeyError: 60 except KeyError:
63 logger.warn('unhandled main opcode %d (args: %r)', instr_type, args) 61 logger.warn('unhandled main opcode %d (args: %r)', instr_type, args)
64 else: 62 else:
65 callback(self, sub, instr_type, *args) 63 callback(self, sub, instr_type, *args)
66
67 self.processes[:] = (process for process in self.processes
68 if process.run_iteration())
69 64
70 if not (self._game.msg_wait or self.boss_wait): 65 if not (self._game.msg_wait or self.boss_wait):
71 self.frame += 1 66 self.frame += 1
72 67
73 68
77 x = self._game.prng.rand_double() * 368 72 x = self._game.prng.rand_double() * 368
78 if y < -990: #102h.exe@0x41184b 73 if y < -990: #102h.exe@0x41184b
79 y = self._game.prng.rand_double() * 416 74 y = self._game.prng.rand_double() * 416
80 if z < -990: #102h.exe@0x411881 75 if z < -990: #102h.exe@0x411881
81 z = self._game.prng.rand_double() * 800 76 z = self._game.prng.rand_double() * 800
82 enemy = self._game.new_enemy((x, y, z), life, instr_type, bonus_dropped, die_score) 77 enemy = self._game.new_enemy((x, y, z), life, instr_type,
83 process = ECLRunner(self._ecl, sub, enemy, self._game) 78 bonus_dropped, die_score)
84 self.processes.append(process) 79 enemy.process = ECLRunner(self._ecl, sub, enemy, self._game) #TODO
85 process.run_iteration() 80 enemy.process.run_iteration()
86 81
87 82
88 @instruction(0) 83 @instruction(0)
89 @instruction(2) 84 @instruction(2)
90 @instruction(4) 85 @instruction(4)
124 119
125 120
126 class ECLRunner(object): 121 class ECLRunner(object):
127 __metaclass__ = MetaRegistry 122 __metaclass__ = MetaRegistry
128 __slots__ = ('_ecl', '_enemy', '_game', 'variables', 'sub', 'frame', 123 __slots__ = ('_ecl', '_enemy', '_game', 'variables', 'sub', 'frame',
129 'instruction_pointer', 'comparison_reg', 'stack') 124 'instruction_pointer', 'comparison_reg', 'stack',
125 'running')
130 126
131 def __init__(self, ecl, sub, enemy, game): 127 def __init__(self, ecl, sub, enemy, game):
132 # Things not supposed to change 128 # Things not supposed to change
133 self._ecl = ecl 129 self._ecl = ecl
134 self._enemy = enemy 130 self._enemy = enemy
135 self._game = game 131 self._game = game
132
133 self.running = True
136 134
137 # Things supposed to change (and be put in the stack) 135 # Things supposed to change (and be put in the stack)
138 self.variables = [0, 0, 0, 0, 136 self.variables = [0, 0, 0, 0,
139 0., 0., 0., 0., 137 0., 0., 0., 0.,
140 0, 0, 0, 0] 138 0, 0, 0, 0]
143 141
144 self.stack = [] 142 self.stack = []
145 143
146 144
147 def switch_to_sub(self, sub): 145 def switch_to_sub(self, sub):
146 self.running = True
148 self.frame = 0 147 self.frame = 0
149 self.sub = sub 148 self.sub = sub
150 self.instruction_pointer = 0 149 self.instruction_pointer = 0
151 150
152 151
205 enm.death_callback = -1 204 enm.death_callback = -1
206 enm.timeout = -1 #TODO: check 205 enm.timeout = -1 #TODO: check
207 else: 206 else:
208 raise Exception('What the hell, man!') 207 raise Exception('What the hell, man!')
209 208
209
210 def run_iteration(self): 210 def run_iteration(self):
211 # First, if enemy is dead, return 211 # Process script
212 if self._enemy.removed: 212 while self.running:
213 return False
214
215 # Then, check for callbacks
216 self.handle_callbacks()
217
218 # Now, process script
219 while True:
220 try: 213 try:
221 frame, instr_type, rank_mask, param_mask, args = self._ecl.subs[self.sub][self.instruction_pointer] 214 frame, instr_type, rank_mask, param_mask, args = self._ecl.subs[self.sub][self.instruction_pointer]
222 except IndexError: 215 except IndexError:
223 return False 216 self.running = False
217 break
224 218
225 if frame > self.frame: 219 if frame > self.frame:
226 break 220 break
227 else: 221 else:
228 self.instruction_pointer += 1 222 self.instruction_pointer += 1
238 else: 232 else:
239 callback(self, *args) 233 callback(self, *args)
240 logger.debug('executed opcode %d (args: %r)', instr_type, args) 234 logger.debug('executed opcode %d (args: %r)', instr_type, args)
241 235
242 self.frame += 1 236 self.frame += 1
243 return True 237
238 # Handle callbacks
239 self.handle_callbacks()
244 240
245 241
246 def _getval(self, value): 242 def _getval(self, value):
247 if -10012 <= value <= -10001: 243 if -10012 <= value <= -10001:
248 return self.variables[int(-10001-value)] 244 return self.variables[int(-10001-value)]
824 life, bonus_dropped, die_score) 820 life, bonus_dropped, die_score)
825 821
826 822
827 @instruction(96) 823 @instruction(96)
828 def kill_enemies(self): 824 def kill_enemies(self):
829 for proc in self._game.ecl_runner.processes: 825 for enemy in self._game.enemies:
830 if proc._enemy.boss: 826 if enemy.boss:
831 pass # Bosses are immune to 96 827 pass # Bosses are immune to 96
832 elif proc._enemy.touchable: 828 elif enemy.touchable:
833 proc._enemy.life = 0 829 enemy.life = 0
834 elif proc._enemy.death_callback > 0: 830 elif enemy.death_callback > 0:
835 #TODO: check 831 #TODO: check
836 #TODO: refactor 832 enemy.process.switch_to_sub(enemy.death_callback)
837 self.switch_to_sub(proc._enemy.death_callback) 833 enemy.death_callback = -1
838 proc._enemy.death_callback = -1
839 834
840 835
841 @instruction(97) 836 @instruction(97)
842 def set_anim(self, script): 837 def set_anim(self, script):
843 self._enemy.set_anim(script) 838 self._enemy.set_anim(script)