-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtank.py
executable file
·242 lines (201 loc) · 8.21 KB
/
tank.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
import pygame, math, random, os
import constants
from geometry import Vector, Point, Line, ORIGIN
from bullet import Bullet
import interpolation
TANK_EXPLODED = 1
class SplashTimer:
def __init__(self, time, tank):
self.tank = tank
self.time_left = time
def update(self, delta):
self.time_left -= delta
self.time_left = max(0, self.time_left)
if self.time_left == 0:
self.tank.splash = False
self.splash_color_time = 0
self.tank.update_image()
return self.time_left == 0
class Tank(pygame.sprite.Sprite):
def __init__(self, position, direction, color=constants.TANK_COLOR):
pygame.sprite.Sprite.__init__(self)
self.color = color
# current coordinates of the centre of the tank
self.position = position
self.old_position = self.position
# current direction of the tank, in radians
self.direction = direction
self.old_direction = self.direction
# speed of the tank
self.speed = 0.0
self.cooldown = 0
self.mine_cooldown = 0
self.bullets = 0
self.mines = 0
self.turret = None
self.health = constants.TANK_HEALTH
self.dead = False
self.taking = set()
self.splash = False
self.splash_color_time = 0
self.shields = []
self.timers = []
self.update_image()
self.update_graphics()
def can_lay_mine(self):
return self.mines < constants.TANK_MAX_MINES and self.mine_cooldown == 0
def lay_mine(self):
self.mines += 1
self.mine_cooldown = constants.TANK_MINE_COOLDOWN
def update_image(self):
size = constants.TILE_SIZE * constants.TANK_SIZE_RATIO
self.original = pygame.Surface([size, size], flags=pygame.SRCALPHA)
diff = 20 * (constants.TANK_HEALTH - self.health)
new_color = pygame.Color(max(0, self.color.r - diff), max(0, self.color.g - diff), max(0, self.color.b - diff))
if self.splash:
splash_color = pygame.Color(max(0, constants.SPLASH_TANK_COLOR.r - diff), max(0,constants.SPLASH_TANK_COLOR.g - diff), max(0, constants.SPLASH_TANK_COLOR.b - diff))
t = interpolation.quadratic_bi(self.splash_color_time % constants.SPLASH_TANK_COLOR_PERIOD, constants.SPLASH_TANK_COLOR_PERIOD)
final_color = interpolation.interpolate_colors(t, splash_color, new_color)
else:
final_color = new_color
self.original.fill(final_color)
def expired(self):
return self.dead
def hurt(self):
if constants.INFINITE_HEALTH:
return
self.health -= 1
if self.health == 0:
self.dead = True
for shield in self.shields:
shield.die()
return TANK_EXPLODED
else:
self.update_image()
def heal(self):
self.health = min(self.health + 1, constants.TANK_HEALTH)
self.update_image()
def has_splash(self):
return self.splash
def activate_splash(self):
self.splash = True
self.splash_color_time = 0
self.timers.append(SplashTimer(constants.SPLASH_TANK_DURATION, self))
# this returns the tank to its position before the last update()
# call. It is important that this be idempotent.
def revert(self):
self.position = self.old_position
self.direction = self.old_direction
self.speed = 0.0
self.update_graphics()
self.turret.revert()
def update(self, delta):
self.cooldown -= delta
self.cooldown = max(self.cooldown, 0)
self.mine_cooldown -= delta
self.mine_cooldown = max(self.mine_cooldown, 0)
self.old_position = self.position
self.position = self.position.translate(
((delta / 1000.0) * self.speed) *
Vector(math.cos(self.direction), math.sin(self.direction)).normalize())
if self.splash:
self.splash_color_time += delta
self.update_image()
for timer in self.timers:
if timer.update(delta):
self.timers.remove(timer)
self.update_graphics()
def update_graphics(self):
self.image = pygame.transform.rotate(self.original, -self.direction * 180.0 / math.pi)
self.rect = self.image.get_rect(center=(self.position.scale(constants.TILE_SIZE)))
def accelerate(self, delta):
self.speed += constants.ACCEL_SPEED * (delta / 1000.0)
if self.speed < 0:
self.neutral(delta)
self.speed = min(constants.MAX_SPEED, self.speed)
def decelerate(self, delta):
self.speed -= constants.DECEL_SPEED * (delta / 1000.0)
if self.speed > 0:
self.neutral(delta)
self.speed = max(-constants.MAX_SPEED, self.speed)
def neutral(self, delta):
if self.speed > 0:
self.speed -= constants.NEUTRAL_SPEED * (delta / 1000.0)
if self.speed < 0.01:
self.speed = 0
elif self.speed < 0:
self.speed += constants.NEUTRAL_SPEED * (delta / 1000.0)
if self.speed > -0.01:
self.speed = 0
def turn_left(self, delta):
self.old_direction = self.direction
self.direction -= 2 * math.pi * delta / (constants.TANK_TURNING_SPEED * 1000.0)
if self.direction < -math.pi:
self.direction += 2 * math.pi
def turn_right(self, delta):
self.old_direction = self.direction
self.direction += 2 * math.pi * delta / (constants.TANK_TURNING_SPEED * 1000.0)
if self.direction > math.pi:
self.direction -= 2 * math.pi
def get_sides(self):
half_width = constants.TANK_SIZE_RATIO / 2
half_height = constants.TANK_SIZE_RATIO / 2
minus_center = ORIGIN - self.position
plus_center = self.position - ORIGIN
back_left = self.position.translate(Vector(-half_width, -half_height)).translate(minus_center).rotate(self.direction).translate(plus_center)
front_left = self.position.translate(Vector(half_width, -half_height)).translate(minus_center).rotate(self.direction).translate(plus_center)
back_right = self.position.translate(Vector(-half_width, half_height)).translate(minus_center).rotate(self.direction).translate(plus_center)
front_right = self.position.translate(Vector(half_width, half_height)).translate(minus_center).rotate(self.direction).translate(plus_center)
front = Line(front_left, front_right)
back = Line(back_left, back_right)
left = Line(back_left, front_left)
right = Line(back_right, front_right)
return [front, back, left, right]
class Turret(pygame.sprite.Sprite):
def __init__(self, tank):
pygame.sprite.Sprite.__init__(self)
self.original = pygame.transform.smoothscale(pygame.image.load(os.path.join(constants.DATA_DIR, "turret.png")).convert_alpha(), (int(round(constants.TURRET_LENGTH_RATIO * constants.TILE_SIZE)), int(round(constants.TURRET_WIDTH_RATIO * constants.TILE_SIZE))))
self.image = self.original
self.rect = self.image.get_rect()
self.direction = 0.0
self.tank = tank
tank.turret = self
def expired(self):
return self.tank.dead
def revert(self):
self.update_graphics()
# creates and returns a Bullet starting at the end of the Turret's
# barrel and going in the direction of the barrel.
def fire(self):
if self.tank.cooldown == 0 and self.tank.bullets < constants.TANK_MAX_BULLETS:
origin = self.tank.position.translate(Vector(constants.TURRET_LENGTH_RATIO / 2, 0)).rotate_about(self.direction + self.tank.direction, self.tank.position)
self.tank.bullets += 1
self.tank.cooldown = constants.TANK_COOLDOWN
return Bullet(origin, self.direction + self.tank.direction, self.tank, self.tank.splash)
# turn towards target point
def turn(self, delta, target):
# target direction
d = target - self.tank.position
# target angle
t_a = d.angle()
return self.turn_direction(delta, t_a)
def turn_direction(self, delta, target_direction):
t_a = target_direction
# current angle
c_a = self.direction + self.tank.direction
difference = t_a - c_a
while difference < -math.pi:
difference += 2 * math.pi
while difference > math.pi:
difference -= 2 * math.pi
if difference > 0:
# turn right
self.direction += min(2 * math.pi * delta / (constants.TURRET_TURNING_SPEED * 1000.0), difference)
elif difference < 0:
# turn left
self.direction -= min(2 * math.pi * delta / (constants.TURRET_TURNING_SPEED * 1000.0), -difference)
def update(self, delta):
self.update_graphics()
def update_graphics(self):
self.image = pygame.transform.rotate(self.original, -(self.direction + self.tank.direction) * 180.0 / math.pi)
self.rect = self.image.get_rect(center=self.tank.rect.center)