comparison pytouhou/game/enemy.py @ 98:d141c851c598

Rename pytouhou.game.enemymanager to pytouhou.game.enemy
author Thibaut Girka <thib@sitedethib.com>
date Sun, 04 Sep 2011 23:51:14 +0200
parents pytouhou/game/enemymanager.py@ac2e5e1c2c3c
children 5c40cc1b8019
comparison
equal deleted inserted replaced
97:ac2e5e1c2c3c 98:d141c851c598
1 # -*- encoding: utf-8 -*-
2 ##
3 ## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
4 ##
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published
7 ## by the Free Software Foundation; version 3 only.
8 ##
9 ## This program is distributed in the hope that it will be useful,
10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ## GNU General Public License for more details.
13 ##
14
15
16 from itertools import chain
17 from io import BytesIO
18 import os
19 from struct import unpack, pack
20 from pytouhou.utils.interpolator import Interpolator
21 from pytouhou.vm.eclrunner import ECLRunner
22 from pytouhou.vm.anmrunner import ANMRunner
23 from pytouhou.game.sprite import Sprite
24 from pytouhou.game.bullet import Bullet
25 from math import cos, sin, atan2, pi
26
27
28 class Enemy(object):
29 def __init__(self, pos, life, _type, anm_wrapper, game_state):
30 self._game_state = game_state
31 self._anm_wrapper = anm_wrapper
32 self._sprite = None
33 self._anmrunner = None
34 self._removed = False
35 self._type = _type
36 self._was_visible = False
37
38 self.frame = 0
39
40 self.x, self.y = pos
41 self.life = life
42 self.max_life = life
43 self.touchable = True
44 self.damageable = True
45 self.death_flags = 0
46 self.bullets = []
47 self.extended_bullet_attributes = (0, 0, 0, 0, 0., 0., 0., 0.)
48 self.bullet_attributes = None
49 self.bullet_launch_offset = (0, 0)
50 self.death_callback = None
51 self.low_life_callback = None
52 self.low_life_trigger = None
53 self.timeout = None
54 self.timeout_callback = None
55 self.remaining_lives = -1
56
57 self.automatic_orientation = False
58
59 self.bullet_launch_interval = 0
60 self.bullet_launch_timer = 0
61 self.delay_attack = False
62
63 self.death_anim = None
64 self.movement_dependant_sprites = None
65 self.direction = None
66 self.interpolator = None #TODO
67 self.speed_interpolator = None
68 self.angle = 0.
69 self.speed = 0.
70 self.rotation_speed = 0.
71 self.acceleration = 0.
72
73 self.hitbox = (0, 0)
74 self.screen_box = None
75
76
77 def set_bullet_attributes(self, type_, anim, sprite_idx_offset,
78 bullets_per_shot, number_of_shots, speed, speed2,
79 launch_angle, angle, flags):
80 self.bullet_attributes = (type_, anim, sprite_idx_offset, bullets_per_shot,
81 number_of_shots, speed, speed2, launch_angle,
82 angle, flags)
83 if not self.delay_attack:
84 self.fire()
85
86
87 def fire(self):
88 (type_, anim, sprite_idx_offset, bullets_per_shot, number_of_shots,
89 speed, speed2, launch_angle, angle, flags) = self.bullet_attributes
90
91 self.bullet_launch_timer = 0
92
93 player = self.select_player()
94
95 if type_ in (67, 69, 71):
96 launch_angle += self.get_player_angle(player)
97 if type_ in (69, 70, 71):
98 angle = 2. * pi / bullets_per_shot
99 if type_ == 71:
100 launch_angle += pi / bullets_per_shot
101
102 launch_angle -= angle * (bullets_per_shot - 1) / 2.
103
104 for shot_nb in range(number_of_shots):
105 shot_speed = speed if shot_nb == 0 else speed + (speed2 - speed) * float(shot_nb) / float(number_of_shots)
106 bullet_angle = launch_angle
107 for bullet_nb in range(bullets_per_shot):
108 self.bullets.append(Bullet((self.x, self.y),
109 anim, sprite_idx_offset,
110 bullet_angle, shot_speed,
111 self.extended_bullet_attributes,
112 flags, player, self._game_state))
113 bullet_angle += angle
114
115
116 def select_player(self, players=None):
117 return (players or self._game_state.players)[0] #TODO
118
119
120 def get_player_angle(self, player=None):
121 player = player or self.select_player()
122 return atan2(player.y - self.y, player.x - self.x)
123
124
125 def set_anim(self, index):
126 self._sprite = Sprite()
127 self._anmrunner = ANMRunner(self._anm_wrapper, index, self._sprite)
128
129
130 def set_pos(self, x, y, z):
131 self.x, self.y = x, y
132 self.interpolator = Interpolator((x, y))
133 self.interpolator.set_interpolation_start(self.frame, (x, y))
134
135
136 def move_to(self, duration, x, y, z, formula):
137 if not self.interpolator:
138 self.interpolator = Interpolator((self.x, self.y), formula)
139 self.interpolator.set_interpolation_start(self.frame, (self.x, self.y))
140 self.interpolator.set_interpolation_end(self.frame + duration - 1, (x, y))
141
142 self.speed = 0.
143 self.angle = atan2(y - self.y, x - self.x)
144
145
146 def stop_in(self, duration, formula):
147 if not self.speed_interpolator:
148 self.speed_interpolator = Interpolator((self.speed,), formula)
149 self.speed_interpolator.set_interpolation_start(self.frame, (self.speed,))
150 self.speed_interpolator.set_interpolation_end(self.frame + duration - 1, (0.,))
151
152 self.speed = 0.
153
154
155 def is_visible(self, screen_width, screen_height):
156 if self._sprite:
157 tx, ty, tw, th = self._sprite.texcoords
158 if self._sprite.corner_relative_placement:
159 raise Exception #TODO
160 else:
161 tx, ty, tw, th = 0., 0., 0., 0.
162
163 max_x = tw / 2.
164 max_y = th / 2.
165 min_x = -max_x
166 min_y = -max_y
167
168 if any((min_x > screen_width - self.x,
169 max_x < -self.x,
170 min_y > screen_height - self.y,
171 max_y < -self.y)):
172 return False
173 return True
174
175
176 def get_objects_by_texture(self, objects_by_texture):
177 for bullet in self.bullets:
178 bullet.get_objects_by_texture(objects_by_texture)
179
180 if not self._sprite:
181 return
182
183 sprite = self._sprite
184 sprite.update_vertices_uvs_colors()
185
186 key = sprite.anm.first_name, sprite.anm.secondary_name
187 key = (key, sprite.blendfunc)
188 rec = objects_by_texture.setdefault(key, ([], [], []))
189 vertices = ((x + self.x, y + self.y, z) for x, y, z in sprite._vertices)
190 rec[0].extend(vertices)
191 rec[1].extend(sprite._uvs)
192 rec[2].extend(sprite._colors)
193
194
195 def update(self):
196 x, y = self.x, self.y
197 if self.interpolator:
198 self.interpolator.update(self.frame)
199 x, y = self.interpolator.values
200
201 self.speed += self.acceleration #TODO: units? Execution order?
202 self.angle += self.rotation_speed #TODO: units? Execution order?
203
204
205 if self.speed_interpolator:
206 self.speed_interpolator.update(self.frame)
207 self.speed, = self.speed_interpolator.values
208
209
210 dx, dy = cos(self.angle) * self.speed, sin(self.angle) * self.speed
211 if self._type & 2:
212 x -= dx
213 else:
214 x += dx
215 y += dy
216
217 if self.movement_dependant_sprites:
218 #TODO: is that really how it works? Almost.
219 # Sprite determination is done only once per changement, and is
220 # superseeded by ins_97.
221 end_left, end_right, left, right = self.movement_dependant_sprites
222 if x < self.x and self.direction != -1:
223 self.set_anim(left)
224 self.direction = -1
225 elif x > self.x and self.direction != +1:
226 self.set_anim(right)
227 self.direction = +1
228 elif x == self.x and self.direction is not None:
229 self.set_anim({-1: end_left, +1: end_right}[self.direction])
230 self.direction = None
231
232
233 if self.screen_box:
234 xmin, ymin, xmax, ymax = self.screen_box
235 x = max(xmin, min(x, xmax))
236 y = max(ymin, min(y, ymax))
237
238
239 self.x, self.y = x, y
240
241 #TODO
242 if self._anmrunner and not self._anmrunner.run_frame():
243 self._anmrunner = None
244
245 if self._sprite:
246 if self._sprite._removed:
247 self._sprite = None
248 else:
249 self._sprite.update(angle_base=self.angle,
250 force_rotation=self.automatic_orientation)
251
252
253 if self.bullet_launch_interval != 0:
254 self.bullet_launch_timer += 1
255 if self.bullet_launch_timer == self.bullet_launch_interval:
256 self.fire()
257
258
259 for bullet in self.bullets:
260 bullet.update()
261
262
263 self.frame += 1
264