def play_game(player: Entity, entities: List[Entity], game_map: GameMap, message_log: MessageLog, game_state: GameStates, root_console: tcod.console.Console, con: tcod.console.Console, panel: tcod.console.Console) -> None: fov_recompute = True fov_map = initialize_fov(game_map) mouse = tcod.event.Point(-1, -1) if player.fighter.hp > 0: game_state = GameStates.PLAYERS_TURN else: game_state = GameStates.PLAYER_DEAD previous_game_state = game_state targeting_item = None while True: action: UserAction = {} for event in tcod.event.wait(1): if event.type == 'QUIT': # XXX: what happens if I do this when in the character screen? # or inventory? or while targeting? will the game load fine? save_game(player, entities, game_map, message_log, game_state) sys.exit() elif event.type == 'KEYDOWN': action = handle_keys(event, game_state, mouse) elif event.type == 'MOUSEMOTION': mouse = event.tile elif event.type == 'MOUSEBUTTONDOWN': mouse = event.tile action = handle_mouse(event) if action: break if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants.fov_radius, constants.fov_light_walls, constants.fov_algorithm) target_radius = 0 if targeting_item and targeting_item.item: target_radius = targeting_item.item.function_kwargs.get( 'radius', 0) render_all( root_console, 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, target_radius, ) fov_recompute = False tcod.console_flush() clear_all(con, entities) move = action.get('move') wait = action.get('wait') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') show_character_screen = action.get('show_character_screen') take_stairs = action.get('take_stairs') level_up = action.get('level_up') exit = action.get('exit') fullscreen = action.get('fullscreen') left_click = action.get('left_click') right_click = action.get('right_click') player_turn_results: ActionResults = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move new_x = player.x + dx new_y = player.y + dy if not game_map.is_blocked(new_x, new_y): target = get_blocking_entities_at_location( entities, new_x, new_y) if target: player_turn_results.extend(player.fighter.attack(target)) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN if wait and game_state == GameStates.PLAYERS_TURN: game_state = GameStates.ENEMY_TURN if pickup and game_state == GameStates.PLAYERS_TURN: for entity in entities: if (entity.x == player.x and entity.y == player.y and entity.item): player_turn_results.extend( player.inventory.add_item(entity)) break else: message_log.add_message( Message('There is nothing here to pick up.', tcod.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): save_game(player, entities, game_map, message_log, game_state) 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('There are no stairs here.', tcod.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 player_turn_results.extend( player.inventory.use(targeting_item, entities=entities, fov_map=fov_map, target_x=target_x, target_y=target_y)) 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: save_game(player, entities, game_map, message_log, game_state) return if fullscreen: tcod.console_set_fullscreen(not tcod.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: Optional[Entity] = 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') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') xp = player_turn_result.get('xp') 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 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(f"You equipped the {equipped.name}.")) if dequipped: message_log.add_message( Message(f"You removed the {dequipped.name}.")) game_state = GameStates.ENEMY_TURN if xp: leveled_up = player.level.add_xp(xp) message_log.add_message( Message(f'You gain {xp} experience points.')) if leveled_up: message_log.add_message( Message( f'Your battle skills grow stronger!' f' You reached level {player.level.current_level}!', tcod.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
def play_game(con, player, entities, animator: Animator, turn_count: int, game_map: GameMap, message_log, game_state, panel, constants): target_x, target_y = player.x, player.y targeting_item = None target_entity = None while True: fov_algorithm = 2 fov_light_walls = True fov_radius = 20 fov_recompute = True fov_map = initialize_fov(game_map) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel, entities, animator, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], game_state, target_x, target_y, target_entity, turn_count) tcod.console_flush() clear_all(con, entities) player_turn_results = [] animator.advance_frame() # Handle Game State if game_state == GameStates.ENEMY_TURN: turn_count += 1 # Generate path map with all static tiles (ignore entities for now) path_map = generate_path_map(game_map, entities=None, player=player) for entity in entities: if entity.ai and entity.ai != Player: recompute_walkable(fov_map, game_map, entities, entity) entity_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities, path_map) for entity_turn_result in entity_turn_results: message = entity_turn_result.get('message') dead_entity = entity_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(player) else: message = kill_entity(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.PLAYER_TURN # Handle Events for event in tcod.event.wait(): if event.type == "QUIT": save_game(player, entities, animator, turn_count, game_map, message_log, game_state) raise SystemExit() if event.type == "KEYDOWN": action: [Action, None] = handle_keys(event.sym, game_state) if action is None: continue action_type: ActionType = action.action_type if action_type == ActionType.EXECUTE: if game_state == GameStates.TARGETING: item_use_results = player.body.use_selected_appendage( entities=entities, fov_map=fov_map, game_map=game_map, target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) game_state = GameStates.ENEMY_TURN elif game_state == GameStates.LOOKING: look_results = [] looked_at_entities = get_entities_at_location( entities, target_x, target_y) if tcod.map_is_in_fov(fov_map, target_x, target_y): if looked_at_entities: for entity in looked_at_entities: look_results.extend( entity.get_description()) target_entity = entity else: if game_map.tiles[target_x][target_y].blocked: look_results.append({ 'message': Message("You stare at the wall.") }) else: look_results.append({ 'message': Message("You stare into empty space.") }) else: look_results.append({ 'message': Message("You can't see that far.") }) game_state = GameStates.PLAYER_TURN player_turn_results.extend(look_results) if action_type == ActionType.MOVEMENT: dx: int = action.kwargs.get("dx", 0) dy: int = action.kwargs.get("dy", 0) # Player Movement if game_state == GameStates.PLAYER_TURN: destination_x = player.x + dx destination_y = player.y + dy tile_results = game_map.tiles[destination_x][ destination_y].overlap_entity(player) player_turn_results.extend(tile_results) if not game_map.is_blocked(destination_x, destination_y): target_appendage = get_blocking_entities_at_location( entities, destination_x, destination_y) if target_appendage: if target_appendage.body: player_fighter = player.body.selected_appendage.fighter if player_fighter: target_entity = target_appendage game_state = GameStates.TARGET_APPENDAGE else: player_turn_results.append({ 'message': Message( "You cannot attack with your {0}." .format( player.body. selected_appendage.name), tcod.yellow) }) elif target_appendage.structure: structure_interact_results = target_appendage.structure.interact( player) player_turn_results.extend( structure_interact_results) else: player.move(dx, dy) else: player_turn_results.append({ 'message': Message("You slam yourself into the wall!", tcod.orange) }) if game_state != GameStates.TARGET_APPENDAGE: game_state = GameStates.ENEMY_TURN # Targeting elif game_state in (GameStates.TARGETING, GameStates.LOOKING): new_x = target_x + dx new_y = target_y + dy if player.distance(new_x, new_y) < targeting_radius: target_x = new_x target_y = new_y elif action_type == ActionType.GRAB: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_result = player.body.grab_entity(entity) player_turn_results.extend(pickup_result) break else: player_turn_results.append({ 'message': Message('You grab at the air', tcod.yellow) }) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.LOOK: game_state = GameStates.LOOKING target_x, target_y = player.x, player.y targeting_radius = 100 elif action_type == ActionType.WAIT: player_turn_results.append({ 'message': Message('You stare blankly into space', tcod.yellow) }) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.CHOOSE_OPTION: option_index = action.kwargs.get("option_index", None) if game_state == GameStates.SWAP_APPENDAGE: if option_index < len(player.body.appendages): item = player.body.appendages[option_index] swap_results = player.body.select_appendage(item) player_turn_results.extend(swap_results) game_state = GameStates.PLAYER_TURN elif game_state == GameStates.TARGET_APPENDAGE: if option_index < len(target_entity.body.appendages): target_appendage = target_entity.body.appendages[ option_index] attack_results = player.body.selected_appendage.fighter.attack_appendage( target_appendage) player_turn_results.extend(attack_results) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.INTERACT: for entity in entities: if entity.structure and entity.x == player.x and entity.y == player.y: interact_results = entity.structure.interact( player) player_turn_results.extend(interact_results) break else: if game_state == GameStates.PLAYER_TURN: activate_item_results = player.body.use_selected_appendage( fov_map=fov_map, game_map=game_map, entities=entities) if activate_item_results: player_turn_results.extend( activate_item_results) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.DROP_INVENTORY_ITEM: grabber = player.body.selected_appendage.grabber if grabber: player_turn_results.extend(grabber.drop()) # game_state = GameStates.DROP_INVENTORY elif action_type == ActionType.SWAP_APPENDAGE: game_state = GameStates.SWAP_APPENDAGE elif action_type == ActionType.ESCAPE: if game_state == GameStates.TARGETING: game_state = GameStates.PLAYER_TURN elif game_state == GameStates.PLAYER_TURN: save_game(player, entities, animator, turn_count, game_map, message_log, game_state) main() elif action_type == ActionType.RESTART: main() # Process player turn results 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') next_floor = player_turn_result.get('next_floor') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(player) else: message_log.add_message(kill_entity(dead_entity)) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if targeting: game_state = GameStates.TARGETING targeting_item = targeting targeting_radius = targeting.item.targeting_radius target_x = player.x target_y = player.y message_log.add_message( Message("You begin aiming the {0}.".format( targeting.name))) # TODO: Replace occurrences of add_message with player_turn_result approach # player_turn_results.append({'message': Message("You begin aiming the {0}.".format(targeting.name))}) if next_floor: entities = game_map.next_floor(player, constants) fov_map = initialize_fov(game_map) tcod.console_clear(con)
def play_game(player: Entity, entities: List[Entity], game_map: GameMap, message_log: MessageLog, game_state: GameStates, constants): game_running: bool = True fov_recompute: bool = True previous_game_state: GameStates = game_state targeting_item = None while game_running: if fov_recompute: game_map.current_floor.compute_fov( x=player.x, y=player.y, radius=constants['fov_radius'], light_walls=constants['fov_light_walls'], algorithm=constants['fov_algorithm'] ) render_all(entities=entities, player=player, game_map=game_map, game_state=game_state, message_log=message_log, constants=constants) fov_recompute = False terminal.refresh() if terminal.has_input(): terminal_input: int = terminal.read() action = handle_keys(key=terminal_input, game_state=game_state) mouse_action = handle_mouse(terminal_input) drop_inventory = action.get('drop_inventory') escape = action.get('escape') movement = action.get('movement') inventory_index = action.get('inventory_index') pickup = action.get('pickup') show_inventory = action.get('show_inventory') take_stairs = action.get('take_stairs') wait = action.get('wait') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') player_turn_results = [] if escape: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: save_game(player, entities, game_map, message_log, game_state) game_running = False if movement and game_state == GameStates.PLAYERS_TURN: dx, dy = movement destination_x: int = player.x + dx destination_y: int = player.y + dy if not game_map.current_floor.is_blocked(destination_x, destination_y): target: Entity = get_blocking_entities_at_location(entities, destination_x, destination_y) if target: attack_results = player.fighter.attack(target=target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN if wait: game_state = GameStates.ENEMY_TURN 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=game_map.fov)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if 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('[color=yellow]There is nothing here to pick up.[/color]') 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 take_stairs: if game_map.current_floor.down_stairs[player.x, player.y]: entities = game_map.next_floor(player=player, message_log=message_log, constants=constants) fov_recompute = True terminal.clear() elif game_map.current_floor.up_stairs[player.x, player.y]: entities = game_map.previous_floor(player=player, message_log=message_log, constants=constants) fov_recompute = True terminal.clear() else: message_log.add_message('[color=yellow]There are no stairs here.[/color]') 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=game_map.fov, 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}) for player_turn_result in player_turn_results: dead_entity = player_turn_result.get('dead') equip = player_turn_result.get('equip') 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') message = player_turn_result.get('message') if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) 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(f'You equipped the [color=blue]{equipped.name}[/color]') if dequipped: message_log.add_message(f'You dequipped the [color=blue]{dequipped.name}[/color]') game_state = GameStates.ENEMY_TURN 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 message: message_log.add_message(message) 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('Targeting cancelled') if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn(target=player, game_map=game_map, entities=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 terminal.clear()