added option to read level data from file

This commit is contained in:
2024-07-21 00:42:10 -04:00
parent 25477c2cf8
commit 401b5b00b5
14 changed files with 366 additions and 58 deletions

View File

@ -19,7 +19,8 @@ feature proc_args(int argc, char **argv,
std::unique_ptr<cursor> &curse, std::unique_ptr<cursor> &curse,
std::unique_ptr<input> &in, std::unique_ptr<input> &in,
std::unique_ptr<output> &out, std::unique_ptr<output> &out,
std::unique_ptr<RNG> &rng) { std::unique_ptr<RNG> &rng,
std::ifstream &data) {
feature result = 0; feature result = 0;
std::string str; std::string str;
std::string fn_fin; std::string fn_fin;
@ -37,6 +38,9 @@ feature proc_args(int argc, char **argv,
result |= FEATURE_NCURSES; result |= FEATURE_NCURSES;
} else if (str == "-r") { } else if (str == "-r") {
if (result & FEATURE_READ_MAP)
return FEATURE_CONFLICT | i;
result |= FEATURE_RAND_MAP; result |= FEATURE_RAND_MAP;
} else if (str == "-c") { } else if (str == "-c") {
result |= FEATURE_ENEMIES_CHASE; result |= FEATURE_ENEMIES_CHASE;
@ -56,6 +60,14 @@ feature proc_args(int argc, char **argv,
result |= FEATURE_EXTRA_LEVELS; result |= FEATURE_EXTRA_LEVELS;
} else if (str == "-o") { } else if (str == "-o") {
result |= FEATURE_WALK_OVER; result |= FEATURE_WALK_OVER;
} else if (str == "-m") {
if (result & FEATURE_RAND_MAP)
return FEATURE_CONFLICT | i;
result |= FEATURE_READ_MAP;
++i;
str = argv[i];
data.open(str);
} else if (str == "-s") { } else if (str == "-s") {
++i; ++i;
str = argv[i]; str = argv[i];
@ -172,7 +184,7 @@ void panic_args(feature panic) {
} }
const char *ARGS_LIST = "\ const char *ARGS_LIST = "\
-n : Use ncurses for I/O\n\ -n : Use ncurses for I/O\n\
-r : Randomly generate maps\n\ -r : Randomly generate maps\n\
-c : Enemies chase the player (CAUTION: THEY CAN REALLY CHASE!!!)\n\ -c : Enemies chase the player (CAUTION: THEY CAN REALLY CHASE!!!)\n\
-d : Enemies can go through doors (CAUTION: DO NOT ENABLE WITH CHASING!)\n\ -d : Enemies can go through doors (CAUTION: DO NOT ENABLE WITH CHASING!)\n\
@ -182,6 +194,7 @@ const char *ARGS_LIST = "\
-e : Enable extra potions and races\n\ -e : Enable extra potions and races\n\
-E : Enable extra levels\n\ -E : Enable extra levels\n\
-o : Allows characters to go over gold and potions\n\ -o : Allows characters to go over gold and potions\n\
-m [file] : Reads map data from file. CANNOT BE USED WITH -r. DOES NOT PANIC!\n\
-s [seed] : Sets initial seed to seed\n\ -s [seed] : Sets initial seed to seed\n\
-I [file] : Reads commands from file. CANNOT BE USED WITH -n.\n\ -I [file] : Reads commands from file. CANNOT BE USED WITH -n.\n\
-O [file] : Outputs to file. CANNOT BE USED WITH -n.\n\ -O [file] : Outputs to file. CANNOT BE USED WITH -n.\n\

View File

@ -12,8 +12,9 @@ typedef unsigned int feature;
feature proc_args(int argc, char **argv, feature proc_args(int argc, char **argv,
std::unique_ptr<cursor> &curse, std::unique_ptr<cursor> &curse,
std::unique_ptr<input> &in, std::unique_ptr<input> &in,
std::unique_ptr<output> &out, std::unique_ptr<output> &out,
std::unique_ptr<RNG> &rng); std::unique_ptr<RNG> &rng,
std::ifstream &data);
void panic_args(feature panic); void panic_args(feature panic);

View File

@ -1,9 +1,11 @@
#include "cc3k.h" #include "cc3k.h"
#include <iostream>
#include <fstream>
#include "constants.h" #include "constants.h"
CC3K::CC3K(const feature enabled_features, CC3K::CC3K(const feature enabled_features,
input *in, output *out, RNG *rng): input *in, output *out, RNG *rng, std::ifstream &data):
enabled_features{enabled_features}, enabled_features{enabled_features},
in{in}, out{out}, rng{rng} { in{in}, out{out}, rng{rng} {
curr_menu = std::make_unique<menu>(enabled_features); curr_menu = std::make_unique<menu>(enabled_features);
@ -11,6 +13,9 @@ CC3K::CC3K(const feature enabled_features,
curr_menu->print(out, rng->get_init_seed()); curr_menu->print(out, rng->get_init_seed());
out->render(); out->render();
gresult.status = game_status::MAIN_MENU; gresult.status = game_status::MAIN_MENU;
if (enabled_features & FEATURE_READ_MAP)
proc_lvls(data);
} }
game_status CC3K::run() { game_status CC3K::run() {
@ -28,7 +33,8 @@ game_status CC3K::run() {
curr_game = curr_game =
std::make_unique<game>(static_cast<race>(tmp), std::make_unique<game>(static_cast<race>(tmp),
enabled_features, enabled_features,
in, out, rng); in, out, rng,
lvls);
out->clear(); out->clear();
curr_game->print(); curr_game->print();
out->render(); out->render();
@ -91,3 +97,148 @@ game_status CC3K::run() {
return TERMINATED; return TERMINATED;
} }
void CC3K::proc_lvls(std::ifstream &data) {
lvls.reserve(DEFAULT_LVL_CNT);
for (int i = 0; i < DEFAULT_LVL_CNT; ++i) {
level_data tmp = {POS_NIL, {}, {}, {}, {}, POS_NIL};
std::string buffer;
for (int y = 0; y < MAP_HEIGHT; ++y) {
getline(data, buffer);
for (int x = 0; x < MAP_WIDTH; ++x)
push_tile(buffer[x], {x, y}, tmp);
}
number_rooms(tmp);
lvls.push_back(tmp);
}
}
size_t CC3K::remap_pos(const position &pos) {
return pos.x + pos.y * MAP_WIDTH;
}
void CC3K::number_rooms(level_data &lvl) {
int curr_room_cnt = 0;
for (int y = 0; y < MAP_HEIGHT; ++y)
for (int x = 0; x < MAP_WIDTH; ++x)
if (lvl.map_data[remap_pos({x, y})] == '.') {
number_block(lvl.map_data, {x, y}, curr_room_cnt);
++curr_room_cnt;
}
}
bool CC3K::in_bounds(const position &pos) {
return pos.x >= 0 && pos.x < MAP_WIDTH &&
pos.y >= 0 && pos.y < MAP_HEIGHT;
}
void CC3K::number_block(std::string &map_data, const position &pos, int num) {
if (map_data[remap_pos(pos)] != '.')
return;
map_data[remap_pos(pos)] = num + '0';
for (int i = 0; i < DIRECTION_CNT; ++i)
if (in_bounds(pos + MOVE[i]) &&
map_data[remap_pos(pos + MOVE[i])] == '.')
number_block(map_data, pos + MOVE[i], num);
}
void CC3K::push_tile(const char ch, const position &pos, level_data &lvl) {
switch (ch) {
case '-':
case '|':
case ' ':
case '+':
case '#':
lvl.map_data += ch;
return;
case '@':
lvl.pc_pos = pos;
break;
case '\\':
lvl.stairs = pos;
break;
case '0':
lvl.potions.push_back({RESTORE_HEALTH, pos});
break;
case '1':
lvl.potions.push_back({BOOST_ATK, pos});
break;
case '2':
lvl.potions.push_back({BOOST_DEF, pos});
break;
case '3':
lvl.potions.push_back({POISON_HEALTH, pos});
break;
case '4':
lvl.potions.push_back({WOUND_ATK, pos});
break;
case '5':
lvl.potions.push_back({WOUND_DEF, pos});
break;
case '6':
lvl.gold_piles.push_back({pos, GOLD_NORMAL});
break;
case '7':
lvl.gold_piles.push_back({pos, GOLD_SMALL});
break;
case '8':
lvl.gold_piles.push_back({pos, GOLD_MERCHANT});
break;
case '9':
lvl.gold_piles.push_back({pos, GOLD_DRAGON});
break;
case 'H':
lvl.enemies.push_back({HUMAN, pos});
break;
case 'W':
lvl.enemies.push_back({DWARF, pos});
break;
case 'E':
lvl.enemies.push_back({ELF, pos});
break;
case 'O':
lvl.enemies.push_back({ORC, pos});
break;
case 'M':
lvl.enemies.push_back({MERCHANT, pos});
break;
case 'D':
lvl.enemies.push_back({DRAGON, pos});
break;
case 'L':
lvl.enemies.push_back({HALFLING, pos});
break;
default:
break;
}
lvl.map_data += '.';
}

View File

@ -1,6 +1,7 @@
#ifndef __CC3K_H__ #ifndef __CC3K_H__
#define __CC3K_H__ #define __CC3K_H__
#include <string>
#include "input.h" #include "input.h"
#include "output.h" #include "output.h"
#include "game.h" #include "game.h"
@ -9,6 +10,7 @@
class CC3K final { class CC3K final {
private: private:
static const int DEFAULT_LVL_CNT = 5;
const feature enabled_features; const feature enabled_features;
input *in; input *in;
output *out; output *out;
@ -17,9 +19,17 @@ private:
game_result gresult; game_result gresult;
std::unique_ptr<game> curr_game; std::unique_ptr<game> curr_game;
std::unique_ptr<menu> curr_menu; std::unique_ptr<menu> curr_menu;
std::vector<level_data> lvls;
void proc_lvls(std::ifstream &data);
void push_tile(const char ch, const position &pos, level_data &lvl);
void number_rooms(level_data &lvl);
size_t remap_pos(const position &pos);
void number_block(std::string &map_data, const position &pos, int num);
bool in_bounds(const position &pos);
public: public:
CC3K(const feature enabled_features, CC3K(const feature enabled_features,
input *in, output *out, RNG *rng); input *in, output *out, RNG *rng, std::ifstream &data);
game_status run(); game_status run();
}; };

View File

@ -145,6 +145,7 @@ static const feature FEATURE_OUT_FILE = 1 << 9;
static const feature FEATURE_EXTRA_STUFF = 1 << 10; static const feature FEATURE_EXTRA_STUFF = 1 << 10;
static const feature FEATURE_EXTRA_LEVELS = 1 << 11; static const feature FEATURE_EXTRA_LEVELS = 1 << 11;
static const feature FEATURE_DOORS = 1 << 12; static const feature FEATURE_DOORS = 1 << 12;
static const feature FEATURE_READ_MAP = 1 << 13;
static const feature FEATURE_LIST_COMMANDS = 1 << 23; static const feature FEATURE_LIST_COMMANDS = 1 << 23;
static const feature FEATURE_LIST_RACES = 1 << 24; static const feature FEATURE_LIST_RACES = 1 << 24;
@ -170,4 +171,6 @@ static const size_t WHAT_ENEMY = 1 << 29;
static const size_t WHAT_WALL = 1 << 30; static const size_t WHAT_WALL = 1 << 30;
static const size_t WHAT_SPACE = 1 << 31; static const size_t WHAT_SPACE = 1 << 31;
static const position POS_NIL = {0, 0};
#endif #endif

View File

@ -55,8 +55,6 @@ enum race get_normal_race(RNG *rng) {
std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos, std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
const feature enabled_features, const feature enabled_features,
int which_room) { int which_room) {
using std::make_unique;
enum race r; enum race r;
if (enabled_features & FEATURE_EXTRA_STUFF) if (enabled_features & FEATURE_EXTRA_STUFF)
@ -64,7 +62,15 @@ std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
else else
r = get_normal_race(rng); r = get_normal_race(rng);
switch (r) { return new_enemy(r, pos, enabled_features, which_room, rng);
}
std::unique_ptr<enemy_base> new_enemy(const race &race, const position &pos,
const feature enabled_features,
int which_room, RNG *rng) {
using std::make_unique;
switch (race) {
case DWARF: case DWARF:
return make_unique<dwarf>(rng, enabled_features, return make_unique<dwarf>(rng, enabled_features,
pos, which_room); pos, which_room);
@ -113,6 +119,10 @@ std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
return make_unique<baby_dragon>(rng, enabled_features, return make_unique<baby_dragon>(rng, enabled_features,
pos, which_room); pos, which_room);
case DRAGON:
return make_unique<dragon>(rng, enabled_features,
pos, which_room);
default: default:
break; break;
} }

View File

@ -4,6 +4,8 @@
#include <memory> #include <memory>
#include "enemy.h" #include "enemy.h"
enum race : int;
std::unique_ptr<enemy_base> new_dragon(RNG *rng, const position &pos, std::unique_ptr<enemy_base> new_dragon(RNG *rng, const position &pos,
const position &fallback, const position &fallback,
const feature enabled_features, const feature enabled_features,
@ -13,4 +15,8 @@ std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
const feature enabled_features, const feature enabled_features,
int which_room); int which_room);
std::unique_ptr<enemy_base> new_enemy(const race &race, const position &pos,
const feature enabled_features,
int which_room, RNG *rng);
#endif #endif

View File

@ -7,8 +7,9 @@ game::game(const enum race starting_race,
const feature enabled_features, const feature enabled_features,
input *new_in, input *new_in,
output *new_out, output *new_out,
RNG *new_rng): RNG *new_rng,
enabled_features{enabled_features}, const std::vector<level_data> &lvls):
enabled_features{enabled_features}, lvls{lvls},
in{new_in}, out{new_out}, rng{new_rng}, in{new_in}, out{new_out}, rng{new_rng},
curr_turn{0} { curr_turn{0} {
const position nil{0, 0}; const position nil{0, 0};
@ -33,12 +34,19 @@ game::game(const enum race starting_race,
} }
void game::new_level() { void game::new_level() {
if (enabled_features & FEATURE_RAND_MAP) if (enabled_features & FEATURE_RAND_MAP)
levels.push_back(std::make_unique<level>(player.get(), levels.push_back(std::make_unique<level>(player.get(),
rng, enabled_features)); rng, enabled_features));
else if (enabled_features & FEATURE_READ_MAP)
levels.push_back(std::make_unique<level>(lvls[curr_level],
player.get(), rng, enabled_features));
else else
levels.push_back(std::make_unique<level>(DEFAULT_MAP, player.get(), levels.push_back(std::make_unique<level>(
rng, enabled_features)); level_data{POS_NIL, {}, {}, {},
DEFAULT_MAP, POS_NIL},
player.get(), rng,
enabled_features));
} }
enemy_list game::sort_enemies(const enemy_list &elist) { enemy_list game::sort_enemies(const enemy_list &elist) {
@ -115,11 +123,8 @@ game_result game::run() {
case result::GO_DOWN: { case result::GO_DOWN: {
if (curr_level == max_level - 1) if (curr_level == max_level - 1)
return {WON, "You won! You collected " + return {WON, "You won! Your final score is: " +
std::to_string(player->get_gold()) + str_score() + ". "};
" pieces of gold after " +
std::to_string(curr_turn + 1) +
" turns!"};
player->start_turn(); player->start_turn();
@ -138,11 +143,9 @@ game_result game::run() {
case result::GO_UP: { case result::GO_UP: {
if (curr_level == 0) if (curr_level == 0)
return {ESCAPED, "You escaped the dungeon with " + return {ESCAPED,
std::to_string(player->get_gold()) + "You escaped the dungeon with a score of" +
" pieces of gold after " + str_score() + ". Coward!"};
std::to_string(curr_turn + 1) +
" turns! Coward!"};
player->start_turn(); player->start_turn();
@ -185,10 +188,11 @@ game_result game::run() {
if (player->is_dead()) { if (player->is_dead()) {
if (killer == nullptr) if (killer == nullptr)
return {DEAD, "You died: killed by your own stupidity!"}; return {DEAD,
"You died: killed by your own stupidity!"};
return {DEAD, std::string{"You died: killed by a(n) "} + return {DEAD, std::string{"You died: killed by a(n) "} +
killer->get_race_name()}; killer->get_race_name() + ". "};
} }
return {IN_GAME, ""}; return {IN_GAME, ""};
@ -227,3 +231,11 @@ void game::print() {
out->print_str(STATUS_DEF, "Def: " + std::to_string(player->get_DEF())); out->print_str(STATUS_DEF, "Def: " + std::to_string(player->get_DEF()));
out->print_str(STATUS_ACTION, "Action: " + msg); out->print_str(STATUS_ACTION, "Action: " + msg);
} }
std::string game::str_score() const {
if (player->get_race() == SHADE)
return std::to_string(static_cast<int>(player->get_gold() *
SHADE_SCORE_MUL));
return std::to_string(player->get_gold());
}

View File

@ -18,8 +18,10 @@ private:
static const size_t MIN_LEVEL_CNT = 30; static const size_t MIN_LEVEL_CNT = 30;
static const size_t DEFAULT_MAX_LEVEL = 5; static const size_t DEFAULT_MAX_LEVEL = 5;
static const size_t MAX_MSG_LENGTH = 300; static const size_t MAX_MSG_LENGTH = 300;
inline static const float SHADE_SCORE_MUL = 1.5f;
const feature enabled_features; const feature enabled_features;
const std::vector<level_data> &lvls;
input *in; input *in;
output *out; output *out;
@ -43,7 +45,8 @@ public:
const feature enabled_features, const feature enabled_features,
input *new_in, input *new_in,
output *new_out, output *new_out,
RNG *new_rng); RNG *new_rng,
const std::vector<level_data> &lvls);
game_result run(); game_result run();
size_t get_curr_level() const; size_t get_curr_level() const;
size_t get_curr_turn() const; size_t get_curr_turn() const;
@ -53,6 +56,7 @@ private:
void new_level(); void new_level();
character *move_enemies(); character *move_enemies();
enemy_list sort_enemies(const enemy_list &elist); enemy_list sort_enemies(const enemy_list &elist);
std::string str_score() const;
}; };
#endif #endif

View File

@ -19,13 +19,13 @@ level::level(character *player, RNG *rng, const feature enabled_features):
gen_potions(rng, tiles); gen_potions(rng, tiles);
gen_gold(rng, tiles); gen_gold(rng, tiles);
gen_enemies(rng, tiles); gen_enemies({}, rng, tiles);
} }
level::level(const std::string &map_data, character *player, RNG *rng, level::level(const level_data &lvl, character *player, RNG *rng,
const feature enabled_features): const feature enabled_features):
enabled_features{enabled_features}, enabled_features{enabled_features},
map{player, map_data, rng, enabled_features}, player{player} { map{lvl, player, rng, enabled_features}, player{player} {
auto tiles = map.get_room_list(); auto tiles = map.get_room_list();
for (size_t i = 0; i < tiles.size(); ++i) for (size_t i = 0; i < tiles.size(); ++i)
@ -38,23 +38,63 @@ level::level(const std::string &map_data, character *player, RNG *rng,
for (size_t i = 0; i < tiles.size(); ++i) for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], map.get_up_stairs()); remove_from_list(tiles[i], map.get_up_stairs());
if (enabled_features & FEATURE_READ_MAP)
fill(lvl, tiles, rng);
gen_potions(rng, tiles); gen_potions(rng, tiles);
gen_gold(rng, tiles); gen_gold(rng, tiles);
gen_enemies(rng, tiles); gen_enemies(lvl, rng, tiles);
} }
gold_list level::dragon_hoard() { void level::fill(const level_data &lvl, std::vector<position_list> &tiles,
RNG *rng) {
for (auto [type, pos] : lvl.enemies) {
for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], pos);
pelist.push_back(new_enemy(type, pos, enabled_features,
map.which_room(pos), rng));
elist.push_back(pelist[pelist.size() - 1].get());
}
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);
}
}
gold_list level::dragon_hoard(const level_data &lvl) {
gold_list result; gold_list result;
for (auto g : glist) for (auto g : glist)
if (g.get_amount() == GOLD_DRAGON) if (g.get_amount() == GOLD_DRAGON &&
!is_in_glist(g, lvl.gold_piles))
result.push_back(g); result.push_back(g);
return result; return result;
} }
void level::gen_enemies(RNG *rng, std::vector<position_list> &tiles) { bool level::is_in_glist(gold &g, const gold_list &gl) {
auto dhoard = dragon_hoard(); 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<position_list> &tiles) {
auto dhoard = dragon_hoard(lvl);
int cnt = enabled_features & FEATURE_EXTRA_STUFF ? int cnt = enabled_features & FEATURE_EXTRA_STUFF ?
rng->rand_between(MIN_ENEMIE_CNT, MAX_ENEMIE_CNT + 1) : rng->rand_between(MIN_ENEMIE_CNT, MAX_ENEMIE_CNT + 1) :
MIN_ENEMIE_CNT + dhoard.size(); MIN_ENEMIE_CNT + dhoard.size();
@ -87,7 +127,7 @@ void level::gen_enemies(RNG *rng, std::vector<position_list> &tiles) {
} }
for (int i = dhoard.size(); i < cnt; ++i) { for (int i = elist.size(); i < cnt; ++i) {
auto p = get_rand_pos(rng, tiles); auto p = get_rand_pos(rng, tiles);
pelist.push_back(new_enemy(rng, p, enabled_features, pelist.push_back(new_enemy(rng, p, enabled_features,
map.which_room(p))); map.which_room(p)));
@ -116,7 +156,7 @@ void level::add_gold(gold g) {
void level::gen_gold(RNG *rng, std::vector<position_list> &tiles) { void level::gen_gold(RNG *rng, std::vector<position_list> &tiles) {
glist.reserve(GOLD_CNT); glist.reserve(GOLD_CNT);
for (int i = 0; i < GOLD_CNT; ++i) for (int i = glist.size(); i < GOLD_CNT; ++i)
glist.push_back(gold{get_rand_pos(rng, tiles), rand_gold_pile(rng)}); glist.push_back(gold{get_rand_pos(rng, tiles), rand_gold_pile(rng)});
} }
@ -135,7 +175,7 @@ void level::gen_potions(RNG *rng, std::vector<position_list> &tiles) {
plist.reserve(cnt); plist.reserve(cnt);
pplist.reserve(cnt); pplist.reserve(cnt);
for (int i = 0; i < cnt; ++i) { for (int i = pplist.size(); i < cnt; ++i) {
pplist.push_back(new_potion(static_cast<potion_type> pplist.push_back(new_potion(static_cast<potion_type>
(rng->rand_under(max_type)), (rng->rand_under(max_type)),
get_rand_pos(rng, tiles))); get_rand_pos(rng, tiles)));

View File

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <utility>
#include "output.h" #include "output.h"
#include "gold.h" #include "gold.h"
#include "enemies.h" #include "enemies.h"
@ -11,6 +12,17 @@
#include "potions.h" #include "potions.h"
#include "map.h" #include "map.h"
enum race : int;
struct level_data {
position pc_pos;
std::vector<std::pair<race, position>> enemies;
std::vector<std::pair<potion_type, position>> potions;
gold_list gold_piles;
std::string map_data;
position stairs;
};
class level { class level {
private: private:
static const int MAX_POTION_CNT = 15; static const int MAX_POTION_CNT = 15;
@ -30,8 +42,8 @@ private:
public: public:
// randomly generate a map // randomly generate a map
level(character *player, RNG *rng, const feature enabled_features); level(character *player, RNG *rng, const feature enabled_features);
// read map from a string // read map from data
level(const std::string &map_data, character *player, RNG *rng, level(const level_data &lvl_data, character *player, RNG *rng,
const feature enabled_features); const feature enabled_features);
void print(output *out) const; void print(output *out) const;
@ -59,10 +71,13 @@ private:
// every gen will delete the positions in tiles // every gen will delete the positions in tiles
void gen_potions(RNG *rng, std::vector<position_list> &tiles); void gen_potions(RNG *rng, std::vector<position_list> &tiles);
void gen_gold(RNG *rng, std::vector<position_list> &tiles); void gen_gold(RNG *rng, std::vector<position_list> &tiles);
void gen_enemies(RNG *rng, std::vector<position_list> &tiles); void gen_enemies(const level_data &lvl, RNG *rng,
std::vector<position_list> &tiles);
position get_rand_pos(RNG *rng, std::vector<position_list> &tiles); position get_rand_pos(RNG *rng, std::vector<position_list> &tiles);
gold_list dragon_hoard(); gold_list dragon_hoard(const level_data &lvl);
bool is_in_glist(gold &g, const gold_list &gl);
void fill(const level_data &lvl, std::vector<position_list> &tiles, RNG *rng);
}; };
#endif #endif

View File

@ -1,4 +1,5 @@
#include <iostream> #include <iostream>
#include <fstream>
#include "cc3k.h" #include "cc3k.h"
#include "arguments.h" #include "arguments.h"
@ -9,8 +10,9 @@ int main(int argc, char **argv) {
std::unique_ptr<input> in; std::unique_ptr<input> in;
std::unique_ptr<output> out; std::unique_ptr<output> out;
std::unique_ptr<RNG> rng; std::unique_ptr<RNG> rng;
std::ifstream data;
feature enabled_features = proc_args(argc, argv, curse, in, out, rng); feature enabled_features = proc_args(argc, argv, curse, in, out, rng, data);
if (enabled_features & if (enabled_features &
(FEATURE_PANIC | FEATURE_PANIC_FILE | (FEATURE_PANIC | FEATURE_PANIC_FILE |
@ -25,7 +27,7 @@ int main(int argc, char **argv) {
return RETURN_FINE; return RETURN_FINE;
} }
CC3K game_proc(enabled_features, in.get(), out.get(), rng.get()); CC3K game_proc(enabled_features, in.get(), out.get(), rng.get(), data);
while (1) while (1)
if (game_proc.run() == game_status::TERMINATED) if (game_proc.run() == game_status::TERMINATED)

View File

@ -4,6 +4,7 @@
#include <iostream> #include <iostream>
#include "constants.h" #include "constants.h"
#include "level.h"
game_map::game_map(character *player, RNG *rng, const feature enabled_features): game_map::game_map(character *player, RNG *rng, const feature enabled_features):
enabled_features{enabled_features} { enabled_features{enabled_features} {
@ -44,10 +45,18 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features):
int player_room = rng->rand_under(room_data.size()); int player_room = rng->rand_under(room_data.size());
player->set_pos(rng->get_rand_in_vector(rooms_tile_list[player_room])); player->set_pos(rng->get_rand_in_vector(rooms_tile_list[player_room]));
gen_stairs(player_room, rng); gen_stairs({}, player_room, rng);
} }
void game_map::gen_stairs(int player_room, RNG *rng) { void game_map::gen_stairs(const level_data &lvl, int player_room,
RNG *rng) {
if (enabled_features & FEATURE_READ_MAP) {
int stairs_room = rng->exclude_middle(0, room_data.size(),
player_room);
down_stairs = get_available_spot(lvl, stairs_room, rng);
return;
}
down_stairs = rng->get_rand_in_vector(rooms_tile_list[ down_stairs = rng->get_rand_in_vector(rooms_tile_list[
rng->exclude_middle(0, room_data.size(), player_room)]); rng->exclude_middle(0, room_data.size(), player_room)]);
@ -69,7 +78,7 @@ position game_map::random_size(int min_width, int min_height,
rng->rand_between(min_height, max_height + 1)}; rng->rand_between(min_height, max_height + 1)};
} }
game_map::game_map(character *player, const std::string &map_data, game_map::game_map(const level_data &lvl, character *player,
RNG *rng, const feature enabled_features): RNG *rng, const feature enabled_features):
enabled_features{enabled_features} { enabled_features{enabled_features} {
int map_size = MAP_HEIGHT * MAP_WIDTH; int map_size = MAP_HEIGHT * MAP_WIDTH;
@ -82,15 +91,17 @@ game_map::game_map(character *player, const std::string &map_data,
int max_room_num = 0; int max_room_num = 0;
for (int i = 0; i < map_size; ++i) { for (int i = 0; i < map_size; ++i) {
if (map_data[i] >= '0' && map_data[i] <= '9') { if (lvl.map_data[i] >= '0' && lvl.map_data[i] <= '9') {
map.push_back(map_data[i] - '0'); map.push_back(lvl.map_data[i] - '0');
max_room_num = std::max(max_room_num, map_data[i] - '0'); max_room_num = std::max(max_room_num,
lvl.map_data[i] - '0');
} else { } else {
map.push_back(map_data[i]); map.push_back(lvl.map_data[i]);
} }
if (map_data[i] >= '0' && map_data[i] <= '9') if (lvl.map_data[i] >= '0' && lvl.map_data[i] <= '9')
rooms_tile_list[map_data[i] - '0'].push_back(remap_index(i)); rooms_tile_list[lvl.map_data[i] - '0'].push_back(
remap_index(i));
} }
while (rooms_tile_list[rooms_tile_list.size() - 1].size() == 0) while (rooms_tile_list[rooms_tile_list.size() - 1].size() == 0)
@ -98,11 +109,38 @@ game_map::game_map(character *player, const std::string &map_data,
for (int i = 0; i <= max_room_num; ++i) for (int i = 0; i <= max_room_num; ++i)
room_data.push_back({0, 0, 0, 0}); room_data.push_back({0, 0, 0, 0});
int player_room;
int player_room = rng->rand_under(room_data.size()); if (lvl.pc_pos != POS_NIL) {
player->set_pos(rng->get_rand_in_vector(rooms_tile_list[player_room])); player->set_pos(lvl.pc_pos);
player_room = which_room(lvl.pc_pos);
} else {
player_room = rng->rand_under(room_data.size());
player->set_pos(get_available_spot(lvl, player_room, rng));
}
gen_stairs(player_room, rng); if (lvl.stairs == POS_NIL) {
gen_stairs(lvl, player_room, rng);
} else {
down_stairs = lvl.stairs;
map[remap_position(down_stairs)] = '\\';
}
}
position game_map::get_available_spot(const level_data &lvl, int room,
RNG *rng) {
position_list spots = rooms_tile_list[room];
for (auto [type, pos] : lvl.enemies)
remove_from_list(spots, pos);
for (auto [type, pos] : lvl.potions)
remove_from_list(spots, pos);
for (auto g : lvl.gold_piles)
remove_from_list(spots, g.get_pos());
return rng->get_rand_in_vector(spots);
} }
bool game_map::is_available(const position &pos) const { bool game_map::is_available(const position &pos) const {

View File

@ -11,6 +11,8 @@
#include "player.h" #include "player.h"
#include "characters.h" #include "characters.h"
struct level_data;
class game_map final { class game_map final {
private: private:
static const int MAX_ROOM_CNT = 20; static const int MAX_ROOM_CNT = 20;
@ -54,7 +56,7 @@ public:
// randomly generate a map // randomly generate a map
game_map(character *player, RNG *rng, const feature enabled_features); game_map(character *player, RNG *rng, const feature enabled_features);
// read map from a string // read map from a string
game_map(character *player, const std::string &map_data, RNG *rng, game_map(const level_data &lvl, character *player, RNG *rng,
const feature enabled_features); const feature enabled_features);
bool is_available(const position &pos) const; bool is_available(const position &pos) const;
@ -74,6 +76,7 @@ public:
int get_room_cnt() const; int get_room_cnt() const;
private: private:
position get_available_spot(const level_data &lvl, int room, RNG *rng);
std::vector<room> room_data; std::vector<room> room_data;
position remap_index(const int idx) const; position remap_index(const int idx) const;
int remap_position(const position &pos) const; int remap_position(const position &pos) const;
@ -107,7 +110,7 @@ private:
room hit_room(const position &a); room hit_room(const position &a);
bool hit_room(const position &a, const room &r); bool hit_room(const position &a, const room &r);
void gen_stairs(int player_room, RNG *rng); void gen_stairs(const level_data &lvl, int player_room, RNG *rng);
room get_room(std::size_t idx) const; room get_room(std::size_t idx) const;
}; };