-
Notifications
You must be signed in to change notification settings - Fork 0
/
collision_processor.py
executable file
·197 lines (164 loc) · 6.34 KB
/
collision_processor.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
import constants
import collision_detection as cd
from bullet import BOUNCED, EXPLODED
from explosion import Shockwave, BigExplosion
from tank import TANK_EXPLODED
class CollisionProcessor(object):
def __init__(self, level):
self.level = level
def process(self):
self.resolve_tanks()
self.resolve_bullets()
self.resolve(self.level.mines, [
self.resolve_tanks_and_mine
])
self.resolve_explosions()
self.resolve_powerups()
def resolve(self, group, resolvers):
for item in group:
for resolver in resolvers:
if item.expired(): continue
resolver(item)
def resolve_powerups(self):
for powerup in self.level.powerups:
for player in self.level.player_tanks:
if not powerup.taken:
if powerup.can_take(player) and cd.sprite_collide(player, powerup):
self.level.play_sound("pickup", 0.2)
powerup.take(player)
player.taking.add(powerup)
def resolve_explosions(self):
for explosion in self.level.explosions:
for mine in self.level.mines:
if not mine.expired() and explosion.damages and cd.sprite_collide(explosion, mine):
self.level.play_sound("tank_explode")
self.mine_explode(mine)
for shield in self.level.shields:
if shield.active and explosion.damages and cd.sprite_collide(shield, explosion):
self.level.play_sound("shield_die")
shield.die()
explosion.damaged.add(shield.tank)
for tank in self.level.all_tanks():
if explosion.damages and not tank in explosion.damaged and cd.sprite_collide(tank, explosion):
#self.level.stats.bullet_hit(explosion.bullet, tank)
if self.tank_damage(tank):
self.level.play_sound("tank_explode")
# TODO, add a bullet origin to explosions so they can be
# credited to the correct player.
#self.level.stats.kill(explosion.bullet, tank)
explosion.damaged.add(tank)
def tank_explode(self, tank):
self.level.explosions.add(BigExplosion(tank.position))
tank.kill()
tank.turret.kill()
def tank_damage(self, tank):
if tank.hurt() is TANK_EXPLODED:
self.tank_explode(tank)
return True
return False
def mine_explode(self, mine):
self.level.explosions.add(mine.get_explosion())
mine.die()
def mine_attack(self, mine, target):
mine.swell_and_explode()
def bullet_explode(self, bullet):
self.level.explosions.add(bullet.get_explosion())
bullet.die()
def resolve_tanks_and_mine(self, mine):
for tank in self.level.all_tanks():
if not mine.exploding and not mine.expired() and not tank.dead and mine.active() and cd.sprite_collide(tank, mine.collision_circle):
self.level.play_sound("tank_explode")
self.mine_attack(mine, tank)
def resolve_bullets(self):
resolvers = [
self.resolve_shields_and_bullet,
self.resolve_tanks_and_bullet,
self.resolve_bullets_and_bullet,
self.resolve_distance_and_bullet,
self.resolve_walls_and_bullet,
self.resolve_mines_and_bullet,
]
for bullet in self.level.bullets:
for resolver in resolvers:
if bullet.expired(): continue
resolver(bullet)
def resolve_mines_and_bullet(self, bullet):
for mine in self.level.mines:
if cd.sprite_collide(mine, bullet):
self.level.play_sound("tank_explode")
self.bullet_explode(bullet)
self.mine_explode(mine)
break
def resolve_walls_and_bullet(self, bullet):
results = bullet.bounce(self.level.solid)
for (result, position) in results:
if result == EXPLODED:
self.level.play_sound("bullet_explode")
self.bullet_explode(bullet)
elif result == BOUNCED:
self.level.play_sound("pong", 0.35)
self.level.shockwaves.add(Shockwave(position))
def resolve_distance_and_bullet(self, bullet):
if bullet.total_distance > constants.BULLET_MAX_RANGE:
self.level.play_sound("bullet_explode")
self.bullet_explode(bullet)
def resolve_bullets_and_bullet(self, bullet):
for bullet2 in filter(lambda x: x is not bullet, self.level.bullets):
if cd.bullet_collides_with_bullet(bullet, bullet2):
self.level.play_sound("bullet_explode")
self.bullet_explode(bullet)
self.bullet_explode(bullet2)
self.level.stats.bullet_collision(bullet, bullet2)
break
def resolve_shields_and_bullet(self, bullet):
for shield in self.level.shields:
if shield.active and cd.bullet_collides_with_shield(bullet, shield):
# destroy the shield
self.level.play_sound("shield_die")
shield.die()
# explode the bullet
self.level.play_sound("bullet_explode")
self.bullet_explode(bullet)
self.level.stats.bullet_hit(bullet, shield.tank)
break
def resolve_tanks_and_bullet(self, bullet):
for tank in self.level.all_tanks():
if not tank.dead and cd.bullet_collides_with_tank(bullet, tank):
# do something to the player
self.level.stats.bullet_hit(bullet, tank)
if self.tank_damage(tank):
self.level.play_sound("tank_explode")
self.level.stats.kill(bullet, tank)
else:
self.level.play_sound("bullet_explode")
# explode the bullet
self.bullet_explode(bullet)
break
def resolve_tanks(self):
for tank in self.level.all_tanks():
if cd.tank_collides_with_tile(tank, self.level.solid):
tank.revert()
# check for tank to tank collisions
conflicts = True
loops = 0
while conflicts:
loops += 1
# break out if we've obviously hit an infinite loop
if loops > 20:
if self.level.game.settings['debug']:
print("tank collision resolution hit an infinite loop")
break
conflicts = False
for tank1 in self.level.all_tanks():
for tank2 in self.level.all_tanks():
if tank1 is not tank2 and not tank1.dead and not tank2.dead and cd.tank_collides_with_tank(tank1, tank2):
conflicts = True
def revert(t1, t2):
t1.revert()
if cd.tank_collides_with_tank(t1, t2):
t2.revert()
# revert the enemy first, if there is one
if tank2 in self.level.enemies:
revert(tank2, tank1)
else:
revert(tank1, tank2)