def fill_status_panel(player, log) -> None: log.messages.clear() #Fill out char stats entries = gen_status_panel(player) for entry in entries: log.add_message(Message(entry))
def place_entities(self, area, entities, max_monsters_per_area, max_items_per_area, kolors, current_roster, current_mm): # Get a random number of monsters number_of_monsters = randint(0, max_monsters_per_area) number_of_items = randint(0, max_items_per_area) for i in range(0, number_of_monsters): #This and the one for items should be a function tile_clear = False x = 0 y = 0 while tile_clear != True: # Choose a random location in the area x = randint(area.x1 + 1, area.x2 - 1) y = randint(area.y1 + 1, area.y2 - 1) if self.tiles[x][y].blocked == False: tile_clear = True if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): #Roll for what monster to populate monster_name = roll_monster('dungeon', current_roster) #Pull in the dictionary entry for this monster m_loader = gen_monster(monster_name, current_mm) #Set the monster stats fighter_component = Fighter(hp=m_loader['hp'], protection=m_loader['protection'], power=m_loader['power']) #Set the ai if m_loader['ai_component'] == 'basic': ai_component = BasicMonster() #Create the monster entity monster = Entity(x, y, m_loader['display_char'], kolors[m_loader['color']], m_loader['display_name'], blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) #Append the monster to the list of entities entities.append(monster) for i in range(0, number_of_items): #This and the one for monsters should be a function tile_clear = False x = 0 y = 0 while tile_clear != True: # Choose a random location in the area x = randint(area.x1 + 1, area.x2 - 1) y = randint(area.y1 + 1, area.y2 - 1) if self.tiles[x][y].blocked == False: tile_clear = True if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): item_chance = randint(0, 99) #Create the item entity if item_chance < 70: item_component = Item(use_function=heal, amount=4) item = Entity(x, y, '!', kolors['potion_violet'], 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_chance < 80: item_component = Item( use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', kolors['targeting_cyan']), damage=12, radius=3) item = Entity(x, y, '?', kolors['scroll_amber'], 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_chance < 90: item_component = Item( use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left-click an enemy to confuse it, or right-click to cancel.', kolors['targeting_cyan'])) item = Entity(x, y, '?', kolors['scroll_amber'], 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item(use_function=cast_lightning, damage=20, maximum_range=5) item = Entity(x, y, '?', kolors['scroll_amber'], 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) #Append the item to the list of entities entities.append(item)
def kill_player(player): player.char = '%' player.color = libtcod.dark_red return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD
def kill_player(player): player.char = '%' player.color = 'red' return Message('You died!', 'red'), GameStates.PLAYER_DEAD
def exit_world_tile(self): self.engine.message_log.add_message(Message('There is nowhere to climb here', color_vars.warning))
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 place_entities(self, room, entities): '''Place monsters and items randomly into a room on the map.''' max_monsters_per_room = from_dungeon_level([[2, 1], [3, 4], [5, 6]], self.dlevel) max_items_per_room = from_dungeon_level([[1, 1], [2, 4]], self.dlevel) number_of_monsters = randint(0, max_monsters_per_room) number_of_items = randint(0, max_items_per_room) monster_chances = { "orc": 80, "troll": from_dungeon_level([[15, 3], [30, 5], [60, 7]], self.dlevel) } item_chances = { "healing_potion": 35, "sword": from_dungeon_level([[5, 4]], self.dlevel), "shield": from_dungeon_level([[15, 8]], self.dlevel), "lightning_scroll": from_dungeon_level([[25, 4]], self.dlevel), "fireball_scroll": from_dungeon_level([[25, 6]], self.dlevel), "confusion_scroll": from_dungeon_level([[10, 2]], self.dlevel) } # place monsters for i in range(number_of_monsters): x, y = choice(room.coords) if not any([e for e in entities if e.x == x and e.y == y]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == "orc": fighter_component = Fighter(hp=20, defense=0, power=4, xp=35) ai_component = BasicMonster() inv_component = Inventory(5) soul_char = choice([".", "*", "+"]) soul_color = choice([tcod.red, tcod.orange, tcod.yellow]) m_soul = Soul(soul_char, soul_color) m_name = "Orc" + str(len(entities)) monster = Entity(len(entities), x, y, 'o', tcod.desaturated_green, m_name, blocks=True, soul=m_soul, fighter=fighter_component, ai=ai_component, inventory=inv_component, render_order=RenderOrder.ACTOR) else: fighter_component = Fighter(hp=30, defense=2, power=8, xp=100) ai_component = BasicMonster() inv_component = Inventory(10) soul_char = choice(["*", "+"]) soul_color = choice([tcod.orange, tcod.yellow, tcod.green]) m_soul = Soul(soul_char, soul_color) m_name = "Troll" + str(len(entities)) monster = Entity(len(entities), x, y, 'T', tcod.darker_green, m_name, blocks=True, soul=m_soul, fighter=fighter_component, ai=ai_component, inventory=inv_component, render_order=RenderOrder.ACTOR) entities.append(monster) # place items for i in range(number_of_items): x, y = choice(room.coords) if not any([e for e in entities if e.x == x and e.y == y]): item_choice = random_choice_from_dict(item_chances) if item_choice == "healing_potion": item_component = Item(use_function=heal, amount=40) item = Entity(len(entities), x, y, '!', tcod.violet, "Healing Potion", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "sword": equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(len(entities), x, y, "/", tcod.sky, "Sword", equippable=equippable_component) elif item_choice == "shield": equippable_component = Equippable(EquipmentSlots.OFF_HAND, defense_bonus=1) item = Entity(len(entities), x, y, "[", tcod.dark_orange, "Shield", equippable=equippable_component) elif item_choice == "fireball_scroll": msg_str = (f"Left-click a target tile for the fireball, " f"or right-click to cancel.") msg = Message(msg_str, tcod.light_cyan) item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=msg, damage=25, radius=3) item = Entity(len(entities), x, y, "#", tcod.red, "Fireball Scroll", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "confusion_scroll": msg_str = (f"Left-click an enemy to confuse it, " f"or right-click to cancel") msg = Message(msg_str, tcod.light_cyan) item_component = Item(use_function=cast_confuse, targeting=True, targeting_message=msg) item = Entity(len(entities), x, y, "#", tcod.light_pink, "Confusion Scroll", render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item(use_function=cast_lightning, damage=40, max_range=5) item = Entity(len(entities), x, y, "#", tcod.yellow, "Lightning Scroll", render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants): # Intialize FOV map. fov_recompute = True # Recompute FOV after the player moves fov_map = initialize_fov(game_map) target_fov_map = initialize_fov(game_map) fov_map_no_walls = initialize_fov(game_map) # Capture keyboard and mouse input key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state # Store the item that the player used to enter targeting mode (ie lightning scroll). This is so that we know what item we need to remove from inventory etc. targeting_item = None cursor_radius = 1 # For showing object descriptions description_recompute = True description_list = [] description_index = 0 prev_mouse_y = None prev_mouse_x = None # Start music mixer.init() mixer.music.load(os.path.join(definitions.ROOT_DIR, 'data', 'music', 'bgm2.mp3')) #mixer.music.play(loops=-1) #Our main loop while not libtcod.console_is_window_closed(): # Check for input libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) for animator in Animator.animators: animator.update() if Animator.blocking > 0: if not game_state == GameStates.BLOCKING_ANIMATION: previous_game_state = game_state game_state = GameStates.BLOCKING_ANIMATION if Animator.blocking == 0 and game_state == GameStates.BLOCKING_ANIMATION: game_state = previous_game_state # Recompute FOV if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) recompute_fov(fov_map_no_walls, player.x, player.y, constants['fov_radius'], light_walls=False, algorithm=constants['fov_algorithm']) # Show object descriptions if description_recompute == True: for entity in entities: if (prev_mouse_x != mouse.cx) or (prev_mouse_y != mouse.cy): description_list = [] description_index = 0 if entity.x == mouse.cx and entity.y == mouse.cy: description_list.append(entity) prev_mouse_x = mouse.cx prev_mouse_y = mouse.cy if len(description_list) > 0: description_recompute = False # We need to check to see if the mouse position changed and then clear our description list if it did, otherwise it will keep growing if (prev_mouse_x != mouse.cx) or (prev_mouse_y != mouse.cy): description_list = [] description_index = 0 description_recompute = True if mouse.lbutton_pressed: description_index += 1 if description_index > (len(description_list) - 1): description_index = 0 # Draw our scene render_all(con, panel, mouse, 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'], constants['colors'], game_state, description_list, description_index, cursor_radius, target_fov_map, fov_map_no_walls) fov_recompute = False libtcod.console_flush() # Clear our 'drawing consoles' so we dont leave a trail on the main console screen clear_all(con, entities) # Store input results action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) key = libtcod.Key() mouse = libtcod.Mouse() move = action.get('move') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') ability_1 = action.get('ability_1') exit = action.get('exit') fullscreen = action.get('fullscreen') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') #Instatiate our message queue for the players turn player_turn_results = [] # Player Actions # Move if move and game_state == GameStates.PLAYERS_TURN: if not move == 'wait': dx, dy = move destination_x = player.x + dx destination_y = player.y + dy # Check to see if the location we want to move to is blocked by a wall or inhabited by a creature if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location(entities, destination_x, destination_y) # If blocked by a creature, attack if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) # Otherwise, move. else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN else: 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)) #Ability complete checks: for ability in player.abilities: if player.animator.caller == ability and player.animator.complete: player_turn_results.extend(ability.use(complete=True)) player.animator.caller = None player.animator.complete = None if ability_1: player_turn_results.extend(player.abilities[0].use()) if show_inventory: if game_state != GameStates.SHOW_INVENTORY: previous_game_state = game_state player.inventory.sort_items() game_state = GameStates.SHOW_INVENTORY if drop_inventory: if game_state != GameStates.DROP_INVENTORY: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY #Use or drop item in 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, constants) fov_map = initialize_fov(game_map) target_fov_map = initialize_fov(game_map) fov_map_no_walls = 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.con += 1 message_log.add_message(Message('Your Constitution has increased by 1!', libtcod.yellow)) elif level_up == 'str': player.fighter.base_power += 1 message_log.add_message(Message('Your Strength has increased by 1!', libtcod.yellow)) elif level_up == 'def': player.fighter.base_defense += 1 message_log.add_message(Message('Your Defense has increased by 1!', libtcod.yellow)) hp_increase = randint(player.fighter.hitdie[0], player.fighter.hitdie[1]) + int((player.fighter.con - 10) / 2) player.fighter.base_max_hp += hp_increase player.fighter.hp += hp_increase message_log.add_message(Message('Your HP has increased by {0}'.format(hp_increase) + '!', libtcod.yellow)) game_state = previous_game_state if show_character_screen: if not game_state == GameStates.CHARACTER_SCREEN: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if game_state == GameStates.TARGETING: if hasattr(targeting_item, 'item'): cursor_radius = targeting_item.item.function_kwargs.get('radius') else: cursor_radius = targeting_item.function_kwargs.get('radius') if left_click: target_x, target_y = left_click if hasattr(targeting_item, 'item'): item_use_results = player.inventory.use(targeting_item, entities=entities, fov_map=fov_map, target_fov_map=target_fov_map,target_x=target_x, target_y=target_y) else: item_use_results = targeting_item.use(entities=entities, fov_map=fov_map, target_fov_map=target_fov_map,target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) cursor_radius = 1 elif right_click: player_turn_results.append({'targeting_cancelled': True}) cursor_radius = 1 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}) cursor_radius = 1 else: save_game(player, entities, game_map, message_log, game_state) return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) # Check player message queue and react accordingly for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') ability_used = player_turn_result.get("ability_used") xp = player_turn_result.get('xp') 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 if hasattr(targeting_item, 'item'): message_log.add_message(targeting_item.item.targeting_message) else: message_log.add_message(targeting_item.targeting_message) if ability_used: if Animator.blocking == 0: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message(Message('You equipped the {0}'.format(equipped.name))) if dequipped: message_log.add_message(Message('You removed the {0}'.format(dequipped.name))) game_state = GameStates.ENEMY_TURN 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)) if (player.level.current_level % 2) == 0: previous_game_state = game_state game_state = GameStates.LEVEL_UP else: hp_increase = randint(player.fighter.hitdie[0], player.fighter.hitdie[1]) + int((player.fighter.con - 10) / 2) player.fighter.base_max_hp += hp_increase player.fighter.hp += hp_increase message_log.add_message(Message('Your HP has increased by {0}'.format(hp_increase) + '!', libtcod.yellow)) # 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) # Capture enemy turn message queue, analyze and react accordingly. 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 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, entities, player, game_map, fov_map, 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) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) 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') 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 main(): """ 사전 준비 작업 """ SYS_LOG = init_data() player, entities = init_player_and_entities(SYS_LOG['player_name']) game_map, fov_map, fov_radius, \ fov_algorithm, fov_recompute, light_recompute, camera = init_game_map(player, entities) message_log, game_state, previous_game_state, targeting_item = init_message_and_states( ) root, console, panel, animation, context = init_console() debug, mouse, keyboard = init_others() quit = False """ 메인 루프 """ while not quit: #root = tcod.Console(*context.recommended_console_size()) """ 화면 표시 """ if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) if light_recompute: light_map = initialize_light(game_map, fov_map, entities) render_all( game_state, root, console, panel, entities, player, mouse, game_map, fov_map, light_map, camera, message_log, fov_recompute, ) context.present(root, keep_aspect=True, align=(0.5, 0.5)) # integer_scaling=True align=(0.5,1)) #print(*context.recommended_console_size()) clear_all_entities(console, entities, camera) fov_recompute = False light_recompute = False """ 입력에 대한 상호작용 """ action = handle_input_per_state(keyboard, mouse, context, game_state) #print(f'Mx:{mouse.x} My:{mouse.y} Clk:{mouse.click}') #Ridiculous failsafe if action == None: action = {} move = action.get('move') rest = action.get('rest') pickup = action.get('pickup') show_inventory = action.get('show_inventory') inventory_index = action.get('inventory_index') drop_inventory = action.get('drop_inventory') show_character_screen = action.get('show_character_screen') toggle_light = action.get('toggle_light') create_luminary = action.get('create_light') toggle_wall = action.get('toggle_wall') exit = action.get('exit') quit = action.get('quit') #print(action) player_turn_results = [] 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: pass # 최대화면이 True일 시, 전체화면이 아니라면 콘솔을 전체화면으로 전환함 if action.get('fullscreen'): toggle_fullscreen(context) #print(f'center x{CENTER_X},y{CENTER_Y}') """ 플레이어 차례에 플레이어가 할 수 있는 행동들 """ # move변수에 대입된 값이 있을 시 이동 if move and game_state == GameStates.PLAYERS_TURN: dx, dy = action.get('move') destix = player.x + dx destiy = player.y + dy if debug.passwall == False: # 가려는 곳이 지형으로 안 막혀있으면 if not game_map.is_blocked(destix, destiy): # 거기에 이미 엔티티가 있나 확인 target = get_blocking_entities_at_location( entities, destix, destiy) if target: battle = target._Fighter if target.name == "메리": pass elif battle: attack_results = player._Fighter.attack(target) player_turn_results.extend(attack_results) else: """ 다음 층으로 """ if target._Portal: entities = game_map.next_depth( player, message_log) fov_map = initialize_fov(game_map) fov_recompute = True light_recompute = True console.clear() else: print( "This is the weirdest bug I've ever seen") else: player.move(dx, dy) camera.update(player) fov_recompute = True light_recompute = True game_state = GameStates.ENEMY_TURN else: if game_map.is_blocked(player.x + dx, player.y + dy): debug.dbg_msg("You magically pass through solid wall.") player.move(dx, dy) camera.update(player) 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.log( Message(SYS_LOG['cannot_get_item'], tcod.yellow)) if toggle_light: if player._Luminary.luminosity: player._Luminary.luminosity = 0 fov_radius = 1 fov_algorithm = fov_algorithm_dark else: player._Luminary.luminosity = player._Luminary.init_luminosity fov_radius = max_fov_radius fov_algorithm = fov_algorithm_lit fov_recompute = True light_recompute = True game_state = GameStates.ENEMY_TURN 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, camera=camera, entities=entities, animation=animation, root=root, context=context, fov_map=fov_map, game_map=game_map)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player._Inventory.drop_item(item)) if game_state == GameStates.TARGETING: if mouse.click == "L": target_x = mouse.x - camera.x - CENTER_X target_y = mouse.y - camera.y - CENTER_Y #print(f"타게팅 x,y: {target_x},{target_y}") item_use_results = player._Inventory.use(targeting_item, entities=entities, fov_map=fov_map, camera=camera, animation=animation, root=root, context=context, target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) elif mouse.click == "R": player_turn_results.append({'targeting_cancelled': True}) if rest: game_state = GameStates.ENEMY_TURN if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if player._Fighter.sanity <= 0: message, game_state = insane_player(player) message_log.log(message) for r in player_turn_results: message = r.get('message') dead_entity = r.get('dead') equip = r.get('equip') item_added = r.get('item_added') item_consumed = r.get('consumed') item_used = r.get('used') item_dropped = r.get('item_dropped') targeting = r.get('targeting') targeting_cancelled = r.get('targeting_cancelled') if message: message_log.log(message) if targeting_cancelled: game_state = previous_game_state message_log.log(Message('Targeting cancelled')) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity, game_map) message_log.log(message) 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') equip_log = SYS_LOG['equip_log'] if equipped: way = equipped._Equippable.slot.name msg = cout(equip_log, equip_log['ways_to_wear'][way], 받침(equipped.name, 1)) message_log.log(Message(msg)) if dequipped: way = dequipped._Equippable.slot.name msg = cout(equip_log, equip_log['ways_to_unwear'][way], 받침(dequipped.name, 1)) message_log.log(Message(msg)) game_state = GameStates.ENEMY_TURN if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed or item_used: 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.TARGETING targeting_item = targeting message_log.log(targeting_item._Item.targeting_message) """ 적의 차례에 적이 할 수 있는 행동들 """ if game_state == GameStates.ENEMY_TURN: # 정신력 고갈 기능. 따로 변수로 넣던가 Gamemap에 넣어야 하나. if not game_map.monsters == 0: clear_message_shown = False """ 정신력 피해 계산 """ sanity_damage = random.randint( int(-30 / math.sqrt(game_map.depth)), game_map.monsters) s_resist_percent = int( (100 + 3 * (fov_radius - 1) + player._Equipment.total_sanity_resist) / 100) sanity_resistance = random.randint(0, int(10 * s_resist_percent)) sanity_damage -= sanity_resistance #광기저항을 계산 안 했네 if sanity_damage < 0: sanity_damage = 0 else: sanity_damage = int(math.sqrt(sanity_damage)) player._Fighter.heal_sanity(-sanity_damage) if sanity_damage > 3: log = SYS_LOG['enemies_exist'] message_log.log( Message(log[random.randint(0, len(log) - 1)], tcod.dark_chartreuse)) else: if not clear_message_shown: clear_message_shown = True message_log.log( Message(SYS_LOG['enemies_nonexistant'], tcod.green)) for entity in entities: if entity.name == 'light source': pass #message_log.log(f"The {entity.name} is glowing") elif entity._Ai: enemy_turn_results = entity._Ai.take_turn( player, fov_map, game_map, entities) for er in enemy_turn_results: message = er.get('message') dead_entity = er.get('dead') game_won = er.get('game_won') if game_won: message_log.log( Message(SYS_LOG['found_mary_log'], tcod.green)) message_log.log( Message(SYS_LOG['game_won_log'], tcod.green)) game_state = GameStates.GOOD_ENDING if message: message_log.log(message) if player._Fighter.sanity <= 0: message, game_state = insane_player(player) message_log.log(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) game_map.monsters -= 1 message_log.log(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: if not game_state == GameStates.GOOD_ENDING: game_state = GameStates.PLAYERS_TURN """ 디버그 기능들 """ # 플레이어 위치 표시 if debug.showpos: debug.show_pos(player, 'player') # 벽 설치 if toggle_wall: game_map.toggle_wall(player.x, player.y) # 지형이 변했으니 새로 지형 맵을 짜야 함 fov_map = initialize_fov(game_map) light_recompute = True if create_luminary: game_map.create_luminary(entities, player.x, player.y, 15) # 광원이 새로 생겼으니 다시 계산 light_recompute = True
def kill_player(player, colors): player.char = "%" player.color = colors.get("dark_red") return Message("It's a sad thing that your adventures have ended here!", colors.get("red")), GameStates.PLAYER_DEAD
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants, root_con): tcod.console_set_custom_font( "arial10x10.png", tcod.FONT_LAYOUT_TCOD | tcod.FONT_TYPE_GREYSCALE) fov_recompute = True fov_map = initialize_fov(game_map) previous_game_state = game_state targeting_item = None while True: if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants["screen_width"], constants["screen_height"], constants["bar_width"], constants["panel_y"], constants["colors"], root_con, game_state) tcod.console_flush() clear_all(con, entities) for event in tcod.event.get(): move = None pickup = None look_enemy = None left_click = None right_click = None show_inventory = None drop_inventory = None inventory_index = None take_stairs = None leave = None fullscreen = None show_character_screen = None player_turn_results = [] if isinstance(event, tcod.event.KeyDown): action = handle_keys(event, game_state) move = action.get("move") pickup = action.get("pickup") look_enemy = action.get("look_enemy") show_inventory = action.get("show_inventory") drop_inventory = action.get("drop_inventory") inventory_index = action.get("inventory_index") take_stairs = action.get('take_stairs') leave = action.get("leave") fullscreen = action.get("fullscreen") show_character_screen = action.get('show_character_screen') if isinstance(event, tcod.event.MouseButtonDown): mouse_action = handle_mouse(event) left_click = mouse_action.get("left_click") right_click = mouse_action.get("right_click") if look_enemy and game_state == GameStates.PLAYER_TURN: game_state = GameStates.LOOK_ENEMY player_turn_results.extend([{ 'message': Message('You are in the looking mode', tcod.green) }]) elif look_enemy and game_state == GameStates.LOOK_ENEMY: game_state = GameStates.PLAYER_TURN player_turn_results.extend([{ 'message': Message('You left the looking mode', tcod.green) }]) elif left_click and game_state == GameStates.LOOK_ENEMY: entities_at_location = check_entity_at_location( entities, left_click[0], left_click[1]) player_turn_results.extend(entities_at_location) elif pickup and game_state == 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('There is nothing here to pick up.', tcod.yellow)) elif show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY MenuState.menu_state = 0 elif drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY elif 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)) elif game_state == GameStates.TARGETING: if left_click and targeting_item: 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}) 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.melee_attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif take_stairs and game_state == GameStates.PLAYER_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, constants) fov_map = initialize_fov(game_map) fov_recompute = True con.clear(fg=(63, 127, 63)) break else: message_log.add_message( Message('There are no stairs here.', tcod.yellow)) elif game_state == GameStates.LEVEL_UP: player.fighter.max_hp += DiceRoll("1d8").roll_dice() game_state = previous_game_state elif show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if leave: 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 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') entity_identified = player_turn_result.get("entity_identified") 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') 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 entity_identified: game_state = GameStates.PLAYER_TURN if targeting: previous_game_state = GameStates.PLAYER_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) + '!', tcod.yellow)) player.fighter.cr = str(player.level.current_level / 4) 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.PLAYER_TURN
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants): fov_recompute = True 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 # game loop while not libtcod.console_is_window_closed(): # captures user input - will update the key and mouse variables with what the user inputs 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']) # draw the entities and blit the changes to the screen - only render the item inventory when the game state is in the inventory state render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False # present everything on the screen libtcod.console_flush() # clear entities after drawing to screen - this makes it so that when entities move, they do not leave a trail behind clear_all(con, entities) # gives a way to gracefully exit program by hitting the ESC key # gets any keyboard input to the program and stores in the key variable action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) 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') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') exit = action.get('exit') fullscreen = action.get('fullscreen') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') player_turn_results = [] # move the player only on the players turn if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy # check if there is something at the destination that would block the player - if not, move the player there 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 # change to enemy's turn after player's move game_state = GameStates.ENEMY_TURN elif wait: game_state = GameStates.ENEMY_TURN # if the player did not move and performed the pickup action by pressing the key 'g'... elif pickup and game_state == GameStates.PLAYERS_TURN: # loop through each entity on the map, check if it is an item and occupies the same space as the player for entity in entities: # if the entity is an item and in the same position as the player if entity.item and entity.x == player.x and entity.y == player.y: # add the item to the inventory and append the results to player_turn_results pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break # makes it so the player only picks up one item at a time 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 # take the index selected, use the item selected 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, constants) 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.max_hp += 20 player.fighter.hp += 20 elif level_up == 'str': player.fighter.power += 1 elif level_up == 'def': player.fighter.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}) # checks if the key pressed was the Esc key - if it was, then exit the loop 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 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') 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_dropped: entities.append(item_dropped) 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 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 # note that this is a for-else statement; without a break statement, this else will always happen else: game_state = GameStates.PLAYERS_TURN
def place_entities(room, entities, dungeon_level, colors): max_monsters_per_room = from_dungeon_level([[2, 1], [3, 4], [5, 6]], dungeon_level) max_items_per_room = from_dungeon_level([[1, 1], [2, 4]], dungeon_level) number_of_monsters = randint(0, max_monsters_per_room) number_of_items = randint(0, max_items_per_room) monster_chances = { "husk": 60, "rusted_automaton": from_dungeon_level([[25, 3], [40, 5], [60, 7]], dungeon_level), "kobold_bandit": from_dungeon_level([[10, 3], [20, 5], [25, 7]], dungeon_level) } item_chances = { "health_drink": 40, "wicked_blade": from_dungeon_level([[15, 3]], dungeon_level), "battered_armor": from_dungeon_level([[15, 4]], dungeon_level), "hp_ring": from_dungeon_level([[10, 5]], dungeon_level), "seeker_orb": from_dungeon_level([[25, 4]], dungeon_level), "flame_grenade": from_dungeon_level([[25, 6]], dungeon_level), "scrambler": from_dungeon_level([[10, 2]], dungeon_level), "pearl": 10 } for i in range(number_of_monsters): # get a random location in the room x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any( [entity for entity in entities if entity.x == x and entity.y == y]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == "husk": fighter_component = Fighter(hp=15, defense=0, power=5, xp=50) ai_component = BasicMonster() # husk monster = Entity(x, y, "h", colors.get("dark_gray"), "husk", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) elif monster_choice == "rusted_automaton": # rusted automaton fighter_component = Fighter(hp=20, defense=2, power=6, xp=75) ai_component = BasicMonster() monster = Entity(x, y, "a", colors.get("brass"), "rusted automaton", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) elif monster_choice == "kobold_bandit": # kobold bandit fighter_component = Fighter(hp=35, defense=1, power=8, xp=100) ai_component = BasicMonster() monster = Entity(x, y, "b", colors.get("darker_flame"), "kobold bandit", blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) entities.append(monster) for i in range(number_of_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any( [entity for entity in entities if entity.x == x and entity.y == y]): item_choice = random_choice_from_dict(item_chances) if item_choice == "health_drink": item_component = Item(use_function=heal, amount=50) item = Entity(x, y, "!", colors.get("violet"), "health drink", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "wicked_blade": equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(x, y, ")", colors.get("darker_flame"), "wicked blade", equippable=equippable_component) elif item_choice == "battered_armor": equippable_component = Equippable(EquipmentSlots.BODY, defense_bonus=1) item = Entity(x, y, "T", colors.get("darker_orange"), "battered armor", equippable=equippable_component) elif item_choice == "hp_ring": equippable_component = Equippable(EquipmentSlots.RING, max_hp_bonus=30) item = Entity(x, y, "o", colors.get("copper"), "stalwart ring", equippable=equippable_component) elif item_choice == "flame_grenade": item_component = Item( use_function=flame_grenade, targeting=True, targeting_message=Message( "Left-click where you'd like to throw the grenade, or right-click to cancel.", colors.get("light_cyan")), damage=25, radius=3) item = Entity(x, y, ".", colors.get("red"), "flame grenade", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "scrambler": item_component = Item( use_function=confuse, targeting=True, targeting_message=Message( "Left-click an enemy you'd like to confuse, or right-click to cancel.", colors.get("light_cyan"))) item = Entity(x, y, ".", colors.get("light_pink"), "scrambler", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "pearl": item_component = Item(use_function=xpboost, amount=100) item = Entity(x, y, ".", colors.get("silver"), "pearl", render_order=RenderOrder.ITEM, item=item_component) elif item_choice == "seeker_orb": item_component = Item(use_function=seeker_bolt, damage=30, maximum_range=5) item = Entity(x, y, ".", colors.get("sky"), "seeker orb", render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
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 attack(self, target): results = [] attack_roll = roll_dice(self.attack_die['dice_count'], self.attack_die['die_faces']) if attack_roll == 1: damage = 0 results.append({ 'message': Message( '{0} fumbles the strike on {1}!'.format( self.owner.name.capitalize(), target.name), libtcod.yellow) }) #Need to have a critical miss elif attack_roll == 20: if self.owner and self.owner.equipment and self.owner.equipment.main_hand: damage = 2 * max({ 1, (self.owner.equipment.main_hand.equippable. damage_dice_count * self.owner.equipment.main_hand.equippable.damage_die_face + self.main_hand_damage_bonus) }) else: damage = 2 * max( {1, (1 + self.str_mod)} ) #unarmed strike, max needed to ensure always do at least one pt of dmg results.append({ 'message': Message( '{0} critically hits {1}! ({2})'.format( self.owner.name.capitalize(), target.name, str(damage)), libtcod.red) }) results.extend(target.fighter.take_damage(damage)) elif attack_roll + self.attack_die[ 'die_bonus'] >= target.fighter.defense: if self.owner and self.owner.equipment and self.owner.equipment.main_hand: damage = max({ 1, (roll_dice( self.owner.equipment.main_hand.equippable. damage_dice_count, self.owner.equipment.main_hand. equippable.damage_die_face) + self.main_hand_damage_bonus) }) else: damage = max( {1, (1 + self.str_mod)} ) #unarmed strike, max needed to ensure always do at least one pt of dmg results.append({ 'message': Message( '{0} hits {1}! ({2})'.format(self.owner.name.capitalize(), target.name, str(damage)), libtcod.red) }) results.extend(target.fighter.take_damage(damage)) else: damage = 0 results.append({ 'message': Message( '{0} misses {1}!'.format(self.owner.name.capitalize(), target.name), libtcod.yellow) }) return results
def place_entities(self, room, entities, max_monsters_per_room, max_items_per_room): # Get a random number of monsters number_of_monsters = randint(0, max_monsters_per_room) number_of_items = randint(0, max_items_per_room) for i in range(number_of_monsters): # Choose a random location in the room x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([entity for entity in entities if entity.x == x and entity.y == y]): if randint(0, 100) < 80: fighter_component = Fighter(hp=10, defense=0, power=3) ai_component = BasicMonster() monster = Entity(x, y, 'o', libtcod.desaturated_green, 'Orc', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) else: fighter_component = Fighter(hp=16, defense=1, power=4) ai_component = BasicMonster() monster = Entity(x, y, 'T', libtcod.darker_green, 'Troll', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) entities.append(monster) for i in range(number_of_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([entity for entity in entities if entity.x == x and entity.y == y]): item_chance = randint(0, 100) if item_chance < 70: item_component = Item(use_function=heal, amount=4) item = Entity(x, y, '!', libtcod.violet, 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_chance < 80: item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=12, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_chance < 90: item_component = Item(use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item(use_function=cast_lightning, damage=20, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
def place_entities(self, room, entities): # [NUMBER OF ITEMS/MONSTERS PER ROOM, DUNGEON LEVEL] max_monsters_per_room = from_dungeon_level([[2, 1], [3, 4], [5, 6]], self.dungeon_level) max_items_per_room = from_dungeon_level([[10, 1], [2, 4]], self.dungeon_level) # Get a random number of monsters number_of_monsters = randint(0, max_monsters_per_room) # Get a random number of items number_of_items = randint(0, max_items_per_room) # [CHANCE OF SPAWNING, DUNGEON LEVEL] monster_chances = { 'ragged_sailor': 60, 'skeleton': from_dungeon_level([[30, 1], [40, 2], [50, 3]], self.dungeon_level), 'troll': from_dungeon_level([[15, 1], [30, 2], [60, 3]], self.dungeon_level) } item_chances = { 'healing_potion': 35, 'rapier': from_dungeon_level([[5, 1]], self.dungeon_level), 'buckler': from_dungeon_level([[15, 1]], self.dungeon_level), 'fancy_hat': from_dungeon_level([[5, 1]], self.dungeon_level), 'fancy_shirt': from_dungeon_level([[5, 1]], self.dungeon_level), 'lightning_scroll': from_dungeon_level([[25, 1]], self.dungeon_level), 'fireball_scroll': from_dungeon_level([[25, 1]], self.dungeon_level), 'confusion_scroll': from_dungeon_level([[10, 1]], self.dungeon_level), 'flintlock': from_dungeon_level([[50, 1]], self.dungeon_level) } for i in range(number_of_monsters): #Choose a random location in the room x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) # Check if an entity is already at that lockation if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == 'ragged_sailor': fighter_component = Fighter(hp=15, defense=1, power=4, xp=35) ai_component = BasicMonster() monster = Entity(x, y, 's', libtcod.desaturated_green, 'Ragged Sailor', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) elif monster_choice == 'skeleton': fighter_component = Fighter(hp=20, defense=3, power=4, xp=100) ai_component = BasicMonster() monster = Entity(x, y, 'k', libtcod.darker_green, 'Skeleton', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) else: fighter_component = Fighter(hp=30, defense=4, power=8, xp=100) ai_component = BasicMonster() monster = Entity(x, y, 'T', libtcod.darker_green, 'Troll', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) entities.append(monster) for i in range(number_of_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): item_choice = random_choice_from_dict(item_chances) ## ADD SOME ITEMS HERE FOR FUN if item_choice == 'healing_potion': item_component = Item(use_function=heal, amount=40) item = Entity(x, y, '!', libtcod.violet, 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'rapier': equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(x, y, '/', libtcod.sky, 'Rapier', equippable=equippable_component) elif item_choice == 'buckler': equippable_component = Equippable(EquipmentSlots.OFF_HAND, defense_bonus=1) item = Entity(x, y, '[', libtcod.darker_orange, 'Buckler', equippable=equippable_component) elif item_choice == 'fancy_hat': equippable_component = Equippable(EquipmentSlots.HEAD, defense_bonus=1) item = Entity(x, y, '^', libtcod.crimson, 'Fancy Hat', equippable=equippable_component) elif item_choice == 'fancy_shirt': equippable_component = Equippable(EquipmentSlots.TORSO, defense_bonus=1) item = Entity(x, y, ';', libtcod.crimson, 'Fancy Shirt', equippable=equippable_component) elif item_choice == 'fireball_scroll': item_component = Item( use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'confusion_scroll': item_component = Item( use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'flintlock': item_component = Item( use_function=cast_bullet, targeting=True, targeting_message=Message( 'Left-click an enemy to shoot it, or right-click to cancel.', libtcod.light_cyan), damage=40, maximum_range=5) item = Entity(x, y, '+', libtcod.light_pink, 'Flintlock', render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item( use_function=cast_lightning, targeting=True, targeting_message=Message( 'Left-click an enemy to lightning strike it, or right-click to cancel.', libtcod.light_cyan), damage=40, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
def play_game(player, entities, game_map, message_log, game_state, con, hud, panel, constants): fov_recompute = True 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 while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, hud, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['map_x'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], constants['map_chars'], game_state) colors = constants['colors'] fov_recompute = False libtcod.console_flush() clear_all(con, entities, constants['map_chars'], game_map, colors) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) move = action.get('move') wait = action.get('wait') a_attack = action.get('a_attack') s_attack = action.get('s_attack') gab = action.get('gab') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') exit = action.get('exit') fullscreen = action.get('fullscreen') 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 previous_facing = player.facing if dx > 0: player.set_char('>') player.set_facing('Left') elif dx < 0: player.set_char('<') player.set_facing('Right') elif dy > 0: player.set_char('v') player.set_facing('Down') elif dy < 0: player.set_char('^') player.set_facing('Up') if not game_map.is_blocked(destination_x, destination_y) and player.facing == previous_facing: 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: 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)) elif a_attack and game_state == GameStates.PLAYERS_TURN: attack_results = player.fighter.forward_attack(entities) player_turn_results.extend(attack_results) game_state = GameStates.ENEMY_TURN elif s_attack and game_state == GameStates.PLAYERS_TURN: attack_results = player.fighter.circle_attack(entities) player_turn_results.extend(attack_results) game_state = GameStates.ENEMY_TURN elif gab and game_state == GameStates.PLAYERS_TURN: entities_near_player = get_blocking_entities_in_rectangle(entities, player.x, player.y, 3, 3) npcs_near_player = get_npcs_in_list(entities_near_player) remove_entity_from_sublist_of_entities(player, entities_near_player) if npcs_near_player: remove_entity_from_sublist_of_entities(npcs_near_player, entities_near_player) if npcs_near_player: you_said = [{'message': Message('You say hello, and you get a wave back!', libtcod.white)}] player_turn_results.extend(you_said) game_state = GameStates.ENEMY_TURN elif entities_near_player: you_said = [{'message': Message('You say hello, but you only get a growl in return!', libtcod.white)}] player_turn_results.extend(you_said) game_state = GameStates.ENEMY_TURN else: you_said = [{'message': Message('You say hello, but no one responds. No one is near!', libtcod.white)}] player_turn_results.extend(you_said) game_state = GameStates.ENEMY_TURN 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, constants) fov_map = initialize_fov(game_map) fov_recompute = True libtcod.console_set_default_background(con, libtcod.black) 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: save_game(player, entities, game_map, message_log, game_state) return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') 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, colors.get('bad_alert'), colors.get('bad_alert')) else: message = kill_monster(dead_entity, colors.get('moderate_alert'), colors.get('moderate_alert')) message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message(Message('You equipped the {0}'.format(equipped.name))) if dequipped: message_log.add_message(Message('You dequipped the {0}'.format(dequipped.name))) game_state = GameStates.ENEMY_TURN 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: 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, colors.get('bad_alert'), colors.get('bad_alert')) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN fov_recompute = True
def return_scrolls(self, x, y): item_chances = { 'a': 10, 'b': from_dungeon_level([[2, 4]], self.dungeon_level), 'c': from_dungeon_level([[3, 4]], self.dungeon_level), 'd': from_dungeon_level([[4, 4]], self.dungeon_level), 'e': from_dungeon_level([[5, 4]], self.dungeon_level), 'f': from_dungeon_level([[6, 4]], self.dungeon_level), 'g': from_dungeon_level([[7, 4]], self.dungeon_level), 'h': 10, 'i': from_dungeon_level([[2, 4]], self.dungeon_level), 'j': from_dungeon_level([[3, 4]], self.dungeon_level), 'k': from_dungeon_level([[4, 4]], self.dungeon_level), 'l': from_dungeon_level([[5, 4]], self.dungeon_level), 'm': from_dungeon_level([[6, 4]], self.dungeon_level) } item_choice = random_choice_from_dict(item_chances) if item_choice == 'a': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=12, radius=2) item = Entity(x, y, '#', libtcod.red, 'Smudged Burnt Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'b': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=15, radius=2) item = Entity(x, y, '#', libtcod.red, 'Burnt Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'c': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=17, radius=3) item = Entity(x, y, '#', libtcod.red, 'Smudged Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'd': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'e': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=35, radius=3) item = Entity(x, y, '#', libtcod.red, 'Calculated Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'f': item_component = Item(use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=50, radius=5) item = Entity(x, y, '#', libtcod.red, 'Bloodfeud Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'g': item_component = Item(use_function=cast_confuse, targeting=True, targeting_message=Message('Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'h': item_component = Item(use_function=cast_lightning, damage=15, maximum_range=4) item = Entity(x, y, '#', libtcod.yellow, 'Smudged Burnt Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'i': item_component = Item(use_function=cast_lightning, damage=20, maximum_range=4) item = Entity(x, y, '#', libtcod.yellow, 'Burnt Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'j': item_component = Item(use_function=cast_lightning, damage=25, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Smudged Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'k': item_component = Item(use_function=cast_lightning, damage=40, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'l': item_component = Item(use_function=cast_lightning, damage=60, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Calculated Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'm': item_component = Item(use_function=cast_lightning, damage=80, maximum_range=7) item = Entity(x, y, '#', libtcod.yellow, 'Bloodfeud Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) return item
def place_entities(self, room, entities): # 部屋のオブジェクト数の決定 max_monsters_per_room = from_dungeon_level( [[2, 1], [3, 4], [5, 6], [6, 12]], self.dungeon_level) max_items_per_room = from_dungeon_level([[1, 1], [2, 6]], self.dungeon_level) number_of_monsters = randint(0, max_monsters_per_room) number_of_items = randint(0, max_items_per_room) # [確率,発生する階数] monster_chances = { 'goblin': from_dungeon_level([[80, 1], [60, 7], [30, 10], [0, 14]], self.dungeon_level), 'troll': from_dungeon_level([[15, 3], [60, 5], [80, 14]], self.dungeon_level), 'ogre': from_dungeon_level([[15, 6], [30, 10], [60, 14]], self.dungeon_level) } item_chances = { 'healing_potion': 15, 'bronze_sword': from_dungeon_level([[5, 4]], self.dungeon_level), 'shield': from_dungeon_level([[5, 6]], self.dungeon_level), 'iron_sword': from_dungeon_level([[5, 8]], self.dungeon_level), 'lightning_scroll': from_dungeon_level([[25, 4], [40, 8]], self.dungeon_level), 'fireball_scroll': from_dungeon_level([[25, 6], [40, 10]], self.dungeon_level), 'confusion_scroll': from_dungeon_level([[10, 2], [20, 4]], self.dungeon_level) } # モンスターの数だけ実行 for i in range(number_of_monsters): # 部屋のランダムな位置を取得 x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) # 得られた座標に既にEntitiyが存在しないならば if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == 'goblin': fighter_component = Fighter(hp=25, defense=0, power=3, xp=35) ai_component = BasicMonster() monster = Entity(x, y, 'g', libtcod.desaturated_green, 'goblin', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) elif monster_choice == 'troll': fighter_component = Fighter(hp=40, defense=2, power=4, xp=70) ai_component = BasicMonster() monster = Entity(x, y, 'T', libtcod.darker_green, 'Troll', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) elif monster_choice == 'ogre': fighter_component = Fighter(hp=70, defense=1, power=4, xp=120) ai_component = BasicMonster() equipment_component = Equipment() equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_daice=2, daice=2, base_power=3) club = Entity(0, 0, 'l', libtcod.sky, 'club', equippable=equippable_component) monster = Entity(x, y, 'O', libtcod.darker_green, 'Ogre', blocks=True, fighter=fighter_component, equipment=equipment_component, render_order=RenderOrder.ACTOR, ai=ai_component) monster.equipment.toggle_equip(club) entities.append(monster) else: i -= 1 # アイテムの数だけ実行 for i in range(number_of_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) # 得られた座標に既にEntitiyが存在しないならば if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): item_choice = random_choice_from_dict(item_chances) if item_choice == 'healing_potion': item_component = Item(use_function=heal, amount=40) item = Entity(x, y, '!', libtcod.violet, 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'fireball_scroll': item_component = Item( use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'confusion_scroll': item_component = Item( use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'bronze_sword': equippable_component = Equippable( EquipmentSlots.MAIN_HAND, power_daice=2, daice=3, base_power=1) item = Entity(x, y, '/', libtcod.sky, 'Bronze Sword', equippable=equippable_component) elif item_choice == 'shield': equippable_component = Equippable( EquipmentSlots.OFF_HAND, defense_bonus=1) item = Entity(x, y, '[', libtcod.darker_orange, 'Shield', equippable=equippable_component) elif item_choice == 'iron_sword': equippable_component = Equippable( EquipmentSlots.MAIN_HAND, power_daice=4, daice=3, base_power=2) item = Entity(x, y, '/', libtcod.sky, 'Iron Sword', equippable=equippable_component) else: item_component = Item(use_function=cast_lightning, damage=40, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item) else: i -= 1
def main(): #initialize constants, game variables, fov, message log, and font const = get_constants() player, entities, game_map, fov_recompute, fov_map, message_log, hp_regen_tick, mp_regen_tick = get_game_variables( const) # font from: http://rogueliketutorials.com/tutorials/tcod/part-0/ tcod.console_set_custom_font( 'arial10x10.png', tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_TCOD) #sets game state to be players turn to begin with game_state = GameStates.PLAYERS_TURN # main game loop. Root console being created in context with 'with' so that it closes nicely instead of freezing and crashing with tcod.console_init_root(const['screen_width'], const['screen_height'], const['window_title'], fullscreen=False, order='F', vsync=True) as root_console: # initializes main console and console for UI elements con = tcod.console.Console(const['screen_width'], const['screen_height'], order='F') panel = tcod.console.Console(const['screen_width'], const['panel_height'], order='F') # main game loop while True: # gets user input and sends input events to a handler that returns a dictionary of actions for event in tcod.event.wait(): action = event_dispatcher(event) # recomputes fov when necessary if (fov_recompute == True): recompute_fov(fov_map, player.x, player.y, player.fov_range, const['fov_light_walls'], const['fov_algorithm']) # renders whole map on console render_all(con, panel, message_log, entities, player, game_map, fov_map, fov_recompute, const['screen_width'], const['screen_height'], const['bar_width'], const['panel_height'], const['panel_y']) tcod.console_flush() # resets fov_recompute to prevent wasteful computation fov_recompute = False # clears console to avoid trails when moving clear_all(con, entities) # gets actions inputted by player from dictionary move = action.get('move') exit_game = action.get('exit_game') fullscreen = action.get('fullscreen') restart = action.get('restart') take_stairs = action.get('take_stairs') toggle_lights = action.get('toggle_lights') spell = action.get('spell') # player's turn taken only if player gives input if (move != None) and (game_state == GameStates.PLAYERS_TURN) and ( player.is_dead == False): dx, dy = move # only move player if the cell is unblocked and moving within the map if ((player.x + dx < game_map.width - 1) and (player.y + dy < game_map.height - 1) and (not game_map.is_blocked(player.x + dx, player.y + dy))): # checks for entities that block movement in destination being moved to target = get_blocking_entities_at_location( entities, player.x + dx, player.y + dy) # space is empty so player moves. if player is waiting in place it is treated as a normal move to prevent them from atacking themselves if (target == None) or (dx == 0 and dy == 0): player.move(dx, dy) # only recomputes fov when player moves to avoid unnecessary computation fov_recompute = True # attack target else: player.attack(target, message_log) # increments tick by one if the player is below full health/mp. Player must always wait for the full delay after losing hp/mp at max if (player.hp < player.max_hp): hp_regen_tick += 1 if (player.mp < player.max_mp): mp_regen_tick += 1 # player statically regenerates hp and mp periodically if (hp_regen_tick == const['hp_regen_delay']): player.heal(1) hp_regen_tick = 0 # resets tick if (mp_regen_tick == const['mp_regen_delay']): player.restore_mp(1) mp_regen_tick = 0 # resets tick # resets players fov range in case they have casted flash player.fov_range = const['default_fov_radius'] # passes turn to enemies. this happens even if player walks into a wall game_state = GameStates.ENEMY_TURN # handles spells if (spell != None) and (game_state == GameStates.PLAYERS_TURN ) and (player.is_dead == False): # f key casts a fireball spell that does damage in a radius around the player if (spell == 'fireball'): player.cast_fireball(entities, message_log) # h key casts cure which heals for a portion of players max hp elif (spell == 'cure'): player.cast_cure(message_log) # v key casts clairvoyance which reveals position of stairs elif (spell == 'clairvoyance'): player.cast_clairvoyance(game_map, message_log) # g key casts flash which increases the distance the player can see for a turn elif (spell == 'flash'): player.cast_flash(message_log) # arrow keys cast in appropriate direction elif (spell == 'tunnel_up'): player.cast_tunnel(game_map, fov_map, message_log, 0, -1) elif (spell == 'tunnel_down'): player.cast_tunnel(game_map, fov_map, message_log, 0, 1) elif (spell == 'tunnel_left'): player.cast_tunnel(game_map, fov_map, message_log, -1, 0) elif (spell == 'tunnel_right'): player.cast_tunnel(game_map, fov_map, message_log, 1, 0) # increments tick by one if the player is below full health. if (player.hp < player.max_hp): hp_regen_tick += 1 # resets mp regen tick so a player must wait full delay after casting a spell to start mp regen mp_regen_tick = 0 # hp regen unaffected by casting a spell if (hp_regen_tick == const['hp_regen_delay']): player.heal(1) hp_regen_tick = 0 # resets tick # resets player's fov range unless they cast flash. this makes flash only last one turn if (spell != 'flash'): player.fov_range = const['default_fov_radius'] # recomputes fov since some spells affect the map/fov fov_recompute = True game_state = GameStates.ENEMY_TURN # enemy's turn if (game_state == GameStates.ENEMY_TURN): # loops through all enemies for entity in entities: if (isinstance(entity, Enemy)): # enemy gets a real turn if they are within the players fov, or if they have seen the player if ((fov_map.fov[entity.y, entity.x]) or (entity.knows_player_location == True)): # attacks player if they are adjacent or moves towards player using bfs pathfinding algorithm entity.take_turn(player, game_map, entities, message_log) # enemy will continue to follow the player after being seen for the first time, regardless of fov if entity.knows_player_location == False: entity.knows_player_location = True # enemies outside of fov just move around at random else: entity.random_move(game_map, entities) # checks to see if the player died if (player.is_dead == True): game_state = GameStates.PLAYER_DEATH else: # passes turn back to player even if the enemies didn't do anything game_state = GameStates.PLAYERS_TURN # handles death of player if (game_state == GameStates.PLAYER_DEATH): message_log.add_message(Message('You Lose', tcod.darker_red)) # takes player to new floor if (take_stairs == True) and (player.x == game_map.stairs.x) and ( player.y == game_map.stairs.y): # makes new floor and resets entities list entities = game_map.stairs.take_stairs(player, game_map, entities, message_log) # resets fov fov_map = initialize_fov(game_map) fov_recompute = True # clears the console to remove the last floor from the screen clear_console(con, const['screen_width'], const['screen_height']) # player wins if they make it to the 4th floor if (game_map.depth == const['winning_floor']): game_state = GameStates.PLAYER_WINS # player wins after getting to a set floor if game_state == GameStates.PLAYER_WINS: message_log.add_message(Message('You Win!', tcod.yellow)) # restarts game if (restart == True): # resets everything to beginning state const = get_constants() player, entities, game_map, fov_recompute, fov_map, message_log, hp_regen_tick, mp_regen_tick = get_game_variables( const) # clears the console to remove the last floor from the screen clear_console(con, const['screen_width'], const['screen_height']) # new game starts with players turn game_state = GameStates.PLAYERS_TURN # toggles lights to see the whole map for deomonstration if (toggle_lights == True): game_map.lights_on = not game_map.lights_on if game_map.lights_on == False: clear_console(con, const['screen_width'], const['screen_height']) fov_recompute = True # breaks loop if player exits if (exit_game == True): raise SystemExit() # toggles fullscreen if (fullscreen == True): tcod.console_set_fullscreen(not tcod.console_is_fullscreen())
def attacktarget(self, target, attack): results = [] # determine if it's even a hit, otherwise, damage is zero. lowlevel_equalizer = 2 speed_calc = float(self.speed + lowlevel_equalizer) / \ float(target.fighter.speed + lowlevel_equalizer) dodge_chance = speed_calc - 0.5 dodged = False critical = False if (dodge_chance >= 1.0): dodged = True elif (dodge_chance <= 0.0): dodged = False critical = True else: dodged = (random() >= dodge_chance) damage = 0 if (not dodged): # pokemon damage calc dmg_mod = (random() * 0.15) + 0.85 # [0.85,1.0) if (critical): dmg_mod *= 1.5 effective_atkdef = 0 assert (attack.atk_type == AttackType.PHYSICAL or attack.atk_type == AttackType.SPECIAL) if (attack.atk_type == AttackType.PHYSICAL): effective_atkdef = float(self.attack) / float( target.fighter.defense) elif (attack.atk_type == AttackType.SPECIAL): effective_atkdef = float(self.spattack) / float( target.fighter.spdefense) damage = int( float(attack.atk_power) * effective_atkdef * dmg_mod * 0.5) + 1 # if AI attacks non-hostile AI, fight back! if (not target.ai is None): if ((target.ai.aistate != AI.AIStates.FOLLOWING) or (target.ai.targetentity != self.owner)): target.ai.aistate = AI.AIStates.FOLLOWING target.ai.targetentity = self.owner if damage > 0: if (critical): results.append({'message': Message( '%s uses its %s attack on the %s for %s hit points. It\'s a critical hit!' % \ (self.owner.name, attack.name, target.name, str(damage)), libtcod.white)}) else: results.append({'message': Message( '%s uses its %s attack on the %s for %s hit points.' % \ (self.owner.name, attack.name, target.name, str(damage)), libtcod.white)}) results.extend(target.fighter.take_damage(damage)) elif (dodged): results.append({'message': Message( '%s uses its %s attack, but the %s completely dodges it.' % \ (self.owner.name, attack.name, target.name), libtcod.white)}) else: results.append({'message': Message( '%s uses its %s attack on the %s, but does no damage.' % \ (self.owner.name, attack.name, target.name), libtcod.white)}) return results
def kill_player(player): player.char = "%" player.color = libtcod.dark_red player.name = 'Remains of ' + player.name return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD
def place_entities(self, room, entities): num_monsters = from_dungeon_level([[2, 1], [3, 4], [5, 6]], self.dungeon_level) num_items = from_dungeon_level([[1, 1], [2, 4]], self.dungeon_level) monster_chances = { 'orc': 80, 'troll': from_dungeon_level([[15, 3], [30, 5], [60, 7]], self.dungeon_level) } item_chances = { 'healing_potion': 35, 'sword': from_dungeon_level([[5, 4]], self.dungeon_level), 'shield': from_dungeon_level([[15, 8]], self.dungeon_level), 'lightning_scroll': from_dungeon_level([[25, 4]], self.dungeon_level), 'fireball_scroll': from_dungeon_level([[25, 6]], self.dungeon_level), 'confusion_scroll': from_dungeon_level([[10, 2]], self.dungeon_level) } for i in range(num_monsters): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == 'orc': fighter_component = Fighter(hp=20, defense=0, power=4, xp=35) ai_component = BasicMonster() monster = Entity(x, y, 'o', libtcod.desaturated_green, 'Orc', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) else: fighter_component = Fighter(hp=30, defense=2, power=8, xp=100) ai_component = BasicMonster() monster = Entity(x, y, 'T', libtcod.darker_green, 'Troll', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) entities.append(monster) for i in range(num_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): item_choice = random_choice_from_dict(item_chances) if item_choice == 'healing_potion': item_component = Item(use_function=heal, amount=40) item = Entity(x, y, '!', libtcod.violet, 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'sword': equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(x, y, '/', libtcod.sky, 'Sword', equippable=equippable_component) elif item_choice == 'shield': equippable_component = Equippable(EquipmentSlots.OFF_HAND, defense_bonus=1) item = Entity(x, y, ']', libtcod.darker_orange, 'Shield', equippable=equippable_component) elif item_choice == 'fireball_scroll': item_component = Item( use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'confusion_scroll': item_component = Item( use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left click on an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item(use_function=cast_lightning, damage=40, max_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
def play_game(con, player, entities, animator: Animator, turn_count: int, game_map: GameMap, message_log, game_state, panel, constants): target_x, target_y = player.x, player.y targeting_item = None target_entity = None while True: fov_algorithm = 2 fov_light_walls = True fov_radius = 20 fov_recompute = True fov_map = initialize_fov(game_map) if fov_recompute: recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm) render_all(con, panel, entities, animator, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], game_state, target_x, target_y, target_entity, turn_count) tcod.console_flush() clear_all(con, entities) player_turn_results = [] animator.advance_frame() # Handle Game State if game_state == GameStates.ENEMY_TURN: turn_count += 1 # Generate path map with all static tiles (ignore entities for now) path_map = generate_path_map(game_map, entities=None, player=player) for entity in entities: if entity.ai and entity.ai != Player: recompute_walkable(fov_map, game_map, entities, entity) entity_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities, path_map) for entity_turn_result in entity_turn_results: message = entity_turn_result.get('message') dead_entity = entity_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(player) else: message = kill_entity(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN # Handle Events for event in tcod.event.wait(): if event.type == "QUIT": save_game(player, entities, animator, turn_count, game_map, message_log, game_state) raise SystemExit() if event.type == "KEYDOWN": action: [Action, None] = handle_keys(event.sym, game_state) if action is None: continue action_type: ActionType = action.action_type if action_type == ActionType.EXECUTE: if game_state == GameStates.TARGETING: item_use_results = player.body.use_selected_appendage( entities=entities, fov_map=fov_map, game_map=game_map, target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) game_state = GameStates.ENEMY_TURN elif game_state == GameStates.LOOKING: look_results = [] looked_at_entities = get_entities_at_location( entities, target_x, target_y) if tcod.map_is_in_fov(fov_map, target_x, target_y): if looked_at_entities: for entity in looked_at_entities: look_results.extend( entity.get_description()) target_entity = entity else: if game_map.tiles[target_x][target_y].blocked: look_results.append({ 'message': Message("You stare at the wall.") }) else: look_results.append({ 'message': Message("You stare into empty space.") }) else: look_results.append({ 'message': Message("You can't see that far.") }) game_state = GameStates.PLAYER_TURN player_turn_results.extend(look_results) if action_type == ActionType.MOVEMENT: dx: int = action.kwargs.get("dx", 0) dy: int = action.kwargs.get("dy", 0) # Player Movement if game_state == GameStates.PLAYER_TURN: destination_x = player.x + dx destination_y = player.y + dy tile_results = game_map.tiles[destination_x][ destination_y].overlap_entity(player) player_turn_results.extend(tile_results) if not game_map.is_blocked(destination_x, destination_y): target_appendage = get_blocking_entities_at_location( entities, destination_x, destination_y) if target_appendage: if target_appendage.body: player_fighter = player.body.selected_appendage.fighter if player_fighter: target_entity = target_appendage game_state = GameStates.TARGET_APPENDAGE else: player_turn_results.append({ 'message': Message( "You cannot attack with your {0}." .format( player.body. selected_appendage.name), tcod.yellow) }) elif target_appendage.structure: structure_interact_results = target_appendage.structure.interact( player) player_turn_results.extend( structure_interact_results) else: player.move(dx, dy) else: player_turn_results.append({ 'message': Message("You slam yourself into the wall!", tcod.orange) }) if game_state != GameStates.TARGET_APPENDAGE: game_state = GameStates.ENEMY_TURN # Targeting elif game_state in (GameStates.TARGETING, GameStates.LOOKING): new_x = target_x + dx new_y = target_y + dy if player.distance(new_x, new_y) < targeting_radius: target_x = new_x target_y = new_y elif action_type == ActionType.GRAB: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_result = player.body.grab_entity(entity) player_turn_results.extend(pickup_result) break else: player_turn_results.append({ 'message': Message('You grab at the air', tcod.yellow) }) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.LOOK: game_state = GameStates.LOOKING target_x, target_y = player.x, player.y targeting_radius = 100 elif action_type == ActionType.WAIT: player_turn_results.append({ 'message': Message('You stare blankly into space', tcod.yellow) }) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.CHOOSE_OPTION: option_index = action.kwargs.get("option_index", None) if game_state == GameStates.SWAP_APPENDAGE: if option_index < len(player.body.appendages): item = player.body.appendages[option_index] swap_results = player.body.select_appendage(item) player_turn_results.extend(swap_results) game_state = GameStates.PLAYER_TURN elif game_state == GameStates.TARGET_APPENDAGE: if option_index < len(target_entity.body.appendages): target_appendage = target_entity.body.appendages[ option_index] attack_results = player.body.selected_appendage.fighter.attack_appendage( target_appendage) player_turn_results.extend(attack_results) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.INTERACT: for entity in entities: if entity.structure and entity.x == player.x and entity.y == player.y: interact_results = entity.structure.interact( player) player_turn_results.extend(interact_results) break else: if game_state == GameStates.PLAYER_TURN: activate_item_results = player.body.use_selected_appendage( fov_map=fov_map, game_map=game_map, entities=entities) if activate_item_results: player_turn_results.extend( activate_item_results) game_state = GameStates.ENEMY_TURN elif action_type == ActionType.DROP_INVENTORY_ITEM: grabber = player.body.selected_appendage.grabber if grabber: player_turn_results.extend(grabber.drop()) # game_state = GameStates.DROP_INVENTORY elif action_type == ActionType.SWAP_APPENDAGE: game_state = GameStates.SWAP_APPENDAGE elif action_type == ActionType.ESCAPE: if game_state == GameStates.TARGETING: game_state = GameStates.PLAYER_TURN elif game_state == GameStates.PLAYER_TURN: save_game(player, entities, animator, turn_count, game_map, message_log, game_state) main() elif action_type == ActionType.RESTART: main() # Process player turn results for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') targeting = player_turn_result.get('targeting') next_floor = player_turn_result.get('next_floor') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(player) else: message_log.add_message(kill_entity(dead_entity)) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if targeting: game_state = GameStates.TARGETING targeting_item = targeting targeting_radius = targeting.item.targeting_radius target_x = player.x target_y = player.y message_log.add_message( Message("You begin aiming the {0}.".format( targeting.name))) # TODO: Replace occurrences of add_message with player_turn_result approach # player_turn_results.append({'message': Message("You begin aiming the {0}.".format(targeting.name))}) if next_floor: entities = game_map.next_floor(player, constants) fov_map = initialize_fov(game_map) tcod.console_clear(con)
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants): fov_recompute = True 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 while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) move = action.get('move') wait = action.get('wait') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') show_equipment_inventory = action.get('show_equipment_inventory') show_bag = action.get('show_bag') inventory_index = action.get('inventory_index') equipment_inventory_index = action.get('equipment_inventory_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') show_character_screen = action.get('show_character_screen') show_help_menu = action.get('show_help_menu') exit = action.get('exit') fullscreen = action.get('fullscreen') cast_magic_wand = action.get('cast_magic_wand') 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 wait: 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 elif entity.equipment_item and entity.x == player.x and entity.y == player.y: pickup_results = player.equipment_inventory.add_equipment_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 show_equipment_inventory: previous_game_state = game_state game_state = GameStates.SHOW_EQUIPMENT_INVENTORY if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if show_bag: previous_game_state = game_state game_state = GameStates.SHOW_BAG 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 equipment_inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and equipment_inventory_index < len( player.equipment_inventory.equipment_items): equipment_item = player.equipment_inventory.equipment_items[ equipment_inventory_index] player_turn_results.extend( player.equipment_inventory.use_equipment(equipment_item)) if take_stairs and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, constants) fov_map = initialize_fov(game_map) fov_recompute = True libtcod.console_clear(con) break else: message_log.add_message( Message("There are no stairs here...", libtcod.yellow)) if cast_magic_wand and game_state == GameStates.PLAYERS_TURN: #item_component = Item(use_function=cast_magic, damage=2, maximum_range=3) message_log.add_message( Message("You use the magic wand...", libtcod.orange)) game_state = GameStates.ENEMY_TURN 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 elif level_up == 'mgc': player.fighter.base_magic += 1 game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if show_help_menu: previous_game_state = game_state game_state = GameStates.HELP_MENU 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, GameStates.HELP_MENU, GameStates.SHOW_EQUIPMENT_INVENTORY): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) elif game_state == GameStates.SHOW_BAG: game_state = GameStates.PLAYERS_TURN else: save_game(player, entities, game_map, message_log, game_state) 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') equipment_item_added = player_turn_result.get( 'equipment_item_added') item_consumed = player_turn_result.get('consumed') equipment_consumed = player_turn_result.get('equipment_consumed') item_dropped = player_turn_result.get('item_dropped') staff_used = player_turn_result.get('staff_used') 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 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 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 equipment_item_added: entities.remove(equipment_item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if equipment_consumed: game_state = GameStates.ENEMY_TURN if staff_used: 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 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
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants): fov_recompute = True 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 while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) mouse_action = handle_mouse(mouse) move = action.get('move') aim_weapon = action.get('aim_weapon') 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') take_stairs = action.get('take_stairs') show_character_screen = action.get('show_character_screen') exit = action.get('exit') level_up = action.get('level_up') fullscreen = action.get('fullscreen') exit_menu = action.get('exit_menu') 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 dx == 0 and dy == 0: player.move(dx, dy) fov_recompute = True elif target: # ATTACK! if player.equipment.power_bonus == 4: attack_results = player.fighter.basic_bow_attack( target) player_turn_results.extend(attack_results) else: 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 aim_weapon and game_state == GameStates.PLAYERS_TURN: # ALSO ATTACK! Working on this at the moment if player.equipment.power_bonus == 4 and game_state == GameStates.PLAYERS_TURN: message_log.add_message( Message( 'Left click a tile to fire at it or right click to cancel!', libtcod.yellow)) game_state = GameStates.AIMING else: message_log.add_message( Message('You do not have a ranged weapon equipped!', libtcod.yellow)) elif wait: 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, constants) 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 game_state == GameStates.AIMING: if left_click: target_x, target_y = left_click coordinates = target_x, target_y # OKAY NOW WHAT THE F**K I DONT UNDESTAND WHY THIS WORKS OR HOW THE # VARIABLES GET FROM HERE TO THE FUNCTION I NEED THEM I MEAN JESUS CHRIST I JUST WOUND UP WITH THIS # ARRANGEMENT BY F*****G AROUND I MEAN IT WORKS BUT SERIOUSLY I DONT UNDERSTAND WHY THIS WORKS player_turn_results.append({'fire_weapon': True}) elif right_click: player_turn_results.append({'targeting_cancelled': True}) if exit_menu: 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}) if exit: save_game(player, entities, game_map, message_log, game_state) 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') fire_weapon = player_turn_result.get('fire_weapon') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') xp = player_turn_result.get('xp') if fire_weapon: destination_x, destination_y = coordinates target = get_blocking_entities_at_location( entities, destination_x, destination_y) try: if target == player: message_log.add_message( Message( 'No hitting yourself. Targeting cancelled.', libtcod.yellow)) game_state = previous_game_state elif target.ai and libtcod.map_is_in_fov( fov_map, target.x, target.y): attack_results = player.fighter.basic_bow_attack( target) player_turn_results.extend(attack_results) game_state = GameStates.ENEMY_TURN elif target.ai and not libtcod.map_is_in_fov( fov_map, target.x, target.y): message_log.add_message( Message( 'That cannot be targeted. Targeting cancelled.', libtcod.yellow)) game_state = previous_game_state except: message_log.add_message( Message( 'That cannot be targeted. Targeting cancelled.', libtcod.yellow)) game_state = previous_game_state 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 config.win == True: game_state = GameStates.PLAYER_DEAD if game_state == GameStates.PLAYER_DEAD: break if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message( Message('You equipped the {0}'.format( equipped.name))) if dequipped: message_log.add_message( Message('You dequipped the {0}'.format( dequipped.name))) game_state = GameStates.ENEMY_TURN 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 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 cast_sleep_aura(*args, **kwargs): caster = args[0] entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') radius = kwargs.get('radius') + math.floor(caster.fighter.magic / 2) target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') mana_cost = kwargs.get('mana_cost') if caster.fighter.magic + 5 >= 20: turns_asleep = 20 else: turns_asleep = 5 + caster.fighter.magic results = [] if caster.fighter.mana >= mana_cost: if not libtcod.map_is_in_fov(fov_map, target_x, target_y): results.append({ 'consumed': False, 'message': Message( "You can't target a location outside your field of view.", libtcod.red) }) return results results.append({ 'consumed': True, 'message': Message( "You cast the sleeping aura, putting everything to sleep within {0} tiles!" .format(radius), libtcod.orange) }) for entity in entities: if entity.distance(target_x, target_y) <= radius and entity.ai: asleep_ai = AsleepMonster(entity.ai, turns_asleep) asleep_ai.owner = entity entity.ai = asleep_ai results.append({ 'message': Message( "The {0} is asleep for {1} turns.".format( entity.name, turns_asleep), libtcod.orange) }) results.extend(caster.fighter.use_mana(mana_cost)) else: results.append({ 'consumed': False, 'target': None, 'message': Message("You don't have enough mana!", libtcod.orange) }) return results
def kill_player(player): """Change char and color for death.""" player.char = '%' player.color = libtcod.dark_red return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD