added throwing

This commit is contained in:
2024-07-17 21:39:10 -04:00
parent ea381c24f6
commit dbae34e3a2
10 changed files with 258 additions and 37 deletions

View File

@ -43,9 +43,11 @@ feature proc_args(int argc, char **argv,
} else if (str == "-d") { } else if (str == "-d") {
result |= FEATURE_DOORS; result |= FEATURE_DOORS;
} else if (str == "-i") { } else if (str == "-i") {
result |= FEATURE_INVENTORY; result |= FEATURE_INVENTORY | FEATURE_WALK_OVER;
} else if (str == "-t") { } else if (str == "-t") {
result |= FEATURE_THROW; result |= FEATURE_THROW |
FEATURE_INVENTORY |
FEATURE_WALK_OVER;
} else if (str == "-R") { } else if (str == "-R") {
result |= FEATURE_REVISIT; result |= FEATURE_REVISIT;
} else if (str == "-e") { } else if (str == "-e") {
@ -166,8 +168,8 @@ void print_args_list() {
-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\
-i : Enable inventory (player can pick up potions)\n\ -i : Enable inventory (player can pick up potions, will turn on -o)\n\
-t : Enable throw\n\ -t : Enable throw (will turn on -i)\n\
-R : Enable revisiting levels\n\ -R : Enable revisiting levels\n\
-e : Enable extra potions and races\n\ -e : Enable extra potions and races\n\
-E : Enable extra levels\n\ -E : Enable extra levels\n\

View File

@ -12,7 +12,8 @@ static const int INF = 0x3F3F3F3F;
// fine will waste a turn // fine will waste a turn
enum result : int {fine, died, go_down, go_up, hit, moved, enum result : int {fine, died, go_down, go_up, hit, moved,
miss, terminate, applied, applied_nothing, miss, terminate, applied, applied_nothing,
toggle_the_world, restart_game, unknown toggle_the_world, restart_game, unknown,
inventory, thrown
}; };
struct long_result { struct long_result {
@ -38,7 +39,10 @@ enum game_command : int {game_command_terminate = 0,
up_stairs, down_stairs, up_stairs, down_stairs,
the_world, game_restart, the_world, game_restart,
game_command_pass, game_command_panic, 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 // Character generation related
@ -143,4 +147,8 @@ static const int GOLD_NORMAL = 2;
static const int GOLD_MERCHANT = 4; static const int GOLD_MERCHANT = 4;
static const int GOLD_DRAGON = 6; 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 #endif

View File

@ -154,6 +154,7 @@ game_result game::run() {
case unknown: case unknown:
case fine: case fine:
case applied_nothing: case applied_nothing:
case inventory:
return {in_game, ""}; return {in_game, ""};
default: default:

View File

@ -10,7 +10,6 @@ console_input::console_input(std::istream &cin):
game_command console_input::get_command() { game_command console_input::get_command() {
std::string cmd; std::string cmd;
in >> cmd; in >> cmd;
game_command tmp;
if (in.eof()) if (in.eof())
return game_command_terminate; return game_command_terminate;
@ -21,20 +20,25 @@ game_command console_input::get_command() {
return the_world; return the_world;
else if (cmd == "r") else if (cmd == "r")
return game_restart; return game_restart;
else if (cmd == "u" || cmd == "a") { else if (cmd == "u" || cmd == "a" || cmd == "t") {
bool use = cmd == "u"; auto cmdtmp = cmd;
in >> cmd; in >> cmd;
if (in.eof()) if (in.eof())
return game_command_panic; return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) == auto tmp = get_direction(cmd);
game_command_panic
? tmp : tmp + if (cmdtmp == "u")
(use ? apply_north : attack_north)); 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") { } else if (cmd == "yes") {
return game_command::enter; return game_command::enter;
} else if (cmd == "i") {
return toggle_inventory;
} else { } else {
auto tmp = get_direction(cmd); auto tmp = get_direction(cmd);

View File

@ -6,6 +6,8 @@ curses_input::curses_input(cursor *new_curse):
curse{new_curse} {} curse{new_curse} {}
game_command curses_input::get_command() { game_command curses_input::get_command() {
int tmp;
switch (curse->getcmd()) { switch (curse->getcmd()) {
case 'h': case 'h':
return game_command::move_west; return game_command::move_west;
@ -32,6 +34,7 @@ game_command curses_input::get_command() {
return game_command::move_southeast; return game_command::move_southeast;
case 'a': case 'a':
tmp = apply_north;
break; // wait for another command break; // wait for another command
case '<': case '<':
@ -52,37 +55,44 @@ game_command curses_input::get_command() {
case 'e': case 'e':
return game_command::enter; return game_command::enter;
case 'i':
return toggle_inventory;
case 't':
tmp = throw_north;
break;
default: default:
return game_command_pass; return game_command_pass;
} }
switch (curse->getcmd()) { switch (curse->getcmd()) {
case 'h': case 'h':
return game_command::apply_west; return (game_command)(tmp + west);
case 'j': case 'j':
return game_command::apply_south; return (game_command)(tmp + south);
case 'k': case 'k':
return game_command::apply_north; return (game_command)(tmp + north);
case 'l': case 'l':
return game_command::apply_east; return (game_command)(tmp + east);
case 'y': case 'y':
return game_command::apply_northwest; return (game_command)(tmp + northwest);
case 'u': case 'u':
return game_command::apply_northeast; return (game_command)(tmp + northeast);
case 'b': case 'b':
return game_command::apply_southwest; return (game_command)(tmp + southwest);
case 'n': case 'n':
return game_command::apply_southeast; return (game_command)(tmp + southeast);
default: default:
return game_command::apply_panic; return game_command_panic;
} }
return game_command::game_command_panic; return game_command::game_command_panic;

View File

@ -10,7 +10,6 @@ file_input::file_input(std::ifstream &&ifs):
game_command file_input::get_command() { game_command file_input::get_command() {
std::string cmd; std::string cmd;
in >> cmd; in >> cmd;
game_command tmp;
if (in.eof()) if (in.eof())
return game_command_terminate; return game_command_terminate;
@ -21,20 +20,25 @@ game_command file_input::get_command() {
return the_world; return the_world;
else if (cmd == "r") else if (cmd == "r")
return game_restart; return game_restart;
else if (cmd == "u" || cmd == "a") { else if (cmd == "u" || cmd == "a" || cmd == "t") {
bool use = cmd == "u"; auto cmdtmp = cmd;
in >> cmd; in >> cmd;
if (in.eof()) if (in.eof())
return game_command_panic; return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) == auto tmp = get_direction(cmd);
game_command_panic
? tmp : tmp + if (cmdtmp == "u")
(use ? apply_north : attack_north)); 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") { } else if (cmd == "yes") {
return game_command::enter; return game_command::enter;
} else if (cmd == "i") {
return toggle_inventory;
} else { } else {
auto tmp = get_direction(cmd); auto tmp = get_direction(cmd);

View File

@ -17,7 +17,6 @@ level::level(character *player, RNG *rng, const feature enabled_features):
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());
gen_potions(rng, tiles); gen_potions(rng, tiles);
gen_gold(rng, tiles); gen_gold(rng, tiles);
gen_enemies(rng, tiles); gen_enemies(rng, tiles);
@ -65,11 +64,11 @@ void level::gen_enemies(RNG *rng, std::vector<position_list> &tiles) {
for (size_t i = 0; i < dhoard.size(); ++i) { for (size_t i = 0; i < dhoard.size(); ++i) {
position_list spots; position_list spots;
for (int i = 0; i < DIRECTION_CNT; ++i) { for (int dir = 0; dir < DIRECTION_CNT; ++dir) {
position tmp = dhoard[i].pos + MOVE[i]; position tmp = dhoard[i].pos + MOVE[dir];
if (map.which_room(tmp) != -1) 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) : 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); 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;
}

View File

@ -53,6 +53,8 @@ public:
void erase_enemy(character *ch); void erase_enemy(character *ch);
std::unique_ptr<potion> detach_potion(size_t idx); std::unique_ptr<potion> detach_potion(size_t idx);
size_t what_is_at(const position &pos) const;
private: 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);

View File

@ -6,10 +6,14 @@
player_base::player_base(RNG *rng, const feature enabled_features, player_base::player_base(RNG *rng, const feature enabled_features,
const enum race &nrace): 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) { void player_base::print(output *out) {
out->print_char(pos, '@', COLOR_PAIR(COLOR_BLUE)); out->print_char(pos, '@', COLOR_PAIR(COLOR_BLUE));
if (inv.enabled)
inv.print(out, known_potions);
} }
std::string player_base::get_abbrev() const { std::string player_base::get_abbrev() const {
@ -39,6 +43,8 @@ long_result player_base::apply(std::unique_ptr<potion> p) {
std::string name = p->get_name(); std::string name = p->get_name();
known_potions |= 1 << p->get_type();
apply_effect(std::move(p)); apply_effect(std::move(p));
if (race == rt_800) if (race == rt_800)
@ -119,7 +125,55 @@ void player_base::add_gold(int amount) {
gold_cnt += amount; gold_cnt += amount;
} }
long_result player_base::throw_potion(level *lvl, std::unique_ptr<potion> 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) { 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) { if (cmd == game_command_terminate) {
return {result::terminate, ""}; return {result::terminate, ""};
} else if (cmd >= move_north && cmd <= move_southwest) { } 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) + res.msg += "PC picked up " + std::to_string(g.amount) +
" pieces of gold. "; " 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; return res;
} else if (cmd >= apply_north && cmd <= apply_southwest) { } else if (cmd >= apply_north && cmd <= apply_southwest) {
size_t idx = get_potion_at(pos + MOVE[cmd - apply_north], 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, ""}; return {restart_game, ""};
} else if (cmd == game_command_pass) { } else if (cmd == game_command_pass) {
return {result::fine, ""}; 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. "}; return {unknown, "PC tried to produce some undefined behaviour. "};
} }
void player_base::inventory::start() {
curr = 0;
enabled = true;
}
std::pair<std::unique_ptr<potion>, 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<potion> 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<potion> 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<potion> 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},
"->");
}

View File

@ -4,11 +4,37 @@
#include "characters.h" #include "characters.h"
enum game_command : int; enum game_command : int;
enum direction : int;
class player_base: public character { 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: protected:
int gold_cnt; 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<std::unique_ptr<potion>, int> run(game_command cmd,
const feature enabled_features);
void print(output *out, unsigned long known_potions);
void insert(std::unique_ptr<potion> p);
void start();
};
inventory inv;
int MAX_THROW_DIST;
public: public:
player_base(RNG *rng, const feature enabled_features, player_base(RNG *rng, const feature enabled_features,
const enum race &nrace); const enum race &nrace);
@ -32,6 +58,9 @@ public:
int get_ATK() const; int get_ATK() const;
int get_DEF() const; int get_DEF() const;
int get_HP() const; int get_HP() const;
long_result throw_potion(level *lvl,
std::unique_ptr<potion> p, direction dir);
}; };
#endif #endif