def play_game(): screen_width = 70 screen_height = 45 bar_width = 20 panel_horiz_height = 10 panel_horiz_y = screen_height - panel_horiz_height panel_horiz = libtcod.console_new(screen_width, panel_horiz_height) panel_vert_height = panel_horiz_y panel_vert_y = 0 panel_vert = libtcod.console_new(48, panel_vert_height) message_x = 1 message_width = screen_width - 1 message_height = panel_horiz_height - 2 map_width = 47 map_height = panel_horiz_y room_max_size = 8 room_min_size = 3 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 max_items_per_room = 2 colors = { 'dark_wall': libtcod.Color(0, 0, 100), 'dark_ground': libtcod.Color(50, 50, 150), 'light_wall': libtcod.Color(130, 110, 50), 'light_ground': libtcod.Color(200, 180, 50) } fighter_component = Fighter(hp=100, defense=1, power=2) inventory_component = Inventory(26) level_component = Level() equipment_component = Equipment() player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component, level=level_component, equipment=equipment_component) entities = [player] equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=2) dagger = Entity(0, 0, '-', libtcod.sky, 'Dagger', equippable=equippable_component) player.inventory.add_item(dagger) player.equipment.toggle_equip(dagger) libtcod.console_set_custom_font(consts.FONT, libtcod.FONT_LAYOUT_ASCII_INROW) libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False) con = libtcod.console_new(screen_width, screen_height) game_map = GameMap(map_width, map_height) game_map.make_map(max_rooms, room_min_size, room_max_size, map_width, map_height, player, entities) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(message_x, message_width, message_height) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state targeting_item = None while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel_vert, panel_horiz, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_horiz_height, panel_horiz_y, panel_vert_height, panel_vert_y, mouse, colors, game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) move = action.get('move') exit = action.get('exit') pickup = action.get('pickup') fullscreen = action.get('fullscreen') show_inventory = action.get('show_inventory') inventory_index = action.get('inventory_index') drop_inventory = action.get('drop_inventory') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') wait = action.get('wait') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') player_turn_results = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if not game_map.is_blocked(player.x + dx, player.y + dy): if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location(entities, destination_x, destination_y) if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif wait: message_log.add_message(Message('You wait for a moment.', libtcod.yellow)) game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: message_log.add_message(Message('There is nothing here to pick up.', libtcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len( player.inventory.items): item = player.inventory.items[inventory_index] if game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend(player.inventory.use(item, entities=entities, fov_map=fov_map)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if take_stairs and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, max_rooms, room_min_size, room_max_size, map_width, map_height) fov_map = initialize_fov(game_map) fov_recompute = True libtcod.console_clear(con) break else: message_log.add_message(Message('There are no stairs here.', libtcod.yellow)) if level_up: if level_up == 'hp': player.fighter.base_max_hp += 20 player.fighter.hp += 20 elif level_up == 'str': player.fighter.base_power += 1 elif level_up == 'def': player.fighter.base_defense += 1 game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if game_state == GameStates.TARGETING: if left_click: target_x, target_y = left_click item_use_results = player.inventory.use(targeting_item, entities=entities, fov_map=fov_map, target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) elif right_click: player_turn_results.append({'targeting_cancelled': True}) if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') xp = player_turn_result.get('xp') equip = player_turn_result.get('equip') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if targeting: previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.TARGETING targeting_item = targeting message_log.add_message(targeting_item.item.targeting_message) if targeting_cancelled: game_state = previous_game_state message_log.add_message(Message('Targeting cancelled')) if xp: leveled_up = player.level.add_xp(xp) message_log.add_message(Message('You gain {0} experience points.'.format(xp))) if leveled_up: message_log.add_message(Message( 'Your battle skills grow stronger! You reached level {0}'.format( player.level.current_level) + '!', libtcod.yellow)) previous_game_state = game_state game_state = GameStates.LEVEL_UP if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message(Message('You equipped the {0}'.format(equipped.name))) if dequipped: message_log.add_message(Message('You dequipped the {0}'.format(dequipped.name))) game_state = GameStates.ENEMY_TURN if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn(player, fov_map, game_map, entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN
class Rogue(tcod.event.EventDispatch): def __init__(self): self.recompute = True self.game_map = None self.map_console = tcod.console.Console(CONFIG.get('map_width'), CONFIG.get('map_height'), 'F') self.info_console = tcod.console.Console( CONFIG.get('map_width'), CONFIG.get('info_panel_height'), 'F') self.message_console = tcod.console.Console( CONFIG.get('map_width'), CONFIG.get('message_panel_height'), 'F') self.menu_console = tcod.console.Console(CONFIG.get('map_width'), CONFIG.get('map_height'), 'F') self.game_state = GameStates.PLAYER_TURN self.previous_game_state = None self.message_log = None self.motion = tcod.event.MouseMotion() self.lbut = self.mbut = self.rbut = 0 self.quest_request = None self.using_item = None self.last_debug_entity = None def start_fresh_game(self): logging.basicConfig( filename= f'{resource_path("log")}/{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.log', filemode='w', format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=CONFIG.get('logging_level')) pubsub.pubsub = pubsub.PubSub() self.message_log = MessageLog(CONFIG.get('message_width'), CONFIG.get('message_height')) pubsub.pubsub.subscribe( pubsub.Subscription(self.message_log, pubsub.PubSubTypes.MESSAGE, pubsub.add_to_messages)) self.player = create_player() self.game_map = GameMap() self.game_map.create_floor(self.player) self.start_game() def start_game(self): self.update_fov() quest.active_quests = [] self.game_state = GameStates.PLAYER_TURN self.previous_game_state = None self.message_log.add_message( Message('Let\'s get ready to rock and/or roll!', tcod.yellow)) def on_enter(self): tcod.sys_set_fps(60) def update_fov(self): self.game_map.current_level.compute_fov( self.player.x, self.player.y, algorithm=self.player.fov.fov_algorithm, radius=self.player.fov.fov_radius, light_walls=self.player.fov.fov_light_walls) if self.player.sleep: self.game_map.current_level.npc_fov = tcod.map.compute_fov( self.game_map.current_level.transparent, pov=(self.player.x, self.player.y), algorithm=tcod.FOV_RESTRICTIVE, light_walls=True, radius=10) else: self.game_map.current_level.npc_fov = self.game_map.current_level.fov if not CONFIG.get('debug'): where_fov = np.where(self.game_map.current_level.fov[:]) self.game_map.current_level.explored[where_fov] = True else: self.game_map.current_level.fov[:] = True self.game_map.current_level.explored[:] = True def on_draw(self): #--------------------------------------------------------------------- # Recompute the player's field of view. #--------------------------------------------------------------------- self.update_fov() #--------------------------------------------------------------------- # Render and display the dungeon and its inhabitates. #--------------------------------------------------------------------- self.game_map.current_level.render(self.map_console) if not CONFIG.get('debug'): self.game_map.current_level.render_torch( self.player.x, self.player.y, self.player.fov.fov_radius, self.map_console) if CONFIG.get('debug') and self.game_map.current_level.within_bounds( self.motion.tile.x, self.motion.tile.y): for entity in self.game_map.current_level.entities.get_entities_in_position( (self.motion.tile.x, self.motion.tile.y)): if entity.movement: dijkstra = calculate_dijkstra(self.game_map, [(entity.x, entity.y)], avoid_entity=self.player) self.game_map.current_level.render_dijkstra( dijkstra, self.map_console) if entity.ai: path = entity.ai.tree.namespace.get("path") target = entity.ai.tree.namespace.get("target") if path or target: self.game_map.current_level.render_entity_detail( path, target, self.map_console) if not (entity == self.last_debug_entity): entity.debug() self.last_debug_entity = entity #--------------------------------------------------------------------- # Render infomation panels. #--------------------------------------------------------------------- render_info_console(self.info_console, self.player, self.game_map) render_message_console(self.message_console, self.message_log) #--------------------------------------------------------------------- # Blit the subconsoles to the main console and flush all rendering. #--------------------------------------------------------------------- root_console.clear(fg=COLORS.get('console_background')) self.map_console.blit(root_console, 0, 0, 0, 0, self.map_console.width, self.map_console.height) under_mouse_text = get_names_under_mouse(self.motion.tile.x, self.motion.tile.y, self.game_map.current_level) text_height = root_console.get_height_rect(1, 0, root_console.width - 2, 10, under_mouse_text) root_console.print_box( 1, CONFIG.get('info_panel_y') - text_height - 1, root_console.width - 2, text_height, under_mouse_text, fg=tcod.white, bg=None, alignment=tcod.LEFT, ) self.info_console.blit(root_console, 0, CONFIG.get('info_panel_y'), 0, 0, CONFIG.get('full_screen_width'), CONFIG.get('info_panel_height')) self.message_console.blit(root_console, 0, CONFIG.get('message_panel_y'), 0, 0, CONFIG.get('full_screen_width'), CONFIG.get('message_panel_height')) if self.game_state in MENU_STATES: #--------------------------------------------------------------------- # Render any menus. #--------------------------------------------------------------------- exclude = [] if self.using_item: exclude.append(self.using_item) self.menu_console = render_menu_console(self.game_state, self.player, self.quest_request, exclude) self.menu_console.blit(root_console, 0, 0, 0, 0, CONFIG.get('full_screen_width'), CONFIG.get('full_screen_height')) def ev_keydown(self, event: tcod.event.KeyDown): #--------------------------------------------------------------------- # Get key input from the self.player. #--------------------------------------------------------------------- input_result = handle_keys(event, self.game_state) if (len(input_result) == 0): if CONFIG.get('debug'): #logging.info("No corresponding result for key press.") pass return action, action_value = unpack_single_key_dict(input_result) self.process_turn(action, action_value) def ev_mousemotion(self, event: tcod.event.MouseMotion): self.motion = event def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown): input_type = None if event.button == tcod.event.BUTTON_LEFT: self.lbut = True input_type = InputTypes.TARGETING elif event.button == tcod.event.BUTTON_MIDDLE: self.mbut = True elif event.button == tcod.event.BUTTON_RIGHT: self.rbut = True input_type = InputTypes.EXIT self.process_turn(input_type, (event.tile.x, event.tile.y)) def ev_mousebuttonup(self, event: tcod.event.MouseButtonUp): if event.button == tcod.event.BUTTON_LEFT: self.lbut = False elif event.button == tcod.event.BUTTON_MIDDLE: self.mbut = False elif event.button == tcod.event.BUTTON_RIGHT: self.rbut = False def ev_quit(self, event: tcod.event.Quit): raise SystemExit() def game_actions(self, action, action_value): if action == InputTypes.GAME_EXIT: self.game_state = GameStates.GAME_EXIT return True if action == InputTypes.GAME_SAVE: #FIXME: Saves don't work #save_game(self.player, self.game_map, message_log, self.game_state, pubsub.pubsub) return True if action == InputTypes.GAME_RESTART: self.start_fresh_game() return True if action == InputTypes.GAME_RESET: self.game_map.first_floor(self.player) self.start_game() return True if action == InputTypes.RELOAD_LEVEL: self.game_map.next_floor(self.player) self.update_fov() return True return False def change_state_action(self, action, action_value): if action == InputTypes.CHARACTER_SCREEN: self.previous_game_state = self.game_state self.game_state = GameStates.CHARACTER_SCREEN return True if action == InputTypes.INVENTORY_DROP: self.previous_game_state = self.game_state self.game_state = GameStates.INVENTORY_DROP return True if action == InputTypes.INVENTORY_EXAMINE: self.previous_game_state = self.game_state self.game_state = GameStates.INVENTORY_EXAMINE return True if action == InputTypes.INVENTORY_THROW: self.previous_game_state = self.game_state self.game_state = GameStates.INVENTORY_THROW return True if action == InputTypes.INVENTORY_USE: self.previous_game_state = self.game_state self.game_state = GameStates.INVENTORY_USE return True if action == InputTypes.LEVEL_UP: self.player.level.level_up_stats(action_value) self.game_state = self.previous_game_state #This needs to come after leveling up or we get stuck in a loop if not (self.game_state in MENU_STATES) and self.player.level.can_level_up(): self.previous_game_state = self.game_state self.game_state = GameStates.LEVEL_UP return True if action == InputTypes.QUEST_LIST: self.previous_game_state = self.game_state self.game_state = GameStates.QUEST_LIST return True return False def debug_actions(self, action, action_value): if action == InputTypes.DEBUG_ON: CONFIG.update({'debug': True}) self.update_fov() if action == InputTypes.DEBUG_OFF: CONFIG.update({'debug': False}) self.update_fov() def menu_actions(self, action, action_value): pass def quest_actions(self, action, action_value): if action == InputTypes.QUEST_RESPONSE: if action_value: self.quest_request.owner.start_quest(self.game_map) self.message_log.add_message( Message(f"Started quest: {self.quest_request.title}", tcod.yellow)) self.quest_request = None self.game_state = self.previous_game_state if (action == InputTypes.QUEST_INDEX and self.previous_game_state != GameStates.GAME_OVER and action_value < len(quest.active_quests)): self.message_log.add_message( quest.active_quests[action_value].status()) self.game_state = self.previous_game_state def player_actions(self, action, action_value): self.player.energy.take_action() player_turn_results = [] player_on_turn_results = self.player.on_turn(self.game_map) self.process_results_stack(self.player, player_on_turn_results) if action == InputTypes.SLEEP: if not self.player.sleep: self.player.add_component(Sleep(), 'sleep') self.player.sleep.start() self.update_fov() pubsub.pubsub.add_message( pubsub.Publish(None, pubsub.PubSubTypes.MESSAGE, message=Message('You have gone asleep.', COLORS.get('effect_text')))) if self.player.health.dead: self.game_state = GameStates.GAME_OVER elif self.player.sleep: finished = self.player.sleep.on_turn(self.game_map) if finished: pubsub.pubsub.add_message( pubsub.Publish(None, pubsub.PubSubTypes.MESSAGE, message=Message('You have woken up.', COLORS.get('effect_text')))) self.update_fov() self.game_state = GameStates.ENEMY_TURN elif action == InputTypes.WAIT: self.game_state = GameStates.ENEMY_TURN elif action == InputTypes.MOVE: dx, dy = action_value if self.game_map.current_level.accessible_tile( self.player.x + dx, self.player.y + dy): if self.game_map.current_level.blocked[self.player.x + dx, self.player.y + dy]: targets = self.game_map.current_level.entities.get_entities_in_position( (self.player.x + dx, self.player.y + dy)) targets_in_render_order = sorted( targets, key=lambda x: x.render_order.value, reverse=True) target = targets_in_render_order[0] if target.interaction.interaction_type == Interactions.QUESTGIVER: quest_results = target.questgiver.talk(self.player) player_turn_results.extend(quest_results) elif target.interaction.interaction_type == Interactions.DOOR: if target.locked: can_unlock = False if target.locked.requires_key: all_keys = self.player.inventory.search( name='key') for key_to_check in all_keys: if key_to_check.unlock.unlocks == target.uuid: can_unlock = True player_turn_results.extend([{ ResultTypes.DISCARD_ITEM: key_to_check }]) break else: can_unlock = True if can_unlock: target.locked.toggle() self.game_map.current_level.update_entity_position( target) self.update_fov() message = Message( f"You have unlocked the {target.name}.", tcod.yellow) player_turn_results.extend([{ ResultTypes.MESSAGE: message }]) else: message = Message( f"The {target.name} is locked.", tcod.yellow) player_turn_results.extend([{ ResultTypes.MESSAGE: message }]) elif target.interaction.interaction_type == Interactions.FOE: if target.health and not target.health.dead: attack_results = self.player.offence.attack( target, self.game_map) player_turn_results.extend(attack_results) else: self.player.movement.move(dx, dy, self.game_map.current_level) player_turn_results.extend( quest.check_quest_for_location(self.player)) self.update_fov() self.game_state = GameStates.ENEMY_TURN elif action == InputTypes.PICKUP: entities = self.game_map.current_level.entities.get_entities_in_position( (self.player.x, self.player.y)) pickup = False for entity in entities: if entity.item: if entity.identifiable and identified_items.get( entity.base_name): entity.identifiable.identified = True player_turn_results.extend([{ ResultTypes.ADD_ITEM_TO_INVENTORY: entity }]) pickup = True if not pickup: message = Message('There is nothing here to pick up.', tcod.yellow) player_turn_results.extend([{ResultTypes.MESSAGE: message}]) elif action == InputTypes.DOWN_LEVEL: self.game_map.next_floor(self.player) self.update_fov() message = Message( 'You take a moment to rest and recover your strength.', tcod.light_violet) player_turn_results.extend([{ResultTypes.MESSAGE: message}]) #continue return elif action == InputTypes.TAKE_STAIRS: stair_state = self.game_map.check_for_stairs( self.player.x, self.player.y) if stair_state == StairOption.GODOWN: self.game_map.next_floor(self.player) self.update_fov() message = Message( 'You take a moment to rest and recover your strength.', tcod.light_violet) player_turn_results.extend([{ResultTypes.MESSAGE: message}]) #continue return elif stair_state == StairOption.GOUP: self.game_map.previous_floor(self.player) self.update_fov() return elif stair_state == StairOption.EXIT: self.game_state = GameStates.GAME_PAUSED else: message = Message('There are no stairs here.', tcod.yellow) player_turn_results.extend([{ResultTypes.MESSAGE: message}]) self.process_results_stack(self.player, player_turn_results) pubsub.pubsub.process_queue(self.game_map) def npc_actions(self): self.game_map.current_level.clear_paths() for entity in self.game_map.current_level.entities: if entity == self.player: continue entity.energy.increase_energy() if entity.energy.take_action(): if entity.level and entity.level.can_level_up(): entity.level.random_level_up(1) enemy_turn_results = entity.on_turn(self.game_map) self.process_results_stack(entity, enemy_turn_results) enemy_turn_results.clear() if entity.health and entity.health.dead: entity.death.decompose(self.game_map) elif entity.ai: # Enemies move and attack if possible. enemy_turn_results.extend( entity.ai.take_turn(self.game_map)) self.process_results_stack(entity, enemy_turn_results) enemy_turn_results.clear() pubsub.pubsub.process_queue(self.game_map) def process_turn(self, action, action_value): player_turn_results = [] if self.game_actions(action, action_value): return if self.change_state_action(action, action_value): return self.debug_actions(action, action_value) self.quest_actions(action, action_value) if action == InputTypes.EXIT: if self.game_state in CANCEL_STATES: self.game_state = self.previous_game_state self.using_item = None if self.game_state == GameStates.QUEST_ONBOARDING: player_turn_results.append( {ResultTypes.QUEST_CANCELLED: True}) else: self.previous_game_state = self.game_state self.game_state = GameStates.GAME_PAUSED return if (action == InputTypes.TARGETING and self.game_state == GameStates.TARGETING): target_x, target_y = action_value player_turn_results.extend( self.using_item.usable.use(game_map=self.game_map, user=self.player, target_x=target_x, target_y=target_y)) if (action == InputTypes.INVENTORY_INDEX and self.previous_game_state != GameStates.GAME_OVER and action_value < len(self.player.inventory.items)): items = self.player.inventory.items.copy() if self.using_item: items.remove(self.using_item) item = items[action_value] if self.game_state == GameStates.INVENTORY_USE: if item.usable: self.using_item = item player_turn_results.extend( item.usable.use(self.game_map, self.player)) else: player_turn_results.extend([{ResultTypes.EQUIP: item}]) elif self.game_state == GameStates.INVENTORY_SELECT: player_turn_results.extend( self.using_item.usable.use(self.game_map, self.player, item)) self.using_item = None elif self.game_state == GameStates.INVENTORY_DROP: player_turn_results.extend( self.player.inventory.drop_item(item)) elif self.game_state == GameStates.INVENTORY_EXAMINE: player_turn_results.extend( self.player.inventory.examine_item(item)) self.process_results_stack(self.player, player_turn_results) pubsub.pubsub.process_queue(self.game_map) #------------------------------------------------------------------- # Player takes their turn. #------------------------------------------------------------------- if (self.game_state == GameStates.PLAYER_TURN or self.game_state == GameStates.PLAYER_SLEEP): self.player_actions(action, action_value) if (self.game_state in INPUT_STATES or self.game_state == GameStates.GAME_OVER): return #------------------------------------------------------------------- # NPCs take their turns. #------------------------------------------------------------------- self.npc_actions() self.player.energy.increase_energy() if self.player.energy.can_act: if self.player.sleep: self.game_state = GameStates.PLAYER_SLEEP else: if not self.game_state in INPUT_STATES: self.game_state = GameStates.PLAYER_TURN else: if not self.game_state in INPUT_STATES: self.game_state = GameStates.ENEMY_TURN if not self.game_state == GameStates.PLAYER_TURN: sleep(CONFIG.get('time_between_enemy_turns')) #--------------------------------------------------------------------- # And done...so broadcast a tick #--------------------------------------------------------------------- pubsub.pubsub.add_message(pubsub.Publish(None, pubsub.PubSubTypes.TICK)) pubsub.pubsub.process_queue(self.game_map) def process_results_stack(self, entity, turn_results): #---------------------------------------------------------------------- # Process the results stack #...................................................................... # We are done processing inputs, and may have some results on # the entity turn stack. Process the stack by popping off the top # result from the queue. There are many different possible results, # so each is handled with a dedicated handler. # # Note: Handling a result may result in other results being added to # the stack, so we continually process the results stack until it is # empty. #---------------------------------------------------------------------- while turn_results != []: # Sort the turn results stack by the priority order. turn_results = sorted( flatten_list_of_dictionaries(turn_results), key=lambda d: get_key_from_single_key_dict(d)) result = turn_results.pop() result_type, result_data = unpack_single_key_dict(result) # Handle a simple message. if result_type == ResultTypes.MESSAGE: message = result_data pubsub.pubsub.add_message( pubsub.Publish(None, pubsub.PubSubTypes.MESSAGE, message=message)) if result_type == ResultTypes.FOV_RECOMPUTE: self.update_fov() if result_type == ResultTypes.END_TURN: self.game_state = GameStates.ENEMY_TURN if result_type == ResultTypes.EARN_XP: if result_data['xp'] > 0: result_data['earner'].level.add_xp(result_data['xp']) message = Message( f"{result_data['earner'].name} gained {result_data['xp']} xp", COLORS.get('success_text'), target=result_data['earner'], type=MessageType.EVENT) turn_results.extend([{ResultTypes.MESSAGE: message}]) # Handle death. if result_type == ResultTypes.DEAD_ENTITY: self.game_state = result_data['dead'].death.npc_death( self.game_map) if entity == result_data['dead']: turn_results = [] if result_data['attacker'] and result_data['attacker'].ai: result_data['attacker'].ai.remove_target( target=result_data['dead']) result_data['dead'].deregister_turn_all() if result_type == ResultTypes.TARGET_ITEM_IN_INVENTORY: self.game_state = GameStates.INVENTORY_SELECT if result_type == ResultTypes.CANCEL_TARGET_ITEM_IN_INVENTORY: self.using_item = None self.game_state = GameStates.PLAYER_TURN # Add an item to the inventory, and remove it from the game map. if result_type == ResultTypes.ADD_ITEM_TO_INVENTORY: turn_results.extend(entity.inventory.add_item(result_data)) self.game_state = GameStates.ENEMY_TURN # Remove consumed items from inventory if result_type == ResultTypes.DISCARD_ITEM: entity.inventory.remove_item(result_data) self.game_state = GameStates.ENEMY_TURN self.using_item = None # Remove dropped items from inventory and place on the map if result_type == ResultTypes.DROP_ITEM_FROM_INVENTORY: self.game_map.current_level.add_entity(result_data) message = Message( f"{entity.name} dropped the {result_data.name}", COLORS.get('success_text'), target=entity, type=MessageType.EVENT) turn_results.extend([{ResultTypes.MESSAGE: message}]) self.game_state = GameStates.ENEMY_TURN if result_type == ResultTypes.EQUIP: equip_results = entity.equipment.toggle_equip(result_data) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message = Message( f"{entity.name} equipped the {equipped.name}", target=entity, type=MessageType.EVENT) if dequipped: message = Message( f"{entity.name} dequipped the {dequipped.name}", target=entity, type=MessageType.EVENT) turn_results.extend([{ResultTypes.MESSAGE: message}]) self.game_state = GameStates.ENEMY_TURN if result_type == ResultTypes.QUEST_ONBOARDING: self.quest_request = result_data self.previous_game_state = self.game_state self.game_state = GameStates.QUEST_ONBOARDING if result_type == ResultTypes.QUEST_CANCELLED: pass if result_type == ResultTypes.SET_POSITION: npc, point = result_data npc.movement.place(point.x, point.y, self.game_map.current_level) # Handle a move towards action. Move towards a target. if result_type == ResultTypes.MOVE_TOWARDS: npc, target_x, target_y = result_data npc.movement.attempt_move(Point(target_x, target_y), self.game_map) # Handle a move towards action. Move towards a target following a particular path. if result_type == ResultTypes.MOVE_WITH_PATH: npc, path = result_data self.game_map.current_level.paths.append(path) npc.movement.attempt_move(Point(path[0][0], path[0][1]), self.game_map) # Handle a move random adjacent action. Move to a random adjacent # square. if result_type == ResultTypes.MOVE_RANDOM_ADJACENT: npc = result_data npc.movement.move_to_random_adjacent(self.game_map) if result_type == ResultTypes.MOVE_FORCE: target, dx, dy, damage = result_data if damage > 0 and not target.movement.move( dx, dy, self.game_map.current_level): damage_results, total_damage = target.health.take_damage( damage) msg_text = '{0} crashes into the wall and takes {1} hit points damage.' message = Message( msg_text.format(target.name, str(total_damage)), COLORS.get('damage_text')) turn_results.extend([{ResultTypes.MESSAGE: message}]) turn_results.extend(damage_results) # Add a new entity to the game. if result_type == ResultTypes.ADD_ENTITY: self.game_map.current_level.add_entity(result_data) # Remove an entity from the game. if result_type == ResultTypes.REMOVE_ENTITY: self.game_map.current_level.remove_entity(result_data) if result_type == ResultTypes.TARGETING: self.previous_game_state = self.game_state self.game_state = GameStates.TARGETING if result_type == ResultTypes.COMMON_IDENT: identified_items[result_data] = True
class Engine: def __init__(self, iteration, agent=None): self.agent = agent self.game_ended = False self.path = None self.game_result = None self.iteration = iteration self.root_console = None self.panel = None self.player = None self.entities = None self.game_map = None self.fov_recompute = False self.fov_map = None self.message_log = None self.game_state = None self.previous_game_state = None # self.power_difference = None # self.defence_difference = None self.current_enemy = None # Set the game up and play the game loop def play(self): libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'Moria - {0}'.format(self.iteration), False, libtcod.RENDERER_SDL2, "F", False) self.root_console = libtcodconsole.Console(SCREEN_WIDTH, SCREEN_HEIGHT) self.panel = libtcodconsole.Console(SCREEN_WIDTH, PANEL_HEIGHT) fighter_component = Fighter(hp=PLAYER_MAX_HEALTH, defence=PLAYER_DEFENCE, power=PLAYER_POWER) inventory_component = Inventory(26) equipment_component = Equipment() coin_pouch = CoinPouch(0) self.player = Entity(0, 0, '@', libtcod.white, "Player", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component, equipment=equipment_component, coin_pouch=coin_pouch) self.entities = [self.player] self.game_map = GameMap() self.game_map.make_map(self.player, self.entities) self.fov_recompute = True self.fov_map = initialize_fov(self.game_map) self.message_log = MessageLog() self.game_state = GameStates.PLAYERS_TURN self.previous_game_state = self.game_state self.init_game() while not self.game_ended and self.player is not None: if self.agent is not None: print("Shop has", len(self.game_map.shop.items)) self.agent.action = None self.run_agent(self.player, self.agent, self.fov_map) self.current_enemy = None self.run_game() return self.game_result # The agent decides their turn def run_agent(self, player, agent, fov_map): agent.take_turn(self, player, self.game_map, fov_map, self.game_state) # Initialise the game def init_game(self): libtcodconsole.default_fg = libtcod.white if self.fov_recompute: recompute_fov(self.fov_map, self.player.x, self.player.y) render_all(self.root_console, self.panel, self.entities, self.player, self.game_map, self.fov_map, self.fov_recompute, self.message_log, self.game_state) self.fov_recompute = False libtcod.console_flush() # Run the games turn def run_game(self): libtcodconsole.default_fg = libtcod.white if self.fov_recompute: recompute_fov(self.fov_map, self.player.x, self.player.y) render_all(self.root_console, self.panel, self.entities, self.player, self.game_map, self.fov_map, self.fov_recompute, self.message_log, self.game_state) self.fov_recompute = False libtcod.console_flush() if self.agent is None: action = {'pass': False} for event in libtcodevent.get(): if event.type == "QUIT": return if event.type == "KEYDOWN": action = handle_keys(event.sym, self.game_state) else: if self.player.equipment.head is not None: head_name = self.player.equipment.head.name else: head_name = "Nothing" if self.player.equipment.body is not None: body_name = self.player.equipment.body.name else: body_name = "Nothing" if self.player.equipment.main_hand is not None: main_name = self.player.equipment.main_hand.name else: main_name = "Nothing" if self.player.equipment.off_hand is not None: off_name = self.player.equipment.off_hand.name else: off_name = "Nothing" print( "Head: {0} Body: {1} Main hand: {2} Off hand: {3} Coins: {4}" .format(head_name, body_name, main_name, off_name, self.player.coin_pouch.get_amount())) action = self.agent.get_action() self.handle_action(action) # Deal with the players chosen action whether human or CPU def handle_action(self, action): # print(action) pass_action = action.get('pass') move = action.get('move') pickup = action.get('pickup') show_inventory = action.get('show_inventory') menu_index = action.get('menu_index') drop_inventory = action.get('drop_inventory') show_shop = action.get('show_shop') take_stairs = action.get('take_stairs') exit_action = action.get('exit') fullscreen = action.get('fullscreen') player_turn_results = [] if pass_action: self.game_state = GameStates.ENEMY_TURN if move and self.game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = self.player.x + dx destination_y = self.player.y + dy if not self.game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location( self.entities, destination_x, destination_y) if target: attack_results = self.player.fighter.attack(target) self.current_enemy = target player_turn_results.extend(attack_results) else: self.player.move(dx, dy) self.fov_recompute = True self.game_state = GameStates.ENEMY_TURN elif pickup and self.game_state == GameStates.PLAYERS_TURN: for entity in self.entities: if entity.item and entity.x == self.player.x and entity.y == self.player.y: pickup_results = self.player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: self.message_log.add_message( Message('There is nothing here to pick up.', libtcod.yellow)) if show_inventory: self.previous_game_state = self.game_state self.game_state = GameStates.SHOW_INVENTORY if drop_inventory: self.previous_game_state = self.game_state self.game_state = GameStates.DROP_INVENTORY if show_shop: if self.game_map.tiles[self.player.x][self.player.y].shop: self.previous_game_state = self.game_state self.game_state = GameStates.SHOW_SHOP if take_stairs: for entity in self.entities: if entity.stairs and entity.x == self.player.x and entity.y == self.player.y: if entity.char == DOWN_STAIRS_CHAR: self.entities = self.game_map.next_floor( self.player, self.message_log) else: self.entities = self.game_map.previous_floor( self.player, self.message_log) self.fov_map = initialize_fov(self.game_map) self.fov_recompute = True self.root_console.clear() break else: self.message_log.add_message( Message('There are no stairs here.', libtcod.yellow)) if menu_index is not None and self.game_state == GameStates.SHOW_SHOP and menu_index < len( GameMap.shop.items): item = GameMap.shop.items[menu_index] player_turn_results.extend(GameMap.shop.buy(item, self.player)) # player_turn_results.extend(self.player.inventory.use(item, entities=self.entities, fov_map=self.fov_map)) if menu_index is not None and self.previous_game_state != GameStates.PLAYER_DEAD and menu_index < len( self.player.inventory.items): item = self.player.inventory.items[menu_index] if self.game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend( self.player.inventory.use(item, entities=self.entities, fov_map=self.fov_map)) elif self.game_state == GameStates.DROP_INVENTORY: player_turn_results.extend( self.player.inventory.drop_item(item)) if self.game_state == GameStates.TARGETING: # TODO Make a menu of the visible opponents to attack pass if exit_action: if self.game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.SHOW_SHOP): self.game_state = self.previous_game_state else: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') equip = player_turn_result.get('equip') item_dropped = player_turn_result.get('item_dropped') targeting = player_turn_result.get('targeting') if message: self.message_log.add_message(message) if dead_entity: if dead_entity == self.player: message, self.game_state = kill_player( dead_entity, self.game_map) self.game_result = 0 else: # self.power_difference = self.player.fighter.power - dead_entity.fighter.power # self.defence_difference = self.player.fighter.defence - dead_entity.fighter.defence # self.current_enemy = dead_entity message, self.game_ended = kill_monster( dead_entity, self.player) if self.game_ended: self.game_result = 1 self.message_log.add_message(message) if item_added: if item_added in self.entities: self.entities.remove(item_added) self.game_state = GameStates.ENEMY_TURN if item_consumed: self.game_state = GameStates.ENEMY_TURN if equip: equip_results = self.player.equipment.toggle_equip(equip) for result in equip_results: equipped = result.get('equipped') dequipped = result.get('dequipped') if equipped: self.message_log.add_message( Message('You equipped the {0}'.format( equipped.name))) if dequipped: self.message_log.add_message( Message('You dequipped the {0}'.format( dequipped.name))) self.game_state = GameStates.ENEMY_TURN if item_dropped: self.entities.append(item_dropped) self.game_state = GameStates.ENEMY_TURN if targeting: self.previous_game_state = GameStates.PLAYERS_TURN self.game_state = GameStates.TARGETING targeting_item = targeting self.message_log.add_message( targeting_item.item.targeting_message) if self.game_state == GameStates.ENEMY_TURN: for entity in self.entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( self.player, self.fov_map, self.game_map, self.entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: self.message_log.add_message(message) if dead_entity: if dead_entity == self.player: message, self.game_state = kill_player( dead_entity, self.game_map) else: message, self.game_ended = kill_monster( dead_entity, self.player) if self.game_ended: self.game_result = 1 self.message_log.add_message(message) if self.game_state == GameStates.PLAYER_DEAD: self.game_ended = True self.game_result = 0 break if self.game_state == GameStates.PLAYER_DEAD: self.game_ended = True break else: self.game_state = GameStates.PLAYERS_TURN def end_game(self): self.game_ended = True def get_entities(self): return self.entities
def main(): constants = get_constants() libtcod.console_set_custom_font( 'tiledfont.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD, 32, 10) libtcod.console_init_root(constants['screen_width'], constants['screen_height'], constants['window_title'], False) # load the custom font rows load_customfont() # assign the custom font rows numbers to text (for easier calling when defining entities with custom tiles) # defining tiles (rather than numbers) wall_tile = 257 #note: see render_functions for where the wall and floor tiles are defined, these are not used. floor_tile = 256 player_tile = 258 quiz_tile = 259 exam_tile = 260 healingpotion_tile = 261 sword_tile = 263 shield_tile = 264 stairsdown_tile = 265 dagger_tile = 266 fighter_component = Fighter(hp=30, defense=1, power=3, name='Guardian') inventory_component = Inventory(8) level_component = Level() equipment_component = Equipment() player = Entity(0, 0, player_tile, libtcod.white, 'Guardian', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component, level=level_component, equipment=equipment_component) entities = [player] equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=1) dagger = Entity(0, 0, dagger_tile, libtcod.white, 'Elemental Absorber + 1', equippable=equippable_component) player.inventory.add_item(dagger) player.equipment.toggle_equip(dagger) equippable_component = Equippable(EquipmentSlots.OFF_HAND, max_hp_bonus=5, defense_bonus=0) shield = Entity(0, 0, shield_tile, libtcod.white, 'Elemental Deflector + 0', equippable=equippable_component) player.inventory.add_item(shield) player.equipment.toggle_equip(shield) player.fighter.hp += shield.equippable.max_hp_bonus con = libtcod.console.Console(constants['screen_width'], constants['screen_height']) panel = libtcod.console.Console(constants['screen_width'], constants['panel_height']) game_map = GameMap(constants['map_width'], constants['map_height']) game_map.make_map(constants['max_rooms'], constants['room_min_size'], constants['room_max_size'], constants['map_width'], constants['map_height'], player, entities) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(constants['message_x'], constants['message_width'], constants['message_height']) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state # Welcome the player message_log.add_message( Message( 'Welcome, Guardian, to the elemental kingdom of Empyria! Aquire all the elements...or die trying! Fearsome foes await in the deepest depths of Empyria, where many Guardians have disappeared...', libtcod.white)) while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) move = action.get('move') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') wait = action.get('wait') exit = action.get('exit') fullscreen = action.get('fullscreen') player_turn_results = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location( entities, destination_x, destination_y) if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) fov_recompute = True else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: message_log.add_message( Message('There is nothing here to pick up.', libtcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len( player.inventory.items): item = player.inventory.items[inventory_index] if game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend(player.inventory.use(item)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if take_stairs and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, constants) fov_map = initialize_fov(game_map) fov_recompute = True con.clear() break else: message_log.add_message( Message( 'You search around, but the stairs are nowhere to be seen.', libtcod.yellow)) if level_up: if level_up == 'hp': player.fighter.base_max_hp += 30 player.fighter.hp += 30 elif level_up == 'str': player.fighter.base_power += 5 elif level_up == 'def': player.fighter.base_defense += 1 game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if wait == True: game_state = GameStates.ENEMY_TURN fov_recompute = True if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN): game_state = previous_game_state else: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_pick_gold = player_turn_result.get('got_gold') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: # Using xp to increase player's stats, "absorbing" the element if dead_entity.fighter.name == 'Corrupted Guardian': previous_game_state = game_state game_state = GameStates.WIN message_log.add_message( Message( 'You have brought peace to Empyria and vanquished the Corrputed Guardian! Congratulations!', libtcod.yellow)) if dead_entity.fighter.element == 'blank': player.fighter.blank_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.blank_element > player.fighter.max_blank_element: player.fighter.blank_element = player.fighter.max_blank_element elif dead_entity.fighter.element == 'fire': player.fighter.fire_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.fire_element > player.fighter.max_fire_element: player.fighter.fire_element = player.fighter.max_fire_element elif dead_entity.fighter.element == 'air': player.fighter.air_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.air_element > player.fighter.max_air_element: player.fighter.air_element = player.fighter.max_air_element elif dead_entity.fighter.element == 'ice': player.fighter.ice_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.ice_element > player.fighter.max_ice_element: player.fighter.ice_element = player.fighter.max_ice_element elif dead_entity.fighter.element == 'lightning': player.fighter.lightning_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.lightning_element > player.fighter.max_lightning_element: player.fighter.lightning_element = player.fighter.max_lightning_element elif dead_entity.fighter.element == 'earth': player.fighter.earth_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.earth_element > player.fighter.max_earth_element: player.fighter.earth_element = player.fighter.max_earth_element elif dead_entity.fighter.element == 'psychic': player.fighter.psychic_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.psychic_element > player.fighter.max_psychic_element: player.fighter.psychic_element = player.fighter.max_psychic_element elif dead_entity.fighter.element == 'water': player.fighter.water_element += dead_entity.fighter.xp message = kill_monster(dead_entity) if player.fighter.water_element > player.fighter.max_water_element: player.fighter.water_element = player.fighter.max_water_element message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN # if item_pick_gold: # entities.remove(item_gold) # game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message( Message('You equipped the {0}.'.format( equipped.name))) if dequipped: message_log.add_message( Message('You dequipped the {0}.'.format( dequipped.name))) game_state = GameStates.ENEMY_TURN xp = player_turn_result.get('xp') if xp: # if not GameStates.WIN: leveled_up = player.level.add_xp(xp) message_log.add_message( Message('You gained {0} exp!'.format(xp))) if (player.fighter.blank_element == player.fighter.max_blank_element and player.fighter.fire_element == player.fighter.max_fire_element and player.fighter.air_element == player.fighter.max_air_element and player.fighter.ice_element == player.fighter.max_ice_element and player.fighter.lightning_element == player.fighter.max_lightning_element and player.fighter.earth_element == player.fighter.max_earth_element and player.fighter.psychic_element == player.fighter.max_psychic_element and player.fighter.water_element == player.fighter.max_water_element): message_log.add_message( Message( 'You have collected all of the elements and are now a true Elemental Guardian! You won! Or did you...?', libtcod.yellow)) if leveled_up: # if player.level.current_level == 5: # previous_game_state = game_state # game_state = GameStates.WIN # message_log.add_message(Message('You have collected 180 exp! You won!', libtcod.yellow)) # else: message_log.add_message( Message( 'Level up! You are now level {0}'.format( player.level.current_level) + '!', libtcod.yellow)) previous_game_state = game_state game_state = GameStates.LEVEL_UP if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN fov_recompute = True
def main(): constants = get_constants() libtcod.console_set_custom_font( 'tiledfont.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD, 32, 10) libtcod.console_init_root(constants['screen_width'], constants['screen_height'], constants['window_title'], False) # load the custom font rows load_customfont() # assign the custom font rows numbers to text (for easier calling when defining entities with custom tiles) # defining tiles (rather than numbers) wall_tile = 256 floor_tile = 257 player_tile = 258 quiz_tile = 259 exam_tile = 260 healingpotion_tile = 261 sword_tile = 263 shield_tile = 264 stairsdown_tile = 265 dagger_tile = 266 fighter_component = Fighter(hp=100, defense=1, power=3, name='Student') inventory_component = Inventory(26) level_component = Level() equipment_component = Equipment() player = Entity(0, 0, player_tile, libtcod.white, 'Student', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component, level=level_component, equipment=equipment_component) entities = [player] equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=1) dagger = Entity(0, 0, dagger_tile, libtcod.white, 'Pencil', equippable=equippable_component) player.inventory.add_item(dagger) player.equipment.toggle_equip(dagger) con = libtcod.console.Console(constants['screen_width'], constants['screen_height']) panel = libtcod.console.Console(constants['screen_width'], constants['panel_height']) game_map = GameMap(constants['map_width'], constants['map_height']) game_map.make_map(constants['max_rooms'], constants['room_min_size'], constants['room_max_size'], constants['map_width'], constants['map_height'], player, entities) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(constants['message_x'], constants['message_width'], constants['message_height']) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state # Welcome the player message_log.add_message( Message( 'Welcome, Student, to the College of Doom! Aquire 180 credits to graduate...or die trying!', libtcod.white)) while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) move = action.get('move') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') wait = action.get('wait') exit = action.get('exit') fullscreen = action.get('fullscreen') player_turn_results = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location( entities, destination_x, destination_y) if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) fov_recompute = True else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: message_log.add_message( Message('There is nothing here to pick up.', libtcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len( player.inventory.items): item = player.inventory.items[inventory_index] if game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend(player.inventory.use(item)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if take_stairs and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, constants) fov_map = initialize_fov(game_map) fov_recompute = True con.clear() break else: message_log.add_message( Message( 'You search around, but Summer break is nowhere to be seen.', libtcod.yellow)) if level_up: if level_up == 'hp': player.fighter.base_max_hp += 20 player.fighter.hp += 20 elif level_up == 'str': player.fighter.base_power += 1 elif level_up == 'def': player.fighter.base_defense += 1 game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if wait == True: game_state = GameStates.ENEMY_TURN fov_recompute = True if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN): game_state = previous_game_state else: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message( Message('You equipped the {0}.'.format( equipped.name))) if dequipped: message_log.add_message( Message('You dequipped the {0}.'.format( dequipped.name))) game_state = GameStates.ENEMY_TURN xp = player_turn_result.get('xp') if xp: leveled_up = player.level.add_xp(xp) message_log.add_message( Message( 'You passed the Final Exam and gained {0} credits!'. format(xp))) if leveled_up: if player.level.current_level == 5: previous_game_state = game_state game_state = GameStates.WIN message_log.add_message( Message( 'You have completed 180 credits at the College of Doom! You graduated!', libtcod.yellow)) else: message_log.add_message( Message( 'You made it through another year of school! You move on to school year {0}' .format(player.level.current_level) + '!', libtcod.yellow)) previous_game_state = game_state game_state = GameStates.LEVEL_UP if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN fov_recompute = True