class Engine: def __init__(self, stdscr, objects): self.map = Map(stdscr, objects) self.msgscr = stdscr # main messages self.sttscr = stdscr # show status self.errscr = stdscr # show errors self.inpscr = stdscr # get inputs self.invscr = stdscr # inventory overview self.itmscr = stdscr # item description for x in range(amountGold): self.spawn('Gold') for x in range(amountMonster): monster = random.choice(enemy_keys) self.spawn(monster) for x in range(amountWall): self.spawn('Wall') for x in range(amountFood): self.spawn('Food') for x in range(amountItem): self.spawn('Map item') self.current_actor = self.map.objects.lists.get('Hero')[0] def __del__(self): endwin() ########################### drawing ############################ def all_refresh(self): self.map.stdscr.refresh() self.msgscr.refresh() self.sttscr.refresh() self.errscr.refresh() self.inpscr.refresh() self.invscr.refresh() self.itmscr.refresh() def all_erase(self): self.map.stdscr.erase() self.msgscr.erase() self.sttscr.erase() self.errscr.erase() self.inpscr.erase() self.invscr.erase() self.itmscr.erase() def draw(self): self.map.draw() self.show_stats() self.all_refresh() def show_stats(self): for hero_id in range(0, len(self.map.objects.lists.get('Hero'))): hero = self.map.objects.lists.get('Hero')[hero_id] if hero.status != 'dead': if hero == self.current_actor: self.sttscr.addstr(0, max_x + 5 + 10 * hero_id, "=" + str(hero.name) + "=") else: self.sttscr.addstr(0, max_x + 5 + 10 * hero_id, " " + str(hero.name)) self.sttscr.addstr(1, max_x + 5 + 10 * hero_id, "LV " + str(hero.level)) self.sttscr.addstr(2, max_x + 5 + 10 * hero_id, "HP " + str(hero.hp)) self.sttscr.addstr(3, max_x + 5 + 10 * hero_id, "XP " + str(hero.xp)) self.sttscr.addstr(4, max_x + 5 + 10 * hero_id, "G " + str(hero.gold)) else: self.sttscr.addstr(0, max_x + 5 + 10 * hero_id, " ___") self.sttscr.addstr(1, max_x + 5 + 10 * hero_id, "/ \\") self.sttscr.addstr(2, max_x + 5 + 10 * hero_id, "|o o|") self.sttscr.addstr(3, max_x + 5 + 10 * hero_id, "\\ \" /") self.sttscr.addstr(4, max_x + 5 + 10 * hero_id, " |m|") def show_level_up(self): if self.current_actor.type_name == 'Hero': self.all_erase() self.map.stdscr.addstr(2, 2, " _ _") self.map.stdscr.addstr(3, 2, "| |_\\ /|_ |") self.map.stdscr.addstr(4, 2, "|_ |_ \\/ |_ |_") self.map.stdscr.addstr(5, 6, " _") self.map.stdscr.addstr(6, 6, "| | |_) |") self.map.stdscr.addstr(7, 6, ":_: | o") self.map.stdscr.addstr(10, 2, "attack up!") self.map.stdscr.addstr(11, 2, "defense up!") self.map.stdscr.addstr(12, 2, "max hp up!") self.map.stdscr.addstr(15, 2, "Press any key to return") self.all_refresh() self.map.stdscr.getkey() def show_stage_clear(self): if self.current_actor.type_name == 'Hero': self.all_erase() self.msgscr.addstr(2, 2, "Congratulations, you have cleared the stage.") self.msgscr.addstr(3, 2, "But your quest is not over.") self.msgscr.addstr(15, 2, "Press any key to continue to the next stage.") self.all_refresh() self.msgscr.getkey() def show_message(self, message): if self.current_actor.type_name == 'Hero': self.msgscr.addstr(max_y + 2, 0, message) def show_error(self, message): self.errscr.addstr(max_y + 5, 0, message) def show_input(self, message): self.inpscr.addstr(2, 0, "You've entered: ") self.inpscr.addstr(3, 0, message) self.inpscr.addstr(4, 0, "Accept? [y/n]") key = self.inpscr.getkey() if (key == "y"): return 1 if (key == "n"): return 0 return 2 def prompt_input(self, message): self.inpscr.addstr(0, 0, message) return self.inpscr.getstr() def show_help(self): self.map.stdscr.addstr( 0, 0, """ 1-9 - select actor arrows - move """ + look_keys + """ - look """ + attack_keys + """ - attack [S]ave [L]oad [I]nventory [q]uit any button - Start """) ########################### interactions ############################ def spawn(self, object_type): """Generator of new objects of any available type""" # verify if object limit not reached if (self.map.objects.check_limit()): self.show_error("Can't create more objects!") return # roll new coords new_x = random.randint(1, max_x) new_y = random.randint(1, max_y) # verify if coords are valid (no object there) # repeat spawn if cords were bad if (self.map.objects.check_coords(new_x, new_y)): self.spawn(object_type) return # create object otherwise self.map.objects.create_object(object_type, new_x, new_y) def pick_up(self): """ pick up an object lying by actor's feet :return: 0 - ok. 1 - not item. 2 - inventory full. """ target = self.map.objects.get_object(self.current_actor.x, self.current_actor.y, self.current_actor.type_name) if not isinstance(target, MapItem): return 1 # Try adding to inventory result = self.current_actor.inventory.inventory_add( InventoryItem(target.ItemID)) if (result == 0): # Item added self.map.objects.remove_object(target) return result ########################### control ############################ def game_start(self): # show_animation(self.map.stdscr) self.show_help() # Clean unused keys while (self.map.stdscr.getch() in ignore_keys): self.map.stdscr.getch() self.all_erase() self.draw() def action_spawn(self, key): if (key == 'n' and debug): self.show_error(self.map.objects.spawn('Gold')) self.show_error(self.map.objects.spawn('Monster')) self.show_error(self.map.objects.spawn('Orc')) self.show_error(self.map.objects.spawn('Troll')) self.show_error(self.map.objects.spawn('Wall')) self.show_error(self.map.objects.spawn('Food')) self.show_error(self.map.objects.spawn('Map item')) return def action_pick_up(self, key): """result of looking at objects but only if attack button was pressed""" if (key == ' '): self.pick_up() def action_look(self, key): """result of looking at objects but only if attack button was pressed""" if (key in look_keys): self.show_message(self.map.objects.look_at(key, self.current_actor)) def action_attack(self, key): """result of attack but only if attack button was pressed""" if (key in attack_keys): self.show_message(self.map.objects.attack(key, self.current_actor)) def action_move(self, key): """result of moving but only if arrow button was pressed""" if (key in move_keys): self.show_message(self.map.objects.move(key, self.current_actor)) def action_inventory(self, key): if (key == 'I'): self.all_erase() dropped_items = self.current_actor.inventory.view( self.invscr, self.current_actor) if dropped_items is not None: for item in dropped_items: self.map.objects.lists.get('Map item').append( MapItem(item, self.current_actor.x, self.current_actor.y)) self.draw() def action_saveload(self, key): """ TODO, nothing is completed here :param key: pressed key :return: nothing yer """ if (key == 'S'): prompt = "Save Game menu. Give savefile name." savename = self.prompt_input(prompt) result = self.show_input(savename) self.inpscr.erase() if (result == 0): # Return to map "?" if (result == 1): # Check if exists, ask to confirm overwrite message = "Game saved!" self.show_message(message) if (result == 2): self.action_saveload(key) if (key == 'L'): prompt = "Load Game menu. Give savefile name." savename = self.prompt_input(prompt) result = self.show_input(savename) self.inpscr.erase() if (result == 0): # Return to map "?" if (result == 1): # Check if file exists, raise error otherwise message = "Game Loaded!" self.show_message(message) if (result == 2): self.action_saveload(key) def check_quit(self, key): score = 0 for hero_id in range(0, len(self.map.objects.lists.get('Hero'))): hero = self.map.objects.lists.get('Hero')[hero_id] if hero.hp <= 0: self.map.objects.remove_object(hero) if hero.status != 'dead': score += 5 * hero.xp + 4 * hero.gold + 10 * hero.hp - hero.steps if score == 0: self.all_erase() self.show_stats() self.map.stdscr.addstr(0, 0, "Try again!") self.map.stdscr.addstr(1, 1, "THE END") self.all_refresh() return False enemies_left = self.map.objects.count_enemies() if key == 'q': self.all_erase() self.show_stats() self.map.stdscr.addstr(0, 0, "final score: " + str(score)) self.map.stdscr.addstr(1, 1, "THE END") return False # level clear and standing on the stairs if enemies_left == 0 and self.current_actor.x == max_x and self.current_actor.y == max_y: self.show_stage_clear() return False return True def action(self, key): """main function in the game given input is put to all main actions and results are redrawn, afterwards program is ready for another input. """ if (ord(key) in ignore_keys): return True # force hero switch if current is dead if self.current_actor.status == 'dead': for hero_id in range(0, len(self.map.objects.lists.get('Hero'))): selected_hero = self.map.objects.lists.get('Hero')[hero_id] if selected_hero.status != 'dead': self.current_actor = selected_hero if self.current_actor.status == 'dead': return False # user-requested hero switch (only to living one) if key in "123456789" and int(key) in range( 0, len(self.map.objects.lists.get('Hero')) + 1): selected_hero = self.map.objects.lists.get('Hero')[int(key) - 1] if selected_hero.status != 'dead': self.current_actor = selected_hero bad_tmp = self.current_actor.level # check key in every action self.all_erase() self.action_spawn(key) self.action_pick_up(key) self.action_look(key) self.action_attack(key) self.action_move(ord(key)) self.action_inventory(key) self.action_saveload(key) # Monster action self.map.objects.enemy_action(1) if self.current_actor.level > bad_tmp: self.show_level_up() self.all_erase() self.draw() self.current_actor.steps += 1 # Depending on result - continue, game over OR (TODO) next level return self.check_quit(key)
class Camera(object): def __init__(self, screen_size, roster, flags): self.screen_size = screen_size self.flags = flags self.controller = Controller() # build team roster self.team = [] for hero in roster: self.add_hero(hero_data=hero) self.player = self.team[0] self.center_box = pygame.Rect(self.screen_size[0] / 4, self.screen_size[1] / 4, self.screen_size[0] / 2, self.screen_size[1] / 2) self.cam_center = (self.screen_size[0] / 2, self.screen_size[1] / 2) self.cam_offset_x = 0 self.cam_offset_y = 0 self.map = None self.menu = None self.show_menu = False self.notification = None self.destination_door = None self.fade_alpha = 0 self.fade_speed = 5 self.fade_out = False self.fade_in = False self.text_box = self.build_text_box() self.blackness = pygame.Surface(self.screen_size) self.blackness.fill((0, 0, 0)) self.blackness.set_alpha(self.fade_alpha) # battle self.in_battle = False self.battle = None def build_text_box(self, left=4, top=500, height=200, color=(20, 30, 200)): return TextBox( pygame.Rect(left, top, self.screen_size[0] - left * 2, height), "TEXT", color) def convert_door_destination(self, cords): x = int(cords[0]) * self.map.tileset_data["t_width"] y = int(cords[1]) * self.map.tileset_data["t_height"] return x, y def load_map(self, map_name, goto=None): self.map = Map(camera=self, directory=map_name) if goto: self.player.teleport(x=goto[0], y=goto[1]) elif self.map.starting_location: self.player.teleport(x=self.map.starting_location[0], y=self.map.starting_location[1]) self.cam_offset_x = self.player.sprite_rect.left self.cam_offset_y = self.player.sprite_rect.top def draw_map(self, screen, draw_player=True): view = pygame.Surface(self.map.map_size) self.map.draw(view, passmap=False) if draw_player: self.player.draw(view) self.map.draw_upper(view) screen.blit(view, (self.cam_center[0] - self.cam_offset_x, self.cam_center[1] - self.cam_offset_y)) def open_menu(self): self.menu = Menu(screen_size=self.screen_size, color=(20, 30, 200), actor_list=self.team) self.show_menu = True self.menu.open() def close_menu(self): self.menu.close() self.show_menu = False self.menu = None def add_hero(self, hero_data): with open(path.join(settings.ASS_DATA, "ally_specs.json")) as data_file: data = json.load(data_file)[hero_data["DATA"]] player_battle = BattleObject( name=hero_data["NAME"] or data["NAME"], sprite_sheet=SpriteSheet( path.join(settings.BATTLE, data["BATTLE"]["SPRITE"]), data["BATTLE"]["WIDTH"], data["BATTLE"]["HEIGHT"]), sprite_rect=pygame.Rect(0, 0, data["BATTLE"]["WIDTH"], data["BATTLE"]["HEIGHT"]), team=0, stats=hero_data["STATS"] or data["BATTLE"]["BASE_STATS"]) player_data = {"properties": data} player_data["properties"]["SPEED"] = settings.PLAYER_SPEED self.team.append( Player(actor_json=player_data, battle_object=player_battle)) data_file.close() def start_battle(self, battle_index): self.battle = Battle(screen_size=self.screen_size, battle_index=battle_index, team=[self.player]) self.in_battle = True def update(self, screen): if self.notification: self.notification.draw(screen) if self.controller.any_key(): self.notification = None elif self.in_battle: if self.battle.state == "END": self.in_battle = False self.battle = None else: self.controller.poll_battle(self.battle) self.battle.draw(screen) elif self.show_menu: self.controller.poll_main_menu(camera=self) # performance hit might be if you draw this map under the box self.draw_map(screen=screen, draw_player=False) if self.menu: self.menu.draw(screen) else: if not self.fade_in and not self.fade_out: # check for door intersection here? for d in self.map.door_list: if d.door.colliderect(self.player.pass_rect): self.destination_door = d self.fade_alpha = 0 self.fade_out = True if self.player.acting: self.player.move_to() else: self.controller.poll(camera=self, tbox=self.text_box, action_map=self.map.action_map) # check if camera should shift if self.player.sprite_rect.left < self.cam_offset_x - self.center_box.left: self.cam_offset_x -= settings.CAM_SPEED elif self.player.sprite_rect.left > self.cam_offset_x + self.center_box.left: self.cam_offset_x += settings.CAM_SPEED if self.player.sprite_rect.top < self.cam_offset_y - self.center_box.top: self.cam_offset_y -= settings.CAM_SPEED elif self.player.sprite_rect.top > self.cam_offset_y + self.center_box.top: self.cam_offset_y += settings.CAM_SPEED # update NPC movement for actor in self.map.actor_list: if actor.behavior == "WANDER": if actor.internal_clock > 1: actor.move_to() actor.internal_clock -= 1 else: x = random.randrange( -3, 4) * actor.position.width + actor.position.left y = random.randrange( -3, 4) * actor.position.height + actor.position.top actor.set_destination(x=x, y=y) # print("MOVING ACTOR from {0}, {1} to {2}, {3}".format(actor.position.left, # actor.position.top, x, y)) actor.internal_clock = random.randrange(200, 400) # draw things self.draw_map(screen=screen) screen.blit(self.blackness, (0, 0)) if self.text_box: self.text_box.draw(screen) if self.fade_in: if self.fade_alpha > 0: self.fade_alpha -= self.fade_speed self.blackness.set_alpha(self.fade_alpha) screen.blit(self.blackness, (0, 0)) else: self.fade_in = False elif self.fade_out: if self.fade_alpha < 255: self.fade_alpha += self.fade_speed self.blackness.set_alpha(self.fade_alpha) screen.blit(self.blackness, (0, 0)) else: self.fade_out = False self.fade_in = True print(self.destination_door.destination_map) self.load_map( map_name=self.destination_door.destination_map) door_cords = self.convert_door_destination( self.destination_door.destination_cords) self.player.teleport(x=door_cords[0], y=door_cords[1]) def save_game(self): save_path = path.join("..", "data", "save.json") char_block = { "DATA": "BOBMAN", "STATS": self.player.battle_object.stats } loc_block = { "MAP": self.map.name, "POSITION": (self.player.sprite_rect.left, self.player.sprite_rect.top) } save_block = { "CHAR": char_block, "LOC": loc_block, "FLAGS": self.flags } with open(save_path, 'w') as out_file: json.dump(save_block, out_file) out_file.close() self.notification = Notice(screen_size=self.screen_size, text="GAME SAVED", color=(80, 80, 150)) def exit(self): print("Exiting gracefully...") sys.exit(0)