def next_floor(self, player: Entity, message_log: MessageLog, constants: Type['constants']) -> List[Entity]: self.dungeon_level += 1 entities = [player] self.tiles = self.initialize_tiles() self.make_map( constants.max_rooms, constants.room_min_size, constants.room_max_size, player, entities) player.fighter.heal(player.fighter.max_hp // 2) message_log.add_message( Message('You take a moment to rest, and recover your strength.', tcod.light_violet)) return entities
def get_game_variables(constants): player = Fighter(0, 0, '@', None, constants['player_max_hp'], constants['player_max_mp'], constants['player_start_power'], 'player', constants['default_fov_radius']) entities = [player] # initializes variables to track when a player regenerates hp and mp hp_regen_tick = 0 mp_regen_tick = 0 # initializes a blank map game_map = GameMap(constants['map_width'], constants['map_height']) # chooses algorithm at random to make a map game_map.make_map(player, entities) # sets up fov map and initializes value telling game whether or not it needs to redraw the players fov fov_recompute = True fov_map = initialize_fov(game_map) # sets up message log and prints start-up messages message_log = MessageLog(constants['message_x'], constants['message_width'], constants['message_height']) message_log.add_message(Message('Welcome to Ex Oblivione', tcod.yellow)) message_log.add_message(Message('Reach floor 4 to win')) return player, entities, game_map, fov_recompute, fov_map, message_log, hp_regen_tick, mp_regen_tick
class EventHandler: def __init__(self, game): self.events = [] self.game = game self.message_log = MessageLog() def add_event(self, event): self.events.append(event) def resolve_events(self): for event in self.events: message = event.get("message") color = event.get("color") victory = event.get("victory") # True level_up = event.get("level_up") # Entity quit_target_mode = event.get("quit_target_mode") # True if not color: color = color_config.NEUTRAL_INFO_COLOR if message: self.message_log.add_message(message, color) if victory: self.game.current_menu = VictoryMenu() if level_up: self.game.current_menu = LevelUpMenu(level_up) if quit_target_mode: self.game.quit_target_mode() self.reset_event_list() def reset_event_list(self): self.events = []
def take_stairs(self, player: Entity, message_log: MessageLog, constants: Dict, dungeon: Dict, direction: int) -> None: assert (direction == -1 or direction == 1), "Invalid Direction" if direction == 1: self.dungeon_level += 1 self.entities = [player] self.fov_map = tcod.map.Map(self.width, self.height, order="F") self.explored = np.zeros((self.width, self.height), dtype=bool) self.fov_map.walkable[:] = False self.fov_map.transparent[:] = False self.make_map(constants["max_rooms"], constants["room_min_size"], constants["room_max_size"], constants["map_width"], constants["map_height"], player) player.fighter.heal(player.fighter.max_hp // 2) dungeon.update({self.dungeon_level: self}) message_log.add_message(Message('You take a moment to rest, and recover your strength.', tcod.light_violet)) else: pass
def main(): # Initiate important variables screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 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), } # Initiate the objects that will be important in rendering and the map fighter_component = Fighter(hp=30, defense=2, power=5) inventory_component = Inventory(26) player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component) entities = [player] libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'Cardinal Code', False) #boolean is fullscreen or not con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_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, max_monsters_per_room, max_items_per_room) 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 # Main game loop while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) # fov_recompute tells if the render function should recompute the FOV # recompute_fov will recompute the FOV from render_functions.py based on the initialized variables if(fov_recompute): recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # Renders the map and the screens render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors) libtcod.console_flush() # Clears entities whose position changed clear_all(con, entities) # Get what key was pressed, from sets of dictionaries action = handle_keys(key) # Then get the action from the sets of dictionaries established in input_handlers.py move = action.get('move') pickup = action.get('pickup') exit = action.get('exit') fullscreen = action.get('fullscreen') player_turn_results = [] # If move has a value and the game_state is the player's state if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy # If the player's destination is not blocked, do something if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location(entities, destination_x, destination_y) # If there is an entity at the destination, do this if target: player.fighter.attack(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 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 pickup.', libtcod.yellow)) if exit: 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') 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 game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: entity.ai.take_turn(player, fov_map, game_map, entities) 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 main(): # Adding the main function for Python 3 compatibility # Setting constants and global variables screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height # Adding variables Message log display to show events. message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 brown_color = libtcod.flame * libtcod.light_blue colors = { 'dark_wall': brown_color, # Color(0, 0, 100), 'dark_ground': libtcod.desaturated_orange, # Color(50, 50, 150) 'light_wall': libtcod.dark_flame, 'light_ground': libtcod.light_orange } # Coloring our tiles # LIMIT_FPS = 20 # Unused for now # Setting player coordinate starting point at center of console fighter_component = Fighter(hp=30, defense=2, power=5) # Setting player attributes player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] # Initializing the library font libtcod.console_set_custom_font( 'arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) # Now creating the window with size constants, title, and whether fullscreen libtcod.console_init_root(screen_width, screen_height, 'python/libtcod tutorial', False) con = libtcod.console_new( screen_width, screen_height) # Allows the ability to create new consoles panel = libtcod.console_new( screen_width, panel_height) # New console to hold HP and Messages game_map = GameMap(map_width, map_height) # Initialize the game map game_map.make_map(max_rooms, room_min_size, room_max_size, map_width, map_height, player, entities, max_monsters_per_room) fov_recompute = True # Whether to reset the Field of View, True for start of game fov_map = initialize_fov(game_map) #Initialize the Field of View message_log = MessageLog(message_x, message_width, message_height) key = libtcod.Key() # Setting keyboard variable for input mouse = libtcod.Mouse() # Setting mouse variable for input game_state = GameStates.PLAYERS_TURN # Sets initial game_state to players turn # Next is the main game loop. We basically print the @ character to the screen in white while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) #libtcod.console_set_default_foreground(0, libtcod.white) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # Changing the way the console is initialized so we can reference different consoles later render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors) fov_recompute = False libtcod.console_flush( ) # Flush the console which writes any changes to the screen clear_all(con, entities) # New setup to call handle_keys() function from input_handlers.py action = handle_keys(key) move = action.get('move') exit = action.get('exit') fullscreen = action.get('fullscreen') player_turn_results = [] # new dictionary 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: # Are you running into a monster? attack_results = player.fighter.attack( target) # Attack monster player_turn_results.extend( attack_results) # Get results of attack else: player.move(dx, dy) fov_recompute = True # Recompute the FOV upon movement game_state = GameStates.ENEMY_TURN # Sets state to players turn. if exit: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) # Iterate through the results # Player Results Loop for player_turn_result in player_turn_results: message = player_turn_result.get('message') # Get the message part dead_entity = player_turn_result.get( 'dead') # Get the part as to whether dead or not if message: message_log.add_message( message) # Prints any messages for the player turn if dead_entity: # Check is something dead this turn if dead_entity == player: # Is the dead thing the player? message, game_state = kill_player( dead_entity) # Run kill_player function else: message = kill_monster( dead_entity) # Run kill_monster function message_log.add_message(message) # Enemy Results Loop if game_state == GameStates.ENEMY_TURN: # Checks to see if enemy turn for entity in entities: # Cycles through entities looking for monsters if entity.ai: # If entity is not the player and has ai. # Set a list that calls the take_turn function for the ai enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities) for enemy_turn_result in enemy_turn_results: # Iterate through the list message = enemy_turn_result.get( 'message') # Gather any messages for that ai dead_entity = enemy_turn_result.get( 'dead') # get and dead comments if message: message_log.add_message( message ) # Print any messages for the turn of the ai if dead_entity: # Check if dead entity this turn if dead_entity == player: # Is it the player? message, game_state = kill_player( dead_entity ) # If yes then run kill_player and show message from results else: message = kill_monster( dead_entity ) # If it's the monster, then kill it. message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: # Did the player die? break # If dead player then end game. if game_state == GameStates.PLAYER_DEAD: break # Ends game if player dies and monster has died at same time. else: # Set the game_state back to players turn game_state = GameStates.PLAYERS_TURN
def main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_challenges_per_room = 3 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) } player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True) entities = [player] entities = [player] libtcod.console_set_custom_font( 'img01.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'trabalho de RV', False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_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, max_challenges_per_room) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(message_x, message_width, message_height) key = libtcod.Key() mouse = libtcod.Mouse() while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, colors) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get('move') exit = action.get('exit') fullscreen = action.get('fullscreen') if move: 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: message_log.add_message(Message(target.question.text)) else: player.move(dx, dy) fov_recompute = True if exit: return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
def main(): # Screen Params screen_width = 80 screen_height = 50 # UI Params bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height # Message Log Params message_x = bar_width + 2 message_width = screen_height - bar_width - 2 message_height = panel_height - 1 # Map Params map_width = 80 map_height = 43 # Room Params room_max_size = 10 room_min_size = 6 max_rooms = 30 # Field of View Params fov_algorithm = 0 fov_light_walls = True fov_radius = 10 # Monsters & Items Params max_monsters_per_room = 3 max_items_per_room = 2 # Colors for tiles colors = { 'dark_wall': tcod.Color(0, 0, 100), 'dark_ground': tcod.Color(50, 50, 150), 'light_wall': tcod.Color(130, 110, 50), 'light_ground': tcod.Color(200, 180, 50) } # Entities initialization fighter_component = Fighter(hp=30, defense=2, power=5) player = Entity(0, 0, '@', tcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] # Game Window initialization tcod.console_set_custom_font( 'arial10x10.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_TCOD) tcod.console_init_root(screen_width, screen_height, 'Move Fast & Kill Things', False) con = tcod.console_new(screen_width, screen_height) # UI Panel initialization panel = tcod.console_new(screen_width, panel_height) # Map initialization 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, max_monsters_per_room, max_items_per_room) # Field of View Map Computation fov_recompute = True fov_map = initialize_fov(game_map) # Message log initialization message_log = MessageLog(message_x, message_width, message_height) # Keyboard & Mouse initialization key = tcod.Key() mouse = tcod.Mouse() # Game State initialized to player's turn game_state = GameStates.PLAYERS_TURN # GAME LOOP while not tcod.console_is_window_closed(): tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS | tcod.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, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors) fov_recompute = False tcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get('move') 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 enemy attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: # Move player player.move(dx, dy) # Recompute field of vision fov_recompute = True game_state = GameStates.ENEMY_TURN if exit: return True if fullscreen: tcod.console_set_fullscreen(not tcod.console_is_fullscreen()) # Handling action results log for player for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_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) # Handling enemy movement & actions 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 else: game_state = GameStates.PLAYERS_TURN
def main(): #initilize main variables screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 max_items_per_room = 2 #define color palette colors = { 'dark_wall': libtcod.Color(55, 50, 45), 'dark_ground': libtcod.Color(15, 15, 15), 'light_wall': libtcod.Color(70, 55, 40), 'light_ground': libtcod.Color(30, 20, 15) } #Create Player fighter_component = Fighter(hp=30, defense=2, power=5) inventory_component = Inventory(26) player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component) entities = [player] player_won = False #set up screen libtcod.console_set_custom_font( 'arial12x12.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'SimpleRogue', False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_height) key = libtcod.Key() mouse = libtcod.Mouse() #Generate map with entities 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, max_monsters_per_room, max_items_per_room) fov_recompute = True fov_map = initializ_fov(game_map) game_state = GameStates.PLAYERS_TURN previous_game_state = game_state message_log = MessageLog(message_x, message_width, message_height) # main game loop while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) #Update Display if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors, game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) #Define possible actions action = handle_keys(key, game_state) move = action.get('move') pickup = action.get('pickup') show_inventory = action.get('show_inventory') inventory_index = action.get('inventory_index') exit = action.get('exit') fullscreen = action.get('fullscreen') #Clear the last set of results from actions player_turn_results = [] #Handle actions #move the player 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: player.fighter.attack(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 #try to pick up something 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)) #display the inventory screen if show_inventory: if game_state != GameStates.SHOW_INVENTORY: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY #use an item in the 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] player_turn_results.extend(player.inventory.use(item)) #exit screen or close game if exit: if game_state == GameStates.SHOW_INVENTORY: game_state = previous_game_state else: return True #toggle full screen view if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) #Display results of player actions 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') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_states = 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 #check for win (player killed everything) fighter_count = 0 for entity in entities: if entity.fighter is not None and entity.name != 'Player': fighter_count += 1 if fighter_count == 0 and player_won != True: player_won = True message_log.add_message( Message("I hope you're proud of yourself...", libtcod.yellow)) message_log.add_message(Message("You monster!", libtcod.red)) #Handle enemy actions and display results 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 else: game_state = GameStates.PLAYERS_TURN
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants, turns, poison_turns, burned_turns, frozen_turns): fov_recompute = True pet = 0 boss = None for entity in entities: if entity.name == 'Pet': pet = entity break for entity in entities: if entity.name == 'Dragonlord': boss = entity break fov_map = initialize_fov(game_map) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state targeting_item = None arrow_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, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) if game_map.biome == 'The Dungeon': colors = { 'dark_wall': libtcod.darkest_grey, 'dark_ground': libtcod.darker_grey, 'light_wall': libtcod.grey, 'light_ground': libtcod.white, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'The Icebleak Cavern': colors = { 'dark_wall': libtcod.darkest_cyan, 'dark_ground': libtcod.darker_cyan, 'light_wall': libtcod.dark_cyan, 'light_ground': libtcod.white, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'The Underglade': colors = { 'dark_wall': libtcod.darker_green, 'dark_ground': libtcod.darkest_green, 'light_wall': libtcod.desaturated_green, 'light_ground': libtcod.brass, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'The Hadalrealm': colors = { 'dark_wall': libtcod.darkest_red, 'dark_ground': libtcod.darker_red, 'light_wall': libtcod.dark_red, 'light_ground': libtcod.white, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'Dragonroost': colors = { 'dark_wall': libtcod.darkest_grey, 'dark_ground': libtcod.darker_grey, 'light_wall': libtcod.dark_grey, 'light_ground': libtcod.grey, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'Oblivion\'s Gate': colors = { 'dark_wall': libtcod.darkest_grey, 'dark_ground': libtcod.darker_grey, 'light_wall': libtcod.dark_grey, 'light_ground': libtcod.white, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } elif game_map.biome == 'The Vault': colors = { 'dark_wall': libtcod.darker_yellow, 'dark_ground': libtcod.dark_yellow, 'light_wall': libtcod.yellow, 'light_ground': libtcod.white, 'dark_water': libtcod.blue, 'light_water': libtcod.light_blue } render_all(turns, 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, colors, game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) player.sound = 0 Scent.set_scent(game_map, player.x, player.y, turns) 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') exit = action.get('exit') fullscreen = action.get('fullscreen') wait = action.get('wait') take_up_stairs = action.get('take_up_stairs') take_down_stairs = action.get('take_down_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') show_help_screen = action.get('show_help_screen') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') up = action.get('up') down = action.get('down') left = action.get('left') right = action.get('right') player_turn_results = [] if player.frozen and frozen_turns > 0: game_state = GameStates.ENEMY_TURN if player.frozen == False: frozen_turns = -42 if player.burned == False: burned_turns = -42 if player.poisoned == False: poison_turns = -42 if poison_turns == 0: player_turn_results.append({ 'message': Message('You feel a lot better', libtcod.light_cyan) }) poison_turns = -42 player.poisoned = False if burned_turns == 0: player_turn_results.append({ 'message': Message('Your burn feels a lot better', libtcod.light_cyan) }) burned_turns = -42 player.burned = False if frozen_turns == 0: player_turn_results.append( {'message': Message('You defrost', libtcod.light_cyan)}) frozen_turns = -42 player.frozen = False if wait: game_state = GameStates.ENEMY_TURN d = randint(0, 100) if d < 25: message_log.add_message( Message('You stand around doing nothing', libtcod.brass)) elif d < 50: message_log.add_message( Message('You lean on your sword', libtcod.brass)) elif d < 75: message_log.add_message( Message('You stand around looking like a lemon', libtcod.brass)) else: message_log.add_message( Message('You examine your surroundings intensely', libtcod.light_amber)) 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: if target.name == 'Pet': target = None if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: if game_map.tiles[player.x][player.y].water == True: if randint(0, 10) == 1: game_map.tiles[destination_x][ destination_y].water = True player_turn_results.append({ 'message': Message( 'You splash water all over the dry bit of floor', libtcod.cyan) }) player.move(dx, dy) player.sound = 10 if turns % randint(9, 11) == 0: player.fighter.heal(1) levelup = player.level.add_con_xp(1) if levelup: player.fighter.hp += 20 player.fighter.base_max_hp += 20 player_turn_results.append({ 'message': Message( 'You feel healthy, you must have been very active', libtcod.fuchsia) }) fov_recompute = True game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYERS_TURN: here = 0 for entity in entities: if entity.name != 'Player' and not entity.stairs and not entity.monster_class == '(Pet)': if (entity.item or entity.equippable ) and entity.x == player.x and entity.y == player.y: here = 1 player.sound = 5 pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) else: if here == 0: 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 player.sound = 5 game_state = GameStates.DROP_INVENTORY if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len( player.items): item = player.items[inventory_index] if type(item) != str: if game_state == GameStates.SHOW_INVENTORY: success = True if item.item_class == '(Scroll)': difficulty = randint(0, 10) if difficulty > player.fighter.intelligence: success = False else: levelup = player.level.add_int_xp(1) if levelup: message_log.add_message( Message( 'Your reading grows more accurate!', libtcod.fuchsia)) player.fighter.base_intelligence += 1 player_turn_results.extend( player.inventory.use(success, item, entities=entities, fov_map=fov_map)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend( player.inventory.drop_item(item)) else: message_log.add_message( Message('You can\'t use Headers', libtcod.yellow)) if take_up_stairs and game_state == GameStates.PLAYERS_TURN and not boss: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: pet, entities = game_map.next_floor( player, message_log, constants, entities, 'up') fov_map = initialize_fov(game_map) fov_recompute = True player.sound = 10 libtcod.console_clear(con) break else: message_log.add_message( Message('You can\'t go up here.', libtcod.yellow)) if take_down_stairs and game_state == GameStates.PLAYERS_TURN and not boss: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: pet, entities = game_map.next_floor( player, message_log, constants, entities, 'down') fov_map = initialize_fov(game_map) fov_recompute = True player.sound = 10 libtcod.console_clear(con) break else: message_log.add_message( Message('You can\'t go down 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_defence += 1 elif level_up == 'int': player.fighter.base_intelligence += 1 elif level_up == 'dex': player.fighter.base_dexterity += 1 game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if show_help_screen: previous_game_state = game_state game_state = GameStates.HELP_SCREEN if game_state == GameStates.TARGETING: if left_click: target_x, target_y = left_click item_use_results = player.inventory.use(success, 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 game_state == GameStates.ARROW_TARGETING: direction = None if up: direction = 'up' elif down: direction = 'down' elif left: direction = 'left' elif right: direction = 'right' if direction: item_use_results = player.inventory.use(success, arrow_targeting_item, entities=entities, fov_map=fov_map, direction=direction) player_turn_results.extend(item_use_results) if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN, GameStates.HELP_SCREEN): game_state = previous_game_state elif game_state == GameStates.TARGETING or game_state == GameStates.ARROW_TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: save_game(player, entities, game_map, message_log, game_state, poison_turns, turns, burned_turns, frozen_turns) 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') arrow_consumed = player_turn_result.get('arrow_consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') targeting = player_turn_result.get('targeting') arrow_targeting = player_turn_result.get('arrow_targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') player_burned = player_turn_result.get('burned') enemy_turn = player_turn_result.get('pass') if enemy_turn: game_state = GameStates.ENEMY_TURN if player_burned: player_turn_results.append( {'message': Message('You get Burned', libtcod.flame)}) burned_turns = randint(5, 10) message = player_turn_result.get('message') 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, game_map, entities) arrows = 0 for i in range(0, dead_entity.arrows): arrow_break = randint(0, 1) if not arrow_break: item_component = Item(use_function=None) arrow = Entity(dead_entity.x, dead_entity.y, '|', libtcod.sepia, 'Arrow', item=item_component, item_class='(Arrow)') entities.append(arrow) else: arrows += 1 if arrows == 1: MessageLog.add_message( self=message_log, message=Message( 'The arrow stuck in the {0} breaks as it falls over' .format(dead_entity.name), libtcod.red)) elif arrows: MessageLog.add_message( self=message_log, message=Message( 'The {0} arrows stuck in the {1} breaks as it falls over' .format(arrows, dead_entity.name), libtcod.red)) 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 arrow_consumed: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: player.sound = 10 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 targeting: previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.TARGETING targeting_item = targeting message_log.add_message(targeting_item.item.targeting_message) if arrow_targeting: player.sound = 5 previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.ARROW_TARGETING arrow_targeting_item = arrow_targeting message_log.add_message( arrow_targeting_item.item.targeting_message) if targeting_cancelled: game_state = previous_game_state message_log.add_message(Message('Targeting cancelled')) if game_state == GameStates.ENEMY_TURN: if turns % int( (300 - (game_map.dungeon_level * 2)) / 2) == 0 and turns != 0: game_map.add_entity(message_log, entities, fov_map) if burned_turns >= 0 and player.burned: dead_entity = False burned_results = player.fighter.take_damage(4) for burned_result in burned_results: dead_entity = burned_result.get('dead') if dead_entity: if dead_entity.name == 'Player': message, game_state = kill_player(dead_entity) message_log.add_message(message) burned_turns -= 1 if poison_turns >= 0 and player.poisoned: dead_entity = False poison_results = player.fighter.take_damage(1) for poison_result in poison_results: dead_entity = poison_result.get('dead') if dead_entity: if dead_entity.name == 'Player': message, game_state = kill_player(dead_entity) message_log.add_message(message) poison_turns -= 1 if frozen_turns >= 0 and player.frozen: frozen_turns -= 1 if frozen_turns == 0: player.frozen = False for entity in entities: if entity.ai: pet_there = 0 if pet: if entity.distance_to(player) >= entity.distance_to( pet) and entity.name != 'Pet': pet_there = 1 enemy_turn_results = entity.ai.take_turn( pet, fov_map, game_map, entities, turns) else: enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities, turns) else: enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities, turns) for enemy_turn_result in enemy_turn_results: if burned_turns >= 0: burned_results = entity.fighter.take_damage(4) for burned_result in burned_results: dead_entity = burned_result.get('dead') if dead_entity.name == 'Player': message, game_state = kill_player( dead_entity) message_log.add_message(message) burned_turns -= 1 poisoned, player_burned, frozen = False, False, False if not player.fighter.poison_resistance and not pet_there: poisoned = enemy_turn_result.get('poisoned') if not player.fighter.fire_resistance and not pet_there: player_burned = enemy_turn_result.get('burned') if not player.fighter.poison_resistance and not pet_there: frozen = enemy_turn_result.get('frozen') if player_burned: enemy_turn_results.append({ 'message': Message('You get Burned', libtcod.flame) }) burned_turns = randint(5, 10) player.burned = True if poisoned: enemy_turn_results.append({ 'message': Message('you start to feel ill', libtcod.green) }) poison_turns = randint(20, 50) player.poisoned = True if frozen: enemy_turn_results.append({ 'message': Message('you stop being able to move!', libtcod.light_blue) }) frozen_turns = randint(2, 5) player.frozen = True 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) elif dead_entity == pet: pet = 0 message = kill_pet(dead_entity) elif dead_entity == boss: game_state = GameStates.WINNING message = kill_boss(dead_entity) else: message = kill_monster(dead_entity, pet, game_map) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: turns += 1 game_state = GameStates.PLAYERS_TURN
def main(): screen_width = 80 screen_height = 50 cardtable_width = 18 cardtable_height = screen_height cardtable_x = screen_width - cardtable_width panel_height = 10 panel_y = screen_height - panel_height panel_width = screen_width - cardtable_width map_width = screen_width - cardtable_width map_height = screen_height - panel_height message_x = 2 message_width = panel_width - 2 message_height = panel_height - 1 message_log = MessageLog(message_x, message_width, message_height) panel = tcod.console.Console(panel_width, panel_height) mapcon = tcod.console.Console(map_width, map_height) cardtable = tcod.console.Console(cardtable_width, cardtable_height) # number of dice, sideness of dice. values used taken from gunslinger pregen, pg88 player_charactersheet = Character() print(player_charactersheet) player = Entity(int(screen_width / 2), int(screen_height / 2), '@', tcod.white, 'Player', True, RenderOrder.ACTOR, Fighter(6)) entities = [player] game_map = tcod.map.Map(map_width, map_height) generate_map(game_map, player, entities) fov_recompute = True fate_pot = Counter({'white': 50, 'red': 25, 'blue':10}) player_fate = Counter() player_fate.update(sample(list(fate_pot.elements()), 3)) fate_pot.subtract(player_fate) # FIXME: Currently, this does not include Jokers, which are required # for decks used in Deadlands. The class can be instantiated to # use jokers, but its use of jokers does not differentiate between # red and black jokers (as required in Deadlands) so the issue of # jokers is left to another day. # Also probably the suit hierarchy is not the same as Deadlands; that should be easy # to fix when I get to it. marshal_deck = pydealer.Deck() posse_deck = pydealer.Deck() marshal_deck.shuffle() posse_deck.shuffle() player_hand = pydealer.Stack() # pydealer.Stack() marshal_hand = pydealer.Stack() player_hand.sort() posse_discard = pydealer.Stack() marshal_discard = pydealer.Stack() tcod.console_set_custom_font('cp437_10x10.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_CP437) root_console = tcod.console_init_root(screen_width, screen_height, 'Deadlands Duel', False, tcod.RENDERER_SDL2, vsync=True) player_round_movement_budget = player_charactersheet.get_movement_budget() move_this_action = 0 active_card = pydealer.Stack() # posse_deck.deal(1) colt_army = {'shots': 6, 'max_shots': 6, 'range': 10, 'damage': {'number_of_dice': 3, 'sideness_of_dice': 6}} game_state = GameStates.PLAYERS_TURN # FIXME: There's probably an elegant solution to be found in generalizing this # list to include the player, and sorting it by action cards, or something. enemy_combatants = [] while True: if fov_recompute: game_map.compute_fov(player.x, player.y, algorithm=tcod.FOV_PERMISSIVE(5)) if game_state ==GameStates.PLAYERS_TURN: for entity in entities: if entity.name == 'Bandit' and game_map.fov[entity.y, entity.x]: game_state = GameStates.BEGIN_DETAILED_COMBAT_ROUND break if game_state ==GameStates.ROUNDS_PLAYERS_ACTION: enemies_in_view = False for entity in entities: if entity.name == 'Bandit' and game_map.fov[entity.y, entity.x]: enemies_in_view = True if enemies_in_view == False: message_log.add_message(Message("All visible bandits dead, leaving combat rounds...")) posse_discard.add(active_card.deal(active_card.size)) posse_discard.add(player_hand.deal(player_hand.size)) game_state =GameStates.PLAYERS_TURN enemy_combatants = [] if game_state == GameStates.ENEMY_TURN: for entity in entities: if (entity.name == 'Bandit') and (not game_map.fov[entity.y, entity.x]): if entity.fighter.shots < 6: # When the player ducks behind a wall to reload their revolver, # the bandits also take advantage of the opportunity! entity.fighter.shots += 1 game_state = GameStates.PLAYERS_TURN if game_state == GameStates.BEGIN_DETAILED_COMBAT_ROUND: # Let the player know what's going on message_log.add_message(Message("Beginning of combat round!")) # Deal the player a hand from the posse deck and calc their movement player_round_movement_budget = player_charactersheet.get_movement_budget() roll_new_round(player_hand, player_charactersheet, posse_deck, posse_discard, message_log) # We *should* deal the enemies an action hand, and calc their movement. # FIXME: Currently, we use the ultra-simple shortcut for enemies # from the Marshal Tricks section of the rules, dealing them a single card. # This combat really isn't on a scale where that is justified, at least not until # more enemies are clued in to the combat via sound or a vague awareness metric. # Also, enemy movement is not yet implemented, so we don't calc their move rate. for entity in entities: if entity.name == 'Bandit' and game_map.fov[entity.y, entity.x]: entity.fighter.action_hand = marshal_deck.deal(1) enemy_combatants.append(entity) game_state = GameStates.MEDIATE_COMBAT_ROUNDS if game_state == GameStates.MEDIATE_COMBAT_ROUNDS: # FIXME: Should we keep track of a list of combat activated enemies? # As a quick hack, right now it's just FOV. # (calculated in BEGIN_DETAILED_COMBAT_ROUNDS conditional above) remaining_enemy_cards = False for combatant in enemy_combatants: if combatant.fighter.action_hand.size > 0: # print(combatant.fighter.action_hand.size) remaining_enemy_cards = True if remaining_enemy_cards or (player_hand.size > 0): # and (active_card.size > 0)): highest_player = None if player_hand.size > 0: highest_player = player_hand[player_hand.size - 1] # print("highest player card " + str(highest_player)) highest_combatant = None highest_comb_card = None if remaining_enemy_cards: for combatant in enemy_combatants: print("combatant card: " + str(combatant.fighter.action_hand[combatant.fighter.action_hand.size - 1])) if highest_comb_card == None: highest_combatant = combatant highest_comb_card = combatant.fighter.action_hand[combatant.fighter.action_hand.size - 1] elif combatant.fighter.action_hand[combatant.fighter.action_hand.size - 1] > highest_comb_card: highest_combatant = combatant highest_comb_card = combatant.fighter.action_hand[combatant.fighter.action_hand.size - 1] # print("highest combatant card " + str(highest_comb_card)) if remaining_enemy_cards and ((highest_combatant) and ((highest_player == None)) or (highest_comb_card > highest_player)): # Enemy turn, in combat rounds. Placeholder. message_log.add_message(Message("The " + highest_combatant.name + " acts on a " + str(highest_comb_card) + "!", tcod.orange)) if highest_combatant.fighter.shots > 0: tn = 5 modifier = 0 - highest_combatant.fighter.get_most_severe_wound()[1] range_increments = (highest_combatant.distance_to(player) / 3) // colt_army['range'] tn += range_increments shootin_roll = skill_roll(2, 8, tn, modifier) success = shootin_roll.get('success') if success: vital_hit = False body_part = None hitlocation = unexploding_roll(20) if (hitlocation == 20): vital_hit = True body_part = 'head' elif 15 <= hitlocation <= 19: body_part = 'guts' #upper elif 11 <= hitlocation <= 14: body_part = '_arm' if unexploding_roll(2) == 1: body_part = 'left' + body_part else: body_part = 'right' + body_part elif hitlocation == 10: vital_hit = True body_part = 'guts' #gizzards elif 5 <= hitlocation <= 9: body_part = 'guts' #lower else: body_part = '_leg' if unexploding_roll(2) == 1: body_part = 'left' + body_part else: body_part = 'right' + body_part message_log.add_message(Message("The bandit takes aim and shoots, hitting you in the " + body_part + "!", tcod.red)) dmg = ranged_weapon_damage_roll(colt_army['damage']['sideness_of_dice'], colt_army['damage']['number_of_dice'], vital_bonus = vital_hit) message_log.add_message(player.fighter.take_positional_damage(dmg, body_part, fate_pot, player_fate)) if (player.fighter.body_wounds['guts'] >= 5) or (player.fighter.body_wounds['head'] >= 5): message_log.add_message(kill_monster(player)) game_state = GameStates.PLAYER_DEAD else: message_log.add_message(Message("The bandit takes aim and shoots! The bullet whizzes past you!", tcod.orange)) highest_combatant.fighter.shots -= 1 else: message_log.add_message(Message("The bandit loads his revolver...")) highest_combatant.fighter.shots += 1 marshal_discard.add(highest_combatant.fighter.action_hand.deal(1)) enemy_combatants.remove(highest_combatant) else: # FIXME: This erroneously includes tied situations, which the rules say # should result in simultaneous actions. # Player's turn, in combat rounds. game_state = GameStates.ROUNDS_PLAYERS_ACTION else: game_state = GameStates.BEGIN_DETAILED_COMBAT_ROUND if game_state == GameStates.ROUNDS_ENEMY_ACTION: print("Shouldn't be possible???") render_all(root_console, entities, mapcon, game_map, cardtable, cardtable_x, player_hand, active_card, player_fate, panel, panel_y, message_log, player.fighter.body_wounds) tcod.console_flush() action = handle_events() move = action.get('move') activate_card = action.get('activate_card') shoot = action.get('shoot') pass_turn = action.get('pass_turn') reload = action.get('reload') if move and (game_state == GameStates.PLAYERS_TURN): dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if (0 <= destination_x < game_map.width) and (0 <= destination_y < game_map.height): if game_map.walkable[destination_y, destination_x]: target = get_blocking_entities_at_location(entities, destination_x, destination_y) if target: message_log.add_message(Message('You kick the ' + target.name + ' in the shins, much to its annoyance!')) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN if pass_turn and (game_state == GameStates.ROUNDS_PLAYERS_ACTION): # pass action would be more accurate, for how i have modified this since creating it # if active_card.size == 0: # posse_discard.add(player_hand.deal(player_hand.size)) # # player_round_movement_budget = player_charactersheet.get_movement_budget() # roll_new_round(player_hand, player_charactersheet, posse_deck, posse_discard, message_log) #elif if active_card.size > 0: posse_discard.add(active_card.deal(active_card.size)) game_state = GameStates.MEDIATE_COMBAT_ROUNDS # The following should be covered by the MEDIATE_COMBAT_ROUNDS # if player_hand.size == 0: # posse_discard.add(player_hand.deal(player_hand.size)) # # player_round_movement_budget = player_charactersheet.get_movement_budget() # roll_new_round(player_hand, player_charactersheet, posse_deck, posse_discard, message_log) if activate_card and (active_card.size == 0) and (game_state == GameStates.ROUNDS_PLAYERS_ACTION): #nominate new active card. (test-only terminiology) if activate_card == -1: move_this_action = 0 if player_hand.size > 0: active_card.add(player_hand.deal(1, 'top')) # player_hand.sort() if reload and ((game_state == GameStates.ROUNDS_PLAYERS_ACTION) or (game_state ==GameStates.PLAYERS_TURN)): if colt_army['shots'] == colt_army['max_shots']: message_log.add_message(Message("Your revolver is fully loaded.", tcod.blue)) elif (game_state ==GameStates.PLAYERS_TURN) or ((game_state == GameStates.ROUNDS_PLAYERS_ACTION) and (active_card.size > 0)): colt_army['shots'] += 1 message_log.add_message(Message("You load a bullet into your revolver.", tcod.green)) if (game_state ==GameStates.ROUNDS_PLAYERS_ACTION): posse_discard.add(active_card.deal(active_card.size)) game_state = GameStates.MEDIATE_COMBAT_ROUNDS # if player_hand.size == 0: # player_round_movement_budget = player_charactersheet.get_movement_budget() # roll_new_round(player_hand, player_charactersheet, posse_deck, posse_discard, message_log) if shoot and (game_state == GameStates.ROUNDS_PLAYERS_ACTION): if (active_card.size > 0) and (colt_army['shots'] == 0): message_log.add_message(Message("You need to reload!", tcod.red)) elif (active_card.size > 0) and (colt_army['shots'] > 0): # Shoot is currently the only "real" action that uses up the active card. modifier = 0 if move_this_action > ((player_charactersheet.pace * 3) // 5): message_log.add_message(Message("You attempt to draw a bead while running...", tcod.orange)) modifier = -4 elif move_this_action > 0: message_log.add_message(Message("You fire while walking...", tcod.yellow)) modifier = -2 wound_modifier = 0 - player.fighter.get_most_severe_wound()[1] modifier += wound_modifier nearest_target = None nearest_distance = 999 for entity in entities: if game_map.fov[entity.y, entity.x]: if entity.fighter: if not entity.name is 'Player': new_distance = entity.distance_to(player) if new_distance < nearest_distance: nearest_distance = new_distance nearest_target = entity tn = 5 range_increments = (nearest_distance / 3) // colt_army['range'] tn += range_increments shootin_roll = skill_roll(player_charactersheet.shootin_pistol['trait'], player_charactersheet.shootin_pistol['aptitude'], tn, modifier) bust = shootin_roll.get('bust') failure = shootin_roll.get('failure') success = shootin_roll.get('success') message_log.add_message(Message("BANG!!", tcod.brass)) colt_army['shots'] -= 1 if colt_army['shots'] == 0: message_log.add_message(Message("That was your last loaded bullet!", tcod.red)) if bust: message_log.add_message(Message("You went bust, and narrowly avoided shooting your own foot!", tcod.red)) else: if not nearest_target: if failure: message_log.add_message(Message("You shoot the broad side of a barn!")) elif success: if success == 1: message_log.add_message(Message("You shoot some bottles for target practice!", tcod.green)) else: message_log.add_message(Message("You put a bullet hole in the forehead of a Wanted poster!", tcod.blue)) else: if failure: message_log.add_message(Message("The bullet whizzes past your target!")) elif success: vital_hit = False hitlocation = unexploding_roll(20) if (hitlocation == 20) or (hitlocation == 10): vital_hit = True if success == 1: message_log.add_message(Message("You manage to hit your target!", tcod.green)) else: if ((20 - hitlocation) <= success) or (0 < (10 - hitlocation) <= success) or (0 < (hitlocation - 10) <= success): vital_hit = True message_log.add_message(Message("You accurately shoot your target!", tcod.blue)) dmg = ranged_weapon_damage_roll(colt_army['damage']['sideness_of_dice'], colt_army['damage']['number_of_dice'], vital_bonus = vital_hit) message_log.add_message(nearest_target.fighter.take_simple_damage(dmg)) if nearest_target.fighter.get_most_severe_wound()[1] >= 5: message_log.add_message(kill_monster(nearest_target)) marshal_discard.add(nearest_target.fighter.action_hand.deal(nearest_target.fighter.action_hand.size)) posse_discard.add(active_card.deal(1)) game_state = GameStates.MEDIATE_COMBAT_ROUNDS # if player_hand.size == 0: # player_round_movement_budget = player_charactersheet.get_movement_budget() # roll_new_round(player_hand, player_charactersheet, posse_deck, posse_discard, message_log) # FIXME: As currently written, this lets you move both before and after an action card. # First, the game lets you move a partial movement, # then, you can use your single card to initate a "new" action and reset the # tracking of movements per action, # which lets you evade potential running penalties in some situations (penalty not implemented yet) if (move and (player_round_movement_budget > 0) and (active_card.size > 0) and (game_state ==GameStates.ROUNDS_PLAYERS_ACTION)): dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if (0 <= destination_x < game_map.width) and (0 <= destination_y < game_map.height): if game_map.walkable[destination_y, destination_x]: target = get_blocking_entities_at_location(entities, destination_x, destination_y) if target: message_log.add_message(Message('You kick the ' + target.name + ' in the shins, much to its annoyance!')) else: player.move(dx, dy) player_round_movement_budget -= 1 # We track our movement taking place in this action card. # We can move up to twice our pace the whole round spread across all our actions. # We incur a running penalty on an action where we have also moved more than our pace; # ie, more than half our full movement budget # (which is calculated to a maximum with running in mind) move_this_action += 1 # pace in yards. yds->ft ft->squares if move_this_action > ((player_charactersheet.pace * 3) // 5): message_log.add_message(Message("Running!!!", tcod.orange)) else: message_log.add_message(Message("Walking...", tcod.yellow)) fov_recompute = True
def main(): # Screen size screen_width = 80 screen_height = 50 # UI settings bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 # Map size map_width = 80 map_height = 43 # Room definitions max_rooms = 30 room_min_size = 6 room_max_size = 10 # FoV configurations fov_algorithm = 0 # use defualt algorithm fov_light_walls = True # light up walls we can see fov_radius = 10 # radius of view fov_recompute = True # flag to trigger FoV computations # Monster spawning settings max_monsters_per_room = 3 # Define colors to be used in FoV 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), } # Font settings libtcod.console_set_custom_font( "arial10x10.png", libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) # Player initialization fighter_component = Fighter( hp=30, defense=2, power=5) # define a fighter component for the player inventory_component = Inventory(26) # Inventory component for the player player = Entity( 0, 0, "@", libtcod.white, "Player", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component, ) # World entity list entities = [player] # Map object game_map = GameMap(map_width, map_height) game_map.make_map( max_rooms, room_min_size, room_max_size, player, entities, max_monsters_per_room=max_monsters_per_room, ) # Fov map object fov_map = initialize_fov(game_map) # Game state game_state = GameStates.PLAYERS_TURN previous_game_state = game_state # For item targeting targeting_item = None # Creating screen libtcod.console_init_root(screen_width, screen_height, "Roguelike using libtcod", False) # Console object console = libtcod.console.Console(screen_width, screen_height) # Panel object panel = libtcod.console.Console(screen_width, panel_height) # Message Log object message_log = MessageLog(message_x, message_width, message_height) # input objects key = libtcod.Key() mouse = libtcod.Mouse() # Game loop while not libtcod.console_is_window_closed(): # Capture input events libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) # Trigger FoV calculation if fov_recompute == True: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # Initial screen config render_all( con=console, panel=panel, entities=entities, player=player, game_map=game_map, fov_map=fov_map, fov_recompute=fov_recompute, message_log=message_log, screen_width=screen_width, screen_height=screen_height, bar_width=bar_width, panel_height=panel_height, panel_y=panel_y, mouse=mouse, colors=colors, gs=game_state, ) fov_recompute = False libtcod.console_flush() # Clear all entities clear_all(console, entities) # Capture action for given input action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) # Map values for each action move = action.get("move") pickup = action.get("pickup") show_inventory = action.get("show_inventory") drop_inventory = action.get("drop_inventory") inv_index = action.get("inventory_index") left_click = mouse_action.get("left_click") right_click = mouse_action.get("right_click") _exit = action.get("exit") fullscreen = action.get("fullscreen") player_turn_results = [] # Handle movement. Check if this is players turn if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move dest_x, dest_y = player.x + dx, player.y + dy if not game_map.is_blocked(dest_x, dest_y): target = get_blocking_entities_at_location( entities, dest_x, dest_y) if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True # Now it is enemies turn 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's nothing to pickup", color=libtcod.yellow)) # Show player inventory if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY # Drop item dialog if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if (inv_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inv_index < len(player.inventory.items)): item = player.inventory.items[inv_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 game_state == GameStates.TARGET_MODE: 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}) # Handle game exit if _exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY): game_state = previous_game_state elif game_state == GameStates.TARGET_MODE: player_turn_results.append({"targeting_cancelled": True}) else: return True # toggle fullscreen if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) # Cycle through players action log 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") cancelled_targeting = player_turn_result.get("targeting_cancelled") if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(player) 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 targeting: previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.TARGET_MODE targeting_item = targeting message_log.add_message(targeting_item.item.targeting_message) if cancelled_targeting: game_state = previous_game_state player_turn_result.get("targeting") # After all input is handle, check if this is enemies 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) # Cycle through players action log 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(player) else: message = kill_monster(dead_entity) message_log.add_message(message) # If player has died, no need to continue with enemies if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN
def main(): screen_width = 80 screen_height = 80 bar_width = 20 panel_height = 10 panel_y = screen_height - panel_height map_width = 80 map_height = 80 - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 fov_recompute = True colors = 0 entities = [] items = [] effects = [] libtcod.console_set_custom_font( 'arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'Project Magic Circle', False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_height) seed = 1000 map = GameMap(map_width, map_height) map.create_map(seed) fov_map = initialize_fov(map) nav_map = initialize_fov(map) nav_map_recompute = False message_log = MessageLog(message_x, message_width, message_height) map.place_entities(entities, 5, 5) player = entities[0] key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.STANDART realtime = False action_buffer = None message = Message('To get help press "?"', libtcod.white) message_log.add_message(message) targeting_item = None danger_level = 1 while not libtcod.console_is_window_closed(): if nav_map_recompute: fov_map = initialize_fov(map) nav_map = initialize_fov(map) fov_recompute = True nav_map_recompute = False if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel, entities, effects, map, fov_map, fov_radius, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors, game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities + effects) for entity in entities: try: entity.give_energy(int(entity.speed / 5)) except: pass for entity in entities: if entity == player: if action_buffer == None: if realtime: libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS or libtcod.EVENT_MOUSE, key, mouse) else: while True: libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS or libtcod.EVENT_MOUSE, key, mouse) render_all(con, panel, entities, effects, map, fov_map, fov_radius, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors, game_state) libtcod.console_flush() clear_all(con, entities) if is_valid_input(key, mouse): break keys_action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse, game_state) action_buffer = (keys_action, mouse_action) if game_state != GameStates.TARGETING: targeting_item = None turn_results = entity.ai.take_action(action_buffer[0], action_buffer[1], map, fov_map, entities, game_state, targeting_item) if turn_results: for turn_result in turn_results: message = turn_result.get('message') dead_entity = turn_result.get('dead') fov_recompute = turn_result.get('fov_recompute') energy = turn_result.get('not_enough_energy') exit = turn_result.get('exit') fullscreen = turn_result.get('fullscreen') effect = turn_result.get('create effect') item_added = turn_result.get('item_added') item_dropped = turn_result.get('item_dropped') show_inventory = turn_result.get('show_inventory') drop_inventory = turn_result.get('drop_inventory') targeting = turn_result.get('targeting') show_help = turn_result.get('show_help') toggle_realtime = turn_result.get('toggle_realtime') go_deeper = turn_result.get('go_deeper') if message: message = Message(message, libtcod.white) message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) message = Message(message, libtcod.red) else: message = kill_monster(dead_entity) message = Message(message, libtcod.white) message_log.add_message(message) if effect: superimpose_effect(effect, effects) if energy == None: action_buffer = None if fov_recompute == None: fov_recompute = False if item_added: entities.remove(item_added) 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 item_dropped: entities.append(item_dropped) if targeting: targeting_item = targeting message = Message( targeting_item.item_aspect.targeting_message, libtcod.yellow) message_log.add_message(message) previous_game_state = GameStates.STANDART game_state = GameStates.TARGETING if exit: if game_state in { GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.TARGETING, GameStates.HELP }: if game_state == GameStates.TARGETING: message = Message('Exited targeting', libtcod.yellow) message_log.add_message(message) game_state = previous_game_state else: return True if fullscreen: libtcod.console_set_fullscreen( not libtcod.console_is_fullscreen()) if show_help: previous_game_state = game_state game_state = GameStates.HELP if toggle_realtime: message = Message('Gamemode changed', libtcod.green) message_log.add_message(message) realtime = not realtime if go_deeper: '''clear_all(con, entities) items = [] effects = [] map = GameMap(map_width, map_height) map.initialize_tiles() map.create_map(seed+danger_level) fov_map = initialize_fov(map) nav_map = initialize_fov(map) map.place_entities(entities, 5+danger_level, 5+danger_level, player = player) player = entities[0] danger_level += 2''' pass else: action_buffer = None else: if entity.ai: turn_results = entity.ai.take_action( nav_map, entities, game_state) if turn_results: for turn_result in turn_results: message = turn_result.get('message') dead_entity = turn_result.get('dead') exit = turn_result.get('exit') fullscreen = turn_result.get('fullscreen') effect = turn_result.get('create effect') if message: message = Message(message, libtcod.white) message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player( dead_entity) message = Message(message, libtcod.red) else: message = kill_monster(dead_entity) message = Message(message, libtcod.white) message_log.add_message(message) if effect: superimpose_effect(effect, effects) if game_state == GameStates.PLAYER_DEAD: break
def main(): # Needed Variables screen_width = 80 screen_height = 50 map_width = 80 map_height = 43 # health bar bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height # rooms room_max_size = 10 room_min_size = 6 max_rooms = 30 # fov fov_algorithm = 0 fov_light_walls = True fov_radius = 10 # monsters max_monsters_per_room = 3 # items max_items_per_room = 10 # message log message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 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=30, defense=2, power=5) inventory_component = Inventory(26) player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component) entities = [player] libtcod.console_set_custom_font( 'arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) # Creates initial console libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False) # Sets default console to draw to. console = libtcod.console_new(screen_width, screen_height) # hp panel console panel = libtcod.console_new(screen_width, panel_height) # Creates game map, draws rooms, populates them with monsters and items 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, max_monsters_per_room, max_items_per_room) 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 # Tracks the old game state, used when opening menus 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(console, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors, game_state) fov_recompute = False libtcod.console_flush() clear_all(console, entities) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) # Player actions move = action.get('move') pickup = action.get('pickup') # Inventory actions show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') # Used when interacting with inventory inventory_index = action.get('inventory_index') # Out of game actions, do not take a turn exit = action.get('exit') fullscreen = action.get('fullscreen') # Mouse clicks 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(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 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 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): 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') if message: message_log.add_message(message) if targeting_cancelled: game_state = previous_game_state message_log.add_message(Message('Targeting Cancelled')) 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 item_dropped: entities.append(item_dropped) 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
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 Rogue(screen): max_y, max_x = screen.getmaxyx() # These actually define the height of the canvas (game map) not the host terminal screen_width = 80 screen_height = 30 cy = int((max_y - screen_height) / 2) cx = int((max_x - screen_width) / 2) #height, width = canvas.getmaxyx() map_height = 23 map_width = 78 # Values to define status bars and message log bar_width = 30 panel_height = 6 panel_y = cy + screen_height - panel_height + 1 # message_x = bar_width + 2 message_x = 1 message_width = screen_width - 2 message_height = cy canvas = curses.newwin(screen_height, screen_width, cy, cx) renderer = CursesRenderer(canvas) #status_win = curses.newwin(80, 3, 24, 0) panel = curses.newwin(panel_height, screen_width, panel_y, cx) message_panel = curses.newwin(cy, screen_width, 0, cx) colors = { 'dark_wall': curses.color_pair(236) } fov = FOV(5) fighter = Fighter(hp=30, defense=2, power=5) player = Entity(5, 5, '@', curses.color_pair(6), 'Player', blocks=True, render_order=RenderOrder.ACTOR, fov=fov, fighter=fighter) entities = [player] game_map = GameMap(map_width, map_height-1) game_map.make_map(15, 8, 10, map_width, map_height, player, entities, 3) calculate_fov = True message_log = MessageLog(message_x, message_width, message_height) game_state = GameStates.PLAYER_TURN while True: key = canvas.getch() curses.flushinp() renderer.render_all(canvas, panel, message_panel, bar_width, message_log, game_map, fov, calculate_fov, entities, player, colors) panel.refresh() message_panel.refresh() canvas.border() canvas.refresh() action = handle_keys(key) move = action.get('move') exit = action.get('exit') player_turn_results = [] if exit: return True if move and game_state == GameStates.PLAYER_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) calculate_fov = True game_state = GameStates.ENEMY_TURN for result in player_turn_results: message = result.get('message') dead_entity = 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.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn(player, fov, game_map, entities) for result in enemy_turn_results: message = result.get('message') dead_entity = 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) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN
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
def main(): name = "pythonRL" screenWidth = 80 screenHeight = 50 bar_width = 20 panel_height = 7 panel_y = screenHeight - panel_height message_x = bar_width + 2 message_width = screenWidth - bar_width - 1 message_height = panel_height - 1 mapWidth = 80 mapHeight = 43 room_min_size = 6 room_max_size = 10 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 colors = { 'dark_wall': tcod.Color(61, 31, 0), 'dark_ground': tcod.Color(41, 21, 0), 'light_wall': tcod.Color(77, 38, 0), 'light_ground': tcod.Color(56, 28, 0), 'nothing': tcod.Color(0, 0, 0) } fighter_component = Fighter(hp=30, defense=2, power=5) player = Entity(0, 0, "@", tcod.white, "Player", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] tcod.console_set_custom_font( 'arial10x10.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_TCOD) tcod.console_init_root(screenWidth, screenHeight, name, False, tcod.RENDERER_SDL2, "F", True) con = tcod.console.Console(screenWidth, screenHeight, "F") panel = tcod.console.Console(screenWidth, panel_height) game_map = GameMap(mapWidth, mapHeight) game_map.make_map(max_rooms, room_min_size, room_max_size, mapWidth, mapHeight, player, entities, max_monsters_per_room) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(message_x, message_width, message_height) key = tcod.Key() mouse = tcod.Mouse() game_state = GameStates.PLAYERS_TURN while not tcod.console_is_window_closed(): tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS | tcod.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, entities, player, game_map, fov_map, fov_recompute, message_log, screenWidth, screenHeight, bar_width, panel_height, panel_y, mouse, colors) fov_recompute = False tcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get("move") exit = action.get("exit") fullscreen = action.get("fullscreen") generate = action.get("gen") player_turn_results = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move dest_x = player.x + dx dest_y = player.y + dy if not game_map.is_blocked(dest_x, dest_y): target = get_blocking_entities_at_location( entities, dest_x, dest_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 if exit: return True 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") 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.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 if generate: game_map.clear() game_map.make_map(max_rooms, room_min_size, room_max_size, mapWidth, mapHeight, player) fov_map = initialize_fov(game_map) fov_recompute = True
def main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 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=30, defense=2, power=5) player = Entity(0, 0, "@", libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] libtcod.console_set_custom_font( 'arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'Paradiso', False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_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, max_monsters_per_room) 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 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, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get('move') 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) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN if exit: 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') 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.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 main(): screen_width: int = 80 screen_height: int = 35 bar_width: int = 20 panel_height: int = 7 panel_y: int = screen_height - panel_height ui_layer = 10 message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 28 max_monsters_per_room = 3 con = Console(x=0, y=0, width=screen_width, height=screen_height) panel = Console(0, panel_y, screen_width, panel_height, layer=ui_layer) title = "Rogue Alchemist" font = "mplus-1p-regular.ttf" fighter_component = Fighter(hp=30, defense=2, power=5) player = Entity(x=0, y=0, char='@', color=Color.BLACK, name='Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] game_map = GameMap(map_width, map_height) game_map.generate_dungeon(player, entities, max_monsters_per_room) # game_map.generate_dungeon(map_width, map_height, cave=True) start_room = game_map.dungeon.rooms[0] fov_algorithm = 0 fov_light_walls = True fov_radius = 10 colors = { "dark_wall": Color.DARK_SLATE_GRAY, "dark_ground": Color.DIM_GRAY, "light_wall": Color.LIGHT_SLATE_GRAY, "light_ground": Color.LIGHT_GRAY, "dark_door": Color.SADDLE_BROWN, "light_door": Color.BROWN, "test": Color.GOLD, } fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(message_x, message_width, message_height) key = None blt.open() # initializes BearLib Terminal instance with default parameters terminal_options = f"window: title={title}, size={str(screen_width)}x{str(screen_height)}; font:{font}, size=12" blt.set(terminal_options) game_state = GameStates.PLAYERS_TURN while True: if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, colors) blt.refresh() fov_recompute = False # remove player's previous position clear_all(entities) if blt.has_input(): # if no inputs, don't wait key = blt.read() action = handle_keys(key) key = None movement = action.get("move") exit_game = action.get("exit") player_turn_results = [] if movement and game_state == GameStates.PLAYERS_TURN: dx, dy = movement 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) else: player.move(*movement) fov_recompute = True game_state = GameStates.ENEMY_TURN if exit_game: blt.close() return True for player_turn_result in player_turn_results: message = player_turn_result.get("message") dead_entity = player_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.ENEMY_TURN: for entity in entities: visible = libtcod.map_is_in_fov(fov_map, entity.x, entity.y) 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 game_state = GameStates.PLAYERS_TURN
def main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 'BASIC' fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 colors = { 'dark_wall': (0, 0, 100), 'dark_ground': (50, 50, 150), 'light_wall': (130, 110, 50), 'light_ground': (200, 180, 50), 'desaturated_green': (63, 127, 63), 'darker_green': (0, 127, 0), 'dark_red': (191, 0, 0), 'white': (255, 255, 255), 'black': (0, 0, 0), 'red': (255, 0, 0), 'orange': (255, 127, 0), 'light_red': (255, 114, 114), 'darker_red': (127, 0, 0) } fighter_component = Fighter(hp=30, defense=2, power=5) player = Entity(0, 0, '@', (255, 255, 255), 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] tdl.set_font('arial12x12.png', greyscale=True, altLayout=True) root_console = tdl.init(screen_width, screen_height, title='Roguelike Tutorial Revised') con = tdl.Console(screen_width, screen_height) panel = tdl.Console(screen_width, panel_height) game_map = GameMap(map_width, map_height) make_map(game_map, max_rooms, room_min_size, room_max_size, map_width, map_height, player, entities, max_monsters_per_room, colors) fov_recompute = True message_log = MessageLog(message_x, message_width, message_height) mouse_coordinates = (0, 0) game_state = GameStates.PLAYERS_TURN while not tdl.event.is_window_closed(): if fov_recompute: game_map.compute_fov(player.x, player.y, fov=fov_algorithm, radius=fov_radius, light_walls=fov_light_walls) render_all(con, panel, entities, player, game_map, fov_recompute, root_console, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse_coordinates, colors) tdl.flush() clear_all(con, entities) fov_recompute = False for event in tdl.event.get(): if event.type == 'KEYDOWN': user_input = event break elif event.type == 'MOUSEMOTION': mouse_coordinates = event.cell else: user_input = None if not user_input: continue action = handle_keys(user_input) move = action.get('move') exit_action = action.get('exit') switch_fullscreen = action.get('switch_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 game_map.walkable[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 if exit_action: return True if switch_fullscreen: tdl.set_fullscreen(not tdl.get_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_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, colors) else: message = kill_monster(dead_entity, colors) message_log.add_message(message) if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( player, 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, colors) else: message = kill_monster(dead_entity, colors) 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 if user_input.key == 'ESCAPE': return True
def main(): screen_width = 80 # /4 = 20 screen_height = 50 # /4 ~= 12 # Map panel parameters map_width = 45 map_height = 40 fov_algorithm = libtcod.FOV_SHADOW fov_light_walls = True fov_radius = 9 # Health/Stats panel parameters bar_x = 4 bar_width = 24 panel_height = screen_height - map_height - 1 panel_y = screen_height - panel_height # Message panel parameters message_x = bar_width + bar_x + 2 message_width = screen_width - bar_width - bar_x - 2 message_height = panel_height - 2 message_log = MessageLog(message_x, message_width, message_height) # set up player entity and active entity list # TODO: Allow player to assign stats when starting to play fighter_component = Fighter( hp=30, defense=5, spdefense=5, attack=5, spattack=5, speed=5) player = Entity(int(screen_width / 2), int(screen_height / 2), '@', libtcod.white, 'Player', render_order=RenderOrder.ACTOR, blocks=True, fighter=fighter_component) entities = [] # set up console libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False, libtcod.RENDERER_SDL2, vsync=True) # set up all panels con = libtcod.console.Console(screen_width, screen_height) panel = libtcod.console.Console(screen_width, panel_height) # load map, entities and player game_world = GameWorld(map_width, map_height) game_world.loadfirstfloor(player, entities) # player field of vision variables fov_recompute = True fov_map = initialize_fov(game_world.currmap) # input variables key = libtcod.Key() mouse = libtcod.Mouse() # state variables game_state = GameState.PLAYERS_TURN while not libtcod.console_is_window_closed(): # poll input libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) # compute field of vision if fov_recompute: if game_world.currmap.islair: current_fov_radius = 20 else: current_fov_radius = fov_radius recompute_fov(fov_map, player.x, player.y, current_fov_radius, fov_light_walls, fov_algorithm) # draw screen render_all(con, panel, entities, player, game_world.currmap, message_log, fov_map, fov_recompute, screen_width, screen_height, bar_x, bar_width, panel_height, panel_y, mouse) fov_recompute = False libtcod.console_flush() # erase previous player position clear_all(con, entities) # parse input action = handle_keys(key) move = action.get('move') exit = action.get('exit') fullscreen = action.get('fullscreen') confirm = action.get('confirm') cancel = action.get('cancel') wait = action.get('wait') player_turn_results = [] # update if move and game_state == GameState.PLAYERS_TURN: dx, dy = move # saves dx and dy outside of the while loop too dest_x = player.x + dx dest_y = player.y + dy if not game_world.currmap.tileblocked(dest_x, dest_y): target = get_blocking_entities_at_location( entities, dest_x, dest_y) if target: if target.door: game_world.movetonextroom(player, entities, target.door.direction) fov_map = initialize_fov(game_world.currmap) fov_recompute = True con.clear(fg=(0, 0, 0)) elif target.stairs: game_world.movetonextfloor(player, entities) fov_map = initialize_fov(game_world.currmap) fov_recompute = True con.clear(fg=(0, 0, 0)) elif target.fighter: attack_results = player.fighter.attacktarget( target, player.fighter.attacks[0]) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True if (game_state == GameState.PLAYERS_TURN): game_state = GameState.ENEMY_TURN if wait and game_state == GameState.PLAYERS_TURN: game_state = GameState.ENEMY_TURN if exit: 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') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: if (dead_entity.char == '@'): # dead boss, spawn stairs, update world game_world.bosses_cleared[game_world.current_floor] = True entities.extend( game_world.currmap.spawnstairsdown()) message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameState.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( game_world.currmap, 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: if (dead_entity.char == '@'): # dead boss, spawn stairs, update world game_world.bosses_cleared[game_world.current_floor] = True entities.extend( game_world.currmap.spawnstairsdown()) message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameState.PLAYER_DEAD: break if game_state == GameState.PLAYER_DEAD: break else: game_state = GameState.PLAYERS_TURN
class Engine(): def __init__(self): # Set up the game window #libtcod.console_set_custom_font('spritesheet.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_set_custom_font( 'Winterwing_Curses.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_ASCII_INROW) libtcod.console_init_root(game_constants.screen_width, game_constants.screen_height, 'Ascetic of the Cabal', True, libtcod.RENDERER_SDL2, vsync=True) # Establish the primary console as well as the detail panel self.con = libtcod.console.Console(game_constants.screen_width, game_constants.screen_height) self.panel = libtcod.console.Console(game_constants.screen_width, game_constants.panel_height) # Create references for the player input self.key = libtcod.Key() self.mouse = libtcod.Mouse() self.initialize_game() # Initializes a lot of run-specific items. Kept outside of init because it has to be re-run on a restart def initialize_game(self): # Create and initialize the Message Log self.message_log = MessageLog() #Initialize the player self.player = self.initialize_player() # Create a game map and fill it with enemies self.build_map() self.player_target = None # Establish the Game State self.game_map.compute_dijkstra_map([self.player], 'player', True) self.game_map.compute_dijkstra_map( self.entities.get_sublist(lambda x: x.name != "Ascetic"), "enemies") self.game_state = GameStates.PLAYERS_TURN self.previous_game_state = GameStates.PLAYERS_TURN self.game_running = True # Creates the player object, with all associated defaults # This can and probably should be moved to another file def initialize_player(self): player_components = { "Fighter": Fighter(hp=300, defense=2, power=5, factions=[Factions.PLAYER]), "Inventory": Inventory(26), "Devotee": Devotee(100), "StatusContainer": StatusContainer() } player = Entity(int(game_constants.screen_width / 2), int(game_constants.screen_height / 2), '@', libtcod.white, "Ascetic", True, RenderOrder.ACTOR, message_log=self.message_log, state=AIStates.INANIMATE, components=player_components) player.get_component("Inventory").equip_item( generate_starting_pistol(self.message_log)) return player # Generates a game map and initializes the FOV map of it def build_map(self, level=1): self.game_map = GameMap(self.message_log, level) self.entities = Entities(self.game_map) self.game_map.make_map(self.player, self.entities) self.entities.insert_entity(self.player) self.fov_recompute = True self.fov_map = initialize_fov(self.game_map) # Literally 0 recollection what this does def grade_map_down(self): self.game_map.grade_down() def cull_dead(self): # Finds every entity in the game world with 0 or less health and kills them # Returns true if it kills the player, otherwise false player_killed = False dead_entities = self.entities.get_sublist( lambda x: x.has_component("Fighter") and not x.get_component( "Fighter").isAlive() and x.char != '%') if dead_entities: for dead_entity in dead_entities: drop = dead_entity.get_component("Fighter").die() if drop: self.entities.insert_entity(drop) if dead_entity == self.player: player_killed = True return player_killed def send_invalid_action_message(self): self.message_log.add_message(Message("Can't do that here"), libtcod.red) # Initializes the game's start menu, and captures any player input on that menu and passes it to the appropriate handler def start_screen(self): show_main_menu = True while show_main_menu: libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, self.key, self.mouse) main_menu(self.con, game_constants.main_menu_background_image) libtcod.console_flush() action = handle_keys(self.key, GameStates.MAIN_MENU) game_type = action.get('game_start') exit_game = action.get('action') == 'exit' if exit_game: return False elif game_type == 'from_scratch': return True elif game_type == 'from_save': self.player, self.entities, self.game_map, self.message_log, self.game_state = load_game( ) self.fov_map = initialize_fov(self.game_map) self.entities.set_log_all(self.message_log) return True def main(self): # Game Loop while self.game_running: # Check input streams for an event libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, self.key, self.mouse) # If we need to recompute fov, do so if self.fov_recompute: recompute_fov(self.fov_map, self.player.x, self.player.y) # Render the game world according to current FOV, mark FOV recompute as complete, and flush to console render_all(self.con, self.panel, self.entities, self.player, self.game_map, self.fov_map, self.fov_recompute, self.message_log, self.mouse, self.game_state, self.player_target) self.fov_recompute = False libtcod.console_flush() # Interpret the input into a game action input = handle_keys(self.key, self.game_state) action = input.get('action') inventory_item = input.get( 'inventory_item') if 'inventory_item' in input else None dialogue_option = input.get( 'dialogue_option') if 'dialogue_option' in input else None shop_option = input.get( 'shop_option') if 'shop_option' in input else None unequip_item = input.get('slot') if 'slot' in input else None # If players turned and it's their turn to move if action == 'move' and self.game_state == GameStates.PLAYERS_TURN: # Calculate where they should move dx, dy = input.get('move') destination_x = self.player.x + dx destination_y = self.player.y + dy # TODO: This is where you hid the noclip check. Fix this for release #if not self.game_map.is_blocked(destination_x, destination_y): if True: # If they're not about to walk into a wall, check for enemies at the destination potential_collision_list = self.entities.get_sublist( lambda ent: ent.x == destination_x and ent.y == destination_y and ent.blocks) target = potential_collision_list[ 0] if potential_collision_list else None if target and target.state == AIStates.HOSTILE: # If there are enemies, attack them self.player.get_component("Fighter").attack(target) self.game_state = GameStates.ENEMY_TURN elif target and target.state == AIStates.FRIENDLY: self.previous_game_state = self.game_state self.player_target = target self.game_state = GameStates.DIALOGUE else: # If there are not enemies, move and mark FOV for recomputation self.player.move(dx, dy, self.game_map) self.fov_recompute = True self.game_map.compute_dijkstra_map([self.player], 'player', True) self.game_state = GameStates.ENEMY_TURN # If the player grabs something, check if there is an object at their feet, and either have them pick it up (if it's an Item) or add it to their wallet (if it's money) elif action == 'grab' and self.game_state == GameStates.PLAYERS_TURN: for item in self.entities.get_sublist( lambda entity: (entity.has_component("Item") or entity. has_component("Money")) and entity.x == self.player.x and entity.y == self.player.y): if item.has_component("Money"): self.player.get_component("Fighter").pick_up_money( item) else: self.player.get_component("Inventory").add_item(item) self.entities.remove_entity(item) self.game_state = GameStates.ENEMY_TURN # Open up the inventory menu elif action == 'inventory' and inventory_item is None: self.previous_game_state = self.game_state self.game_state = GameStates.INVENTORY_OPEN # Open up the equipped menu elif action == 'equipped': self.previous_game_state = self.game_state self.game_state = GameStates.EQUIPPED_OPEN elif action == 'unequip' and self.game_state == GameStates.EQUIPPED_OPEN: self.player.get_component("Inventory").unequip_slot( unequip_item) # if the player has selected an inventory item to use, get the item object, and equip it if it's vgear, or use it if it's a consumable (like a potion) elif inventory_item is not None and self.previous_game_state != GameStates.PLAYER_DEAD and inventory_item < len( self.player.get_component("Inventory").items): item_entity = self.player.get_component( "Inventory").items[inventory_item] if ItemType(item_entity.get_component( "Item").item_type) != ItemType.NONE: self.player.get_component("Inventory").equip_item( item_entity) else: print("In the else") if item_entity.get_component("Item").use(self.player): self.player.get_component("Inventory").remove_item( item_entity) # if the player is in dialogue, provide the dialogue option to the target's Character object elif dialogue_option is not None: dialogue_response = self.player_target.get_component( "Character").talk(dialogue_option) if dialogue_response.shop: self.game_state = GameStates.SHOPPING # if the player attempts to go down some stairs, make sure they're on stairs, then build a new map and clear the console elif action == 'go_down' and self.game_state == GameStates.PLAYERS_TURN: stairs_candidates = self.entities.get_sublist( lambda entity: entity.x == self.player.x and entity.y == self.player.y and entity.has_component("Stairs")) if stairs_candidates: self.build_map( stairs_candidates[0].get_component("Stairs").floor) libtcod.console_clear(self.con) # Save the game elif action == 'save': save_game(self.player, self.entities, self.game_map, self.message_log, self.game_state) # if the player draws their gun, change to a player shoot state and await gunfire elif self.game_state == GameStates.PLAYERS_TURN and action == 'gun': if (self.player.get_component("Inventory").slot_filled( "RANGED")): self.previous_game_state = self.game_state self.game_state = GameStates.PLAYER_SHOOT self.message_log.add_message( Message( "Taking aim. Click on your target, or e to holster" )) else: self.message_log.add_message( Message("No ranged weapon equipped!")) # if the player already has their gun drawn and presses the draw button, holster it instead elif self.game_state == GameStates.PLAYER_SHOOT and action == 'holster': self.game_state = self.previous_game_state self.message_log.add_message(Message("Holstered your weapon")) # if the player has their gun drawn and clicks on a target, check if there is line of sight # and if so, shoot the target. This sets the AI to hostile if it isn't already (this should be handled by Fighter) elif self.game_state == GameStates.PLAYER_SHOOT and self.mouse.lbutton_pressed: target = get_shoot_target(self.mouse, self.entities, self.fov_map) if (target): line_of_sight = draw_line((self.player.x, self.player.y), (target.x, target.y)) if not [ space for space in line_of_sight if self.game_map.is_blocked(space[0], space[1]) ]: self.player.get_component("Fighter").ranged_attack( target) target.state = AIStates.HOSTILE self.game_state = GameStates.ENEMY_TURN else: self.message_log.add_message( Message("You don't have a clear line of sight!")) # if the player right clicks something, get open up the inspect menu for that target elif self.mouse.rbutton_pressed and self.game_state != GameStates.INSPECT_OPEN: target = get_shoot_target(self.mouse, self.entities, self.fov_map, False) if (target): self.player_target = target self.previous_game_state = self.game_state self.game_state = GameStates.INSPECT_OPEN # If the player is buying something, they make the purchase elif action == 'buy' and shop_option is not None: target.get_component("Shop").purchase(shop_option, self.player) elif action == 'status': self.previous_game_state = self.game_state self.game_state = GameStates.STATUS # Exit the game if action == 'exit' and (self.game_state in [ GameStates.INVENTORY_OPEN, GameStates.DIALOGUE, GameStates.EQUIPPED_OPEN, GameStates.SHOPPING, GameStates.INSPECT_OPEN, GameStates.STATUS ]): self.game_state = self.previous_game_state elif action == 'exit': return True # Set the game to fullscreen if action == 'fullscreen': libtcod.console_set_fullscreen( not libtcod.console_is_fullscreen()) # cull_dead returns true if the player is dead, so this conditional calls it to cull the dead, and then # checks if the game is over if self.cull_dead(): self.game_state = GameStates.PLAYER_DEAD # when it's the AI's turn, find every entity that has AI and move it (if it's hostile) if self.game_state == GameStates.ENEMY_TURN: for entity in self.entities.get_entity_set(): if entity.has_component( "AI") and entity.state == AIStates.HOSTILE: entity.get_component("AI").take_turn( self.player, self.fov_map, self.game_map, self.entities) if self.cull_dead(): self.game_state = GameStates.PLAYER_DEAD if entity.has_component("StatusContainer"): entity.get_component("StatusContainer").tick_clocks() for status in entity.get_component( "StatusContainer").get_statuses(): status_mapping[status](entity, self.entities, self.game_map) if self.game_state != GameStates.PLAYER_DEAD: self.player.get_component("StatusContainer").tick_clocks() for status in self.player.get_component( "StatusContainer").get_statuses(): status_mapping[status](self.player, self.entities, self.game_map) self.game_map.compute_dijkstra_map( self.entities.get_sublist( lambda x: x.name != "Ascetic"), "enemies") self.game_state = GameStates.PLAYERS_TURN # TODO: need a check somewhere around here to tick condition clocks, and then to apply conditions if action == 'restart': libtcod.console_clear(self.con) self.initialize_game()
def main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 fov_algorithm = 'BASIC' fov_light_walls = True fov_radius = 10 colors = { 'dark_wall': (0, 0, 100), 'dark_ground': (50, 50, 150), 'light_wall': (130, 110, 50), 'light_ground': (200, 180, 50), 'white': (255, 255, 255), 'black': (0, 0, 0), 'light red': (255, 100, 100), 'red': (255, 0, 0), 'yellow': (255, 255, 0), 'orange': (255, 127, 0), 'green': ( 0, 255, 0, ), 'light_red': (255, 114, 114), 'darker_red': (127, 0, 0), 'highlight': (199, 234, 70) } mech_component = Mech(hp=30, peak_momentum=6) weapon_component = Weapon(name="Laser", damage=5, min_targets=0, max_targets=5, color=colors.get('green'), range=10) player = Entity(int(screen_width / 2), int(screen_height / 2), '@', colors.get('white'), "player", mech=mech_component, weapon=weapon_component) npc = Entity(int(screen_width / 2 - 5), int(screen_height / 2), '@', colors.get('yellow'), "NPC") cursor_component = Cursor() cursor = Entity( -1, -1, ' ', colors.get('red'), "cursor", cursor=cursor_component ) # The ' ' isn't actually "nothing". To have nothing, I would have to mess with a render order. entities = [npc, player, cursor] tdl.set_font('arial10x10.png', greyscale=True, altLayout=True) root_console = tdl.init(screen_width, screen_height, title='MVP v0.0') con = tdl.Console(screen_width, screen_height) panel = tdl.Console(screen_width, panel_height) game_map = GameMap(map_width, map_height) make_map(game_map) message_log = MessageLog(message_x, message_width, message_height) mouse_coordinates = (0, 0) game_state = GameStates.PLAYER_TURN previous_game_state = game_state turn_state = TurnStates.UPKEEP_PHASE fov_recompute = True while not tdl.event.is_window_closed(): if fov_recompute: game_map.compute_fov(player.x, player.y, fov=fov_algorithm, radius=fov_radius, light_walls=fov_light_walls) render_all(con, panel, entities, game_map, fov_recompute, root_console, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse_coordinates, colors) tdl.flush() clear_all(con, entities) for event in tdl.event.get(): if event.type == 'KEYDOWN': user_input = event break elif event.type == 'MOUSEMOTION': mouse_coordinates = event.cell else: user_input = None fov_recompute = False action = handle_keys(user_input, game_state) impulse = None # This is to avoid logic problems. change_game_state = None # This is to avoid logic problems. move = action.get('move') # Attempt to move. impulse = action.get('impulse') # Adjust mech impulse. next_turn_phase = action.get( 'next turn phase') # Move to the next phase. change_game_state = action.get( 'change game state') # Go to different game_state select = action.get( 'select') # A target has been selected via keyboard. exit = action.get('exit') # Exit whatever screen is open. fullscreen = action.get('fullscreen') # Set game to full screen. if exit: if game_state == GameStates.TARGETING: # Turn off cursor cursor.char = ' ' cursor.x = -1 cursor.y = -1 fov_recompute = True game_state = previous_game_state else: return True if fullscreen: tdl.set_fullscreen(not tdl.get_fullscreen()) if game_state == GameStates.PLAYER_TURN: # See game_states.py for the turn structure. # Turns order is reversed so ensure that the loop runs once for each if turn_state == TurnStates.POST_ATTACK_PHASE: # Reset map flags and remove targets. reset_flags(game_map) for x, y in player.weapon.targets: erase_cell(con, x, y) turn_state = TurnStates.UPKEEP_PHASE game_state = GameStates.ENEMY_TURN if turn_state == TurnStates.ATTACK_PHASE: if change_game_state == GameStates.TARGETING: # Turn on cursor. cursor.char = 'X' # If there were no previous targets, start on the player. if len(player.weapon.targets) == 0: cursor.x = player.x cursor.y = player.y else: cursor.x, cursor.y = player.weapon.targets[-1] fov_recompute = True previous_game_state = game_state game_state = GameStates.TARGETING if next_turn_phase: turn_state = TurnStates.POST_ATTACK_PHASE if turn_state == TurnStates.PRE_ATTACK_PHASE: message_log.add_message( Message('Begin ATTACK PHASE.', colors.get('white'))) message_log.add_message( Message( 'Press f to target. Press ESC to stop targeting. Enter to change phase.', colors.get('orange'))) fov_recompute = True turn_state = TurnStates.ATTACK_PHASE if turn_state == TurnStates.POST_MOVEMENT_PHASE: reset_flags(game_map) player.reset( ) # Reset the mech for the next turn. ### Move this to the post-attack phase fov_recompute = True turn_state = TurnStates.PRE_ATTACK_PHASE if turn_state == TurnStates.MOVEMENT_PHASE: if move: dx, dy = move if game_map.walkable[player.x + dx, player.y + dy]: player.move(dx, dy) fov_recompute = True if next_turn_phase and player.mech.has_spent_minimum_momentum( ): turn_state = TurnStates.POST_MOVEMENT_PHASE elif next_turn_phase and not player.mech.has_spent_minimum_momentum( ): message_log.add_message( Message('Must spend more momentum.', colors.get('red'))) if turn_state == TurnStates.PRE_MOVEMENT_PHASE: if impulse is not None: player.mech.impulse = impulse turn_state = TurnStates.MOVEMENT_PHASE message_log.add_message( Message('Impulse set to {0}.'.format(impulse), colors.get('orange'))) fov_recompute = True highlight_legal_moves(player, game_map) if turn_state == TurnStates.UPKEEP_PHASE and game_state == GameStates.PLAYER_TURN: # This is added to avoid starting the Upkeep Phase when the turn just ended. message_log.add_message( Message('Begin PLAYER TURN.', colors.get('white'))) message_log.add_message( Message('Begin MOVEMENT PHASE.', colors.get('white'))) message_log.add_message( Message('Choose impulse. PAGEUP, PAGEDOWN or HOME.', colors.get('orange'))) turn_state = TurnStates.PRE_MOVEMENT_PHASE fov_recompute = True if game_state == GameStates.ENEMY_TURN: message_log.add_message( Message('Begin ENEMY TURN.', colors.get('white'))) fov_recompute = True game_state = GameStates.PLAYER_TURN if game_state == GameStates.TARGETING: if move: dx, dy = move # Ensure the first target is in firing range. if len(player.weapon.targets) == 0: if player.distance(cursor.x + dx, cursor.y + dy) <= player.weapon.range: cursor.fly(dx, dy) fov_recompute = True else: message_log.add_message( Message('Out of range.', colors.get('red'))) # Ensure that the next targets are adjacent to the previous target elif len(player.weapon.targets) > 0: tar_x, tar_y = player.weapon.targets[ -1] # Get the most recent target added. if abs(tar_x - (cursor.x + dx)) + abs(tar_y - (cursor.y + dy)) <= 1: cursor.fly(dx, dy) fov_recompute = True else: message_log.add_message( Message('Invalid target.', colors.get('red'))) if select: if len(player.weapon.targets) < player.weapon.max_targets: if set_targeted( game_map, cursor.x, cursor.y ): # At the moment, this always returns True. In the future, this may change. fov_recompute = True player.weapon.targets.append((cursor.x, cursor.y)) else: message_log.add_message( Message('Targeting failed.', colors.get('red')))
def main(): # Limit the FPS tcod.sys_set_fps(max_fps) # Set the font to be used tcod.console_set_custom_font('terminal8x8_gs_as.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_ASCII_INCOL) # Create the screen tcod.console_init_root(screen_width, screen_height, 'Rogue', False) con = tcod.console_new(screen_width, screen_height) panel = tcod.console_new(screen_width, screen_height) # Generate the tile map game_map = GameMap(map_width, map_height) # Initialize the player and the entities list player = Player(player_stats[0], player_stats[1], player_stats[2], player_stats[3], player_stats[4], game_map.tiles, screen_width // 2, screen_height // 2, '@', colors.get("player"), "player", True) entities.append(player) # Represents the current dungeon floor floor_number = 1 # Generate the rest of the game map generate_all(game_map, map_width, map_height, max_rooms, min_room_size, max_room_size, min_npcs, max_npcs, colors, entities, floor_number) # Initialize user input key = tcod.Key() mouse = tcod.Mouse() # Initialize the game state game_state = GameStates.PLAYER_TURN # Initialize the message log message_log = MessageLog(message_x, message_width, message_height) # Determine whether or not the FOV needs to be recalculated fov_recalculate = True fov_map = initialize_fov(game_map) # Game loop while not tcod.console_is_window_closed(): # Update key and mouse with the user inputs tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS, key, mouse) if fov_recalculate: calculate_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # Render everything render_all(con, panel, message_log, entities, game_map, fov_map, fov_recalculate, screen_width, screen_height, bar_width, panel_height, panel_y, debug) fov_recalculate = False # Apply the updates on screen tcod.console_flush() # Clear the entities in preparation for updates clear_all(con, entities) # Handle key presses action = handle_keys(key) move = action.get("move") use = action.get("use") exit = action.get("exit") fullscreen = action.get("fullscreen") reset = action.get("reset") regenerate = action.get("regenerate") player_turn_results = [] if move and game_state == GameStates.PLAYER_TURN: dx, dy = move if not game_map.is_blocked(player.x + dx, player.y + dy): player.move(game_map.tiles, dx, dy) fov_recalculate = True target = enty.get_entity_at_location(player.x + dx, player.y + dy, entities) if target != -1: player_turn_results.extend(player.attack(target)) game_state = GameStates.ENEMY_TURN if use and game_state == GameStates.PLAYER_TURN: if player.inventory[use - 1] is not None: player_turn_results.extend(player.inventory[use - 1].use(entities)) if exit: return True if fullscreen: tcod.console_set_fullscreen(not tcod.console_is_fullscreen()) if reset: # Reset the game player, game_state, regen_values = reset_map(game_map, map_width, map_height, max_rooms, min_room_size, max_room_size, min_npcs, max_npcs, colors, entities, message_x, message_width, message_height) game_map, fov_recalculate, fov_map, message_log = regen_values if regenerate and debug: # Properly generate a new game map game_map, fov_recalculate, fov_map, message_log = regenerate_map(player, map_width, map_height, max_rooms, min_room_size, max_room_size, min_npcs, max_npcs, colors, entities, floor_number, message_x, message_width, message_height) # Handle the player turn results for result in player_turn_results: dead_attacker = result.get("dead") if dead_attacker: message_log.add_message(Message("You have killed the {0}".format(dead_attacker.name))) dead_attacker.kill(game_map.tiles) message_log.add_message(Message("{0} XP granted".format(dead_attacker.xp))) level_up = player.add_xp(dead_attacker.xp).get("level up") if level_up: message_log.add_message(Message("You reached level {0}".format(level_up[0]))) message_log.add_message(Message(level_up[1])) entities.remove(dead_attacker) damaged_entity = result.get("damaged") if damaged_entity: message_log.add_message( Message("You attacked the {0} for {1} damage".format(damaged_entity[0], damaged_entity[1]))) pickup_used = result.get("pickup_used") if pickup_used: message_log.add_message(Message(pickup_used[0])) if pickup_used[1].deletes: pickup_used[1].delete(player) next_floor = result.get("next_floor") if next_floor: message_log.add_message(Message("You have advanced to floor {0}".format(next_floor + 1))) floor_number += 1 game_map, fov_recalculate, fov_map, null = regenerate_map(player, map_width, map_height, max_rooms, min_room_size, max_room_size, min_npcs, max_npcs, colors, entities, floor_number, message_x, message_width, message_height, preserve_messages=True) # Enemy turn (really every entity that is not the player) if game_state == GameStates.ENEMY_TURN: enemy_turn_results = enty.entity_turn(entities, fov_map, game_map) game_state = GameStates.PLAYER_TURN # Handle the entity turn results for result in enemy_turn_results: dead = result.get("dead") if dead: game_state = player.kill(game_map.tiles) message_log.add_message(Message("You have died")) message_log.add_message(Message("GAME OVER")) break damaged_entity = result.get("damaged") if damaged_entity: message_log.add_message( Message("The {0} attacked you for {1} damage".format(damaged_entity[0], damaged_entity[1]))) pickup_failed = result.get("pickup_failed") if pickup_failed: message_log.add_message( Message("You cannot pick up the {0} since your inventory is full".format(pickup_failed[0]))) pickup_success = result.get("pickup_success") if pickup_success: message_log.add_message(Message("You picked up the {0}".format(pickup_success[0]))) pickup_success[1].kill(game_map.tiles) entities.remove(pickup_success[1]) upgrade_used = result.get("upgrade_used") if upgrade_used: message_log.add_message(Message(upgrade_used[0])) message_log.add_message(Message(upgrade_used[1])) upgrade_used[2].kill(game_map.tiles) entities.remove(upgrade_used[2])
def main(): # map vars map_width = 40 map_height = 60 room_min_size = 6 room_max_size = 10 max_rooms = 50 max_monsters_per_room = 3 max_items_per_room = 2 # screen vars viewport_width = 80 viewport_height = 60 rerender_viewport = True map_x = viewport_width map_y = 0 screen_width = viewport_width + map_width # stats panel vars bar_width = 20 panel_width = screen_width panel_height = 8 panel_x = 0 panel_y = viewport_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 screen_height = viewport_height + panel_height # fov vars fov_algorithm = 12 fov_light_walls = True fov_radius = 10 fov_recompute = True # game vars game_state = GameStates.PLAYER_TURN previous_game_state = game_state colours = { 'dark_wall': tcod.Color(0, 0, 100), 'dark_ground': tcod.Color(50, 50, 150), 'light_wall': tcod.Color(130, 110, 50), 'light_ground': tcod.Color(200, 180, 50) } # init player fighter_component = Fighter(30, 2, 5) inventory_component = Inventory(26) player = Entity(0, 0, '@', tcod.white, 'Player', render_order=RenderOrder.ACTOR, blocks=True, fighter=fighter_component, inventory=inventory_component) player_rot = 0.0 entities = [player] # load assets wall_texture = Image.open('assets/wall_vines0.png') orc_texture = Image.open('assets/orc.png') troll_texture = Image.open('assets/troll.png') potion_texture = Image.open('assets/ruby.png') # init game map 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, max_monsters_per_room, max_items_per_room, orc_texture, troll_texture, potion_texture) game_map.recompute_fov(player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # init message log message_log = MessageLog(message_x, message_width, message_height) tcod.console_set_custom_font( 'arial10x10.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_TCOD) with tcod.console_init_root(screen_width, screen_height, 'firstpersonroguelike', vsync=True) as root_console: viewport_console = tcod.console.Console(viewport_width, viewport_height) map_console = tcod.console.Console(map_width, map_height) panel_console = tcod.console.Console(panel_width, panel_height) mouse = (0, 0) time_start = perf_counter() # game loop begin while True: for event in tcod.event.wait(): tcod.console_flush() player_turn_results = [] # handle inputs if event.type == "QUIT": raise SystemExit() elif event.type == "MOUSEMOTION": # record the new mouse position, render_all will take care of the rest mouse = event.tile # player keyboard interaction elif event.type == "KEYDOWN": action = handle_keys(event, game_state) if len(action) == 0: continue elif action.get('exit'): if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY): game_state = previous_game_state else: raise SystemExit() elif action.get( 'pickup') and game_state is GameStates.PLAYER_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( 'You inspect the empty ground below you. Looks pretty dirty', tcod.yellow)) game_state = GameStates.ENEMIES_TURN elif action.get('show_inventory' ) and game_state is GameStates.PLAYER_TURN: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY elif action.get('drop_inventory' ) and game_state is GameStates.PLAYER_TURN: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY elif action.get( 'inventory_index' ) is not None and previous_game_state is not GameStates.PLAYER_DEAD: inventory_index = action.get('inventory_index') if inventory_index < len(player.inventory.items): item = player.inventory.items[inventory_index] if game_state is GameStates.SHOW_INVENTORY: message_log.add_message( Message('Used ' + item.name)) player_turn_results.extend( player.inventory.use_item(item)) elif game_state is GameStates.DROP_INVENTORY: message_log.add_message( Message('Dropped ' + item.name)) player_turn_results.extend( player.inventory.drop_item(item)) game_state = GameStates.ENEMIES_TURN elif action.get( 'move') and game_state is GameStates.PLAYER_TURN: dx, dy = action.get('move') to_x, to_y = player.x + dx, player.y + dy if game_map.walkable(to_x, to_y): target = get_blocking_entities_at_location( entities, to_x, to_y) if target: player_turn_results.extend( player.fighter.attack(target)) else: player.move(dx, dy) fov_recompute = True # end our turn game_state = GameStates.ENEMIES_TURN elif action.get( 'move2') and game_state is GameStates.PLAYER_TURN: to_x = player.x to_y = player.y if action.get('move2') == 'forward': to_x += int(math.sin(player_rot) * 1.5) to_y += int(math.cos(player_rot) * 1.5) elif action.get('move2') == 'rearward': to_x -= int(math.sin(player_rot) * 1.5) to_y -= int(math.cos(player_rot) * 1.5) if game_map.walkable(to_x, to_y): target = get_blocking_entities_at_location( entities, to_x, to_y) if target: player_turn_results.extend( player.fighter.attack(target)) else: player.x = to_x player.y = to_y fov_recompute = True # end our turn game_state = GameStates.ENEMIES_TURN elif action.get( 'turn') and game_state is GameStates.PLAYER_TURN: dir = action.get('turn') if dir == 'left': player_rot += math.pi / 4.0 elif dir == 'right': player_rot -= math.pi / 4.0 rerender_viewport = True # end_switch event.type # process player's turn results for ptr in player_turn_results: message = ptr.get('message') dead_entity = ptr.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_monster(dead_entity) message_log.add_message(message) if ptr.get('item_added'): entities.remove(ptr.get('item_added')) if ptr.get('item_dropped'): entities.append(ptr.get('item_dropped')) # end_for player_turn_results # run the enemy turn if game_state is GameStates.ENEMIES_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( player, game_map, entities) for etr in enemy_turn_results: message = etr.get('message') dead_entity = etr.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity is player: message, game_state = kill_player( player) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state is GameStates.PLAYER_DEAD: break if game_state is GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN # endif enemy turn if fov_recompute: game_map.recompute_fov(player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) # draw each part of the screen render_viewport( root_console, viewport_console, 0, 0, viewport_width, viewport_height, game_map, entities, colours, player.x, player.y, player_rot, fov_radius, rerender_viewport or game_state is GameStates.ENEMIES_TURN, wall_texture) render_map(root_console, map_console, game_map, entities, map_x, map_y, colours, fov_recompute) # TODO: extend render_menu to accept a top-left co-ordinate (start_x, start_y) render_menu(root_console, player, screen_width, screen_height, game_state) fps = 1.0 / (perf_counter() - time_start) render_panel(root_console, panel_console, entities, player, game_map, message_log, bar_width, panel_width, panel_height, panel_x, panel_y, mouse, fps) time_start = perf_counter() tcod.console_flush() clear_all(map_console, entities) fov_recompute = False
def main(): screen_width = 190 screen_height = 100 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height # panel y informs the y axis of # of the panel for both message log # and health bar message_x = bar_width + 2 # message x informs the x axis message_width = screen_width - bar_width - 2 # of the message message_height = panel_height - 1 map_width = 170 map_height = 80 room_max_size = 20 room_min_size = 8 max_rooms = 100 fov_algorithm = 0 # shape of pov fov_light_walls = True #if Walls will light or not fov_radius = 15 max_monsters_per_room = 4 colors = { "dark_wall": libtcod.Color(127, 127, 127), "dark_ground": libtcod.Color(127, 101, 63), "light_walls": libtcod.Color(127, 137, 127), "light_ground": libtcod.Color(127, 110, 63) } fighter_component = Fighter(hp=(randint(100, 200)), defense=(randint(0, 3)), power=(randint(20, 40))) player = Entity(0, 0, "@", libtcod.green, "Player", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] libtcod.console_set_custom_font( "arial10x10.png", libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, "Ork Brawl", False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_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, max_monsters_per_room) fov_recompute = True #Because FOV doesn't need to be computed every turn (standing still), only need when moving to recompute #True by default because it needs to be computed when the game start fov_map = initialize_fov( game_map) # This call function and store result in fov_map message_log = MessageLog(message_x, message_width, message_height) test = Message( "You wake up naked and angry with an urge to kill 'someting' ", libtcod.red) message = test message_log.add_message(message) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN 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, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, colors) fov_recompute = False # in theory doesn't matter, can remove code but its good practise to # to switch it off when you are not using it libtcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get("move") 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) # return entity or 'None' hence target=entity if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True # will be in False due to game loop above game_state = GameStates.ENEMY_TURN if exit: return True # This could be either return False or break # returns a value to main hence ending the programme. 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") 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.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 main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 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=30, defense=2, power=5) inventory_component = Inventory(26) player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, inventory=inventory_component) entities = [player] libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False) con = libtcod.console_new(screen_width, screen_height) panel = libtcod.console_new(screen_width, panel_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, max_monsters_per_room, max_items_per_room) 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 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, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, mouse, 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') 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) 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 to pickup here.', 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 exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY): 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') 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 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 main(): screen_width = 80 screen_height = 50 bar_width = 20 panel_height = 7 panel_y = screen_height - panel_height message_x = bar_width + 2 message_width = screen_width - bar_width - 2 message_height = panel_height - 1 map_width = 80 map_height = 43 room_max_size = 10 room_min_size = 6 max_rooms = 30 fov_algorithm = 0 fov_light_walls = True fov_radius = 10 max_monsters_per_room = 3 colors = { "dark_wall": lcod.Color(0, 0, 100), "dark_ground": lcod.Color(50, 50, 150), "light_wall": lcod.Color(130, 110, 50), "light_ground": lcod.Color(200, 180, 50), } fighter_component = Fighter(hp=12, defense=3, power=5) player = Entity(0, 0, '@', lcod.white, "Player", [], blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component) entities = [player] lcod.console_set_custom_font( "arial10x10.png", lcod.FONT_TYPE_GRAYSCALE | lcod.FONT_LAYOUT_TCOD) lcod.console_init_root(screen_width, screen_height, "TicTacRogue", False) con = lcod.console_new(screen_width, screen_height) panel = lcod.console_new(screen_width, panel_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, max_monsters_per_room) fov_recompute = True fov_map = initialize_fov(game_map) message_log = MessageLog(message_x, message_width, message_height) key = lcod.Key() mouse = lcod.Mouse() game_state = GameStates.PLAYER_TURN power_armor = PowerArmor(player) while not lcod.console_is_window_closed(): lcod.sys_check_for_event(lcod.EVENT_KEY_PRESS, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, screen_width, screen_height, bar_width, panel_height, panel_y, colors) lcod.console_flush() clear_all(con, entities) action = handle_keys(key) move = action.get("move") exit = action.get("exit") cast = action.get("cast") fullscreen = action.get("fullscreen") player_turn_results = [] if cast and game_state == GameStates.PLAYER_TURN: if power_armor not in player.effects: print("Adding effects to list") player.effects.append(power_armor) player.evaluate_effects() game_state = GameStates.ENEMY_TURN elif move and game_state == GameStates.PLAYER_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) else: player.move(dx, dy) fov_recompute = True player.evaluate_effects() player.clean_effects() game_state = GameStates.ENEMY_TURN if exit: return True if fullscreen: lcod.console_set_fullscreen(not lcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get("message") dead_entity = player_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.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.PLAYER_TURN