#include "level.h" #include "constants.h" level::level(character *player, RNG *rng, const feature enabled_features): enabled_features{enabled_features}, map{player, rng, enabled_features}, player{player} { auto tiles = map.get_room_list(); for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], player->get_pos()); for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_down_stairs()); if (enabled_features & FEATURE_REVISIT) for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_up_stairs()); gen_potions(rng, tiles); gen_gold(rng, tiles); gen_enemies({}, rng, tiles); } level::level(const level_data &lvl, character *player, RNG *rng, const feature enabled_features): enabled_features{enabled_features}, map{lvl, player, rng, enabled_features}, player{player} { auto tiles = map.get_room_list(); for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], player->get_pos()); for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_down_stairs()); if (enabled_features & FEATURE_REVISIT) for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_up_stairs()); if (enabled_features & FEATURE_READ_MAP) fill(lvl, tiles, rng); if (enabled_features & FEATURE_READ_MAP && (lvl.enemies.size() || lvl.potions.size() || lvl.gold_piles.size())) return; gen_potions(rng, tiles); gen_gold(rng, tiles); gen_enemies(lvl, rng, tiles); } void level::fill(const level_data &lvl, std::vector &tiles, RNG *rng) { for (auto [type, pos] : lvl.potions) { for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], pos); pplist.push_back(new_potion(type, pos)); plist.push_back(pplist[pplist.size() - 1].get()); } for (auto g : lvl.gold_piles) { for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], g.get_pos()); glist.push_back(g); } for (auto [type, pos] : lvl.enemies) { for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], pos); if (type != DRAGON) pelist.push_back(new_enemy(type, pos, enabled_features, map.which_room(pos), rng)); else { gold guards{POS_NIL, 0}; for (size_t i = 0; i < glist.size(); ++i) if (glist[i].get_amount() == GOLD_DRAGON && is_adjacent(glist[i].get_pos(), pos)) guards = glist[i]; pelist.push_back(new_enemy(type, pos, enabled_features, map.which_room(pos), rng, guards.get_pos())); } elist.push_back(pelist[pelist.size() - 1].get()); } } gold_list level::dragon_hoard(const level_data &lvl) { gold_list result; for (auto g : glist) if (g.get_amount() == GOLD_DRAGON && !is_in_glist(g, lvl.gold_piles)) result.push_back(g); return result; } bool level::is_in_glist(gold &g, const gold_list &gl) { for (auto tmp : gl) if (tmp.get_pos() == g.get_pos()) return true; return false; } void level::gen_enemies(const level_data &lvl, RNG *rng, std::vector &tiles) { auto dhoard = dragon_hoard(lvl); int cnt = (enabled_features & FEATURE_EXTRA_STUFF ? rng->rand_between(MIN_ENEMIE_CNT, MAX_ENEMIE_CNT + 1) : MIN_ENEMIE_CNT) + dhoard.size(); elist.reserve(cnt); pelist.reserve(cnt); for (size_t i = 0; i < dhoard.size(); ++i) { position_list spots; for (int dir = 0; dir < DIRECTION_CNT; ++dir) { position tmp = dhoard[i].get_pos() + MOVE[dir]; if (map.which_room(tmp) != -1 && tmp != player->get_pos()) spots.push_back(dhoard[i].get_pos() + MOVE[dir]); } auto pos = spots.size() ? rng->get_rand_in_vector(spots) : dhoard[i].get_pos(); pelist.push_back(new_dragon(rng, pos, dhoard[i].get_pos(), enabled_features, map.which_room(dhoard[i].get_pos()), dhoard[i].get_pos())); int room = map.which_room(pos); remove_from_list(tiles[room], pos); if (!tiles[room].size()) tiles.erase(tiles.begin() + room); elist.push_back(pelist[i].get()); } for (int i = elist.size(); i < cnt; ++i) { auto p = get_rand_pos(rng, tiles); pelist.push_back(new_enemy(rng, p, enabled_features, map.which_room(p))); elist.push_back(pelist[i].get()); } } position level::get_rand_pos(RNG *rng, std::vector &tiles) { int room; position pos; room = rng->rand_under(tiles.size()); pos = rng->get_rand_in_vector(tiles[room]); remove_from_list(tiles[room], pos); if (!tiles[room].size()) tiles.erase(tiles.begin() + room); return pos; } void level::add_gold(gold g) { glist.push_back(g); } void level::gen_gold(RNG *rng, std::vector &tiles) { glist.reserve(GOLD_CNT); for (int i = glist.size(); i < GOLD_CNT; ++i) glist.push_back(gold{get_rand_pos(rng, tiles), rand_gold_pile(rng)}); } void level::gen_potions(RNG *rng, std::vector &tiles) { int cnt; int max_type; if (enabled_features & FEATURE_EXTRA_STUFF) { cnt = rng->rand_between(MIN_POTION_CNT, MAX_POTION_CNT + 1); max_type = POTION_TYPE_CNT; } else { cnt = MIN_POTION_CNT; max_type = DEFAULT_POTION_TYPE_CNT; } plist.reserve(cnt); pplist.reserve(cnt); for (int i = pplist.size(); i < cnt; ++i) { pplist.push_back(new_potion(static_cast (rng->rand_under(max_type)), get_rand_pos(rng, tiles))); plist.push_back(pplist[i].get()); } } void level::print(output *out) const { map.print(out); for (size_t i = 0; i < plist.size(); ++i) plist[i]->print(out); for (size_t i = 0; i < glist.size(); ++i) glist[i].print(out); for (size_t i = 0; i < elist.size(); ++i) elist[i]->print(out); } bool level::is_available(const position &pos, bool is_player) const { if (!map.is_available(pos)) return false; if (player->get_pos() == pos) return false; for (size_t i = 0; i < elist.size(); ++i) if (pos == elist[i]->get_pos()) return false; if (!(enabled_features & FEATURE_WALK_OVER)) for (size_t i = 0; i < plist.size(); ++i) if (pos == plist[i]->get_pos()) return false; if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) for (size_t i = 0; i < glist.size(); ++i) if (pos == glist[i].get_pos()) return false; return true; } bool level::is_available_all(const position &pos) const { if (!map.is_available(pos)) return false; if (!(enabled_features & FEATURE_DOORS) && map.which_room(pos) == -1) return false; if (player->get_pos() == pos) return false; for (size_t i = 0; i < elist.size(); ++i) if (pos == elist[i]->get_pos()) return false; for (size_t i = 0; i < plist.size(); ++i) if (pos == plist[i]->get_pos()) return false; for (size_t i = 0; i < glist.size(); ++i) if (pos == glist[i].get_pos()) return false; return true; } int level::get_room(const position &pos) const { return map.which_room(pos); } position_list level::get_available_around(const position &pos) const { position_list result; for (int i = 0; i < DIRECTION_CNT; ++i) if (is_available(pos + MOVE[i])) result.push_back(pos + MOVE[i]); return result; } position_list level::get_available_around_all(const position &pos) const { position_list result; for (int i = 0; i < DIRECTION_CNT; ++i) if (is_available_all(pos + MOVE[i])) result.push_back(pos + MOVE[i]); return result; } position level::get_up_stairs() const { return map.get_up_stairs(); } position level::get_down_stairs() const { return map.get_down_stairs(); } const enemy_list &level::get_elist() { return elist; } const potion_list &level::get_plist() { return plist; } gold_list &level::get_glist() { return glist; } std::unique_ptr level::detach_potion(size_t idx) { if (idx >= plist.size()) return nullptr; auto tmp = std::move(pplist[idx]); plist.erase(plist.begin() + idx); pplist.erase(pplist.begin() + idx); return tmp; } void level::erase_enemy(character *ch) { if (ch == nullptr) return; for (size_t i = 0; i < elist.size(); ++i) if (elist[i] == ch) { elist.erase(elist.begin() + i); pelist.erase(pelist.begin() + i); } } size_t level::what_is_at(const position &pos) const { if (!map.is_available(pos)) return WHAT_WALL; for (size_t i = 0; i < elist.size(); ++i) if (elist[i]->get_pos() == pos) return WHAT_ENEMY | i; return WHAT_SPACE; }