def _place_random_creatures(new_map, player): for r_count in range(0, RUSALKA_GOAL): while True: r_pos = algebra.Location( libtcod.random_get_int(new_map.rng, new_map.pool_x, new_map.width - 3), libtcod.random_get_int(new_map.rng, 3, new_map.height - 3)) if new_map.terrain[r_pos.x][r_pos.y] != map.TERRAIN_WALL: break # TODO: can we ensure this doesn't block the passage? #for x in range(r_pos.x-2, r_pos.x+3): # for y in range(r_pos.y-2, r_pos.y+3): # if new_map.terrain[x][y] == map.TERRAIN_FLOOR: # new_map.terrain[x][y] = map.TERRAIN_WATER mob = bestiary.rusalka(new_map, r_pos, player) _new_equipment(mob, miscellany.tortoiseshell_comb()) for vc_count in range(0, VODANYOI_CLUSTER_GOAL): while True: vc_pos = algebra.Location( libtcod.random_get_int(new_map.rng, new_map.pool_x, new_map.width - 3), libtcod.random_get_int(new_map.rng, 3, new_map.height - 3)) if new_map.terrain[vc_pos.x][vc_pos.y] == map.TERRAIN_FLOOR: break _place_vodanyoi_cluster(new_map, player, vc_pos)
def _dig_about(new_map, room_rect, left_min, left_max, right_min, right_max): x = room_rect.center().x top = new_map.rnd(3, room_rect.y1 / 2) _create_v_tunnel(new_map, room_rect.y1, top, x) _consider_terminal_room(new_map, x, top) cross_tunnel_count = new_map.rnd(2, 4) tunnel_interval = (room_rect.y1 - 2 - top) / cross_tunnel_count for i in range(cross_tunnel_count): y = new_map.rnd(0, tunnel_interval - 1) + top + i * tunnel_interval if new_map.cave_zones[0].contains(algebra.Location(x, y)): continue left = new_map.rnd(left_min, left_max) right = new_map.rnd(right_min, right_max) _create_h_tunnel(new_map, left, right, y) _consider_terminal_room(new_map, left, y) _consider_terminal_room(new_map, right, y) bottom = new_map.rnd(room_rect.y2 + (new_map.height - room_rect.y2) / 2, new_map.height - 3) _create_v_tunnel(new_map, room_rect.y2, bottom, x) _consider_terminal_room(new_map, x, bottom) cross_tunnel_count = new_map.rnd(2, 4) tunnel_interval = (bottom - room_rect.y2 - 2) / cross_tunnel_count for i in range(cross_tunnel_count): y = new_map.rnd( 0, tunnel_interval - 1) + room_rect.y2 + 3 + i * tunnel_interval if new_map.cave_zones[0].contains(algebra.Location(x, y)): continue left = new_map.rnd(left_min, left_max) right = new_map.rnd(right_min, right_max) _create_h_tunnel(new_map, left, right, y) _consider_terminal_room(new_map, left, y) _consider_terminal_room(new_map, right, y)
def _test_attack(): af = Fighter(100, 0, 10, 0) df = Fighter(100, 0, 0, 0) a = Object(algebra.Location(0, 0), 'a', 'test attacker', libtcod.white, fighter=af) d = Object(algebra.Location(1, 1), 'd', 'test defender', libtcod.white, fighter=df) assert af.hp == 100 assert df.hp == 100 # if defense == 0, full damage is done attack(af, d, False) assert df.hp == 90 df.base_defense = 5 attack(af, d, False) assert df.hp == 85 # if defense > attack, no damage is done df.base_defense = 15 attack(af, d, False) assert df.hp == 85
def _test_move(): o = Object(algebra.Location(0, 0), 'o', 'test object', libtcod.white) o.current_map = _MockMap() assert o.pos == algebra.Location(0, 0) move(o, algebra.south) assert o.pos == algebra.Location(0, 1) move(o, algebra.southeast) assert o.pos == algebra.Location(1, 2)
def _test_move_towards(): o = Object(algebra.Location(0, 0), 'o', 'test object', libtcod.white) o.current_map = _MockMap() assert o.pos == algebra.Location(0, 0) move_towards(o, algebra.Location(10, 10)) assert o.pos == algebra.Location(1, 1) move_towards(o, algebra.Location(10, 10)) assert o.pos == algebra.Location(2, 2) move_towards(o, algebra.Location(-10, 2)) assert o.pos == algebra.Location(1, 2) move_towards(o, o.pos) assert o.pos == algebra.Location(1, 2)
def _make_grotto(new_map): if not new_map.grotto_region: return region_center = algebra.Location( new_map.region_seeds[new_map.grotto_region][0], new_map.region_seeds[new_map.grotto_region][1]) print('Grotto at ' + str(region_center.x) + ' ' + str(region_center.y)) stairs = Object(region_center, '<', 'cave mouth', libtcod.white, always_visible=True) stairs.destination = None stairs.dest_position = None stairs.generator = ca_cartographer.make_map new_map.objects.insert(0, stairs) new_map.portals.insert(0, stairs) new_map.grotto_stairs = region_center # Would be nice to have a structure around it, but for now just keep the top clear. for x in range(region_center.x - 2, region_center.x + 3): for y in range(region_center.y - 2, region_center.y + 3): if (new_map.region[x][y] == new_map.grotto_region and new_map.terrain[x][y] != map.TERRAIN_SLOPE): new_map.terrain[x][y] = map.TERRAIN_GROUND
def _link_up_stairs(new_map, old_map, old_quarry_stairs): old_quarry_stairs[1].dest_position = algebra.Location( new_map.width / 2, new_map.height / 2) old_quarry_stairs[ 0].dest_position = old_quarry_stairs[1].dest_position + MINE_SCALE * ( old_quarry_stairs[0].pos - old_quarry_stairs[1].pos) old_quarry_stairs[ 2].dest_position = old_quarry_stairs[1].dest_position + MINE_SCALE * ( old_quarry_stairs[2].pos - old_quarry_stairs[1].pos) map_inset = algebra.Rect(10, 10, new_map.width - 20, new_map.height - 20) old_quarry_stairs[0].dest_position.bound(map_inset) old_quarry_stairs[2].dest_position.bound(map_inset) # print('Map is ' + str(new_map.width) + ' x ' + str(new_map.height)) # print('Stairs come from ', [i.pos for i in old_quarry_stairs]) # print('Stairs to mines connect to ', [i.dest_position for i in old_quarry_stairs]) for i in range(3): old_quarry_stairs[i].destination = new_map stairs = Object(old_quarry_stairs[i].dest_position, '>', 'mine exit', libtcod.white, always_visible=True) stairs.destination = old_map stairs.dest_position = old_quarry_stairs[i].pos new_map.objects.insert(0, stairs) new_map.portals.insert(0, stairs)
def new_game(): """ Starts a new game, with a default player on level 1 of the dungeon. Returns the player object. """ # Must initialize the log before we do anything that might emit a message. log.init() fighter_component = Fighter(hp=100, defense=1, power=2, xp=0, death_function=player_death) player = Object(algebra.Location(0, 0), '@', 'player', libtcod.white, blocks=True, fighter=fighter_component) player.inventory = [] player.level = 1 player.game_state = 'playing' # True if there's a (hostile) fighter in FOV player.endangered = False obj = miscellany.dagger() player.inventory.append(obj) actions.equip(player, obj.equipment, False) obj.always_visible = True cartographer.make_map(player, 1) renderer.clear_console() renderer.update_camera(player) log.message('Welcome stranger! Prepare to perish in the Tombs of the Ancient Kings.', libtcod.red) log.message('Press ? or F1 for help.') return player
def _draw_fov_using_terrain(player): """ Overly optimized: this code inlines Map.terrain_at(), Map.is_explored(), and ScreenCoords.toWorldCoords() in order to get a 2.5x speedup on large maps. """ libtcod.console_clear(_con) current_map = player.current_map pos = algebra.Location(0, 0) for screen_y in range(min(current_map.height, config.MAP_PANEL_HEIGHT)): pos.set(player.camera_position.x, player.camera_position.y + screen_y) for screen_x in range(min(current_map.width, config.MAP_PANEL_WIDTH)): # pos = ScreenCoords.toWorldCoords(player.camera_position, (screen_x, screen_y)) visible = libtcod.map_is_in_fov(current_map.fov_map, pos.x, pos.y) # terrain = current_map.terrain_at(pos) terrain = map.terrain_types[current_map.terrain[pos.x][pos.y]] if not visible: # if current_map.is_explored(pos): if current_map._explored[pos.x][pos.y]: libtcod.console_set_char_background(_con, screen_x, screen_y, terrain.unseen_color, libtcod.BKGND_SET) else: libtcod.console_set_char_background(_con, screen_x, screen_y, terrain.seen_color, libtcod.BKGND_SET) current_map.explore(pos) pos.x += 1
def _probe_for_stair(new_map, x_range, center_y): for y in (center_y, center_y - 1, center_y + 1, center_y - 2, center_y + 2): for x in x_range: if new_map.terrain[x][y] != map.TERRAIN_WALL: return algebra.Location(x, y) return None
def _scatter_ponds(new_map): for x in range(new_map.pool_x + 1, new_map.width - 2): for y in range(2, new_map.height - 2): #if _check_for_openness(new_map, x, y) and new_map.rnd(1, 2) == 1: #pos = algebra.Location(x, y) # + actions.random_direction() if _check_for_dryness(new_map, x, y) and new_map.rnd(1, 2) == 1: pos = algebra.Location(x, y) + random_direction(new_map) new_map.terrain[pos.x][pos.y] = map.TERRAIN_WATER
def _build_map(new_map): new_map.rng = libtcod.random_new_from_seed(new_map.random_seed) new_map.spare_terrain = copy.deepcopy( new_map.terrain ) # [[0 for y in range(new_map.height)] for x in range(new_map.width)] dig_ca_region(new_map, algebra.Rect(0, 0, new_map.width, new_map.height), 4, 3) center = algebra.Location(new_map.width / 2, new_map.height / 2) stair_loc = _probe_for_stair(new_map, range(new_map.width - 2, center.x, -1), center.y) if not stair_loc: # Uh-oh; no guarantee of completion print('Recursing with unenterable map:') # _dump(new_map) new_map.random_seed = libtcod.random_save(new_map.rng) return _build_map(new_map) pool_x = new_map.width / 4 for x in range(pool_x - 6, pool_x + 7): for y in range(center.y - 6, center.y + 7): dx = x - pool_x dy = y - center.y if math.sqrt(dx**2 + dy**2) > 6: continue if new_map.terrain[x][y] == map.TERRAIN_WALL: new_map.terrain[x][y] = map.TERRAIN_GROUND new_map.pool_x = pool_x _scatter_ponds(new_map) # Can we reach from the stairs to the center of the pool? _floodfill(new_map, stair_loc.x, stair_loc.y, map.TERRAIN_GROUND, map.TERRAIN_FLOOR) if new_map.terrain[pool_x][center.y] != map.TERRAIN_FLOOR: # Uh-oh; no guarantee of completion print('Recursing with disconnected map:') # _dump(new_map) new_map.random_seed = libtcod.random_save(new_map.rng) return _build_map(new_map) # Close up any unconnected subcaves; flood any western bits for x in range(1, new_map.width - 1): for y in range(1, new_map.height - 1): if new_map.terrain[x][y] == map.TERRAIN_GROUND: new_map.terrain[x][y] = map.TERRAIN_WALL elif x < pool_x and new_map.terrain[x][y] == map.TERRAIN_FLOOR: new_map.terrain[x][y] = map.TERRAIN_WATER #for x in range(0, new_map.width): # new_map.terrain[x][0] = map.TERRAIN_WALL # new_map.terrain[x][new_map.height-1] = map.TERRAIN_WALL #for y in range(0, new_map.height): # new_map.terrain[0][y] = map.TERRAIN_WALL # new_map.terrain[new_map.width-1][y] = map.TERRAIN_WALL return stair_loc
def _inhabit_rotunda(new_map, peak): goddess = Object( algebra.Location(peak[0], peak[1]), '@', 'The White Goddess', libtcod.white, blocks=True, interactable=Interactable(use_function=quest.goddess_charge)) new_map.objects.append(goddess)
def dagger(): return Object( algebra.Location(0, 0), '-', 'dagger', libtcod.sky, item=Item( description='A leaf-shaped bronze knife; provides +2 Attack'), equipment=Equipment(slot='right hand', power_bonus=2))
def _inhabit_pool(new_map): pos = algebra.Location(new_map.pool_x, new_map.height / 2) nymph = Object(pos, '@', 'nymph', libtcod.azure, blocks=True, interactable=Interactable(use_function=quest.nymph_info)) new_map.objects.append(nymph)
def shield(pos=algebra.Location(0, 0)): return Object( pos, '[', 'shield', libtcod.darker_orange, item=Item( description='A bronze-edged oval shield; provides +1 Defense'), equipment=Equipment(slot='left hand', defense_bonus=1))
def sword(pos=algebra.Location(0, 0)): return Object( pos, '/', 'sword', libtcod.sky, item=Item(description= 'A heavy-tipped bronze chopping sword; provides +3 Attack'), equipment=Equipment(slot='right hand', power_bonus=3))
def healing_potion(pos=algebra.Location(0, 0)): return Object( pos, '!', 'healing potion', libtcod.violet, item=Item( use_function=spells.cast_heal, description='A flask of revivifying alchemical mixtures; heals ' + str(spells.HEAL_AMOUNT) + ' hp.'))
def fireball_scroll(pos=algebra.Location(0, 0)): return Object( pos, '#', 'scroll of fireball', libtcod.light_yellow, item=Item( use_function=spells.cast_fireball, description= 'Reading these runes will cause a burst of flame inflicting ' + str(spells.FIREBALL_DAMAGE) + ' hp on nearby creatures.'))
def confusion_scroll(pos=algebra.Location(0, 0)): return Object( pos, '#', 'scroll of confusion', libtcod.light_yellow, item=Item( use_function=spells.cast_confuse, description= 'Reading these runes will confuse the creature you focus on for a short time.' ))
def _place_random_creatures(new_map, player): start_region = new_map.region[player.pos.x][player.pos.y] terrain_chances = { 'lake': { None: 10 }, 'marsh': { None: 10, bestiary.swamp_goblin: 10 }, 'desert': { None: 20, bestiary.hyena_pair: 5, bestiary.gazelle: 10 }, 'scrub': { None: 20, bestiary.hyena: 2, bestiary.gazelle: 4, bestiary.deer: 4, bestiary.wolf: 2 }, 'forest': { None: 20, bestiary.deer: 10, bestiary.wolf_pair: 5, bestiary.bear: 3 }, 'rock': { None: 10, bestiary.snow_leopard: 1 }, 'ice': { None: 10, bestiary.snow_leopard: 1 } } for r in range(len(new_map.region_seeds)): if (r == start_region or (new_map.quarry_regions and r in new_map.quarry_regions)): continue fn = _random_choice(terrain_chances[new_map.region_terrain[r]]) if fn is not None: pos = algebra.Location(new_map.region_seeds[r][0], new_map.region_seeds[r][1]) while new_map.is_blocked_at(pos): pos += actions.random_direction() pos.bound( algebra.Rect(0, 0, new_map.width - 1, new_map.height - 1)) if new_map.caravanserai and new_map.caravanserai.bounds.contains( pos): continue # print('Creature in region ' + str(r) + ' at ' + str(pos.x) + ' ' + str(pos.y)) fn(new_map, pos, player)
def lightning_scroll(pos=algebra.Location(0, 0)): return Object( pos, '#', 'scroll of lightning bolt', libtcod.light_yellow, item=Item( use_function=spells.cast_lightning, description= 'Reading these runes will strike your nearest foe with lightning for ' + str(spells.LIGHTNING_DAMAGE) + ' hp.'))
def make_map(player, dungeon_level): """ Creates a new simple map at the given dungeon level. Sets player.current_map to the new map, and adds the player as the first object. """ new_map = map.OutdoorMap(config.OUTDOOR_MAP_WIDTH, config.OUTDOOR_MAP_HEIGHT, dungeon_level) new_map.objects.append(player) player.current_map = new_map player.camera_position = algebra.Location(0, 0) new_map.random_seed = libtcod.random_save(0) _build_map(new_map) # Might want to change this later, but this is required in creature placement # routines so we know what region the player starts in so there isn't a # wandering monster jumping down their throat. Unless, of course, this # start point is on a *region border* and there's a monster in the next # region over... player.pos = algebra.Location(config.OUTDOOR_MAP_WIDTH - 8, 12) _place_random_creatures(new_map, player) _inhabit_rotunda(new_map, new_map.peak) if new_map.caravanserai: compound_cartographer.inhabit_caravanserai(new_map, player) if new_map.quarry_regions: _inhabit_quarry(new_map, player) # make sure we're not starting on top of an object or terrain feature while (new_map.terrain_at(player.pos).name != 'ground'): # subtle bug? doesn't use the map-building random number generator player.pos = player.pos + actions.random_direction() player.pos.bound( algebra.Rect(0, 0, new_map.width - 1, new_map.height - 1)) new_map.initialize_fov() # setting an instancemethod breaks shelve save games # new_map.xp_visit = type(map.BaseMap.xp_visit)(_mountain_exploration, new_map, map.BaseMap) new_map.xp_visit = _mountain_exploration return True
def _random_position_in_region(new_map, region): """ Given a region of a map, return an algebra.Location in the region """ center = new_map.region_seeds[region] while True: candidate = algebra.Location( libtcod.random_get_int(0, center[0] - 5, center[0] + 5), libtcod.random_get_int(0, center[1] - 5, center[1] + 5)) if (candidate.x < 0 or candidate.y < 0 or candidate.x >= new_map.width or candidate.y >= new_map.height): continue if new_map.region[candidate.x][candidate.y] == region: return candidate
def _draw_outdoors(player): """ Overly optimized: this code inlines Map.terrain_at(), Map.is_explored(), and ScreenCoords.toWorldCoords() in order to get a 2.5x speedup on large maps. """ libtcod.console_clear(_con) current_map = player.current_map pos = algebra.Location(0, 0) player_elevation = current_map.region_elevations[current_map.region[ player.pos.x][player.pos.y]] for screen_y in range(min(current_map.height, config.MAP_PANEL_HEIGHT)): pos.set(player.camera_position.x, player.camera_position.y + screen_y) for screen_x in range(min(current_map.width, config.MAP_PANEL_WIDTH)): visible = libtcod.map_is_in_fov(current_map.fov_map, pos.x, pos.y) terrain = map.terrain_types[current_map.terrain[pos.x][pos.y]] explored = current_map._explored[pos.x][pos.y] current_elevation = current_map.region_elevations[ current_map.region[pos.x][pos.y]] icon = terrain.icon if icon: if terrain.name == 'slope' and current_elevation < player_elevation: icon = 'v' # draw (player_elevation - 1) so that we can see up-slopes; lower than that just # gets a colored fill if (current_elevation + 1 < player_elevation): if visible or explored: libtcod.console_put_char_ex( _con, screen_x, screen_y, '#', map.region_colors_seen[current_map.region_terrain[ current_map.region[pos.x][pos.y]]], libtcod.black) if visible: current_map.explore(pos) elif not visible: if explored: _draw_unseen(player, screen_x, screen_y, pos, terrain, icon) else: seen_color = terrain.seen_color if not seen_color: seen_color = map.region_colors_seen[ current_map.region_terrain[current_map.region[pos.x][ pos.y]]] libtcod.console_put_char_ex(_con, screen_x, screen_y, icon, terrain.icon_color, seen_color) current_map.explore(pos) pos.x += 1
def _place_vodanyoi_cluster(new_map, player, vc_pos): # print('trying to place vc around ' + str(vc_pos.x) + ' ' + str(vc_pos.y)) for v_count in range(0, VODANYOI_CLUSTER_SIZE): while True: v_pos = algebra.Location( libtcod.random_get_int(new_map.rng, vc_pos.x - 2, vc_pos.x + 2), libtcod.random_get_int(new_map.rng, vc_pos.y - 2, vc_pos.y + 2)) if new_map.terrain[v_pos.x][v_pos.y] != map.TERRAIN_WALL: break # print(' v at ' + str(v_pos.x) + ' ' + str(v_pos.y)) if v_count == 0: v = bestiary.vodanyoi_warrior(new_map, v_pos, player) _new_equipment(v, miscellany.spear()) else: v = bestiary.vodanyoi(new_map, v_pos, player)
def _add_doors(new_map): for x in range(1, new_map.width - 1): for y in range(1, new_map.height - 1): pos = algebra.Location(x, y) if new_map.terrain[x][y] != map.TERRAIN_FLOOR: continue adjacent_walls = 0 for ii in range(x - 1, x + 2): for jj in range(y - 1, y + 2): if new_map.terrain[x][y] == map.TERRAIN_WALL: adjacent_walls += 1 if adjacent_walls != 4: continue if (_check_door_configuration(new_map, pos, algebra.north) or _check_door_configuration(new_map, pos, algebra.east) or _check_door_configuration(new_map, pos, algebra.south) or _check_door_configuration(new_map, pos, algebra.west)): _place_door(new_map, pos)
def make_map(player, dungeon_level): """ Creates a new simple map at the given dungeon level. Sets player.current_map to the new map, and adds the player as the first object. """ new_map = map.Map(config.MAP_HEIGHT, config.MAP_WIDTH, dungeon_level) new_map.objects.append(player) player.current_map = new_map player.camera_position = algebra.Location(0, 0) new_map.random_seed = libtcod.random_save(0) _build_map(new_map) for new_room in new_map.rooms: _place_objects(new_map, new_room, player) player.pos = new_map.rooms[0].center() new_map.initialize_fov() return new_map
def make_map(player, dungeon_level): """ Creates a new simple map at the given dungeon level. Sets player.current_map to the new map, and adds the player as the first object. """ new_map = map.DungeonMap(config.MAP_WIDTH, config.MAP_HEIGHT, dungeon_level) new_map.objects.append(player) player.current_map = new_map player.camera_position = algebra.Location(0, 0) new_map.random_seed = libtcod.random_save(0) player.pos = _build_map(new_map) _inhabit_pool(new_map) _place_random_creatures(new_map, player) new_map.initialize_fov() return True
def toWorldCoords(camera_coords, screen_coords): x = screen_coords[0] + camera_coords.x y = screen_coords[1] + camera_coords.y return algebra.Location(x, y)