full implementation!

bug: level generation will generate nullptrs
This commit is contained in:
2024-07-15 00:09:14 -04:00
parent c3b974c83c
commit 6768d73d16
13 changed files with 407 additions and 46 deletions

76
src/cc3k.cc Normal file
View File

@ -0,0 +1,76 @@
#include "cc3k.h"
#include "constants.h"
CC3K::CC3K(const feature enabled_features,
input *in, display *out, RNG *rng):
enabled_features{enabled_features},
in{in}, out{out}, rng{rng} {
curr_menu = std::make_unique<menu>(enabled_features);
out->clear();
curr_menu->print(out);
out->render();
gresult.status = game_status::main_menu;
}
game_status CC3K::run() {
switch (gresult.status) {
case main_menu: {
auto tmp = curr_menu->run(in);
if (tmp == -2)
return terminated;
if (tmp != -1) {
curr_menu = nullptr;
curr_game = std::make_unique<game>((race)tmp,
enabled_features,
in, out, rng);
out->clear();
curr_game->print();
out->render();
gresult.status = in_game;
return in_game;
}
out->clear();
curr_menu->print(out);
out->render();
return main_menu;
}
case in_game: {
gresult = curr_game->run();
if (gresult.status == restart) {
curr_game = nullptr;
curr_menu = std::make_unique<menu>(enabled_features);
out->clear();
curr_menu->print(out);
out->render();
return main_menu;
} else if (gresult.status == in_game) {
out->clear();
curr_game->print();
out->render();
return in_game;
}
out->clear();
gresult.print(out);
out->render();
return gresult.status;
}
case dead:
case won:
case escaped:
gresult.run(in);
return terminated;
default:
break;
}
return terminated;
}

View File

@ -1,8 +1,26 @@
#ifndef __CC3K_H__ #ifndef __CC3K_H__
#define __CC3K_H__ #define __CC3K_H__
class CC3K; #include "input.h"
#include "display.h"
#include "game.h"
#include "menu.h"
#include "result.h"
// every time it runs, clear first class CC3K final {
private:
const feature enabled_features;
input *in;
display *out;
RNG *rng;
game_result gresult;
std::unique_ptr<game> curr_game;
std::unique_ptr<menu> curr_menu;
public:
CC3K(const feature enabled_features,
input *in, display *out, RNG *rng);
game_status run();
};
#endif #endif

View File

@ -20,7 +20,7 @@ struct long_result {
}; };
enum game_status : int {terminated, main_menu, in_game, options, enum game_status : int {terminated, main_menu, in_game, options,
dead, won, escaped, restart, skip_turn dead, won, escaped, restart
}; };
enum game_command : int {game_command_terminate = 0, enum game_command : int {game_command_terminate = 0,

View File

@ -46,9 +46,9 @@ bool compare_characters(const character *a, const character *b) {
return a->get_pos() < b->get_pos(); return a->get_pos() < b->get_pos();
} }
void game::move_enemies() { character *game::move_enemies() {
if (the_world) if (the_world)
return; return nullptr;
auto enemies = levels[curr_level]->get_elist(); auto enemies = levels[curr_level]->get_elist();
std::sort(enemies.begin(), enemies.end(), ::compare_characters); std::sort(enemies.begin(), enemies.end(), ::compare_characters);
@ -62,16 +62,18 @@ void game::move_enemies() {
ch->act(levels[curr_level].get(), player.get(), hostile); ch->act(levels[curr_level].get(), player.get(), hostile);
if (player->is_dead()) if (player->is_dead())
return; return ch;
if (ch->is_dead()) if (ch->is_dead())
ch->dies(levels[curr_level].get(), player.get()); ch->dies(levels[curr_level].get(), player.get());
msg += res.msg; msg += res.msg;
} }
return nullptr;
} }
game_status game::run() { game_result game::run() {
player->start_turn(); player->start_turn();
auto res = player->interpret_command(levels[curr_level].get(), auto res = player->interpret_command(levels[curr_level].get(),
in->get_command()); in->get_command());
@ -79,28 +81,40 @@ game_status game::run() {
switch (res.res) { switch (res.res) {
case result::terminate: case result::terminate:
return terminated; return {terminated, ""};
case result::died: case result::died:
return game_status::dead; return {dead, "You died: killed by your own stupidity!"};
case result::go_down: { case result::go_down: {
if (curr_level == max_level - 1) if (curr_level == max_level - 1)
return game_status::won; return {won, "You won! You collected " +
std::to_string(player->get_gold()) +
" after " + std::to_string(curr_turn + 1) +
" turns!"};
player->discard_level_effects(); player->discard_level_effects();
++curr_level; ++curr_level;
new_level(); new_level();
break; break;
} }
case result::go_up: { case result::go_up: {
if (curr_level == 0) if (curr_level == 0)
return game_status::escaped; return {escaped, "You escaped the dungeon with " +
std::to_string(player->get_gold()) +
" after " + std::to_string(curr_turn + 1) +
" turns! Coward!"};
player->discard_level_effects(); player->discard_level_effects();
--curr_level; --curr_level;
player->set_pos(levels[curr_level]->get_down_stairs()); player->set_pos(levels[curr_level]->get_down_stairs());
break; break;
} }
@ -109,13 +123,13 @@ game_status game::run() {
break; break;
case restart_game: case restart_game:
return restart; return {restart, ""};
case unknown: case unknown:
case fine: case fine:
case applied_nothing: case applied_nothing:
++curr_turn; ++curr_turn;
return game_status::in_game; return {in_game, ""};
default: default:
break; break;
@ -125,15 +139,20 @@ game_status game::run() {
player->calc_effects(); player->calc_effects();
if (player->is_dead()) if (player->is_dead())
return game_status::dead; return {dead, "You died: killed by your own stupidity!"};
move_enemies(); auto killer = move_enemies();
if (player->is_dead()) if (player->is_dead()) {
return game_status::dead; if (killer == nullptr)
return {dead, "You died: killed by your own stupidity!"};
return {dead, (std::string)"You died: killed by a(n) " +
killer->get_race_name()};
}
++curr_turn; ++curr_turn;
return game_status::in_game; return {in_game, ""};
} }
const position STATUS_RACE{0, 25}; const position STATUS_RACE{0, 25};

View File

@ -8,6 +8,7 @@
#include "characters.h" #include "characters.h"
#include "level.h" #include "level.h"
#include "player.h" #include "player.h"
#include "result.h"
enum game_status : int; enum game_status : int;
@ -43,14 +44,14 @@ public:
input *new_in, input *new_in,
display *new_out, display *new_out,
RNG *new_rng); RNG *new_rng);
game_status 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;
const character *get_player() const; const character *get_player() const;
void print(); void print();
private: private:
void new_level(); void new_level();
void move_enemies(); character *move_enemies();
}; };
#endif #endif

View File

@ -39,7 +39,7 @@ game_command console_input::get_command() {
auto tmp = get_direction(cmd); auto tmp = get_direction(cmd);
if (tmp != game_command_panic) if (tmp != game_command_panic)
return tmp; return (game_command)(tmp + move_north);
} }
return game_command_pass; return game_command_pass;

View File

@ -114,31 +114,31 @@ void level::gen_potions(RNG *rng, std::vector<position_list> &tiles) {
void level::print(display *out) const { void level::print(display *out) const {
map.print(out); map.print(out);
for (auto ch : elist) for (size_t i = 0; i < plist.size(); ++i)
ch->print(out); plist[i]->print(out);
for (auto p : plist) for (size_t i = 0; i < glist.size(); ++i)
p->print(out); out->print_char(glist[i].pos, 'G', COLOR_PAIR(COLOR_YELLOW));
for (auto gold : glist) for (size_t i = 0; i < elist.size(); ++i)
out->print_char(gold.pos, 'G', COLOR_PAIR(COLOR_YELLOW)); elist[i]->print(out);
} }
bool level::is_available(const position &pos, bool is_player) const { bool level::is_available(const position &pos, bool is_player) const {
if (!map.is_available(pos)) if (!map.is_available(pos))
return false; return false;
for (auto ch : elist) for (size_t i = 0; i < elist.size(); ++i)
if (pos == ch->get_pos()) if (pos == elist[i]->get_pos())
return false; return false;
if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) { if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) {
for (auto p : plist) for (size_t i = 0; i < plist.size(); ++i)
if (pos == p->get_pos()) if (pos == plist[i]->get_pos())
return false; return false;
for (auto g : glist) for (size_t i = 0; i < glist.size(); ++i)
if (pos == g.pos) if (pos == glist[i].pos)
return false; return false;
} }
@ -149,16 +149,16 @@ bool level::is_available_all(const position &pos) const {
if (!map.is_available(pos)) if (!map.is_available(pos))
return false; return false;
for (auto ch : elist) for (size_t i = 0; i < elist.size(); ++i)
if (pos == ch->get_pos()) if (pos == elist[i]->get_pos())
return false; return false;
for (auto p : plist) for (size_t i = 0; i < plist.size(); ++i)
if (pos == p->get_pos()) if (pos == plist[i]->get_pos())
return false; return false;
for (auto g : glist) for (size_t i = 0; i < glist.size(); ++i)
if (pos == g.pos) if (pos == glist[i].pos)
return false; return false;
return true; return true;

View File

@ -22,12 +22,11 @@ int main(int argc, char **argv) {
return RETURN_FINE; return RETURN_FINE;
} }
// CC3K game_proc(enabled_features, *in, *out, *log, *rng); CC3K game_proc(enabled_features, in.get(), out.get(), rng.get());
// while (game_proc.run() != game_status::terminated) { while (1)
// out->render(); if (game_proc.run() == game_status::terminated)
// out->clear(); break;
// }
return RETURN_FINE; return RETURN_FINE;
} }

View File

@ -38,7 +38,8 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features):
for (size_t i = 0; i < rooms_tile_list.size(); ++i) for (size_t i = 0; i < rooms_tile_list.size(); ++i)
rooms_tile_list[i].shrink_to_fit(); rooms_tile_list[i].shrink_to_fit();
rooms_tile_list.shrink_to_fit(); while (rooms_tile_list[rooms_tile_list.size() - 1].size() == 0)
rooms_tile_list.pop_back();
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]));
@ -92,6 +93,9 @@ game_map::game_map(character *player, const std::string &map_data,
rooms_tile_list[map_data[i] - '0'].push_back(remap_index(i)); rooms_tile_list[map_data[i] - '0'].push_back(remap_index(i));
} }
while (rooms_tile_list[rooms_tile_list.size() - 1].size() == 0)
rooms_tile_list.pop_back();
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});
@ -99,7 +103,6 @@ game_map::game_map(character *player, const std::string &map_data,
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);
} }
bool game_map::is_available(const position &pos) const { bool game_map::is_available(const position &pos) const {

161
src/menu.cc Normal file
View File

@ -0,0 +1,161 @@
#include "menu.h"
#include "constants.h"
const int NORMAL_CNT = 5;
const int EXTRA_CNT = 5;
const enum race RACES[EXTRA_CNT] = {
rshade, rdrow, rgoblin, rvampire, rtroll
};
const position RACE_POS[EXTRA_CNT] = {
{4, 9}, {4, 11}, {4, 13}, {4, 15}, {4, 17}
};
menu::menu(const feature enabled_features):
enabled_features{enabled_features},
cur{RACE_POS[0]} {}
const int UP_LIMIT = 9;
const int DOWN_LIMIT = 17;
const int LEFT_LIMIT = 4;
const int RIGHT_LIMIT = 4;
const int EXTRA_UP_LIMIT = 9;
const int EXTRA_DOWN_LIMIT = 17;
const int EXTRA_LEFT_LIMIT = 4;
const int EXTRA_RIGHT_LIMIT = 4;
const int X_INCRE = 37;
const int Y_INCRE = 2;
const char *NORMAL_SCREEN =
"+-----------------------------------------------------------------------------+\
| ____ ____ _____ _ __ |\
| / _\\/ _\\\\__ \\/ |/ / |\
| | / | / / || / |\
| | \\_ | \\_ _\\ || \\ |\
| \\____/\\____//____/\\_|\\_\\ |\
+-----------------------------------------------------------------------------+\
| Choose your race | |\
+-----------------------------------------------------------------------------+\
| Shade | |\
+-----------------------------------------------------------------------------+\
| Drow | |\
+-----------------------------------------------------------------------------+\
| Goblin | |\
+-----------------------------------------------------------------------------+\
| Vampire | |\
+-----------------------------------------------------------------------------+\
| Troll | |\
+-----------------------------------------------------------------------------+\
| |\
| |\
| |\
| |\
| |\
+-----------------------------------------------------------------------------+";
const char *EXTRA_SCREEN =
"+-----------------------------------------------------------------------------+\
| ____ ____ _____ _ __ |\
| / _\\/ _\\\\__ \\/ |/ / |\
| | / | / / || / |\
| | \\_ | \\_ _\\ || \\ |\
| \\____/\\____//____/\\_|\\_\\ |\
+-----------------------------------------------------------------------------+\
| Choose your race | |\
+-----------------------------------------------------------------------------+\
| Shade | |\
+-----------------------------------------------------------------------------+\
| Drow | |\
+-----------------------------------------------------------------------------+\
| Goblin | |\
+-----------------------------------------------------------------------------+\
| Vampire | |\
+-----------------------------------------------------------------------------+\
| Troll | |\
+-----------------------------------------------------------------------------+\
| |\
| |\
| |\
| |\
| |\
+-----------------------------------------------------------------------------+";
int menu::run(input *in) {
auto cmd = in->get_command();
int up_limit = UP_LIMIT;
int down_limit = DOWN_LIMIT;
int left_limit = LEFT_LIMIT;
int right_limit = RIGHT_LIMIT;
if (enabled_features & FEATURE_EXTRA_STUFF) {
up_limit = EXTRA_UP_LIMIT;
down_limit = EXTRA_DOWN_LIMIT;
left_limit = EXTRA_LEFT_LIMIT;
right_limit = EXTRA_RIGHT_LIMIT;
}
switch (cmd) {
case game_command::enter: {
if (enabled_features & FEATURE_EXTRA_STUFF) {
for (int i = 0; i < EXTRA_CNT; ++i)
if (cur == RACE_POS[i])
return RACES[i];
return -1;
} else {
for (int i = 0; i < NORMAL_CNT; ++i)
if (cur == RACE_POS[i])
return RACES[i];
return -1;
}
}
case move_north: {
if (cur.y != up_limit)
cur.y -= Y_INCRE;
break;
}
case move_south: {
if (cur.y != down_limit)
cur.y += Y_INCRE;
break;
}
case move_east: {
if (cur.x != left_limit)
cur.x -= X_INCRE;
break;
}
case move_west: {
if (cur.x != right_limit)
cur.x += X_INCRE;
break;
}
case game_command_terminate:
return -2;
default:
break;
}
return -1;
}
void menu::print(display *out) {
if (enabled_features & FEATURE_EXTRA_STUFF)
out->print_str({0, 0}, NORMAL_SCREEN);
else
out->print_str({0, 0}, EXTRA_SCREEN);
out->print_str(cur, "->", COLOR_PAIR(COLOR_BLUE));
}

21
src/menu.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __MENU_H__
#define __MENU_H__
#include "position.h"
#include "display.h"
#include "input.h"
typedef unsigned int feature;
enum race : int;
class menu final {
private:
const feature enabled_features;
position cur;
public:
menu(const feature enabled_features);
int run(input *in);
void print(display *out);
};
#endif

45
src/result.cc Normal file
View File

@ -0,0 +1,45 @@
#include "result.h"
const char *WIN_SCREEN =
"+-----------------------------------------------------------------------------+\
| |\
| |\
| |\
| |\
| ___________ |\
| '._==_==_=_.' |\
| .-\\: /-. |\
| | (|:. |) | |\
| '-|:. |-' |\
| \\::. / |\
| '::. .' |\
| ) ( |\
| _.' '._ |\
| `.......` |\
| |\
| |\
| |\
| |\
| |\
| |\
| |\
| |\
| |\
| > |\
| |\
| |\
| |\
| |\
+-----------------------------------------------------------------------------+";
const position &MSG_START = {11, 24};
void game_result::run(input *in) {
in->get_command();
}
void game_result::print(display *out) {
out->print_str({0, 0}, WIN_SCREEN);
out->print_str(MSG_START, msg, COLOR_PAIR(COLOR_YELLOW));
}

18
src/result.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __RESULT_H__
#define __RESULT_H__
#include <string>
#include "display.h"
#include "input.h"
enum game_status : int;
struct game_result final {
game_status status;
std::string msg;
void run(input *in);
void print(display *out);
};
#endif