From 6768d73d16f5169a79ba39b5151e5d854aeb8278 Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Mon, 15 Jul 2024 00:09:14 -0400 Subject: [PATCH] full implementation! bug: level generation will generate nullptrs --- src/cc3k.cc | 76 +++++++++++++++++ src/cc3k.h | 22 ++++- src/constants.h | 2 +- src/game.cc | 49 +++++++---- src/game.h | 5 +- src/input/console_input.cc | 2 +- src/level.cc | 36 ++++----- src/main.cc | 9 +-- src/map.cc | 7 +- src/menu.cc | 161 +++++++++++++++++++++++++++++++++++++ src/menu.h | 21 +++++ src/result.cc | 45 +++++++++++ src/result.h | 18 +++++ 13 files changed, 407 insertions(+), 46 deletions(-) create mode 100644 src/cc3k.cc create mode 100644 src/menu.cc create mode 100644 src/menu.h create mode 100644 src/result.cc create mode 100644 src/result.h diff --git a/src/cc3k.cc b/src/cc3k.cc new file mode 100644 index 0000000..10e288f --- /dev/null +++ b/src/cc3k.cc @@ -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(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((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(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; +} diff --git a/src/cc3k.h b/src/cc3k.h index 17663f9..4280d6e 100644 --- a/src/cc3k.h +++ b/src/cc3k.h @@ -1,8 +1,26 @@ #ifndef __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 curr_game; + std::unique_ptr curr_menu; +public: + CC3K(const feature enabled_features, + input *in, display *out, RNG *rng); + game_status run(); +}; #endif diff --git a/src/constants.h b/src/constants.h index d7668cd..aee224f 100644 --- a/src/constants.h +++ b/src/constants.h @@ -20,7 +20,7 @@ struct long_result { }; 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, diff --git a/src/game.cc b/src/game.cc index cc835d8..fbc42e8 100644 --- a/src/game.cc +++ b/src/game.cc @@ -46,9 +46,9 @@ bool compare_characters(const character *a, const character *b) { return a->get_pos() < b->get_pos(); } -void game::move_enemies() { +character *game::move_enemies() { if (the_world) - return; + return nullptr; auto enemies = levels[curr_level]->get_elist(); 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); if (player->is_dead()) - return; + return ch; if (ch->is_dead()) ch->dies(levels[curr_level].get(), player.get()); msg += res.msg; } + + return nullptr; } -game_status game::run() { +game_result game::run() { player->start_turn(); auto res = player->interpret_command(levels[curr_level].get(), in->get_command()); @@ -79,28 +81,40 @@ game_status game::run() { switch (res.res) { case result::terminate: - return terminated; + return {terminated, ""}; case result::died: - return game_status::dead; + return {dead, "You died: killed by your own stupidity!"}; case result::go_down: { 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(); + ++curr_level; + new_level(); + break; } case result::go_up: { 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(); + --curr_level; + player->set_pos(levels[curr_level]->get_down_stairs()); + break; } @@ -109,13 +123,13 @@ game_status game::run() { break; case restart_game: - return restart; + return {restart, ""}; case unknown: case fine: case applied_nothing: ++curr_turn; - return game_status::in_game; + return {in_game, ""}; default: break; @@ -125,15 +139,20 @@ game_status game::run() { player->calc_effects(); 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()) - return game_status::dead; + if (player->is_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; - return game_status::in_game; + return {in_game, ""}; } const position STATUS_RACE{0, 25}; diff --git a/src/game.h b/src/game.h index 5f92fab..6596d9f 100644 --- a/src/game.h +++ b/src/game.h @@ -8,6 +8,7 @@ #include "characters.h" #include "level.h" #include "player.h" +#include "result.h" enum game_status : int; @@ -43,14 +44,14 @@ public: input *new_in, display *new_out, RNG *new_rng); - game_status run(); + game_result run(); size_t get_curr_level() const; size_t get_curr_turn() const; const character *get_player() const; void print(); private: void new_level(); - void move_enemies(); + character *move_enemies(); }; #endif diff --git a/src/input/console_input.cc b/src/input/console_input.cc index 561bf60..f54f2a2 100644 --- a/src/input/console_input.cc +++ b/src/input/console_input.cc @@ -39,7 +39,7 @@ game_command console_input::get_command() { auto tmp = get_direction(cmd); if (tmp != game_command_panic) - return tmp; + return (game_command)(tmp + move_north); } return game_command_pass; diff --git a/src/level.cc b/src/level.cc index 03b40b7..c3504e6 100644 --- a/src/level.cc +++ b/src/level.cc @@ -114,31 +114,31 @@ void level::gen_potions(RNG *rng, std::vector &tiles) { void level::print(display *out) const { map.print(out); - for (auto ch : elist) - ch->print(out); + for (size_t i = 0; i < plist.size(); ++i) + plist[i]->print(out); - for (auto p : plist) - p->print(out); + for (size_t i = 0; i < glist.size(); ++i) + out->print_char(glist[i].pos, 'G', COLOR_PAIR(COLOR_YELLOW)); - for (auto gold : glist) - out->print_char(gold.pos, 'G', COLOR_PAIR(COLOR_YELLOW)); + 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; - for (auto ch : elist) - if (pos == ch->get_pos()) + for (size_t i = 0; i < elist.size(); ++i) + if (pos == elist[i]->get_pos()) return false; if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) { - for (auto p : plist) - if (pos == p->get_pos()) + for (size_t i = 0; i < plist.size(); ++i) + if (pos == plist[i]->get_pos()) return false; - for (auto g : glist) - if (pos == g.pos) + for (size_t i = 0; i < glist.size(); ++i) + if (pos == glist[i].pos) return false; } @@ -149,16 +149,16 @@ bool level::is_available_all(const position &pos) const { if (!map.is_available(pos)) return false; - for (auto ch : elist) - if (pos == ch->get_pos()) + for (size_t i = 0; i < elist.size(); ++i) + if (pos == elist[i]->get_pos()) return false; - for (auto p : plist) - if (pos == p->get_pos()) + for (size_t i = 0; i < plist.size(); ++i) + if (pos == plist[i]->get_pos()) return false; - for (auto g : glist) - if (pos == g.pos) + for (size_t i = 0; i < glist.size(); ++i) + if (pos == glist[i].pos) return false; return true; diff --git a/src/main.cc b/src/main.cc index 1e76615..eba5c5d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -22,12 +22,11 @@ int main(int argc, char **argv) { 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) { - // out->render(); - // out->clear(); - // } + while (1) + if (game_proc.run() == game_status::terminated) + break; return RETURN_FINE; } diff --git a/src/map.cc b/src/map.cc index 09ff711..a5af9dd 100644 --- a/src/map.cc +++ b/src/map.cc @@ -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) 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()); 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)); } + 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) 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])); gen_stairs(player_room, rng); - } bool game_map::is_available(const position &pos) const { diff --git a/src/menu.cc b/src/menu.cc new file mode 100644 index 0000000..b0ed548 --- /dev/null +++ b/src/menu.cc @@ -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)); +} diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..903f295 --- /dev/null +++ b/src/menu.h @@ -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 diff --git a/src/result.cc b/src/result.cc new file mode 100644 index 0000000..c0c80fb --- /dev/null +++ b/src/result.cc @@ -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)); +} diff --git a/src/result.h b/src/result.h new file mode 100644 index 0000000..38f64e3 --- /dev/null +++ b/src/result.h @@ -0,0 +1,18 @@ +#ifndef __RESULT_H__ +#define __RESULT_H__ + +#include +#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