class World: # This is the __init__ # its important. def __init__(self, app, screen): # tock started out random, but now is an important variable. # It is a frames count, used for periodic updates on certain # frames. self.p_max_health = 5 self.won = False self.won_msg = "WINNER!" self.particle_ban = False self.wait_stop_count = -1 # -1 means do not count self.wait_stop_max = 120 # wait this many frames after decay # before showing menu self.tock = 0 self.stage_e_bullet_odds = 15 self.mouse_rect = None self.temp_rect = None self.app = app self.screen = screen self.app.music_name = "intro.ogg" # self.app.continue_music() if self.app.settings['music']: # self.app.music_loaded = self.app.music_name # pygame.mixer.music.load(self.app.resource_find( # self.music_name)) # pygame.mixer.music.play() # plays once PLUS repeats param pass left_border = 50 right_border = 50 w, h = screen.get_size() self.world_rect = pygame.Rect(left_border, 0, w-right_border-left_border, h) self.bg = BackgroundManager(self.world_rect) # Yay spritegroups! They make the world go round, and iterate. # Basically each visible game object resides in its own special # spritegroup & then all one needs to do is go through these & # call functions & stuff. # It makes sense in here *points to brain* self.p_swarm = Swarm(self.world_rect) self.particles = Swarm(self.world_rect) self.explosion_images = self.app.load_seq('ex-medium') self.shield_hit_images = self.app.load_seq('shield_hit-tiny') self.damage_images = self.app.load_seq('ex-tiny') # Load player sprite as image list. self.p_unit_images = self.app.load_seq('pship') # self.p_unit_images = [] # self.p_unit_images.append(self.load_file('pship.png')) # self.p_unit_images.append(self.load_file('pship1.png')) # self.p_unit_images.append(self.load_file('pship2.png')) # self.p_unit_images.append(self.load_file('pship3.png')) # Load enemy ship image. self.e_ship_image = self.load_file('eship.png') if self.app.settings['sounds']: self.p_shoot_sound = self.app.load_file('p-weapon0.wav', file_type='sound') self.p_shoot_sound.set_volume(.3) self.e_ex_sound = self.app.load_file('e-ex.wav', file_type='sound') self.p_ex_sound = self.app.load_file('p-ex.wav', file_type='sound') self.e_damage_sound = self.app.load_file('e-damage.wav', file_type='sound') self.e_shield_sound = self.app.load_file('e-shield.wav', file_type='sound') self.p_shield_sound = self.app.load_file('p-shield.wav', file_type='sound') self.p_damage_sound = self.app.load_file('p-damage.wav', file_type='sound') print("loaded sounds...") self.menus = None self.tock = 0 self.lagcount = 0 self.leftkeydown = 0 self.rightkeydown = 0 # self.enemylist = [] # list of dirty rects self.swarm = Swarm(self.world_rect, shoot_odds=self.stage_e_bullet_odds) self.stage = Stage(self.swarm, self.p_swarm) self.p_shot_image = self.load_file('p-laser.png') self.e_shot_image = self.load_file('e-laser.png') self.p_bullet_swarm = Swarm(self.world_rect) self.e_bullet_swarm = Swarm(self.world_rect) # self.bullet_width = 10 self.hud_rect = pygame.Rect(0, 0, 5, 150) self.hud = Hud() self.hud.generate_blip('player.score', 100, fg_color=(255,243,207), text_color=(192,180,180), caption="SCORE") self.hud.generate_blip('player.health', self.p_max_health, fg_color=(0,255,42), text_color=(192,180,180), caption="SHIELD") self.statcounter = self.hud.get_blip('player.score') def load_file(self, name): return self.app.load_file(name) def on_exit(self): print("on_exit...") # Clears all the variables def clear_vars(self): # print("clear_vars: world_rect: " + str(self.world_rect)) self.p_start_x = self.world_rect.width / 2 self.p_start_y = self.world_rect.height - 60 self.bend_y = float(self.p_start_y) # print("clear_vars: p_start_y: " + str(self.p_start_y)) self.bend_rate = 0.02 self.leftkeydown = 0 self.rightkeydown = 0 if self.p_unit is not None: self.hud.set_blip_value('player.health', self.p_unit.health) else: print("WARNING: clear_vars failed to set bar since no" + " player unit exists") self.statcounter.set_val(0) self.stage.set_stage_number(-1) # hax self.stage_e_bullet_odds = 100 self.swarm.empty() self.p_bullet_swarm.empty() self.p_swarm.empty() self.particles.empty() self.e_bullet_swarm.empty() # Define function to draw player ship on X, Y plane def draw_player_units(self): self.p_swarm.draw(self.screen) # Define function to move the enemy ship def emove(self): self.swarm.draw(self.screen) # use spritegroup draw method # Draws all the enemys you ask it def generate_enemies(self): print("generating enemies...") # Some recursive loops: xmin = self.world_rect.left xmax = self.world_rect.right ymin = self.world_rect.top stage_data = self.stage.get_data() self.e_ship_image = self.app.load_file(stage_data['e']+".png") enemy_width, enemy_height = self.e_ship_image.get_size() enemy_spacing_x = 15 enemy_spacing_y = 10 init_enemy_speed = 3 angle = -90 # cartesian self.app.music_name = stage_data['music'] # if self.app.music_name == 'intro.ogg': # self.app.continue_music() # force song change e_max_health = stage_data['e_h'] for enemycol in range(stage_data['x_e_count']): # Now for the rows for enemyrow in range(stage_data['y_e_count']): # Make a new enemy object: new_enemy = Entity(self.app, 'eship', [self.e_ship_image], init_enemy_speed, angle, self.swarm, e_max_health, self.explosion_images, self.particles, ai_enable=True, value=stage_data['e_h'], ex_sound=self.e_ex_sound) new_enemy.set_xy( xmin + enemycol * (enemy_width + enemy_spacing_x), ymin + enemyrow * (enemy_height + enemy_spacing_y) - 150 ) new_enemy.set_range( xmin + enemycol * (enemy_width + enemy_spacing_x), xmax - (stage_data['x_e_count'] - enemycol) * (enemy_height + enemy_spacing_x) ) # Now add the temp enemy to the array and we're good to # go self.swarm.add(new_enemy) # So I'm trying out having the program check for collisions, instead # of the enemy objects i think i might switch to the objects, but # still keep this function just hand the computing to the object def test_collision(self): part_speed = 1 part_angle = -90 part_health = 1 e_hit = pygame.sprite.groupcollide(self.swarm, self.p_bullet_swarm, 0, 0) for sprite, bullets in e_hit.items(): # print("removed " + str(bullet) for bullet in bullets: was_alive = sprite.get_is_alive() prev_health = sprite.health if sprite.get_is_alive(): sprite.set_hit(1) damage = prev_health - sprite.health poof = self.shield_hit_images temper_sound = self.e_shield_sound if damage > 0: poof = self.damage_images temper_sound = self.e_damage_sound if not was_alive: break point = pygame.sprite.collide_mask(sprite, bullet) if ((point is not None) and (not self.particle_ban)): particle = Entity(self.app, 'particle', poof, part_speed, part_angle, self.particles, part_health, None, None, anim_done_remove=True, temper_sound=temper_sound) particle.temper = 1 # start particle death x1, y1 = sprite.get_pos() # top left x = x1 + point[0] - particle.rect.width / 2 y = y1 + point[1] - particle.rect.height / 2 particle.set_xy(x, y) self.particles.add(particle) if not sprite.get_is_alive(): points = sprite.take_value() # only once & if health 0 if points > 0: self.statcounter.add_value(points) self.p_bullet_swarm.remove(bullets) p_hit = pygame.sprite.groupcollide(self.p_swarm, self.e_bullet_swarm, 0, 0) for sprite, bullets in p_hit.items(): for bullet in bullets: was_alive = sprite.get_is_alive() prev_health = sprite.health if sprite.get_is_alive(): sprite.set_hit(1) damage = prev_health - sprite.health poof = self.shield_hit_images temper_sound = self.p_shield_sound if damage > 0: poof = self.damage_images temper_sound = self.p_damage_sound self.hud.set_blip_value('player.health', self.p_unit.health) # New in pygame 1.8.0: point = pygame.sprite.collide_mask(sprite, bullet) if not was_alive: break if (point is not None) and (not self.particle_ban): particle = Entity(self.app, 'particle', poof, part_speed, part_angle, self.particles, part_health, None, None, anim_done_remove=True, temper_sound=temper_sound) particle.temper = 1 # start particle death x1, y1 = sprite.get_pos() # top left x = x1 + point[0] - particle.rect.width / 2 y = y1 + point[1] - particle.rect.height / 2 particle.set_xy(x, y) self.particles.add(particle) # if pygame.sprite.spritecollideany(self.p_unit, # self.e_bullet_swarm): # self.p_unit.set_hit(1) # self.hud.set_blip_value('player.health', # self.p_unit.health) # if there are no enemys left, go to the next stage def check_done(self): if not self.swarm: if self.stage.is_last_stage(): if not self.won: self.won = True # TODO: make ending screen # self.app.music_name = 'victory.ogg' # if self.app.music_name == 'intro.ogg': # self.app.continue_music() # force song change self.app.music_name = None # stop repeating self.app.check_music() # apply None to loop self.app.queue_music('victory.ogg', 1) if not self.won: self.stage.next_stage() if self.stage_e_bullet_odds > 15: self.stage_e_bullet_odds -= 15 self.generate_enemies() # checks to see if we can expand the ranges of the bots so its nice # and.... umm... nice. def check_rows(self): if self.tock % 20 == 0: # simple sorting algorithm to find the highest values xmin = self.world_rect.left xmax = self.world_rect.right highest = xmin lowest = xmax for enemy in self.swarm: if enemy.get_range()[1] > highest: highest = enemy.get_range()[1] if enemy.get_range()[0] < lowest: lowest = enemy.get_range()[0] highest = xmax - highest lowest = lowest - xmin if highest != 0 or lowest != 0: # makes things |--| this much more efficient for enemy in self.swarm: erange = enemy.get_range() enemy.set_range(erange[0]-lowest, erange[1]+highest) # major hack just to get this thing playable..... sorry def again(self): if self.hud.get_blip_value('player.health') <= 0: self.particle_ban = True if self.p_unit.get_is_decayed(): self.particle_ban = True # also wait for particles to finish for prettier ending if len(self.particles) < 1: if self.wait_stop_count < 0: print("player unit decayed, counting down to menu") self.wait_stop_count = 0 # return False if self.wait_stop_count >= 0: self.wait_stop_count += 1 if self.wait_stop_count > self.wait_stop_max: return False if self.won: if self.wait_stop_count < 0: print("won game, counting down to menu") self.wait_stop_count = 0 return True # this is called if the player initiates shooting def pshoot(self): # sx = self.p_unit.rect.centerx - # self.p_shot_image.rect.width / 2 # sy = self.p_unit.rect.top + # self.p_shot_image.rect.height * .75 if self.p_unit.get_is_alive(): self.p_unit.shoot(self.p_shot_image, self.p_bullet_swarm) # self.p_unit.shoot_from(self.p_shot_image, # self.p_bullet_swarm, # sx, sy, self.p_unit.angle) def draw_bullets(self): self.p_bullet_swarm.draw(self.screen) self.e_bullet_swarm.draw(self.screen) def draw_hud(self): if self.tock % 5 == 0: self.hud.update() self.hud.draw(self.screen) # Goes through all the objects and makes each of them move as # necessary def tick(self): self.bend_y += self.bend_rate bend_max = 5.0 if self.bend_rate < 0.0: self.bend_rate -= .02 else: self.bend_rate += .02 if ((self.bend_y > self.p_start_y + bend_max) or (self.bend_y < self.p_start_y)): if self.bend_rate < 0.0: self.bend_rate = .02 self.bend_y = float(self.p_start_y) else: self.bend_rate = -.02 self.bend_y = float(self.p_start_y) + bend_max self.p_unit.set_xy(self.p_unit.get_pos()[0], int(self.bend_y+.5)) self.p_bullet_swarm.update() self.swarm.update() self.e_bullet_swarm.update() ###################### # Here are a bunch of metafunctions. # I break it up so its really easy to add new features, # like if we ant a counter? add something to check() and draw(). # All of these are called once per frame. def check(self): self.check_done() self.test_collision() self.check_rows() self.bg.update() if self.p_unit.get_is_alive(): self.swarm.shoot(self.e_shot_image, self.e_bullet_swarm) self.p_unit.update() for particle in self.particles: particle.update() def draw(self): self.screen.fill(self.bg.bg_color) # if self.world_rect is not None: # self.screen.fill((64, 64, 64), self.world_rect) self.bg.draw(self.screen) self.draw_bullets() self.draw_player_units() self.emove() self.particles.draw(self.screen) self.draw_hud() # if self.p_unit is not None: # if self.p_unit.rect is not None: # self.screen.fill((128, 128, 128), self.p_unit.rect) # if self.mouse_rect is not None: # self.screen.fill((255, 255, 255), self.mouse_rect) # if self.temp_rect is not None: # self.screen.fill((128, 0, 0), self.temp_rect) # does just what it sounds like..... def clear_screen(self): self.screen.fill(self.bg.bg_color) # pygame.display.flip() # for debugging info mostly def dispvars(self): print("The Enemy SpriteGroup size is:" + str(len(self.swarm.sprites()))) print("The Player Bullet Array size is:" + str(len(self.p_bullet_swarm.sprites()))) print("The Enemy Bullet Array size is:" + str(len(self.e_bullet_swarm.sprites()))) # does lots and lots of stuff, it really needs to be cleaned up def process_events(self, events): # print("input: self.p_unit.rect: " + str(self.p_unit.rect)) xmin = self.world_rect.left xmax = self.world_rect.right smooth_scroll_var1 = 10 smooth_scroll_var2 = 2 pygame.event.pump() # redraw Window so OS knows not frozen self.app.check_music() pause_menu_strings = ["RESUME", "ABOUT", "HELP", "EXIT"] if self.won: pause_menu_strings.insert(0, self.won_msg) for event in events: if event.type == QUIT: self.on_exit() sys.exit(0) if event.type == pygame.MOUSEMOTION: pygame.event.get() prev_pos = self.p_unit.get_pos() tempx = (pygame.mouse.get_pos()[0] - self.p_unit.rect.width / 2) # *Just to make sure we don't get the ship way out: if tempx + self.p_unit.rect.width > xmax: # if its outside the world, # just stick it as far as possible self.p_unit.set_xy(xmax - self.p_unit.rect.width, prev_pos[1]) elif tempx < xmin: self.p_unit.set_xy(xmin, prev_pos[1]) elif abs(tempx-self.p_start_x) > \ smooth_scroll_var1: # smooth scrolling if the mouse gets far # from the ship self.p_unit.set_xy( prev_pos[0] + (tempx-prev_pos[0]) / smooth_scroll_var2, prev_pos[1]) else: # if it gets down to this point, # we've passed all sanity checks so just move it self.p_unit.set_xy(tempx, prev_pos[1]) elif event.type == pygame.MOUSEBUTTONDOWN: self.pshoot() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_q: self.menus.show_dialog(pause_menu_strings) if event.key == pygame.K_p: self.menus.show_dialog(pause_menu_strings) if event.key == pygame.K_ESCAPE: self.menus.show_dialog(pause_menu_strings) # keyboard controls if event.key == pygame.K_LEFT: self.leftkeydown = 1 if event.key == pygame.K_RIGHT: self.rightkeydown = 1 if event.key == pygame.K_SPACE: self.pshoot() elif event.type == pygame.KEYUP: if event.key == pygame.K_LEFT: self.leftkeydown = 0 if event.key == pygame.K_RIGHT: self.rightkeydown = 0 elif event.type == pygame.USEREVENT: if event.code == pygame.USEREVENT_DROPFILE: print("Tried to open file on MacOS (this should" + " never happen:") print(" " + str(event)) else: # should be event.code 0 self.app.continue_music() print("music queue ended in game:") if event.code != 0: print("unknown USEREVENT event.code: " + str(event.code)) if self.leftkeydown: self.p_unit.move_one(-1, self.world_rect) if self.rightkeydown: self.p_unit.move_one(1, self.world_rect) pygame.event.clear() #################################################################### def start(self, menus): self.won = False self.particle_ban = False self.wait_stop_count = -1 # -1 means do not count down to menu self.menus = menus p_speed = 10 self.p_unit = Entity(self.app, 'pship', self.p_unit_images, p_speed, 90.0, self.p_swarm, self.p_max_health, self.explosion_images, self.particles, ex_sound = self.p_ex_sound) self.p_unit.shoot_sound = self.p_shoot_sound print("Clearing vars...") self.clear_vars() # does reset player unit (p_unit) position self.p_swarm.add(self.p_unit) self.p_unit.set_xy(self.p_start_x, self.p_start_y) print("Starting main event loop...") self.loop() # Yeah see this one does all of the work def loop(self): # Start loop REFRESH_TIME = self.app.get_fps() * 3 while (not self.menus.get_bool('exit')) and (self.again()): # Refresh screen periodically if self.tock >= REFRESH_TIME: # self.clear_screen() self.tock = 0 self.tock += 1 # Check everythign and see if changes need to be made self.check() # Draw bullets self.draw() # Move everything self.tick() # Initiate input function self.process_events(pygame.event.get()) # applies the smart screen updating pygame.display.update() # TODO: ? pygame.display.update(self.enemylist) # self.enemylist = [] # Pauses and waits timeittook = self.app.clock.tick(self.app.get_fps())