def main(): """this function is called when the program starts. it initializes everything it needs, then runs in a loop until the function returns.""" # Initialize Everything pg.mixer.init() pg.init() screen = pg.display.set_mode((screen_width, screen_height)) pg.display.set_caption("Bullet Helln't") pg.mouse.set_visible(0) # Create The Background bg = pg.image.load("art/background/background.png") bg_offset = 0 # Display The Background screen.blit(bg, (0, 0)) # pg.display.flip() # pg.mixer.music.load('sound/music.wav') pg.mixer_music.play(-1) # Prepare Game Objects clock = pg.time.Clock() potion_list = [] player = Player(screen_width, screen_height, potion_list) hud = HUD(player) inv = Inventory(player) invisible_top_wall = Wall(0, -35, screen_width, 10) invisible_bottom_wall = Wall(0, screen_height + 25, screen_width, 10) invisible_left_wall = Wall(-35, 0, 10, screen_height) invisible_right_wall = Wall(screen_width + 25, 0, 10, screen_height) top_wall = Wall(0, 0, screen_width, 10) bottom_wall = Wall(0, screen_height - 10, screen_width, 10) left_wall = Wall(0, 0, 10, screen_height) right_wall = Wall(screen_width - 10, 0, 10, screen_height) walls = pg.sprite.RenderPlain(top_wall, bottom_wall, left_wall, right_wall) collision_walls = pg.sprite.RenderPlain(invisible_top_wall, invisible_bottom_wall, invisible_left_wall, invisible_right_wall) allsprites = pg.sprite.RenderPlain(player, walls, collision_walls, hud, inv) player.walls = collision_walls.sprites() bullet_list = [] enemy_list = [] for enemy in enemy_list: allsprites.add(enemy) # Tracker item_count = defaultdict(lambda: 0) # Tracker specifically for special potion; and whether player holds it bomb_inuse = False bomb_counter = 0 bomb_delay = 330 # Shows the introduction screen story = "THE STORY SO FAR:" story2 = "In a world filled with swords, you are a superhero with a bow." story3 = "Swing your bow, kill enemies and eliminate swords." story4 = "Pick up health and a special potion along the way to make swords disappear!" story5 = "<TAB> to begin." start_game = False while not start_game: font = pygame.font.SysFont("Arial", 24) text = font.render(story, 1, (0, 0, 0)) text2 = font.render(story2, 1, (0, 0, 0)) text3 = font.render(story3, 1, (0, 0, 0)) text4 = font.render(story4, 1, (0, 0, 0)) text5 = font.render(story5, 1, (0, 0, 0)) text_pos = text.get_rect(centerx=screen.get_width() / 2, centery=100) text_pos2 = text2.get_rect(centerx=screen.get_width() / 2, centery=200) text_pos3 = text3.get_rect(centerx=screen.get_width() / 2, centery=300) text_pos4 = text4.get_rect(centerx=screen.get_width() / 2, centery=400) text_pos5 = text5.get_rect(centerx=screen.get_width() / 2, centery=500) screen.blit(text, text_pos) screen.blit(text2, text_pos2) screen.blit(text3, text_pos3) screen.blit(text4, text_pos4) screen.blit(text5, text_pos5) pg.display.flip() for event in pg.event.get(): if event.type == pg.QUIT: quit() if event.type == pg.KEYDOWN and event.key == pg.K_TAB: start_game = True else: continue # Main Loop going = True while going: # print(item_count[Enemy], item_count[Enemy2], item_count[Potion]) # draw two backgrounds, slowly moving down screen.blit(bg, (0, bg_offset)) screen.blit(bg, (0, bg_offset - screen_height)) if bg_offset < screen_height: bg_offset += 1 else: # once offset goes off the screen, reset it bg_offset = 0 for event in pg.event.get(): if event.type == pg.QUIT: going = False # player attack if event.type == pg.KEYDOWN and event.key == pg.K_SPACE: player.start_attack() # player bomb if player.bomb_ready and event.type == pg.KEYDOWN and event.key == pg.K_c: pg.mixer.Sound('sound/tactical-nuke.wav').play() bomb_inuse = True player.bomb_ready = False if bomb_inuse: if bomb_counter < bomb_delay: bomb_counter += 1 else: player.bomb_count = 0 print("BOOM") bomb_inuse = False bomb_counter = 0 # remove all bullets for bullet in bullet_list: bullet.kill() bullet_list.clear() allsprites.remove(bullet_list) # remove all enemies? for enemy in enemy_list: enemy.kill() enemy_list.clear() allsprites.remove(enemy_list) item_count[Enemy] = 0 item_count[Enemy2] = 0 if random.random() < chance_spawn(item_count[Potion]): potion = Potion(screen_width, screen_height) potion_list.append(potion) allsprites.add(potion) item_count[Potion] = item_count[Potion] + 1 if random.random() < chance_spawn(item_count[Enemy]): enemy = Enemy(screen_width, screen_height) enemy_list.append(enemy) allsprites.add(enemy) item_count[Enemy] = item_count[Enemy] + 1 if item_count[Enemy2] <= 5 and random.random() < chance_spawn( item_count[Enemy2]): enemy = Enemy2(screen_width, screen_height) enemy_list.append(enemy) allsprites.add(enemy) item_count[Enemy2] = item_count[Enemy2] + 1 # a special potion; if you need to collect 4, may as well have them spawn randomly if random.random() < 0.003: potion = Potion2(screen_width, screen_height) potion_list.append(potion) allsprites.add(potion) # update player (movement, attack frame, health) if not player.update(): end_screen(screen, bg, player) if clock.get_time() % 5: for enemy in enemy_list: if player.attack and player.attack_box.colliderect( enemy.hurtbox): enemy.kill_enemy(player) enemy_list.remove(enemy) if isinstance(enemy, Enemy): item_count[Enemy] = item_count[Enemy] - 1 elif isinstance(enemy, Enemy2): item_count[Enemy2] = item_count[Enemy2] - 1 allsprites.remove( enemy ) # TODO: Make enemies stay a while before being removed if player.hurtbox.colliderect(enemy.hurtbox): if player.damage_cooldown == 0: player.get_hurt() # remove if off screen; after calling 'implode' first on Enemy though if enemy.rect.y + 50 > screen_height: if isinstance(enemy, Enemy): bullets = enemy.implode(player.rect.x, screen_height) for bullet in bullets: bullet_list.append(bullet) allsprites.add(bullet) if isinstance(enemy, Enemy): item_count[Enemy] = item_count[Enemy] - 1 elif isinstance(enemy, Enemy2): item_count[Enemy2] = item_count[Enemy2] - 1 allsprites.remove(enemy) enemy_list.remove(enemy) enemy.kill() # get each enemy to go through a 'shoot' cycle; returns None if no bullet generated # bullet = None # if isinstance(enemy, Enemy): bullet = enemy.shoot(player.rect.x, player.rect.y) # elif isinstance(enemy, Enemy2): # bullet = enemy.shoot(player.h) if bullet: bullet_list.append(bullet) allsprites.add(bullet) for bullet in bullet_list: remove = False # bigger hitbox collides with player attack if player.attack and player.attack_box.colliderect( bullet.rect): remove = True # smaller hitbox collides with player elif player.hurtbox.colliderect(bullet.hurtbox): if player.damage_cooldown == 0: player.get_hurt() remove = True # off screen; add 20 to coordinates to centre it (knife is 40x40 px) elif bullet.rect.x + 20 < 10 or bullet.rect.x + 20 > screen_width - 10 \ or bullet.rect.y + 20 < 10 or bullet.rect.y + 20 > screen_height - 10: remove = True # cheeky bodge: homing if isinstance(bullet, Knife2): xdist = abs(player.rect.x - bullet.rect.x) if bullet.rect.x + 40 < player.rect.x + 50 - 20: bullet.true_x += bullet.xspeed * xdist elif bullet.rect.x + 40 > player.rect.x + 50 + 20: bullet.true_x -= bullet.xspeed * xdist if remove: bullet_list.remove(bullet) allsprites.remove(bullet) bullet.kill() # remove potions going off screen for potion in potion_list: if potion.rect.y > screen_height - 30: if isinstance(potion, Potion2): potion2_present = False allsprites.remove(potion) item_count[Potion] = item_count[Potion] - 1 potion_list.remove(potion) potion.kill() # draw allsprites.update() # Draw Everything allsprites.draw(screen) # text_surface = score_text.generate_surface('Health: ') # screen.blit(text_surface, # (screen_width - text_surface.get_width() - 100, screen_height - text_surface.get_height() - 45)) pg.display.flip() clock.tick(60) pg.quit()
bg = Background() enemy_spawn_ctrl = enemy.Controller() fps_target = 30 last = 0 running = True while running: deltaT = clk.tick(60) / 1000 events.process_events() if events.QUIT: running = False # PHYSICS AND CONTROLLER UPDATES player.update(deltaT) bullets.update_all_bullets(deltaT) enemy_spawn_ctrl.update(deltaT) bg.update(deltaT) # RENDERING srf = pygame.display.get_surface() srf.fill((0, 0, 0)) bg.draw() player.draw() bullets.draw_all_bullets() enemy_spawn_ctrl.draw_all_enemies() pygame.display.update()
if event.type == hit_cooldown: player.iframe = False pygame.time.set_timer(hit_cooldown, 0) if event.type == enemy_stun_cooldown: slime.stunned = False pygame.time.set_timer(enemy_stun_cooldown, 0) ground.render() background.render() if player.hp != 0: displaysurface.blit(player.image, player.rect) player.move() player.update() if hits: # slime_atk.render(player) player.hit(slime, hit_cooldown) else: player.kill() if slime.hp != 0: slime.render() slime.hit(player_group, player, enemy_stun_cooldown) else: slime.kill() if player.attacking == True: player.attack()
class Game: def __init__(self, display): self.display = display self.clock = pg.time.Clock() pg.display.set_caption(WINDOW_TITLE) self.joysticks = [ pg.joystick.Joystick(x) for x in range(pg.joystick.get_count()) ] self.joystick = None if len(self.joysticks) > 0: self.joystick = self.joysticks[0] self.joystick.init() # sprite groups self.all_sprites = None self.items_on_floor = None self.solid = None self.doors = None self.background_surface = None self.triggers = [] self.collisions = [] # rectangles self.spritesheet = None self.map = None self.player = None self.camera = None self.playing = False self.dt = 0.0 self.global_time = 0 self.pressed_keys = {} self.keys_just_pressed = {} self.joystick_just_pressed = {} self.gui = Nanogui(display) self.visibility_data = None # [x][y] -> True, False self.fov_data = None # [x][y] -> True, False self.update_fov = True self.light_map = [] def load(self): self.all_sprites = pg.sprite.Group() self.solid = pg.sprite.Group() self.items_on_floor = pg.sprite.Group() self.doors = pg.sprite.Group() assets_folder = path.join(getcwd(), 'assets') self.map = Map(path.join(assets_folder, 'maps/map1.json')) self.spritesheet = Spritesheet( path.join(assets_folder, 'spritesheet.png'), 32) wall_img = self.spritesheet.get_image_at_col_row(0, 0) apple_img = self.spritesheet.get_image_alpha_at_col_row(1, 0) keycard_img = self.spritesheet.get_image_alpha_at_col_row(0, 3) self.background_surface = pg.Surface( (self.map.width * TILE_SIZE, self.map.height * TILE_SIZE)) self.visibility_data = [[True] * self.map.height for i in range(self.map.width)] self.fov_data = [[True] * self.map.height for i in range(self.map.width)] for node in self.map.objects: x, y = node['x'], node['y'] if node["name"] == 'WALL': #self.collisions.append(pg.Rect(x, y, TILE_SIZE, TILE_SIZE)) # TODO big rectangles wall = Wall(self, x, y, wall_img) if not LIMIT_FOV_FOR_STATIC_SPRITES: wall.remove(self.all_sprites) # skip drawing self.background_surface.blit( wall_img, (x * TILE_SIZE, y * TILE_SIZE)) self.visibility_data[x][y] = False elif node["name"] == 'PLAYER': self.player = Player(self, x, y) elif node["name"] == 'APPLE': item = Item(self, x, y, apple_img) item.pickable = Pickable(item, 'apple', False, 1, False) elif node["name"] == 'KEYCARD': # key card item = Item(self, x, y, keycard_img) item.pickable = Pickable(item, 'keycard', False, 1, False) elif node["name"] == "DOOR": Door(self, x, y, node["dir"]) self.visibility_data[x][ y] = False # TODO opened doors visibility for trigger in self.map.triggers: TextTrigger( self, pg.Rect(trigger["x"], trigger["y"], trigger["width"], trigger["height"]), trigger["text"]) self.camera = Camera(self.map.width_screen, self.map.height_screen) def update(self): self.gui.pre(self.joystick) if not message.Message.messages: self.player.update(self.dt) message.update(self) camera_updated = self.camera.update(self.player) if self.update_fov: player_hit_rect = self.player.hit_rect player_tilex = math.floor(player_hit_rect.x / TILE_SIZE) player_tiley = math.floor(player_hit_rect.y / TILE_SIZE) self.fov_data = calc_fov(player_tilex, player_tiley, FOV_RADIUS, self.visibility_data, self.fov_data) self.update_light_map(self.player.x, self.player.y) self.update_fov = False self.gui.after() def draw(self): self.display.fill(BG_COLOR) bg_x, bg_y = self.camera.transform_xy(0, 0) self.display.blit(self.background_surface, (bg_x, bg_y)) # TODO layering if not LIMIT_FOV_FOR_STATIC_SPRITES: for sprite in self.all_sprites: if sprite != self.player and not isinstance(sprite, Item): self.display.blit(sprite.image, self.camera.transform(sprite)) if LIMIT_FOV_FOR_STATIC_SPRITES: fov_group = self.all_sprites else: fov_group = self.items_on_floor for sprite in fov_group: tilex = math.floor(sprite.x) tiley = math.floor(sprite.y) if sprite != self.player: if self.fov_data[tilex][tiley]: self.display.blit(sprite.image, self.camera.transform(sprite)) # darken image self.display.fill(DARKEN_COLOR, special_flags=pg.BLEND_SUB) # draw light for light in self.light_map: self.display.fill(light[1], light[0], pg.BLEND_ADD) if DEBUG_FOV: self.draw_fov() self.display.blit(self.player.image, self.camera.transform(self.player)) if message.Message.messages: message.Message.messages[0].render(self.display) #self.__put_text_on_screen(str(messages)) #if messages.has_picture(): # self.__put_picture_on_screen__(messages.get_picture()) self.gui.draw() pg.display.flip() def __put_picture_on_screen(self, image): self.display.blit(image, (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) def run(self): self.playing = True while self.playing: self.dt = self.clock.tick(FPS) / 1000 self.global_time += self.dt self.events() self.update() self.draw() def quit(self): pg.quit() sys.exit() def events(self): self.keys_just_pressed.clear() self.joystick_just_pressed.clear() self.pressed_keys = pg.key.get_pressed() for event in pg.event.get(): if event.type == pg.QUIT: self.quit() if event.type == pg.KEYDOWN: self.keys_just_pressed[event.key] = True if event.key == pg.K_ESCAPE: self.quit() if event.key == pg.K_F11: self.toggle_fullscreen() if event.type == pg.JOYBUTTONDOWN: self.joystick_just_pressed[event.button] = True def toggle_fullscreen(self): """Taken from http://pygame.org/wiki/toggle_fullscreen""" screen = pg.display.get_surface() tmp = screen.convert() caption = pg.display.get_caption() cursor = pg.mouse.get_cursor() w, h = screen.get_width(), screen.get_height() flags = screen.get_flags() bits = screen.get_bitsize() pg.display.quit() pg.display.init() self.display = pg.display.set_mode((w, h), flags ^ pg.FULLSCREEN, bits) self.display.blit(tmp, (0, 0)) pg.display.set_caption(*caption) pg.key.set_mods(0) pg.mouse.set_cursor(*cursor) return screen def get_vbutton_down(self, name): if name in V_BUTTONS: for key in V_BUTTONS[name]: if self.pressed_keys[key]: return True return False def get_vbutton_jp(self, name): if name in V_BUTTONS: for key in V_BUTTONS[name]: if key in self.keys_just_pressed: return True return False def get_key_jp(self, key): # get key just pressed (clears on new frame) if key in self.keys_just_pressed: return True return False def get_joystick_jp(self, button): # get joystick button just pressed (clears on new frame) if button in self.joystick_just_pressed: return True return False def get_axis(self, number): if self.joystick is not None: return self.joystick.get_axis(number) return 0.0 def set_visibility(self, tilex, tiley, value): self.visibility_data[tilex][tiley] = value self.update_fov = True def draw_fov(self): for x in range(len(self.fov_data)): for y in range(len(self.fov_data[0])): if self.fov_data[x][y]: newx, newy = self.camera.transform_xy( x * TILE_SIZE, y * TILE_SIZE) pg.draw.rect(self.display, (200, 200, 200), pg.Rect(newx, newy, TILE_SIZE, TILE_SIZE), 1) def update_light_map(self, source_x, source_y): self.light_map.clear() radius_sqr = FOV_RADIUS * FOV_RADIUS tmp = 1.0 / (1.0 + radius_sqr) for x in range(len(self.fov_data)): for y in range(len(self.fov_data[0])): if self.fov_data[x][y]: newx, newy = self.camera.transform_xy( x * TILE_SIZE, y * TILE_SIZE) dist_sqr = (source_x - x) * (source_x - x) + ( source_y - y) * (source_y - y) intensity = 1.0 / (1.0 + dist_sqr / 20) intensity = intensity - tmp intensity = intensity / (1.0 - tmp) color = tuple(intensity * v for v in LIGHT_COLOR) self.light_map.append((pg.Rect(newx, newy, TILE_SIZE, TILE_SIZE), color))