comparison pytouhou/game/enemymanager.py @ 49:cbe1cb50f2fd

Refactor ECLRunner/EnemyManager so that all VM stuff goes to ECLRunner
author Thibaut Girka <thib@sitedethib.com>
date Mon, 22 Aug 2011 19:23:00 +0200
parents 8353c33d53d4
children 811cefefb5c8
comparison
equal deleted inserted replaced
48:8353c33d53d4 49:cbe1cb50f2fd
11 from pytouhou.utils.random import Random 11 from pytouhou.utils.random import Random
12 random = Random(0x39f4) 12 random = Random(0x39f4)
13 13
14 14
15 class Enemy(object): 15 class Enemy(object):
16 def __init__(self, pos, life, _type, ecl_runner, anm_wrapper): 16 def __init__(self, pos, life, _type, anm_wrapper):
17 self.anm_wrapper = anm_wrapper 17 self._anm_wrapper = anm_wrapper
18 self.anm = None 18 self._anm = None
19 self.ecl_runner = ecl_runner 19 self._sprite = None
20 self._removed = False
21 self._type = _type
22
23 self.frame = 0
24
20 self.x, self.y = pos 25 self.x, self.y = pos
21 self.life = life 26 self.life = life
22 self.max_life = life 27 self.max_life = life
23 self.type = _type
24 self.frame = 0
25 self.sprite = None
26 self.pending_bullets = [] 28 self.pending_bullets = []
27 self.bullet_attributes = None 29 self.bullet_attributes = None
28 self.bullet_launch_offset = (0, 0) 30 self.bullet_launch_offset = (0, 0)
29 self.vulnerable = True 31 self.vulnerable = True
30 self.death_callback = None 32 self.death_callback = None
34 self.remaining_lives = -1 36 self.remaining_lives = -1
35 37
36 self.bullet_launch_interval = 0 38 self.bullet_launch_interval = 0
37 self.delay_attack = False 39 self.delay_attack = False
38 40
39 self.death_sprite = None 41 self.death_anim = None
40 self.movement_dependant_sprites = None 42 self.movement_dependant_sprites = None
41 self.direction = None 43 self.direction = None
42 self.interpolator = None #TODO 44 self.interpolator = None #TODO
43 self.angle = 0. 45 self.angle = 0.
44 self.speed = 0. 46 self.speed = 0.
45 self.rotation_speed = 0. 47 self.rotation_speed = 0.
46 self.acceleration = 0. 48 self.acceleration = 0.
47 49
48 self.hitbox = (0, 0) 50 self.hitbox = (0, 0)
49
50 self.ecl_runner.implementation.update({67: self.set_bullet_attributes,
51 97: self.set_sprite,
52 98: self.set_multiple_sprites,
53 45: self.set_angle_speed,
54 43: self.set_pos,
55 46: self.set_rotation_speed,
56 47: self.set_speed,
57 48: self.set_acceleration,
58 51: self.target_player,
59 57: self.move_to,
60 77: self.set_bullet_interval,
61 78: self.set_delay_attack,
62 79: self.set_no_delay_attack,
63 81: self.set_bullet_launch_offset,
64 100: self.set_death_sprite,
65 103: self.set_hitbox,
66 105: self.set_vulnerable,
67 108: self.set_death_callback,
68 113: self.set_low_life_trigger,
69 114: self.set_low_life_callback,
70 115: self.set_timeout,
71 126: self.set_remaining_lives}) #TODO
72
73
74 def set_remaining_lives(self, lives):
75 self.remaining_lives = lives
76
77
78 def set_death_callback(self, sub):
79 self.death_callback = sub
80
81
82 def set_low_life_trigger(self, value):
83 self.low_life_trigger = value
84
85
86 def set_low_life_callback(self, sub):
87 self.low_life_callback = sub
88
89
90 def set_timeout(self, timeout):
91 self.timeout = timeout
92
93
94 def set_vulnerable(self, vulnerable):
95 self.vulnerable = bool(vulnerable & 1)
96
97
98 def set_bullet_launch_offset(self, x, y, z):
99 self.bullet_launch_offset = (x, y)
100 51
101 52
102 def set_bullet_attributes(self, bullet_anim, launch_anim, bullets_per_shot, 53 def set_bullet_attributes(self, bullet_anim, launch_anim, bullets_per_shot,
103 number_of_shots, speed, unknown, launch_angle, 54 number_of_shots, speed, unknown, launch_angle,
104 angle, flags): 55 angle, flags):
108 if not self.delay_attack: 59 if not self.delay_attack:
109 pass 60 pass
110 #TODO: actually fire 61 #TODO: actually fire
111 62
112 63
113 def set_bullet_interval(self, value): 64 def set_anim(self, index):
114 self.bullet_launch_interval = value 65 self._anm, self._sprite = self._anm_wrapper.get_sprite(index)
115
116
117 def set_delay_attack(self):
118 self.delay_attack = True
119
120
121 def set_no_delay_attack(self):
122 self.delay_attack = False
123
124
125 def set_death_sprite(self, sprite_index):
126 self.death_sprite = sprite_index % 256 #TODO
127
128
129 def set_hitbox(self, width, height, depth):
130 self.hitbox = (width, height)
131
132
133 def set_sprite(self, sprite_index):
134 self.anm, self.sprite = self.anm_wrapper.get_sprite(sprite_index)
135
136
137 def set_multiple_sprites(self, default, end_left, end_right, left, right):
138 self.movement_dependant_sprites = end_left, end_right, left, right
139 self.anm, self.sprite = self.anm_wrapper.get_sprite(default)
140
141
142 def set_angle_speed(self, angle, speed):
143 self.angle, self.speed = angle, speed
144 66
145 67
146 def set_pos(self, x, y, z): 68 def set_pos(self, x, y, z):
147 self.x, self.y = x, y 69 self.x, self.y = x, y
148 self.interpolator = Interpolator((x, y)) 70 self.interpolator = Interpolator((x, y))
149 self.interpolator.set_interpolation_start(self.frame, (x, y)) 71 self.interpolator.set_interpolation_start(self.frame, (x, y))
150 72
151 73
152 def set_rotation_speed(self, speed):
153 self.rotation_speed = speed
154
155
156 def set_speed(self, speed):
157 self.speed = speed
158
159
160 def set_acceleration(self, acceleration):
161 self.acceleration = acceleration
162
163
164 def target_player(self, unknown, speed):
165 self.speed = speed #TODO: unknown
166 player_x, player_y = 192., 384.#TODO
167 self.angle = atan2(player_y - self.y, player_x - self.x)
168
169
170 def move_to(self, duration, x, y, z): 74 def move_to(self, duration, x, y, z):
171 self.interpolator.set_interpolation_end(self.frame + duration, (x, y)) 75 self.interpolator.set_interpolation_end(self.frame + duration, (x, y))
172 76
173 77
174 def is_visible(self, screen_width, screen_height): 78 def is_visible(self, screen_width, screen_height):
175 if not self.sprite: 79 if not self._sprite:
176 return False 80 return False
177 81
178 tx, ty, tw, th = self.sprite.texcoords 82 tx, ty, tw, th = self._sprite.texcoords
179 if self.sprite.corner_relative_placement: 83 if self._sprite.corner_relative_placement:
180 raise Exception #TODO 84 raise Exception #TODO
181 else: 85 else:
182 max_x = tw / 2. 86 max_x = tw / 2.
183 max_y = th / 2. 87 max_y = th / 2.
184 min_x = -max_x 88 min_x = -max_x
192 return True 96 return True
193 97
194 98
195 def get_objects_by_texture(self): 99 def get_objects_by_texture(self):
196 objects_by_texture = {} 100 objects_by_texture = {}
197 key = self.anm.first_name, self.anm.secondary_name 101 key = self._anm.first_name, self._anm.secondary_name
198 if not key in objects_by_texture: 102 if not key in objects_by_texture:
199 objects_by_texture[key] = (0, [], [], []) 103 objects_by_texture[key] = (0, [], [], [])
200 vertices = tuple((x + self.x, y + self.y, z) for x, y, z in self.sprite._vertices) 104 vertices = tuple((x + self.x, y + self.y, z) for x, y, z in self._sprite._vertices)
201 objects_by_texture[key][1].extend(vertices) 105 objects_by_texture[key][1].extend(vertices)
202 objects_by_texture[key][2].extend(self.sprite._uvs) 106 objects_by_texture[key][2].extend(self._sprite._uvs)
203 objects_by_texture[key][3].extend(self.sprite._colors) 107 objects_by_texture[key][3].extend(self._sprite._colors)
204 #TODO: effects/bullet launch 108 #TODO: effects/bullet launch
205 return objects_by_texture 109 return objects_by_texture
206 110
207 111
208 def update(self, frame): 112 def update(self, frame):
209 self.ecl_runner.update()
210
211 x, y = self.x, self.y 113 x, y = self.x, self.y
212 if self.interpolator: 114 if self.interpolator:
213 self.interpolator.update(self.frame) 115 self.interpolator.update(self.frame)
214 x, y = self.interpolator.values 116 x, y = self.interpolator.values
215 117
216 self.speed += self.acceleration #TODO: units? Execution order? 118 self.speed += self.acceleration #TODO: units? Execution order?
217 self.angle += self.rotation_speed #TODO: units? Execution order? 119 self.angle += self.rotation_speed #TODO: units? Execution order?
218 120
219 dx, dy = cos(self.angle) * self.speed, sin(self.angle) * self.speed 121 dx, dy = cos(self.angle) * self.speed, sin(self.angle) * self.speed
220 if self.type & 2: 122 if self._type & 2:
221 x -= dx 123 x -= dx
222 else: 124 else:
223 x += dx 125 x += dx
224 y += dy 126 y += dy
225 127
226 if self.movement_dependant_sprites: 128 if self.movement_dependant_sprites:
227 #TODO: is that really how it works? 129 #TODO: is that really how it works?
228 if x < self.x: 130 if x < self.x:
229 self.anm, self.sprite = self.anm_wrapper.get_sprite(self.movement_dependant_sprites[2]) 131 self.set_anim(self.movement_dependant_sprites[2])
230 self.direction = -1 132 self.direction = -1
231 elif x > self.x: 133 elif x > self.x:
232 self.anm, self.sprite = self.anm_wrapper.get_sprite(self.movement_dependant_sprites[3]) 134 self.set_anim(self.movement_dependant_sprites[3])
233 self.direction = +1 135 self.direction = +1
234 elif self.direction is not None: 136 elif self.direction is not None:
235 self.anm, self.sprite = self.anm_wrapper.get_sprite(self.movement_dependant_sprites[{-1: 0, +1:1}[self.direction]]) 137 self.set_anim(self.movement_dependant_sprites[{-1: 0, +1:1}[self.direction]])
236 self.direction = None 138 self.direction = None
237 139
238 self.x, self.y = x, y 140 self.x, self.y = x, y
239 if self.sprite: 141 if self._sprite:
240 changed = self.sprite.update() 142 changed = self._sprite.update()
241 visible = self.is_visible(384, 448) 143 visible = self.is_visible(384, 448)
242 if changed and visible: 144 if changed and visible:
243 self.sprite.update_vertices_uvs_colors() 145 self._sprite.update_vertices_uvs_colors()
244 elif not self.sprite.playing: 146 elif not self._sprite.playing:
245 visible = False 147 visible = False
246 self.sprite = None 148 self._sprite = None
247 else: 149 else:
248 visible = False 150 visible = False
249 151
250 152
251 self.frame += 1 153 self.frame += 1
252 return visible 154 return visible
253 155
254 156
255 157
256 class EnemyManager(object): 158 class EnemyManager(object):
257 def __init__(self, stage, anm_wrapper, ecl): 159 def __init__(self, stage, anm_wrapper, ecl, game_state):
160 self._game_state = game_state
258 self.stage = stage 161 self.stage = stage
259 self.anm_wrapper = anm_wrapper 162 self.anm_wrapper = anm_wrapper
260 self.main = [] 163 self.main = []
261 self.ecl = ecl 164 self.ecl = ecl
262 self.objects_by_texture = {} 165 self.objects_by_texture = {}
263 self.enemies = [] 166 self.enemies = []
167 self.processes = []
264 168
265 # Populate main 169 # Populate main
266 for frame, sub, instr_type, args in ecl.main: 170 for frame, sub, instr_type, args in ecl.main:
267 if not self.main or self.main[-1][0] < frame: 171 if not self.main or self.main[-1][0] < frame:
268 self.main.append((frame, [(sub, instr_type, args)])) 172 self.main.append((frame, [(sub, instr_type, args)]))
269 elif self.main[-1][0] == frame: 173 elif self.main[-1][0] == frame:
270 self.main[-1][1].append((sub, instr_type, args)) 174 self.main[-1][1].append((sub, instr_type, args))
271
272
273 def make_enemy_deleter(self, enemy):
274 def _enemy_deleter(unknown): #TODO: unknown
275 self.enemies.remove(enemy)
276 return _enemy_deleter
277 175
278 176
279 def update(self, frame): 177 def update(self, frame):
280 if self.main and self.main[0][0] == frame: 178 if self.main and self.main[0][0] == frame:
281 for sub, instr_type, args in self.main.pop(0)[1]: 179 for sub, instr_type, args in self.main.pop(0)[1]:
286 x = random.rand_double() * 368 #102h.exe@0x411820 184 x = random.rand_double() * 368 #102h.exe@0x411820
287 if y < -990: 185 if y < -990:
288 y = random.rand_double() * 416 #102h.exe@0x41184b 186 y = random.rand_double() * 416 #102h.exe@0x41184b
289 if z < -990: 187 if z < -990:
290 y = random.rand_double() * 800 #102h.exe@0x411881 188 y = random.rand_double() * 800 #102h.exe@0x411881
291 ecl_runner = ECLRunner(self.ecl, sub) 189 enemy = Enemy((x, y), life, instr_type, self.anm_wrapper)
292 enemy = Enemy((x, y), life, instr_type, ecl_runner, self.anm_wrapper)
293 ecl_runner.implementation[1] = self.make_enemy_deleter(enemy)
294
295 self.enemies.append(enemy) 190 self.enemies.append(enemy)
191 self.processes.append(ECLRunner(self.ecl, sub, enemy, self._game_state))
192
193
194 # Run processes
195 self.processes[:] = (process for process in self.processes if process.run_iteration())
196
197 # Filter of destroyed enemies
198 self.enemies[:] = (enemy for enemy in self.enemies if not enemy._removed)
296 199
297 # Update enemies 200 # Update enemies
298 visible_enemies = [enemy for enemy in self.enemies if enemy.update(frame)] 201 visible_enemies = [enemy for enemy in self.enemies if enemy.update(frame)]
299 202
300 # Add enemies to vertices/uvs 203 # Add enemies to vertices/uvs