-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.py
220 lines (186 loc) · 9.24 KB
/
game.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
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 11 20:37:14 2018
@authors: Saveliy Yusufov and Michael Harley
"""
import random
import pygame
from button import Button
from cannon import Cannon
from cannon_display import Cannon_Display
from scoreboard import Scoreboard
from zombie import Zombie
class Game(object):
FRAMES_PER_SEC = 60
# The amount of zombies allowed past the perimeter before game over
ZOMBIE_THRESH = 20
def __init__(self, disp_width, disp_height):
pygame.init()
self.clock = pygame.time.Clock()
self.game_display = pygame.display.set_mode((disp_width, disp_height))
pygame.display.set_caption("Tower Defense")
self.background_img = pygame.image.load("grass.jpg").convert()
self.scoreboard = Scoreboard()
self.running = True
self.herd_size = 15
self.tick_count = 300 # Time to wait before the next wave of zombies
def make_zombie_herd(self, num_of_zombies, zombie_health, delta):
"""Creates a herd of zombies
Creates a list of zombies generated in random positions on the screen
start_x_pos for each zombie is a number in [-1000, 0)
start_y_pos for each zombie is a random position along the y-axis
Args:
num_of_zombies: the amount of zombies to be generated in the herd
height: the height of the display
Returns: a sprite group of zombies
"""
zombie_herd = pygame.sprite.Group()
for _ in range(num_of_zombies):
start_x = random.randint(-1050, -50)
start_y = self.game_display.get_height() * random.uniform(0.1, 0.9)
zombie = Zombie(start_x, start_y)
zombie.health = zombie_health
zombie.delta = delta
zombie_herd.add(zombie)
return zombie_herd
def move_zombie_herd(self, zombie_group):
"""Moves a herd of zombies
This function calls the zombie class move() function (with default delta)
on a given zombie herd. If a zombie makes it past the perimeter, then it
increments the zombies_past_perim counter, and removes the zombie from the
zombie_group list
Args:
zombie_group: the zombie sprite group to be moved along the display
"""
for zomb in zombie_group:
if zomb.x > self.game_display.get_width():
zombie_group.remove(zomb)
self.scoreboard.score += 1
zombie_group.update()
zombie_group.draw(self.game_display)
def display_warning(self, tick_counter):
"Display a countdown on screen till the next wave, on the screen"
text = pygame.font.Font(None, 50).render("Next wave in {}".format((tick_counter / 10)), True, (255, 255, 0))
text_rect = text.get_rect()
text_x = self.game_display.get_width() / 2 - text_rect.width / 2
text_y = self.game_display.get_height() / 2 - text_rect.height / 2
self.game_display.blit(text, [text_x, text_y])
def display_game_over(self):
"Display the Game Over screen with player/user stats"
text = pygame.font.Font(None, 50).render("Game Over!", True, (255, 255, 0))
text_rect = text.get_rect()
text_x = self.game_display.get_width() / 2 - text_rect.width / 2
text_y = self.game_display.get_height() / 2 - text_rect.height / 2
wave_stats = pygame.font.Font(None, 36).render("You held off {} waves".format(self.scoreboard.wave), True, (255, 255, 0))
kill_stats = pygame.font.Font(None, 36).render("You killed {} zombies".format(self.scoreboard.zombies_killed), True, (255, 255, 0))
self.game_display.blit(text, [text_x, text_y])
self.game_display.blit(wave_stats, [text_x-25, text_y+50])
self.game_display.blit(kill_stats, [text_x-20, text_y+100])
def run_game_loop(self):
player_mouse_pos = [0, 0]
button_clicked = False
dragging = False
game_over = False
waves_allowed = True
button = Button(15, 540, 50, 50)
drag_cannon = Cannon([25, 552])
cannon_display = Cannon_Display(self.scoreboard)
cannon_sprite = pygame.sprite.Group(cannon_display)
zombie_health = 110
delta = 0.9
zombies_sprite = self.make_zombie_herd(self.herd_size, zombie_health, delta)
cannons = pygame.sprite.Group()
score_sprite = pygame.sprite.Group(self.scoreboard)
button_sprite = pygame.sprite.Group(button)
wave_count = 0
kills_per_cannon = 50
# Game loop
while self.running:
# Event detection
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
button_clicked = True
elif event.type == pygame.MOUSEBUTTONUP:
player_mouse_pos[0] = event.pos[0]
player_mouse_pos[1] = event.pos[1]
if button_clicked and dragging and self.scoreboard.cannons > 0:
self.scoreboard.cannons -= 1
cannon = Cannon([player_mouse_pos[0]-45, player_mouse_pos[1]-45])
cannons.add(cannon)
button_clicked = False
dragging = False
cannons.update()
cannon_sprite.update()
score_sprite.update()
button_sprite.update("null")
self.game_display.blit(self.background_img, (0, 0))
# Get mouse position
cannon_pos = pygame.mouse.get_pos()
if 10 <= cannon_pos[0] <= 60 and 540 <= cannon_pos[1] <= 590 and button_clicked:
button_sprite.update("shrink")
dragging = True
elif dragging:
button_sprite.update("shrink")
if button_clicked and dragging and self.scoreboard.cannons > 0:
drag_cannon.display(self.game_display, cannon_pos[0]-45, cannon_pos[1]-45)
# Check if the wave of zombies is empty, start the timer, don't allow waves
# increment size of herd, and create a new herd to send as next wave
if not zombies_sprite:
waves_allowed = False
self.herd_size += random.randint(9, 13)
self.scoreboard.increment_wave()
wave_count += 1
if wave_count != 0 and wave_count % 5 == 0:
zombie_health += 60
delta += 0.1
kills_per_cannon = kills_per_cannon + 25
else:
zombie_health += 20
zombies_sprite = self.make_zombie_herd(self.herd_size, zombie_health, delta)
if not game_over:
cannons.draw(self.game_display)
score_sprite.draw(self.game_display)
button_sprite.draw(self.game_display)
cannon_sprite.draw(self.game_display)
# If 10 secs passed since last wave, then release the next zombie wave
if not waves_allowed:
if self.tick_count == 300:
self.scoreboard.cannons += (self.scoreboard.cannon_cash // kills_per_cannon)
self.scoreboard.cannon_cash = (self.scoreboard.cannon_cash % kills_per_cannon)
self.display_warning(self.tick_count)
self.tick_count -= 1
if self.tick_count == 0:
waves_allowed = True
self.tick_count = 300
# If zombie waves are allowed to attack AND the game is not over,
# then move the zombie wave across the screen
if waves_allowed and not game_over:
self.move_zombie_herd(zombies_sprite)
if self.scoreboard.score >= self.ZOMBIE_THRESH:
game_over = True
# If game over is true, draw Game Over and display player's stats
if game_over:
self.display_game_over()
# Check if zombie is within radius of any cannon
if not game_over and waves_allowed:
for cannon in cannons:
zombies_in_radius = pygame.sprite.spritecollide(cannon, zombies_sprite, False, collided=pygame.sprite.collide_circle)
if zombies_in_radius:
for zombie in zombies_in_radius:
cannon.shoot(zombie.rect.centerx, zombie.rect.centery, self.game_display)
# Now check for bullet - zombie collisions
hit_list = pygame.sprite.spritecollide(zombie, cannon.bullets, False)
if hit_list:
for bullet in hit_list:
if zombie.health <= 0:
self.scoreboard.zombies_killed += 1
self.scoreboard.cannon_cash += 1
zombies_sprite.remove(zombie)
else:
zombie.health -= 10
cannon.bullets.remove(bullet)
cannon.bullets.empty()
pygame.display.update()
self.clock.tick(self.FRAMES_PER_SEC)