Mercurial > touhou
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 |