├── README └── infect_the_world.py /README: -------------------------------------------------------------------------------- 1 | I made this game for Week 1 of my Game Design class. 2 | 3 | The green circles are viruses floating around in blood, trying to make the infection (black circle) bigger. They automatically try to infect cells in the blood (white circles), but if they hit an antibody (a blue circle) on the way there, they die. 4 | 5 | You can fling antibodies out of the way of the green circles by dragging them with your mouse. 6 | 7 | Once you have 5 viruses floating in blood, any additional viruses will grow the infection. If the infection gets big enough, you win. 8 | 9 | I built this game in pygame and I think it has pretty good gameplay. -------------------------------------------------------------------------------- /infect_the_world.py: -------------------------------------------------------------------------------- 1 | #Import Modules 2 | import sys, pygame, math, random 3 | from pygame.locals import * 4 | 5 | window_width = 1024 6 | window_height = 768 7 | 8 | #defining some colors 9 | dark_green = ( 115, 163, 24 ) 10 | blue = ( 32, 108, 115 ) 11 | blood_red = ( 214, 67, 46) 12 | gray = ( 219, 219, 219 ) 13 | black = ( 0, 0, 0 ) 14 | 15 | NUM_ANTIBODIES = 5 16 | NUM_CELLS = 3 17 | 18 | def normalize(vector): 19 | length = math.sqrt(vector[0]**2 + vector[1]** 2) 20 | 21 | if length == 0: 22 | return vector 23 | 24 | vector[0] /= length 25 | vector[1] /= length 26 | 27 | return vector 28 | 29 | def vec_length(vector): 30 | return math.sqrt(vector[0]**2 + vector[1]** 2) 31 | 32 | class Ball(pygame.sprite.Sprite): 33 | """Ball class""" 34 | def __init__(self, color, position=None): 35 | pygame.sprite.Sprite.__init__(self) #call Sprite intializer 36 | 37 | self.color = color 38 | 39 | self.flung = False 40 | 41 | # Create the surface for the ball, and draw to that surface: 42 | self.radius = 20 43 | self.image = pygame.Surface((self.radius*2, self.radius*2)) 44 | self.image = self.image.convert_alpha() 45 | self.image.fill((0,0,0,0)); 46 | pygame.draw.circle(self.image, color, (self.radius, self.radius), self.radius) 47 | self.rect = self.image.get_rect() 48 | 49 | self.locked = pygame.sprite.GroupSingle() 50 | 51 | # Set initial position 52 | if position == None: 53 | position = [random.randint(50,590), random.randint(50,430)] 54 | self._position = position 55 | self.rect.center = tuple(position) 56 | 57 | # Set initial velocity 58 | self.velocity = [0, 0] 59 | self.dragged = False 60 | 61 | self.type = 'default' 62 | 63 | def set_radius(self, radius): 64 | self.radius = radius 65 | self.image = pygame.Surface((self.radius*2, self.radius*2)) 66 | self.image = self.image.convert_alpha() 67 | self.image.fill((0,0,0,0)); 68 | pygame.draw.circle(self.image, self.color, (self.radius, self.radius), self.radius) 69 | self.rect = self.image.get_rect() 70 | 71 | 72 | def move_randomly(self): 73 | range = 2 74 | self.velocity = [random.randint(-range*2, -range), random.randint(-range,range)] 75 | 76 | def move_horizontally(self): 77 | self.velocity = [ -2, 0 ] 78 | 79 | def oscillate(self): 80 | new_position = list(self._position) 81 | new_position[0] = window_width/3 82 | #new_position[1] = random.randint(window_height/4, 3*window_height/4) 83 | 84 | direction = [new_position[0] - self.x() , new_position[1] - self.y()] 85 | normalize(direction) 86 | 87 | MAGNITUDE = 3 88 | 89 | self.velocity = [direction[0]*MAGNITUDE, direction[1]*MAGNITUDE] 90 | 91 | def get_position(self): 92 | return list(self._position) 93 | 94 | def set_position(self, position): 95 | self._position = list(position) 96 | self.rect.center = tuple(self._position) 97 | return True 98 | 99 | def x(self): 100 | return self._position[0] 101 | 102 | def y(self): 103 | return self._position[1] 104 | 105 | 106 | def move_towards(self, position): 107 | 108 | direction = [position[0] - self.x() , position[1] - self.y()] 109 | normalize(direction) 110 | 111 | MAGNITUDE = 3 112 | 113 | self.velocity = [direction[0]*MAGNITUDE, direction[1]*MAGNITUDE] 114 | 115 | return None 116 | 117 | def off_screen(self): 118 | off_right_left = self.x() < 0 or self.x() > window_width 119 | off_up_down = self.y() < 0 or self.y() > window_height 120 | if off_right_left or off_up_down: 121 | return True 122 | else: 123 | return False 124 | 125 | def update(self): 126 | # allsprites.update() runs this function 127 | # allsprites.draw() will blit the self.image surface to the screen 128 | 129 | self._position[0] += self.velocity[0] 130 | self._position[1] += self.velocity[1] 131 | 132 | if self.off_screen(): 133 | self.kill() 134 | else: 135 | self.rect.center = tuple(self._position) 136 | 137 | def fling(self, start_pos, end_pos): 138 | if self.type == 'infection': 139 | return False 140 | if self.type == 'virus': 141 | return False 142 | if self.type == 'cell': 143 | self.kill() 144 | return False 145 | 146 | self.flung = True 147 | vel = [end_pos[0] - start_pos[0], end_pos[1] - start_pos[1]] 148 | 149 | MAGNITUDE = 0.02 150 | 151 | self.velocity = [ self.velocity[0] + vel[0] * MAGNITUDE, self.velocity[1] + vel[1] * MAGNITUDE] 152 | 153 | def drag(self): 154 | if self.type == 'virus': 155 | return False 156 | if self.type == 'cell': 157 | self.kill() 158 | return False 159 | mpos = pygame.mouse.get_pos() 160 | self.set_position(mpos) 161 | 162 | def get_distance_to(self, point): 163 | dx = point[0] - (self.x()) 164 | dy = point[1] - (self.y()) 165 | return math.sqrt(dx**2 + dy**2) 166 | 167 | def mouse_over(self): 168 | mpos = pygame.mouse.get_pos() 169 | radius = self.radius 170 | if self.type == 'antibody': 171 | radius *= 2.5 172 | if self.get_distance_to(mpos) < radius: 173 | return True 174 | else: 175 | return False 176 | 177 | def __str__(self): 178 | return "Type: %s position: %s " % (self.type, self.get_position()) 179 | 180 | def make_virus(self): 181 | self.type = 'virus' 182 | 183 | self.image = pygame.Surface((self.radius*2, self.radius*2)) 184 | self.image = self.image.convert_alpha() 185 | self.image.fill((0,0,0,0)); 186 | pygame.draw.circle(self.image, dark_green, (self.radius, self.radius), self.radius) 187 | 188 | 189 | 190 | # Returns the number of antibodies on screen. 191 | def num_antibodies(allsprites): 192 | count = 0 193 | for sprite in allsprites: 194 | if sprite.type == 'antibody': 195 | count += 1 196 | 197 | return count 198 | 199 | def antibodies_far_enough(allsprites, num_converted): 200 | widths = [] 201 | for sprite in allsprites: 202 | # if the y position is 100 pixels away from the right of the screen. 203 | if sprite.type == 'antibody' and not sprite.flung: 204 | widths.append(window_width - sprite.x()) 205 | if not widths: 206 | return True 207 | if min(widths) < window_width/max(num_converted, NUM_ANTIBODIES): 208 | return False 209 | return True 210 | 211 | def num_cells(allsprites): 212 | count = 0 213 | for sprite in allsprites: 214 | if sprite.type == 'cell': 215 | count += 1 216 | 217 | return count 218 | 219 | 220 | def cells_far_enough(allsprites): 221 | widths = [] 222 | for sprite in allsprites: 223 | # if the y position is 100 pixels away from the right of the screen. 224 | if sprite.type == 'cell': 225 | widths.append(window_width - sprite.x()) 226 | if not widths: 227 | return True 228 | if min(widths) < window_width/NUM_CELLS: 229 | return False 230 | return True 231 | 232 | def find_eligible_cell(virus, allsprites): 233 | cells = [] 234 | for sprite in allsprites: 235 | if sprite.type == 'cell' and sprite.locked.has(virus): 236 | return sprite 237 | if sprite.type == 'cell' and sprite.x() < 5 * window_width / 6 and len(sprite.locked.sprites()) == 0: 238 | cells.append(sprite) 239 | 240 | if not cells: 241 | return None 242 | 243 | closest = min(cells, key=lambda cell: cell.x()) 244 | 245 | closest.locked.add(virus) 246 | 247 | return closest 248 | 249 | def game_over(): 250 | while 1: 251 | i = 1 252 | 253 | def num_flung(viruses): 254 | count = 0 255 | for v in viruses: 256 | try: 257 | if v.flung_towards_infection or v.flung: 258 | count += 1 259 | except: 260 | pass 261 | return count 262 | 263 | def main(): 264 | """this function is called when the program starts. 265 | it initializes everything it needs, then runs in 266 | a loop until the function returns.""" 267 | #Initialize Everything 268 | pygame.init() 269 | screen = pygame.display.set_mode((window_width, window_height)) 270 | pygame.display.set_caption('Test Game') 271 | 272 | #Create The Backgound 273 | background = pygame.Surface(screen.get_size()) 274 | background = background.convert() 275 | background.fill(blood_red) 276 | 277 | #Display The Background 278 | screen.blit(background, (0, 0)) 279 | pygame.display.flip() 280 | 281 | #Clock and object list 282 | clock = pygame.time.Clock() 283 | allsprites = pygame.sprite.RenderPlain() 284 | 285 | viruses = pygame.sprite.RenderPlain() 286 | 287 | 288 | virus = Ball(dark_green, [50, window_height/2]) 289 | virus.type = 'virus' 290 | 291 | viruses.add(virus) 292 | 293 | cells = pygame.sprite.RenderPlain() 294 | 295 | allsprites.add(virus) 296 | 297 | antibodies = pygame.sprite.RenderPlain() 298 | 299 | infection = Ball(black, [20, window_height/2]) 300 | infection.set_radius(40) 301 | infection.type = 'infection' 302 | allsprites.add(infection) 303 | 304 | num_converted = 0 305 | 306 | score = 0 307 | 308 | font = pygame.font.Font(None, 30) 309 | 310 | big_font = pygame.font.Font(None, 50) 311 | 312 | difficulty = 0 313 | 314 | 315 | #Main Loop 316 | while 1: 317 | clock.tick(60) # SAVES CPU! 318 | 319 | #Handle Input Events 320 | for event in pygame.event.get(): 321 | if event.type == QUIT: 322 | return 323 | elif event.type == MOUSEBUTTONDOWN: 324 | for sprite in allsprites: 325 | if sprite.mouse_over(): 326 | if not sprite.dragged: 327 | sprite.drag_start = pygame.mouse.get_pos() 328 | sprite.dragged = True 329 | elif event.type == MOUSEBUTTONUP: 330 | for sprite in allsprites: 331 | if sprite.dragged: 332 | sprite.fling(sprite.drag_start, pygame.mouse.get_pos()) 333 | sprite.dragged = False 334 | 335 | if num_antibodies(allsprites) < max(difficulty, NUM_ANTIBODIES) and antibodies_far_enough(allsprites, num_converted): 336 | antibody = Ball(blue, [window_width - 20, random.randint(30, window_height-30)]) 337 | antibody.type = 'antibody' 338 | antibody.move_randomly() 339 | antibodies.add(antibody) 340 | allsprites.add(antibody) 341 | 342 | if num_cells(allsprites) < NUM_CELLS and cells_far_enough(allsprites): 343 | cell = Ball(gray, [window_width - 20, random.randint(30, window_height-30)]) 344 | cell.type = 'cell' 345 | cell.move_horizontally() 346 | allsprites.add(cell) 347 | cells.add(cell) 348 | 349 | for v in viruses: 350 | if v.color == black: 351 | continue 352 | # find the cell to go to, and make virus go to cell. 353 | eligible_cell = find_eligible_cell(v, allsprites) 354 | if not eligible_cell: 355 | if len(viruses.sprites()) - num_flung(viruses) > 5: 356 | v.color = black 357 | v.set_radius(v.radius) 358 | v.flung_towards_infection = True 359 | v.move_towards(infection.get_position()) 360 | elif not v.flung and (v.x() > window_width / 2 or v.x() < window_width / 5): 361 | v.oscillate() 362 | v.flung = False 363 | else: 364 | v.move_towards(eligible_cell.get_position()) 365 | 366 | 367 | collisions = pygame.sprite.groupcollide(cells, viruses, False, False) 368 | 369 | for cell in collisions.keys(): 370 | cell.make_virus() 371 | viruses.add(cell) 372 | cells.remove(cell) 373 | difficulty += 1 374 | num_converted += 1 375 | 376 | collisions = pygame.sprite.groupcollide(antibodies, viruses, True, True) 377 | 378 | for antibody in collisions.keys(): 379 | difficulty -= 1 380 | 381 | if num_converted > 5: 382 | collisions = pygame.sprite.spritecollide(infection, viruses, True) 383 | 384 | for virus in collisions: 385 | infection.set_radius(int(infection.radius*1.2)) 386 | score += 1 387 | 388 | collisions = pygame.sprite.spritecollide(infection, antibodies, True) 389 | for antibody in collisions: 390 | infection.set_radius(int(infection.radius*10/12)) 391 | score -= 1 392 | 393 | text = font.render("Score: %d" % (score), 1, black) 394 | textpos = text.get_rect(center = (50, 50)) 395 | 396 | conv_text = font.render("Cells Infected: %d" % (num_converted), 1, black if num_converted < 10 else dark_green) 397 | conv_textpos = text.get_rect(center = (window_width-150, 50)) 398 | background.fill(blood_red) 399 | background.blit(text, textpos) 400 | background.blit(conv_text, conv_textpos) 401 | 402 | if score >= 5: 403 | text = big_font.render("You have successfully infected the host. Congratulations", 1, black) 404 | textpos = text.get_rect(center=(window_width/2, window_height/2)) 405 | 406 | background.blit(text, textpos) 407 | screen.blit(background, (0, 0)) 408 | allsprites.draw(screen) 409 | pygame.display.flip() 410 | game_over() 411 | 412 | 413 | elif len(viruses) <= 0 or score < 0: 414 | text = big_font.render("Your infection was wiped out. Restart the game to try again", 1, black) 415 | textpos = text.get_rect(center=(window_width/2, window_height/2)) 416 | 417 | background.blit(text, textpos) 418 | screen.blit(background, (0, 0)) 419 | allsprites.draw(screen) 420 | pygame.display.flip() 421 | game_over() 422 | 423 | 424 | 425 | 426 | 427 | 428 | allsprites.update() 429 | 430 | #Draw Everything 431 | screen.blit(background, (0, 0)) 432 | allsprites.draw(screen) 433 | pygame.display.flip() 434 | 435 | #Game Over 436 | 437 | 438 | #this calls the 'main' function when this script is executed 439 | if __name__ == '__main__': main() 440 | --------------------------------------------------------------------------------