def edit_mode(self): self.state.game_state = Constants.PAUSE self.state.game_map.game_maps[self.state.game_map.game_map_id][self.state.get_target_x()][self.state.get_target_y()].targeted = True while not libtcod.console_is_window_closed(): self.state.turn += 1 Util.render_all(self.state) libtcod.console_flush() Input.handle_keys(self.state, False) if self.state.game_state == Constants.PLAYING: self.play_game()
def connect_stairs(state, previous_player_coords): down_stairs_ids = state.stairs[state.dungeon_level - 1][MapConstants.DOWN_STAIRS_OBJECT].keys() up_stairs_ids = state.stairs[state.dungeon_level][MapConstants.UP_STAIRS_OBJECT].keys() old_down_stair_id = Util.get_padded_coords(previous_player_coords[0], previous_player_coords[1]) new_up_stair_id = Util.get_padded_coords(state.player.x, state.player.y) MapCreation.connect_two_stairs(state, new_up_stair_id, old_down_stair_id) down_stairs_ids.remove(old_down_stair_id) up_stairs_ids.remove(new_up_stair_id) for down_stair_id in down_stairs_ids: up_stairs_id = up_stairs_ids.pop() MapCreation.connect_two_stairs(state, up_stairs_id, down_stair_id)
def get_monsters_to_place(state): max_monsters_table = [[2, 0], [3, 3], [5, 5]] max_monsters = Util.from_dungeon_level(state, max_monsters_table) # choose random number of monsters monster_chances = { MonsterConstants.ORC: 80, MonsterConstants.TROLL: Util.from_dungeon_level(state, [[15, 3], [30, 5], [60, 7]]) } num_monsters = libtcod.random_get_int(0, 0, max_monsters) monsters = [Util.random_choice(monster_chances) for i in range(num_monsters)] return monsters
def play_game(self): self.state.game_state = Constants.PLAYING self.state.game_map.game_maps[self.state.game_map.game_map_id][self.state.get_target_x()][self.state.get_target_y()].targeted = False self.state.game_map.game_maps[self.state.game_map.previous_map_id][self.state.get_target_x()][self.state.get_target_y()].targeted = False while not libtcod.console_is_window_closed(): sleep(0.20) self.state.turn += 1 Util.render_all(self.state) libtcod.console_flush() Input.handle_keys(self.state, True) if self.state.game_state == Constants.PAUSE: self.edit_mode() self.state.game_map.process_map(self.state)
def handle_targeting_keys(key, state): if key.vk == libtcod.KEY_CHAR: if key.c == ord('k'): return Util.player_target(state, 0, -1) elif key.c == ord('j'): return Util.player_target(state, 0, 1) elif key.c == ord('h'): return Util.player_target(state, -1, 0) elif key.c == ord('l'): return Util.player_target(state, 1, 0) elif key.c == ord('y'): return Util.player_target(state, -1, -1) elif key.c == ord('u'): return Util.player_target(state, 1, -1) elif key.c == ord('b'): return Util.player_target(state, -1, 1) elif key.c == ord('n'): return Util.player_target(state, 1, 1) else: state.set_player_action(Constants.NOT_VALID_KEY) elif key.vk == libtcod.KEY_ESCAPE: state.set_game_state(Constants.PLAYING) elif key.vk == libtcod.KEY_ENTER: state.set_game_state(Constants.FOUND_TARGET) else: state.set_player_action(Constants.NOT_VALID_KEY)
def inventory_menu(self, header, state): # show a menu with each item of the inventory as an option if len(self.inventory) == 0: options = ['Inventory is empty.'] else: options = [] for item in state.player_inventory.inventory: text = item.name if item.equipment and item.equipment.is_equipped: text = text + ' (on ' + item.equipment.slot + ')' options.append(text) index = self.menu.display_menu_return_index(header, options, Constants.INVENTORY_WIDTH, state.con) if index is None or len(self.inventory) == 0: state.set_player_action(Constants.NOT_VALID_KEY) Util.refresh(state) return None return self.inventory[index].item
def previous_level(self): up_stairs_id = Util.get_padded_coords(self.state.player.x, self.state.player.y) down_stairs_id = self.follow_stairs(MapConstants.UP_STAIRS_OBJECT, up_stairs_id, self.state.dungeon_level) self.state.dijkstra_map_update = True self.state.player.x, self.state.player.y = Util.get_coords_from_padded_coords(down_stairs_id) if self.state.dungeon_level == 0: self.state.set_player_action(Constants.EXIT) return else: self.state.dungeon_level -= 1 self.state.objects = self.state.objects_map[self.state.dungeon_level] self.state.game_map.set_game_map(self.state.dungeon_level) # TODO Make fov_map container class self.state.fov_map = self.state.fov_map_map[self.state.dungeon_level] self.initialize_fov(self.state.dungeon_level) self.state.set_player_action(None) self.state.fov_recompute = True
def create_stairs_of_type(state, stairs_coords, type): for stair_coords in stairs_coords: stairs = Object(stair_coords[0], stair_coords[1], type, MapConstants.STAIRS_NAME, MapConstants.STAIRS_COLOR, always_visible=True) state.objects_map[state.dungeon_level].append(stairs) stairs.send_to_back(state.objects_map[state.dungeon_level]) stairs_id = Util.get_padded_coords(stairs.x, stairs.y) state.stairs[state.dungeon_level][type][stairs_id] = None
def cast_lightning(state): monster = Util.closest_monster(state, Constants.LIGHTNING_RANGE) if monster is None: state.status_panel.message('No enemy is close enough to strike with lightning', libtcod.red) return Constants.CANCELLED state.status_panel.message('A lightning bolt strikes the ' + monster.name + ' with a ZAP! The damage done is ' + str(Constants.LIGHTNING_DAMAGE) + ' hp.', libtcod.light_blue) monster.fighter.take_damage(Constants.LIGHTNING_DAMAGE, state)
def cast_fireball(state): # TODO: Add range check x, y = Util.target_tile(state) state.game_map.get_map()[x][y].set_targeted(False) for object in state.objects: if object.distance(x, y) <= Constants.FIREBALL_RADIUS and object.fighter: state.status_panel.message('You sling a fireball at: ' + object.name + ' with a BAMboosh! The damage done is ' + str(Constants.FIREBALL_DAMAGE) + ' hp.', libtcod.light_blue) object.fighter.take_damage(Constants.FIREBALL_DAMAGE, state)
def target_tile(state, start_x=None, start_y=None): state.set_game_state(Constants.TARGETING) if start_x is None or start_y is None: state.set_target(state.player.x, state.player.y) else: state.set_target(start_x, start_y) while state.get_game_state() == Constants.TARGETING: Input.handle_keys(state) Util.refresh(state) if state.get_game_state() == Constants.FOUND_TARGET: x, y = state.get_target_coords() state.set_game_state(Constants.PLAYING) # TODO: Make target class? how to save/where to save targeting coords? # while if state.get_target_x() is None or state.get_target_y() is None: return Constants.CANCELLED state.game_map.get_map()[state.get_target_x()][state.get_target_y()].set_targeted(False) return state.get_target_x(), state.get_target_y()
def next_level(self): self.state.dijkstra_map_update = True self.state.dungeon_level += 1 self.state.status_panel.message('You take a moment to rest and recover 50% health', libtcod.violet) self.state.player.fighter.heal(self.state.player.fighter.max_hp(self.state) / 2, self.state) self.state.status_panel.message('and now you descend into the depths of the dungeon', libtcod.red) if self.state.dungeon_level in self.state.game_map.complete_game_map: self.state.game_map.set_game_map(self.state.dungeon_level) down_stairs_id = Util.get_padded_coords(self.state.player.x, self.state.player.y) up_stairs_id = self.follow_stairs(MapConstants.DOWN_STAIRS_OBJECT, down_stairs_id, self.state.dungeon_level - 1) self.state.player.x, self.state.player.y = Util.get_coords_from_padded_coords(up_stairs_id) else: self.state.objects_map[self.state.dungeon_level] = [self.state.player] self.state.game_map.generate_map(self.state, self.state.dungeon_level) self.state.score += self.state.dungeon_level * 10 self.state.objects = self.state.objects_map[self.state.dungeon_level] self.initialize_fov(self.state.dungeon_level) self.state.set_player_action(None) self.state.fov_recompute = True Util.render_all(self.state)
def pick_up(self, state): if len(state.player_inventory.inventory) >= 26: state.inventory.status_panel.message('Your inventory is full, cannot pick up ' + self.owner.name + '.', libtcod.red) else: state.player_inventory.inventory.append(self.owner) state.objects.remove(self.owner) state.status_panel.message('You picked up a ' + self.owner.name + '!', libtcod.green) equipment = self.owner.equipment if equipment and Util.get_equipped_in_slot(state, equipment.slot) is None: equipment.equip(state)
def cast_confuse(state): # monster = Constants.closest_monster(util, Constants.CONFUSE_RANGE) monster = Util.target_monster(state, Constants.CONFUSE_RANGE) if monster is None: state.status_panel.message('No enemy is close enough to confuse', libtcod.red) return Constants.CANCELLED old_ai = monster.ai monster.ai = ConfusedMonster(old_ai, state) monster.clear(state.con) monster.ai.owner = monster state.status_panel.message('The eyes of the ' + monster.name + ' look vacant, as he starts to stumble around!', libtcod.light_green)
def spell_menu(self, header, state): # show a menu with each item of the inventory as an option if len(self.spells) == 0: options = ['You have no spells.'] else: options = [] for spell_name in state.player.caster.spells: spell = state.magic.spells[spell_name] text = spell_name + ' : ' + spell.description + ', mp cost: ' + str(spell.mp_cost) + ', spell range: ' + str( spell.range) + \ ', spell power: ' + str(spell.power) options.append(text) index = self.menu.display_menu_return_index(header, options, Constants.INVENTORY_WIDTH, state.con) if index is not None: spell_name = options[index].split(' : ')[0] if index is None or len(self.spells) == 0: state.set_player_action(Constants.NOT_VALID_KEY) Util.refresh(state) return None return state.magic.spells[spell_name]
def process_map(self, state): self.update_game_map_id() for column in self.game_maps[self.previous_map_id]: for tile in column: num_adjacent_alive = Util.get_count_of_adjacent_tile(self.game_maps[self.previous_map_id], tile.x, tile.y) if tile.alive: if num_adjacent_alive < 2 or num_adjacent_alive > 3: self.game_maps[self.game_map_id][tile.x][tile.y].alive = False else: self.game_maps[self.game_map_id][tile.x][tile.y].alive = True else: if num_adjacent_alive == 3: self.game_maps[self.game_map_id][tile.x][tile.y].alive = True else: self.game_maps[self.game_map_id][tile.x][tile.y].alive = False self.game_map = self.game_maps[self.game_map_id]
def dijkstra_on_map(state, source_x, source_y): unvisited_tiles = queue.PriorityQueue() if state.dijkstra_map_update: state.dijkstra_map = [[sys.maxint for col in range(len(state.game_map.game_map[0]))] for row in range(len(state.game_map.game_map))] state.dijkstra_map_update = False for row in state.game_map.game_map: for tile in row: if tile.x == source_x and tile.y == source_y: state.dijkstra_map[source_x][source_y] = 0 unvisited_tiles.put((0, (tile.x, tile.y))) elif tile.blocked: continue else: unvisited_tiles.put((sys.maxint, (tile.x, tile.y))) while not unvisited_tiles.empty(): closest_dist, closest_tile = unvisited_tiles.get() closest_neighbors = Util.get_adjacent_tiles(state, closest_tile[0], closest_tile[1]) for neighbor in closest_neighbors: if not neighbor.blocked: temp_dist = closest_dist + 1 if temp_dist < state.dijkstra_map[neighbor.x][neighbor.y]: state.dijkstra_map[neighbor.x][neighbor.y] = temp_dist unvisited_tiles.put((temp_dist, (neighbor.x, neighbor.y))) neighbor.dist_from_player = temp_dist
def get_items_to_place(state): max_items_table = [[3, 0], [5, 3]] max_items = Util.from_dungeon_level(state, max_items_table) item_chances = { ItemConstants.HEALTH_POTION: 35, ItemConstants.SCROLL_OF_LIGHTNING_BOLT: Util.from_dungeon_level(state, [[25, 1]]), ItemConstants.SCROLL_OF_FIREBALL: Util.from_dungeon_level(state, [[25, 1]]), ItemConstants.SCROLL_OF_CONFUSE: Util.from_dungeon_level(state, [[10, 1]]), } equipment_chances = { EquipmentConstants.SWORD: 50, # Util.from_dungeon_level(state, [[5, 4]]), EquipmentConstants.SHIELD: 50 # Util.from_dungeon_level(state, [[15, 8]]), } num_total_items = libtcod.random_get_int(0, 0, max_items) num_items = libtcod.random_get_int(0, 0, num_total_items) num_equipments = num_total_items - num_items items = [Util.random_choice(item_chances) for i in range(num_items)] equipments = [Util.random_choice(equipment_chances) for i in range(num_equipments)] return items, equipments
def play_game(self): self.state.set_game_state(Constants.PLAYING) self.state.set_player_action(None) self.state.fov_recompute = True while not libtcod.console_is_window_closed(): self.state.turn += 1 Util.render_all(self.state) libtcod.console_flush() Util.check_level_up(self.state) for object in self.state.objects: object.clear(self.state.con) self.state.set_player_action(Constants.DID_NOT_TAKE_TURN) while self.state.get_player_action() == Constants.DID_NOT_TAKE_TURN: Input.handle_keys(self.state) player_action = self.state.get_player_action() if player_action == Constants.EXIT or self.state.player.color == libtcod.dark_red: self.save_game() break if self.state.get_game_state() == Constants.PLAYING and self.state.get_player_action() != Constants.DID_NOT_TAKE_TURN: AiUtils.dijkstra_on_map(self.state, self.state.player.x, self.state.player.y) monsters_still_alive = False for object in self.state.objects: if object.ai: monsters_still_alive = True object.ai.take_turn(self.state) if not monsters_still_alive and self.state.game_type == Constants.BATTLE: old_player_coords = self.state.player.x, self.state.player.y self.state.game_map.generate_battle_map(self.state) self.state.player.x, self.state.player.y = old_player_coords if player_action == Constants.NEXT_LEVEL: self.next_level() elif player_action == Constants.PREVIOUS_LEVEL: self.previous_level() elif player_action == Constants.EXIT or self.state.player.color == libtcod.dark_red: Util.render_all(self.state) self.save_game() break self.state.status_panel.message('###### Turn ' + str(self.state.turn) + ' has ended')
def defense(self, state): bonus = sum(equipment.defense_bonus for equipment in Util.get_all_equiped(state, self.owner)) return self.base_defense + bonus
def equip(self, state): old_equipment = Util.get_equipped_in_slot(state, self.slot) if old_equipment is not None: old_equipment.equipment.dequip(state) self.is_equipped = True state.status_panel.message('Equiped ' + self.owner.name + ' on ' + self.slot + '.', libtcod.light_green)
def power(self, state): bonus = sum(equipment.power_bonus for equipment in Util.get_all_equiped(state, self.owner)) return self.base_power + bonus
def handle_playing_keys(key, state): if key.vk == libtcod.KEY_CHAR: if key.c == ord('k'): Util.player_move_or_attack(state, 0, -1) elif key.c == ord('j'): Util.player_move_or_attack(state, 0, 1) elif key.c == ord('h'): Util.player_move_or_attack(state, -1, 0) elif key.c == ord('l'): Util.player_move_or_attack(state, 1, 0) elif key.c == ord('y'): Util.player_move_or_attack(state, -1, -1) elif key.c == ord('u'): Util.player_move_or_attack(state, 1, -1) elif key.c == ord('b'): Util.player_move_or_attack(state, -1, 1) elif key.c == ord('n'): Util.player_move_or_attack(state, 1, 1) elif key.c == ord('.'): pass elif key.c == ord('v'): Input.look(state) state.set_player_action(Constants.NOT_VALID_KEY) elif key.c == ord('i'): chosen_item = state.player_inventory.inventory_menu( 'Press the key next to an item to use it, or any other to cancel.\n', state) if chosen_item is not None: chosen_item.use(state) # else: # state.set_player_action(Constants.NOT_VALID_KEY) elif key.c == ord('I'): chosen_spell = state.player_spell_inventory.spell_menu( 'Press the key next to a spell to use it, or any other to cancel.\n', state) if chosen_spell is not None: chosen_spell.cast(state, state.player) elif key.c == ord('d'): chosen_item = state.player_inventory.inventory_menu( 'Press the key next to an item to drop it, or any other to cancel.\n', state) if chosen_item is not None: chosen_item.drop(state) elif key.c == ord('g'): # pick up an item for object in state.objects: # look for an item in the player's tile if object.x == state.player.x and object.y == state.player.y and object.item: object.item.pick_up(state) break elif key.c == ord('>'): padded_player_coords = Util.get_padded_coords(state.player.x, state.player.y) if padded_player_coords in state.stairs[state.dungeon_level][MapConstants.DOWN_STAIRS_OBJECT].keys(): state.set_player_action(Constants.NEXT_LEVEL) elif key.c == ord('<'): padded_player_coords = Util.get_padded_coords(state.player.x, state.player.y) if padded_player_coords in state.stairs[state.dungeon_level][MapConstants.UP_STAIRS_OBJECT].keys(): state.set_player_action(Constants.PREVIOUS_LEVEL) elif key.c == ord('c'): # show character information level_up_xp = Constants.LEVEL_UP_BASE + state.player.level * Constants.LEVEL_UP_FACTOR Util.show_character_screen(state, level_up_xp) Util.refresh(state) else: state.set_player_action(Constants.NOT_VALID_KEY)
def max_hp(self, state): bonus = sum(equipment.hp_bonus for equipment in Util.get_all_equiped(state, self.owner)) return self.base_max_hp + bonus
def handle_targeting_keys(key, state): if key.vk == libtcod.KEY_CHAR: if key.c == ord("k"): return Util.player_target(state, 0, -1) elif key.c == ord("j"): return Util.player_target(state, 0, 1) elif key.c == ord("h"): return Util.player_target(state, -1, 0) elif key.c == ord("l"): return Util.player_target(state, 1, 0) elif key.c == ord("y"): return Util.player_target(state, -1, -1) elif key.c == ord("u"): return Util.player_target(state, 1, -1) elif key.c == ord("b"): return Util.player_target(state, -1, 1) elif key.c == ord("n"): return Util.player_target(state, 1, 1) elif key.c == ord("x"): state.status_panel.message( "toggleing x: " + str(state.get_target_x()) + ", y: " + str(state.get_target_y()) ) return Util.toggle_alive(state) elif key.c == ord("p"): if state.game_state == Constants.PAUSE: state.status_panel.message("handle_targeting_keys: going from paused to playing") state.game_state = Constants.PLAYING return elif state.game_state == Constants.PLAYING: state.status_panel.message("handle_targeting_keys: going from playing to paused") state.game_state = Constants.PAUSE return else: state.status_panel.message("game state is: " + str(state.game_state)) elif key.c == ord("s"): Util.save_map(state) else: state.set_player_action(Constants.NOT_VALID_KEY)