-
Notifications
You must be signed in to change notification settings - Fork 2
/
items.py
executable file
·278 lines (217 loc) · 9.73 KB
/
items.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
import math
import pyglet
from graphics import sprite_seq
from track import WALL, DEEP
from vector import Vector, bresenham
class PowerUp(object):
def __init__(self, image, name):
self.image = image
self.name = name
def on_use(self, race, racer, alternate):
pass
class PowerUpNone(PowerUp):
def __init__(self):
super(PowerUpNone, self).__init__(sprite_seq['item empty'], "None")
class PowerUpCoin(PowerUp):
def __init__(self):
super(PowerUpCoin, self).__init__(sprite_seq['item coins'], "Coins")
def on_use(self, race, racer, alternate):
racer.car.state.coins += 3
class PowerUpBanana(PowerUp):
def __init__(self):
super(PowerUpBanana, self).__init__(sprite_seq['item banana'], "Banana")
def on_use(self, race, racer, alternate):
safe_distance = 8 + racer.car.radius() # the radius of a banana is 4
position = racer.car.position - racer.car.get_direction_vector().normalize(safe_distance)
if not race.track.type(position) in [WALL, DEEP]:
# banana cannot appear in deep ground or wall
return ParticleBanana(race, position)
class PowerUpMushroom(PowerUp):
def __init__(self):
super(PowerUpMushroom, self).__init__(sprite_seq['item mushroom'], "Mushroom")
def on_use(self, race, racer, alternate):
if racer.car.speed.norm() < 400.:
racer.car.set_speed(racer.car.speed.normalize(400.))
class PowerUpGreenShell(PowerUp):
def __init__(self):
super(PowerUpGreenShell, self).__init__(sprite_seq['item green shell'], "Green Shell")
def on_use(self, race, racer, alternate):
safe_distance = 8 + racer.car.radius() # the radius of a green shell is 4
if alternate: # shell is shot slowly backwards
position = racer.car.position - racer.car.get_direction_vector().normalize(safe_distance)
direction = racer.car.direction + math.pi # shell goes backwards
speed = 80
else:
position = racer.car.position + racer.car.get_direction_vector().normalize(safe_distance)
direction = racer.car.direction
speed = max(racer.car.speed.norm() + 50, 200)
if not race.track.type(position) in [WALL, DEEP]:
# sheel cannot appear on deep ground or wall
return ParticleGreenShell(race, position, direction, speed)
class PowerUpRedShell(PowerUp):
def __init__(self):
super(PowerUpRedShell, self).__init__(sprite_seq['item red shell'], "Red Shell")
def on_use(self, race, racer, alternate):
safe_distance = 8 + racer.car.radius() # the radius of a red shell is 4
position = racer.car.position + racer.car.get_direction_vector().normalize(safe_distance)
direction = racer.car.direction
speed = max(racer.car.speed.norm() + 50, 200)
rank = race.racers.index(racer)
if rank == 0:
target = None # racer is in first place, no target
else:
target = race.racers[rank - 1].car # target the car in front
if not race.track.type(position) in [WALL, DEEP]:
# sheel cannot appear on deep ground or wall
return ParticleRedShell(race, position, direction, speed, target)
class PowerUpFeather(PowerUp):
def __init__(self):
super(PowerUpFeather, self).__init__(sprite_seq['item feather'], "Feather")
def on_use(self, race, racer, alternate):
if not racer.car.state.aerial and not racer.car.state.jump:
racer.car.state.change(aerial=1.)
class PowerUpLightning(PowerUp):
def __init__(self):
super(PowerUpLightning, self).__init__(sprite_seq['item lightning'], "Lightning")
def on_use(self, race, racer, alternate):
for opponent in race.racers:
if not opponent is racer and opponent.car.is_vulnerable():
opponent.car.state.change(lightning=1.)
opponent.car.spin()
class PowerUpStar(PowerUp):
def __init__(self):
super(PowerUpStar, self).__init__(sprite_seq['item star'], "Star")
def on_use(self, race, racer, alternate):
pass
# create the pool of possible PowerUps
ITEMS = [PowerUpNone(), PowerUpCoin(), PowerUpBanana(), PowerUpMushroom(), PowerUpGreenShell(), PowerUpRedShell(),
PowerUpFeather(), PowerUpLightning(), PowerUpStar()]
class Particle(object):
def __init__(self, race, position, image, radius=4):
self.race = race
self.sprite = pyglet.sprite.Sprite(image, batch=race.window.batch, group=race.track.cars_group)
self.set_position(position)
self.radius = radius
self.vulnerable = True # particles can be destroyed by other elements
self.removed = False
race.particles.append(self)
def update(self, dt):
pass
def get_direction_vector(self):
"""returns the direction as a unit vector (only possible for moving particles)"""
return Vector(math.cos(self.direction), math.sin(self.direction))
def get_position(self):
return self.position
def set_position(self, new_position):
"""this function is called whenever the position of the particle changes"""
self.position = new_position
self.sprite.position = self.position.pair()
def set_speed(self, new_speed):
self.speed = new_speed
def check_collisions(self):
"""check for collisions with other particles or cars"""
for p in self.race.particles:
if self.removed:
return
if not p is self and p.vulnerable and self.position.distance(p.position) <= self.radius + p.radius:
self.particle_collision(p)
for r in self.race.racers:
if self.removed:
return
if self.position.distance(r.car.position) <= self.radius + r.car.radius() and not r.car.state.aerial:
self.car_collision(r.car)
def particle_collision(self, particle):
pass
def car_collision(self, car):
pass
def remove(self):
self.removed = True
self.race.particles.remove(self)
self.sprite.delete()
class ParticleBanana(Particle):
"""a banana peel that remains on the track until something hits it"""
color = (248, 248, 0)
name = "Banana"
def __init__(self, race, position):
super(ParticleBanana, self).__init__(race, position, sprite_seq['banana'], 4)
def update(self, dt):
self.check_collisions()
def car_collision(self, car):
self.remove()
if car.is_vulnerable():
car.spin()
class ParticleGreenShell(Particle):
"""a green shell that bounces across the track"""
color = (64, 224, 64)
name = "Green Shell"
def __init__(self, race, position, direction, speed):
super(ParticleGreenShell, self).__init__(race, position, sprite_seq['green shell'], 4)
self.direction = direction
self.speed = speed
def update(self, dt):
track = self.race.track
current_position = self.position
move = self.get_direction_vector() * self.speed * dt
new_position = current_position + move
path = bresenham(current_position, new_position)
for i, c in enumerate(path):
if track.type(c, hit=True) is WALL:
# the shell bounces
if c[0] != path[i - 1][0]: # vertical wall
self.direction = -self.direction + math.pi
else: # horizontal wall
self.direction = -self.direction
new_position = current_position # don't move
self.set_speed(self.speed - 15.) # slow down after each bounce
if self.speed <= 40:
return self.remove() # if the shell becomes too slow it disappears
break
elif track.type(new_position) is DEEP:
# the shell disappears
return self.remove()
self.set_position(new_position)
self.check_collisions()
def car_collision(self, car):
self.remove()
if car.is_vulnerable():
car.spin()
def particle_collision(self, p):
p.remove()
self.remove()
class ParticleRedShell(Particle):
"""a homing red shell aimed at a car (or possibly nothing if it had no target when shot)"""
color = (248, 0, 0)
name = "Red Shell"
def __init__(self, race, position, direction, speed, target):
super(ParticleRedShell, self).__init__(race, position, sprite_seq['red shell'], 4)
self.direction = direction
self.turn = 2.
self.speed = speed
self.target = target
def update(self, dt):
track = self.race.track
current_position = self.position
if not self.target is None:
target_direction = (self.target.position - self.position).angle()
direction_variation = ((target_direction - self.direction + math.pi) % (2 * math.pi)) - math.pi
if direction_variation > self.turn * dt:
direction_variation = self.turn * dt
elif direction_variation < -self.turn * dt:
direction_variation = -self.turn * dt
self.direction += direction_variation
move = self.get_direction_vector() * self.speed * dt
new_position = current_position + move
path = bresenham(current_position, new_position)
for i, c in enumerate(path):
if track.type(c, hit=True) in [WALL, DEEP]:
# the shell hits a wall or a hole and disappears
return self.remove()
self.set_position(new_position)
self.check_collisions()
def car_collision(self, car):
self.remove()
if car.is_vulnerable():
car.spin()
def particle_collision(self, p):
p.remove()
self.remove()