diff --git a/src/arguments.cc b/src/arguments.cc index e5d98b3..141ec7d 100644 --- a/src/arguments.cc +++ b/src/arguments.cc @@ -43,9 +43,11 @@ feature proc_args(int argc, char **argv, } else if (str == "-d") { result |= FEATURE_DOORS; } else if (str == "-i") { - result |= FEATURE_INVENTORY; + result |= FEATURE_INVENTORY | FEATURE_WALK_OVER; } else if (str == "-t") { - result |= FEATURE_THROW; + result |= FEATURE_THROW | + FEATURE_INVENTORY | + FEATURE_WALK_OVER; } else if (str == "-R") { result |= FEATURE_REVISIT; } else if (str == "-e") { @@ -166,8 +168,8 @@ void print_args_list() { -r : Randomly generate maps\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\ --i : Enable inventory (player can pick up potions)\n\ --t : Enable throw\n\ +-i : Enable inventory (player can pick up potions, will turn on -o)\n\ +-t : Enable throw (will turn on -i)\n\ -R : Enable revisiting levels\n\ -e : Enable extra potions and races\n\ -E : Enable extra levels\n\ diff --git a/src/constants.h b/src/constants.h index 5768ca7..b05d350 100644 --- a/src/constants.h +++ b/src/constants.h @@ -12,7 +12,8 @@ static const int INF = 0x3F3F3F3F; // fine will waste a turn enum result : int {fine, died, go_down, go_up, hit, moved, miss, terminate, applied, applied_nothing, - toggle_the_world, restart_game, unknown + toggle_the_world, restart_game, unknown, + inventory, thrown }; struct long_result { @@ -38,7 +39,10 @@ enum game_command : int {game_command_terminate = 0, up_stairs, down_stairs, the_world, game_restart, game_command_pass, game_command_panic, - enter + enter, toggle_inventory, + throw_north, throw_south, throw_east, throw_west, + throw_northeast, throw_northwest, + throw_southeast, throw_southwest }; // Character generation related @@ -143,4 +147,8 @@ static const int GOLD_NORMAL = 2; static const int GOLD_MERCHANT = 4; static const int GOLD_DRAGON = 6; +static const size_t WHAT_ENEMY = 1 << 29; +static const size_t WHAT_WALL = 1 << 30; +static const size_t WHAT_SPACE = 1 << 31; + #endif diff --git a/src/game.cc b/src/game.cc index f1358a3..3f7d09e 100644 --- a/src/game.cc +++ b/src/game.cc @@ -154,6 +154,7 @@ game_result game::run() { case unknown: case fine: case applied_nothing: + case inventory: return {in_game, ""}; default: diff --git a/src/input/console_input.cc b/src/input/console_input.cc index 8a221b2..efbbcba 100644 --- a/src/input/console_input.cc +++ b/src/input/console_input.cc @@ -10,7 +10,6 @@ console_input::console_input(std::istream &cin): game_command console_input::get_command() { std::string cmd; in >> cmd; - game_command tmp; if (in.eof()) return game_command_terminate; @@ -21,20 +20,25 @@ game_command console_input::get_command() { return the_world; else if (cmd == "r") return game_restart; - else if (cmd == "u" || cmd == "a") { - bool use = cmd == "u"; - + else if (cmd == "u" || cmd == "a" || cmd == "t") { + auto cmdtmp = cmd; in >> cmd; if (in.eof()) return game_command_panic; - return (game_command)((tmp = get_direction(cmd)) == - game_command_panic - ? tmp : tmp + - (use ? apply_north : attack_north)); + auto tmp = get_direction(cmd); + + if (cmdtmp == "u") + return (game_command)(tmp + apply_north); + else if (cmdtmp == "a") + return (game_command)(tmp + attack_north); + else + return (game_command)(tmp + throw_north); } else if (cmd == "yes") { return game_command::enter; + } else if (cmd == "i") { + return toggle_inventory; } else { auto tmp = get_direction(cmd); diff --git a/src/input/curses_input.cc b/src/input/curses_input.cc index 9b650e6..1a2adf0 100644 --- a/src/input/curses_input.cc +++ b/src/input/curses_input.cc @@ -6,6 +6,8 @@ curses_input::curses_input(cursor *new_curse): curse{new_curse} {} game_command curses_input::get_command() { + int tmp; + switch (curse->getcmd()) { case 'h': return game_command::move_west; @@ -32,6 +34,7 @@ game_command curses_input::get_command() { return game_command::move_southeast; case 'a': + tmp = apply_north; break; // wait for another command case '<': @@ -52,37 +55,44 @@ game_command curses_input::get_command() { case 'e': return game_command::enter; + case 'i': + return toggle_inventory; + + case 't': + tmp = throw_north; + break; + default: return game_command_pass; } switch (curse->getcmd()) { case 'h': - return game_command::apply_west; + return (game_command)(tmp + west); case 'j': - return game_command::apply_south; + return (game_command)(tmp + south); case 'k': - return game_command::apply_north; + return (game_command)(tmp + north); case 'l': - return game_command::apply_east; + return (game_command)(tmp + east); case 'y': - return game_command::apply_northwest; + return (game_command)(tmp + northwest); case 'u': - return game_command::apply_northeast; + return (game_command)(tmp + northeast); case 'b': - return game_command::apply_southwest; + return (game_command)(tmp + southwest); case 'n': - return game_command::apply_southeast; + return (game_command)(tmp + southeast); default: - return game_command::apply_panic; + return game_command_panic; } return game_command::game_command_panic; diff --git a/src/input/file_input.cc b/src/input/file_input.cc index a694637..a70aa16 100644 --- a/src/input/file_input.cc +++ b/src/input/file_input.cc @@ -10,7 +10,6 @@ file_input::file_input(std::ifstream &&ifs): game_command file_input::get_command() { std::string cmd; in >> cmd; - game_command tmp; if (in.eof()) return game_command_terminate; @@ -21,20 +20,25 @@ game_command file_input::get_command() { return the_world; else if (cmd == "r") return game_restart; - else if (cmd == "u" || cmd == "a") { - bool use = cmd == "u"; - + else if (cmd == "u" || cmd == "a" || cmd == "t") { + auto cmdtmp = cmd; in >> cmd; if (in.eof()) return game_command_panic; - return (game_command)((tmp = get_direction(cmd)) == - game_command_panic - ? tmp : tmp + - (use ? apply_north : attack_north)); + auto tmp = get_direction(cmd); + + if (cmdtmp == "u") + return (game_command)(tmp + apply_north); + else if (cmdtmp == "a") + return (game_command)(tmp + attack_north); + else + return (game_command)(tmp + throw_north); } else if (cmd == "yes") { return game_command::enter; + } else if (cmd == "i") { + return toggle_inventory; } else { auto tmp = get_direction(cmd); diff --git a/src/level.cc b/src/level.cc index 62b5a1f..e976b49 100644 --- a/src/level.cc +++ b/src/level.cc @@ -17,7 +17,6 @@ level::level(character *player, RNG *rng, const feature enabled_features): 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); @@ -65,11 +64,11 @@ void level::gen_enemies(RNG *rng, std::vector &tiles) { for (size_t i = 0; i < dhoard.size(); ++i) { position_list spots; - for (int i = 0; i < DIRECTION_CNT; ++i) { - position tmp = dhoard[i].pos + MOVE[i]; + for (int dir = 0; dir < DIRECTION_CNT; ++dir) { + position tmp = dhoard[i].pos + MOVE[dir]; if (map.which_room(tmp) != -1) - spots.push_back(dhoard[i].pos + MOVE[i]); + spots.push_back(dhoard[i].pos + MOVE[dir]); } auto pos = spots.size() ? rng->get_rand_in_vector(spots) : @@ -269,3 +268,14 @@ void level::erase_enemy(character *ch) { 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 < plist.size(); ++i) + if (plist[i]->get_pos() == pos) + return WHAT_ENEMY | i; + + return WHAT_SPACE; +} diff --git a/src/level.h b/src/level.h index 2f02e3b..d8a3a78 100644 --- a/src/level.h +++ b/src/level.h @@ -53,6 +53,8 @@ public: void erase_enemy(character *ch); std::unique_ptr detach_potion(size_t idx); + + size_t what_is_at(const position &pos) const; private: // every gen will delete the positions in tiles void gen_potions(RNG *rng, std::vector &tiles); diff --git a/src/player.cc b/src/player.cc index 0a83085..a70c836 100644 --- a/src/player.cc +++ b/src/player.cc @@ -6,10 +6,14 @@ player_base::player_base(RNG *rng, const feature enabled_features, const enum race &nrace): - character{rng, enabled_features, nrace, {0, 0}}, gold_cnt(0) {} + character{rng, enabled_features, nrace, {0, 0}}, +gold_cnt{0}, known_potions{0}, inv{{}, 0, false}, MAX_THROW_DIST{5} {} void player_base::print(output *out) { out->print_char(pos, '@', COLOR_PAIR(COLOR_BLUE)); + + if (inv.enabled) + inv.print(out, known_potions); } std::string player_base::get_abbrev() const { @@ -39,6 +43,8 @@ long_result player_base::apply(std::unique_ptr p) { std::string name = p->get_name(); + known_potions |= 1 << p->get_type(); + apply_effect(std::move(p)); if (race == rt_800) @@ -119,7 +125,55 @@ void player_base::add_gold(int amount) { gold_cnt += amount; } +long_result player_base::throw_potion(level *lvl, std::unique_ptr p, + direction dir) { + if (p == nullptr) + return {thrown, "PC tried to throw a vial of air. "}; + + position tmp{pos}; + + for (int i = 0; i < MAX_THROW_DIST; ++i) { + tmp += MOVE[dir]; + size_t flag = lvl->what_is_at(tmp); + + if (flag & WHAT_WALL) + return {thrown, "The potion shattered against a wall. "}; + else if (flag & WHAT_ENEMY) { + character *ch = lvl->get_elist()[flag ^ WHAT_ENEMY]; + ch->apply_effect(std::move(p)); + return {thrown, "The potion's contents spilled on " + + ch->get_abbrev() + ". "}; + } + } + + return {thrown, "The potion shattered against the floor. "}; +} + long_result player_base::interpret_command(level *lvl, game_command cmd) { + if (inv.enabled) { + if (cmd == toggle_inventory || cmd == game_command_terminate) { + inv.enabled = false; + return {result::inventory, ""}; + } else { + + auto res = inv.run(cmd, enabled_features); + + if (res.second == -1) { + inv.enabled = false; + return apply(std::move(res.first)); + } else if (res.first == nullptr) { + return {result::inventory, ""}; + } else if (res.second == DIRECTION_CNT) { + inv.enabled = false; + return {result::thrown, + "PC tried to throw a vial of air. "}; + } else { + return throw_potion(lvl, std::move(res.first), + (direction)res.second); + } + } + } + if (cmd == game_command_terminate) { return {result::terminate, ""}; } else if (cmd >= move_north && cmd <= move_southwest) { @@ -132,6 +186,19 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { res.msg += "PC picked up " + std::to_string(g.amount) + " pieces of gold. "; + if (enabled_features & FEATURE_INVENTORY) { + size_t idx = get_potion_at(pos, lvl->get_plist()); + + if (idx != lvl->get_plist().size() && + inv.owns.size() < INV_MAX_CNT) { + inv.insert(lvl->detach_potion(idx)); + res.msg += "PC picked up a potion. "; + } else if (idx != lvl->get_plist().size()) { + res.msg += "PC already has a full knapsack. "; + } + } + + return res; } else if (cmd >= apply_north && cmd <= apply_southwest) { size_t idx = get_potion_at(pos + MOVE[cmd - apply_north], @@ -177,7 +244,91 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { return {restart_game, ""}; } else if (cmd == game_command_pass) { return {result::fine, ""}; + } else if (cmd == toggle_inventory && + enabled_features & FEATURE_INVENTORY) { + inv.start(); + return {result::inventory, ""}; } return {unknown, "PC tried to produce some undefined behaviour. "}; } + +void player_base::inventory::start() { + curr = 0; + enabled = true; +} + +std::pair, int> player_base::inventory::run( + game_command cmd, const feature enabled_features) { + if (cmd == move_north) { + if (curr != 0) + --curr; + + return {nullptr, 0}; + } else if (cmd == move_south) { + if (curr < owns.size() - 1) + ++curr; + + return {nullptr, 0}; + } else if (cmd == enter) { + if (owns.size()) { + std::unique_ptr tmp = std::move(owns[curr]); + owns.erase(owns.begin() + curr); + return {std::move(tmp), -1}; + } + + return {nullptr, -1}; + } else if (cmd >= throw_north && cmd <= throw_southwest && + enabled_features & FEATURE_THROW) { + if (owns.size()) { + std::unique_ptr tmp = std::move(owns[curr]); + owns.erase(owns.begin() + curr); + return {std::move(tmp), (direction)(cmd - throw_north)}; + } + + return {nullptr, DIRECTION_CNT}; + } + + return {nullptr, 0}; +} + +void player_base::inventory::insert(std::unique_ptr p) { + owns.push_back(std::move(p)); +} + +void player_base::inventory::print(output *out, unsigned long known_potions) { + if (!enabled) + return; + + // Draw out a window on top of the existing output + for (int y = INV_TOP; y < INV_BOTTOM; ++y) + for (int x = INV_LEFT; x < INV_RIGHT; ++x) + out->print_char({x, y}, ' '); + + for (int x = INV_LEFT; x < INV_RIGHT; ++x) { + out->print_char({x, INV_TOP}, '-', COLOR_PAIR(COLOR_YELLOW)); + out->print_char({x, INV_BOTTOM - 1}, '-', + COLOR_PAIR(COLOR_YELLOW)); + } + + for (int y = INV_TOP; y < INV_BOTTOM; ++y) { + out->print_char({INV_LEFT, y}, '|', COLOR_PAIR(COLOR_YELLOW)); + out->print_char({INV_RIGHT - 1, y}, '|', COLOR_PAIR(COLOR_YELLOW)); + } + + out->print_str({INV_LEFT + 1, INV_TOP}, + "Potions:", COLOR_PAIR(COLOR_BLUE)); + + for (size_t i = 0; i < owns.size(); ++i) { + out->print_str({INV_LEFT + INV_POTION_OFFSET, + (int) i + INV_TOP + INV_Y_OFFSET}, + "potion of " + + (known_potions & (1 << owns[i]->get_type()) ? + owns[i]->get_name() : (std::string)"??")); + } + + if (owns.size()) + out->print_str({INV_LEFT + INV_CURSOR_OFFSET, + (int)curr + INV_TOP + INV_Y_OFFSET}, + "->"); +} diff --git a/src/player.h b/src/player.h index bc6e2f8..8835e1d 100644 --- a/src/player.h +++ b/src/player.h @@ -4,11 +4,37 @@ #include "characters.h" enum game_command : int; +enum direction : int; class player_base: public character { + static const int INV_LEFT = 30; + static const int INV_RIGHT = 49; + static const int INV_TOP = 9; + static const int INV_BOTTOM = 21; + static const int INV_Y_OFFSET = 1; + static const int INV_CURSOR_OFFSET = 2; + static const int INV_POTION_OFFSET = 5; + static const int INV_MAX_CNT = 10; protected: int gold_cnt; - potion_own_list potions; + + unsigned long known_potions; + + struct inventory { + potion_own_list owns; + size_t curr; + bool enabled; + + // second is -1 if applied, is 0-7 if thrown + std::pair, int> run(game_command cmd, + const feature enabled_features); + void print(output *out, unsigned long known_potions); + void insert(std::unique_ptr p); + void start(); + }; + + inventory inv; + int MAX_THROW_DIST; public: player_base(RNG *rng, const feature enabled_features, const enum race &nrace); @@ -32,6 +58,9 @@ public: int get_ATK() const; int get_DEF() const; int get_HP() const; + + long_result throw_potion(level *lvl, + std::unique_ptr p, direction dir); }; #endif