diff --git a/src/arguments.cc b/src/arguments.cc index 8d52f2c..e5d98b3 100644 --- a/src/arguments.cc +++ b/src/arguments.cc @@ -117,9 +117,8 @@ feature proc_args(int argc, char **argv, if (result & FEATURE_SEED) { std::istringstream iss {seed}; unsigned int tmp; - iss >> tmp; - if (!iss.good()) + if (!(iss >> tmp)) return FEATURE_PANIC_SEED; rng = std::make_unique(tmp); diff --git a/src/cc3k.cc b/src/cc3k.cc index b0c8dab..495dd73 100644 --- a/src/cc3k.cc +++ b/src/cc3k.cc @@ -18,8 +18,10 @@ game_status CC3K::run() { case main_menu: { auto tmp = curr_menu->run(in); - if (tmp == -2) + if (tmp == -2) { + gresult.status = terminated; return terminated; + } if (tmp != -1) { curr_menu = nullptr; diff --git a/src/characters.cc b/src/characters.cc index 6203f55..cb1be92 100644 --- a/src/characters.cc +++ b/src/characters.cc @@ -9,12 +9,12 @@ character::character(RNG *rng, const feature enabled_features, rng{rng}, enabled_features{enabled_features}, race{nrace}, HP{STARTING_HP[race]}, pos{pos}, ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]}, - base_hit_rate{1, 1}, base_hit_rate_reset{1, 1} {} + base_hit_rate{STARTING_HR[race]} {} void character::start_turn() { ATK = STARTING_ATK[race]; DEF = STARTING_DEF[race]; - base_hit_rate = base_hit_rate_reset; + base_hit_rate = STARTING_HR[race]; } enum race character::get_race() const { @@ -29,12 +29,12 @@ void character::set_pos(const position &npos) { pos = npos; } -void character::apply_effect(potion *effect) { - insert_effect(effect); +void character::apply_effect(std::unique_ptr effect) { + insert_effect(std::move(effect)); } -void character::insert_effect(potion *effect) { - effects.push_back(effect); +void character::insert_effect(std::unique_ptr effect) { + effects.push_back(std::move(effect)); for (int i = effects.size() - 1; i > 0 && effect->get_priority() < effects[i - 1]->get_priority(); --i) @@ -42,17 +42,17 @@ void character::insert_effect(potion *effect) { } result character::calc_effects() { - potion_list tmp; + potion_own_list tmp; tmp.reserve(effects.size()); - for (auto p : effects) { - p->apply(this->race, HP, ATK, DEF, base_hit_rate); + for (size_t i = 0; i < effects.size(); ++i) { + effects[i]->apply(this->race, HP, ATK, DEF, base_hit_rate); if (HP <= 0) return result::died; - if (p->get_duration() != 0) - tmp.push_back(p); + if (effects[i]->get_duration() != 0) + tmp.push_back(std::move(effects[i])); } tmp.shrink_to_fit(); @@ -63,12 +63,12 @@ result character::calc_effects() { } void character::discard_level_effects() { - potion_list tmp; + potion_own_list tmp; tmp.reserve(effects.size()); - for (auto p : effects) - if (p->get_duration() > 0) - tmp.push_back(p); + for (size_t i = 0; i < effects.size(); ++i) + if (effects[i]->get_duration() > 0) + tmp.push_back(std::move(effects[i])); tmp.shrink_to_fit(); diff --git a/src/characters.h b/src/characters.h index 5b9543f..8163ea8 100644 --- a/src/characters.h +++ b/src/characters.h @@ -26,7 +26,7 @@ public: virtual long_result get_hit(character *ch, const int tATK, const fraction &hit_rate) = 0; - virtual void apply_effect(potion *effect); + virtual void apply_effect(std::unique_ptr effect); // override for different types virtual void print(output *out) = 0; @@ -56,12 +56,11 @@ protected: int ATK; int DEF; fraction base_hit_rate; - fraction base_hit_rate_reset; - potion_list effects; + potion_own_list effects; private: - void insert_effect(potion *effect); + void insert_effect(std::unique_ptr effect); }; int calc_dmg(const int ATK, const int DEF); diff --git a/src/constants.h b/src/constants.h index fbaa5cc..5768ca7 100644 --- a/src/constants.h +++ b/src/constants.h @@ -4,6 +4,7 @@ #include #include #include "position.h" +#include "fraction.h" static const int INF = 0x3F3F3F3F; @@ -41,27 +42,33 @@ enum game_command : int {game_command_terminate = 0, }; // Character generation related -static const int RACE_CNT = 12; +static const int RACE_CNT = 13; enum race : int {rshade = 0, rdrow, rvampire, rtroll, rgoblin, rhuman, rdwarf, relf, - rorc, rmerchant, rdragon, rhalfling + rorc, rmerchant, rdragon, rhalfling, + rt_800 }; static const char CHAR_REP[RACE_CNT] = { - 's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L' + 's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L', 't' }; static const int MAX_HP[RACE_CNT] = { - 125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100 + 125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100, 500 }; static const int STARTING_HP[RACE_CNT] = { - 125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100 + 125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100, 500 }; static const int STARTING_ATK[RACE_CNT] = { - 25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15 + 25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15, 40 }; static const int STARTING_DEF[RACE_CNT] = { - 25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20 + 25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20, 50 +}; +static const fraction STARTING_HR[RACE_CNT] = { + {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, + {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, + {1, 1} }; diff --git a/src/cursor.cc b/src/cursor.cc index 892a90c..c010ca6 100644 --- a/src/cursor.cc +++ b/src/cursor.cc @@ -19,6 +19,7 @@ cursor::cursor() { cursor::~cursor() { endwin(); + refresh(); } int cursor::getcmd() const { diff --git a/src/enemies.cc b/src/enemies.cc index 1665f1a..0ed2561 100644 --- a/src/enemies.cc +++ b/src/enemies.cc @@ -9,17 +9,18 @@ #include "enemies/merchant.h" #include "enemies/orc.h" -void new_dragon(RNG *rng, std::unique_ptr &p, - const position &pos, const position &fallback, - const feature enabled_features, int which_room) { +std::unique_ptr new_dragon(RNG *rng, const position &pos, + const position &fallback, + const feature enabled_features, + int which_room) { const position nil{0, 0}; if (pos != nil) - p = std::make_unique(rng, enabled_features, - pos, which_room); + return std::make_unique(rng, enabled_features, + pos, which_room); else - p = std::make_unique(rng, enabled_features, - fallback, which_room); + return std::make_unique(rng, enabled_features, + fallback, which_room); } const int EXCNT = 6; @@ -44,9 +45,9 @@ enum race get_normal_race(RNG *rng) { return CHOICES[rng->rand_under(CNT)]; } -void new_enemy(RNG *rng, std::unique_ptr &p, - const position &pos, const feature enabled_features, - int which_room) { +std::unique_ptr new_enemy(RNG *rng, const position &pos, + const feature enabled_features, + int which_room) { using std::make_unique; enum race r; @@ -56,34 +57,40 @@ void new_enemy(RNG *rng, std::unique_ptr &p, else r = get_normal_race(rng); - p = nullptr; - switch (r) { case rdwarf: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; case rhuman: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; case relf: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; case rorc: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; case rmerchant: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; case rhalfling: - p = make_unique(rng, enabled_features, pos, which_room); + return make_unique(rng, enabled_features, + pos, which_room); break; default: break; } + + return nullptr; } diff --git a/src/enemies.h b/src/enemies.h index ea44300..14183c8 100644 --- a/src/enemies.h +++ b/src/enemies.h @@ -4,12 +4,13 @@ #include #include "enemy.h" -void new_dragon(RNG *rng, std::unique_ptr &p, - const position &pos, const position &fallback, - const feature enabled_features, int which_room); +std::unique_ptr new_dragon(RNG *rng, const position &pos, + const position &fallback, + const feature enabled_features, + int which_room); -void new_enemy(RNG *rng, std::unique_ptr &p, - const position &pos, const feature enabled_features, - int which_room); +std::unique_ptr new_enemy(RNG *rng, const position &pos, + const feature enabled_features, + int which_room); #endif diff --git a/src/enemies/elf.cc b/src/enemies/elf.cc index ed37011..dc292e0 100644 --- a/src/enemies/elf.cc +++ b/src/enemies/elf.cc @@ -20,6 +20,9 @@ long_result elf::attack(character *ch) { if (res1.res == result::died) return res1; + if (ch->get_race() == rdrow) + return res1; + auto res2 = ch->get_hit(this, ATK, base_hit_rate); if (res1.res == miss && res2.res == miss) diff --git a/src/enemy.cc b/src/enemy.cc index 40810c8..89e8246 100644 --- a/src/enemy.cc +++ b/src/enemy.cc @@ -8,9 +8,7 @@ enemy_base::enemy_base(RNG *rng, const feature enabled_features, const enum race &nrace, const position &pos, const int gen_room_num, std::string abbrev): character{rng, enabled_features, nrace, pos}, - room_num{gen_room_num}, abbrev{abbrev} { - base_hit_rate_reset = {1, 2}; -} + room_num{gen_room_num}, abbrev{abbrev} {} int enemy_base::get_room_num() const { return room_num; @@ -88,27 +86,21 @@ long_result enemy_base::get_hit(character *ch, const int tATK, return {miss, "PC tried to hit " + abbrev + " but missed. "}; } -void enemy_base::dies(level *lvl, character *pc) { - auto &elist = lvl->get_elist(); - - for (size_t i = 0; i < elist.size(); ++i) - if (elist[i] == this) { - elist.erase(elist.begin() + i); - break; - } - +int enemy_base::dies(level *lvl) { if (race == race::rdragon) { - return; + return 0; } else if (race == race::rmerchant) { lvl->add_gold({GOLD_MERCHANT, pos}); + return 0; } else if (race == race::rhuman) { lvl->add_gold({GOLD_NORMAL, pos}); auto plist = lvl->get_available_around_all(pos); lvl->add_gold({GOLD_NORMAL, rng->get_rand_in_vector(plist)}); + return 0; } - ((player_base *)pc)->add_gold(rand_gold_drop(rng)); + return rand_gold_drop(rng); } enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) { diff --git a/src/enemy.h b/src/enemy.h index e43fd32..aed9034 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -26,7 +26,7 @@ public: virtual std::string get_abbrev() const override; - virtual void dies(level *lvl, character *pc); + virtual int dies(level *lvl); }; typedef std::vector enemy_list; diff --git a/src/game.cc b/src/game.cc index 8f41182..f1358a3 100644 --- a/src/game.cc +++ b/src/game.cc @@ -15,7 +15,7 @@ game::game(const enum race starting_race, the_world = false; // TODO: add the other races - init_player(rng, player, enabled_features, starting_race); + player = init_player(rng, enabled_features, starting_race); if (enabled_features & FEATURE_EXTRA_LEVELS) max_level = rng->rand_between(MIN_LEVEL_CNT, MAX_LEVEL_CNT + 1); @@ -73,8 +73,13 @@ character *game::move_enemies() { if (player->is_dead()) return ch; - if (ch->is_dead()) - ch->dies(levels[curr_level].get(), player.get()); + if (ch->is_dead()) { + int g = ch->dies(levels[curr_level].get()); + levels[curr_level]->erase_enemy(ch); + + if (g) + levels[curr_level]->add_gold({g, ch->get_pos()}); + } msg += res.msg; } @@ -105,6 +110,8 @@ game_result game::run() { std::to_string(curr_turn + 1) + " turns!"}; + player->start_turn(); + player->discard_level_effects(); ++curr_level; @@ -112,6 +119,9 @@ game_result game::run() { if (curr_level == levels.size()) new_level(); + if (enabled_features & FEATURE_REVISIT) + player->set_pos(levels[curr_level]->get_up_stairs()); + break; } @@ -123,6 +133,8 @@ game_result game::run() { std::to_string(curr_turn + 1) + " turns! Coward!"}; + player->start_turn(); + player->discard_level_effects(); --curr_level; @@ -142,13 +154,15 @@ game_result game::run() { case unknown: case fine: case applied_nothing: - ++curr_turn; return {in_game, ""}; default: + player->start_turn(); break; } + ++curr_turn; + player->calc_effects(); if (player->is_dead()) @@ -164,7 +178,6 @@ game_result game::run() { killer->get_race_name()}; } - ++curr_turn; return {in_game, ""}; } diff --git a/src/level.cc b/src/level.cc index 6f66f5a..62b5a1f 100644 --- a/src/level.cc +++ b/src/level.cc @@ -7,6 +7,9 @@ level::level(character *player, RNG *rng, const feature enabled_features): player{player} { auto tiles = map.get_room_list(); + for (size_t i = 0; i < tiles.size(); ++i) + remove_from_list(tiles[i], player->get_pos()); + for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_down_stairs()); @@ -26,6 +29,9 @@ level::level(const std::string &map_data, character *player, RNG *rng, map{player, map_data, rng, enabled_features}, player{player} { auto tiles = map.get_room_list(); + for (size_t i = 0; i < tiles.size(); ++i) + remove_from_list(tiles[i], player->get_pos()); + for (size_t i = 0; i < tiles.size(); ++i) remove_from_list(tiles[i], map.get_down_stairs()); @@ -66,18 +72,25 @@ void level::gen_enemies(RNG *rng, std::vector &tiles) { spots.push_back(dhoard[i].pos + MOVE[i]); } - pelist.push_back(nullptr); - new_dragon(rng, pelist[i], rng->get_rand_in_vector(spots), - dhoard[i].pos, enabled_features, - map.which_room(dhoard[i].pos)); + auto pos = spots.size() ? rng->get_rand_in_vector(spots) : + dhoard[i].pos; + pelist.push_back(new_dragon(rng, pos, dhoard[i].pos, + enabled_features, + map.which_room(dhoard[i].pos))); + int room = map.which_room(pos); + remove_from_list(tiles[room], pos); + + if (!tiles[room].size()) + tiles.erase(tiles.begin() + room); + elist.push_back(pelist[i].get()); } for (int i = dhoard.size(); i < cnt; ++i) { - pelist.push_back(nullptr); auto p = get_rand_pos(rng, tiles); - new_enemy(rng, pelist[i], p, enabled_features, map.which_room(p)); + pelist.push_back(new_enemy(rng, p, enabled_features, + map.which_room(p))); elist.push_back(pelist[i].get()); } } @@ -123,10 +136,8 @@ void level::gen_potions(RNG *rng, std::vector &tiles) { pplist.reserve(cnt); for (int i = 0; i < cnt; ++i) { - pplist.push_back(nullptr); - new_potion(pplist[i], - (potion_type)rng->rand_under(max_type), - get_rand_pos(rng, tiles)); + pplist.push_back(new_potion((potion_type)rng->rand_under(max_type), + get_rand_pos(rng, tiles))); plist.push_back(pplist[i].get()); } } @@ -172,6 +183,10 @@ bool level::is_available_all(const position &pos) const { if (!map.is_available(pos)) return false; + if (!(enabled_features & FEATURE_DOORS) && + map.which_room(pos) == -1) + return false; + if (player->get_pos() == pos) return false; @@ -222,14 +237,35 @@ position level::get_down_stairs() const { return map.get_down_stairs(); } -enemy_list &level::get_elist() { +const enemy_list &level::get_elist() { return elist; } -potion_list &level::get_plist() { +const potion_list &level::get_plist() { return plist; } gold_list &level::get_glist() { return glist; } + +std::unique_ptr level::detach_potion(size_t idx) { + if (idx >= plist.size()) + return nullptr; + + auto tmp = std::move(pplist[idx]); + plist.erase(plist.begin() + idx); + pplist.erase(pplist.begin() + idx); + return tmp; +} + +void level::erase_enemy(character *ch) { + if (ch == nullptr) + return; + + for (size_t i = 0; i < elist.size(); ++i) + if (elist[i] == ch) { + elist.erase(elist.begin() + i); + pelist.erase(pelist.begin() + i); + } +} diff --git a/src/level.h b/src/level.h index 34f6ca7..2f02e3b 100644 --- a/src/level.h +++ b/src/level.h @@ -45,11 +45,14 @@ public: position get_down_stairs() const; // you can delete the pointers to the stuff - enemy_list &get_elist(); - potion_list &get_plist(); + const enemy_list &get_elist(); + const potion_list &get_plist(); gold_list &get_glist(); void add_gold(gold g); + + void erase_enemy(character *ch); + std::unique_ptr detach_potion(size_t idx); private: // every gen will delete the positions in tiles void gen_potions(RNG *rng, std::vector &tiles); diff --git a/src/output.cc b/src/output.cc new file mode 100644 index 0000000..0723667 --- /dev/null +++ b/src/output.cc @@ -0,0 +1,17 @@ +#include "output.h" + +#include "constants.h" + +output::output(): + contents{DISPLAY_BUFFER_SIZE, 0} { + for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) + contents.push_back(0); +} + +void output::clear() { + contents.clear(); + contents.reserve(DISPLAY_BUFFER_SIZE); + + for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) + contents.push_back(0); +} diff --git a/src/output.h b/src/output.h new file mode 100644 index 0000000..217aa75 --- /dev/null +++ b/src/output.h @@ -0,0 +1,35 @@ +#ifndef __DISPLAY_H__ +#define __DISPLAY_H__ + +#include +#include +#include "position.h" +#include "cursor.h" + +class output { +protected: + // use an array of ints to keep track of what's on the screen + // This will have a bit of buffer for the last line + // just in case it overflows + std::vector contents; +public: + output(); + + virtual ~output() = default; + + // Only call this to refresh the entire output + virtual void render() = 0; + + // Clears the contents buffer + virtual void clear(); + + virtual void print_char(const position &pos, const char ch, + const int attrs = + A_NORMAL | COLOR_PAIR(COLOR_WHITE)) = 0; + virtual void print_str(const position &pos, const std::string &str, + const int attrs = + A_NORMAL | COLOR_PAIR(COLOR_WHITE)) = 0; + // default arguments are to be set in the base class's declaration +}; + +#endif diff --git a/src/output/console_output.cc b/src/output/console_output.cc new file mode 100644 index 0000000..beff4c2 --- /dev/null +++ b/src/output/console_output.cc @@ -0,0 +1,100 @@ +#include "console_output.h" + +#include +#include +#include + +#include "../constants.h" + +console_output::console_output(std::ostream &cout): out{cout} {} + +/* Attributes + black 30 40 + red 31 41 + green 32 42 + yellow 33 43 + blue 34 44 + magenta 35 45 + cyan 36 46 + white 37 47 + reset 0 (everything back to normal) + bold/bright 1 (often a brighter shade of the same colour) + underline 4 + inverse 7 (swap foreground and background colours) + bold/bright off 21 + underline off 24 + inverse off 27 + + Format: + \033[X;Ym + X Y are numbers from above +*/ + +std::string console_output::get_code(const int attrs) { + if (attrs < 255) + return "\033[0m"; + + std::string result = "\033[0m\033["; + + if (attrs & A_BOLD) + result += "1;"; + + if (attrs & A_UNDERLINE) + result += "4;"; + + if (attrs & A_STANDOUT) + result += "7;"; + + if ((attrs & COLOR_PAIR(COLOR_WHITE)) == COLOR_PAIR(COLOR_WHITE)) + result += "37;"; + else if ((attrs & COLOR_PAIR(COLOR_CYAN)) == COLOR_PAIR(COLOR_CYAN)) + result += "36;"; + else if ((attrs & COLOR_PAIR(COLOR_MAGENTA)) == COLOR_PAIR(COLOR_MAGENTA)) + result += "35;"; + else if ((attrs & COLOR_PAIR(COLOR_BLUE)) == COLOR_PAIR(COLOR_BLUE)) + result += "34;"; + else if ((attrs & COLOR_PAIR(COLOR_YELLOW)) == COLOR_PAIR(COLOR_YELLOW)) + result += "33;"; + else if ((attrs & COLOR_PAIR(COLOR_RED)) == COLOR_PAIR(COLOR_RED)) + result += "31;"; + else if ((attrs & COLOR_PAIR(COLOR_GREEN)) == COLOR_PAIR(COLOR_GREEN)) + result += "32;"; + else if ((attrs & COLOR_PAIR(COLOR_BLACK_ON_WHITE)) == COLOR_BLACK_ON_WHITE) + result += "30;47;"; + + result[result.length() - 1] = 'm'; + return result; +} + +void console_output::render() { + out << "\x1B[2J\x1B[H"; + + for (std::size_t idx = 0; idx < contents.size(); ++idx) { + if (idx % DISPLAY_WIDTH == 0 && idx) + out << std::endl; + + out << get_code(contents[idx]) + << (char)(contents[idx] ? contents[idx] : ' '); + } + + out << std::endl; +} + +void console_output::print_char(const position &pos, const char ch, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + contents[pos.y * DISPLAY_WIDTH + pos.x] = attrs | ch; +} + +void console_output::print_str(const position &pos, const std::string &str, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + int head = pos.y * DISPLAY_WIDTH + pos.x; + + for (std::size_t i = 0; i < str.length(); ++i) + contents[i + head] = attrs | str[i]; +} diff --git a/src/output/console_output.h b/src/output/console_output.h new file mode 100644 index 0000000..c8dbfd1 --- /dev/null +++ b/src/output/console_output.h @@ -0,0 +1,21 @@ +#ifndef __CONSOLE_OUTPUT_H__ +#define __CONSOLE_OUTPUT_H__ + +#include +#include "../output.h" + +class console_output final : public output { +private: + std::ostream &out; + std::string get_code(const int attrs); +public: + console_output(std::ostream &cout); + + void render() override; + void print_char(const position &pos, + const char ch, const int attrs) override; + void print_str(const position &pos, + const std::string &str, const int attrs) override; +}; + +#endif diff --git a/src/output/curses_output.cc b/src/output/curses_output.cc new file mode 100644 index 0000000..bee3adf --- /dev/null +++ b/src/output/curses_output.cc @@ -0,0 +1,38 @@ +#include "curses_output.h" + +#include "../constants.h" + +curses_output::curses_output(cursor *new_curse): + curse{new_curse} {} + +void curses_output::render() { + curse->show(); +} + +void curses_output::clear() { + curse->clear(); +} + +void curses_output::print_char(const position &pos, const char ch, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + curse->print_char(pos, ch, attrs); +} + +void curses_output::print_str(const position &pos, const std::string &str, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + position tmp = pos; + + for (std::size_t i = 0; i < str.length(); ++i) { + curse->print_char(tmp, str[i], attrs); + tmp += {1, 0}; + + if (tmp.x >= DISPLAY_WIDTH) + tmp = {0, tmp.y + 1}; + } +} diff --git a/src/output/curses_output.h b/src/output/curses_output.h new file mode 100644 index 0000000..7d841b9 --- /dev/null +++ b/src/output/curses_output.h @@ -0,0 +1,23 @@ +#ifndef __CURSES_OUTPUT_H__ +#define __CURSES_OUTPUT_H__ + +#include +#include +#include "../cursor.h" +#include "../output.h" + +class curses_output final : public output { +private: + cursor *curse; +public: + curses_output(cursor *new_curse); + + void render() override; + void clear() override; + void print_char(const position &pos, + const char ch, const int attrs) override; + void print_str(const position &pos, + const std::string &str, const int attrs) override; +}; + +#endif diff --git a/src/output/file_output.cc b/src/output/file_output.cc new file mode 100644 index 0000000..5408220 --- /dev/null +++ b/src/output/file_output.cc @@ -0,0 +1,38 @@ +#include "file_output.h" + +#include + +#include "../constants.h" + +file_output::file_output(std::ofstream &&ofs): + out{std::move(ofs)} {} + +void file_output::render() { + for (std::size_t idx = 0; idx < contents.size(); ++idx) { + if (idx % DISPLAY_WIDTH == 0 && idx) + out << std::endl; + + out << (char)(contents[idx] ? contents[idx] : ' '); + } + + out << std::endl; +} + +void file_output::print_char(const position &pos, const char ch, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + contents[pos.y * DISPLAY_WIDTH + pos.x] = ch; +} + +void file_output::print_str(const position &pos, const std::string &str, + const int attrs) { + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; + + int head = pos.y * DISPLAY_WIDTH + pos.x; + + for (std::size_t i = 0; i < str.length(); ++i) + contents[i + head] = str[i]; +} diff --git a/src/output/file_output.h b/src/output/file_output.h new file mode 100644 index 0000000..5e0f80d --- /dev/null +++ b/src/output/file_output.h @@ -0,0 +1,20 @@ +#ifndef __FILE_OUTPUT_H__ +#define __FILE_OUTPUT_H__ + +#include +#include "../output.h" + +class file_output final : public output { +private: + std::ofstream out; +public: + file_output(std::ofstream &&ofs); + + void render() override; + void print_char(const position &pos, + const char ch, const int attrs) override; + void print_str(const position &pos, + const std::string &str, const int attrs) override; +}; + +#endif diff --git a/src/pc.cc b/src/pc.cc index a5df804..572e23b 100644 --- a/src/pc.cc +++ b/src/pc.cc @@ -6,35 +6,40 @@ #include "player/shade.h" #include "player/troll.h" #include "player/vampire.h" +#include "player/t_800.h" -void init_player(RNG *rng, std::unique_ptr &pc, - const feature enabled_features, const enum race &r) { +std::unique_ptr init_player(RNG *rng, + const feature enabled_features, const enum race &r) { using std::make_unique; - pc = nullptr; - switch (r) { case rgoblin: - pc = make_unique(rng, enabled_features); + return make_unique(rng, enabled_features); break; case rdrow: - pc = make_unique(rng, enabled_features); + return make_unique(rng, enabled_features); break; case rshade: - pc = make_unique(rng, enabled_features); + return make_unique(rng, enabled_features); break; case rtroll: - pc = make_unique(rng, enabled_features); + return make_unique(rng, enabled_features); break; case rvampire: - pc = make_unique(rng, enabled_features); + return make_unique(rng, enabled_features); + break; + + case rt_800: + return make_unique(rng, enabled_features); break; default: break; } + + return nullptr; } diff --git a/src/pc.h b/src/pc.h index 7c67f2c..fbede40 100644 --- a/src/pc.h +++ b/src/pc.h @@ -4,7 +4,7 @@ #include #include "player.h" -void init_player(RNG *rng, std::unique_ptr &pc, - const feature enabled_features, const enum race &r); +std::unique_ptr init_player(RNG *rng, + const feature enabled_features, const enum race &r); #endif diff --git a/src/player.cc b/src/player.cc index 2640d7b..0a83085 100644 --- a/src/player.cc +++ b/src/player.cc @@ -32,15 +32,21 @@ int player_base::get_HP() const { return this->HP; } -long_result player_base::apply(potion *p) { +long_result player_base::apply(std::unique_ptr p) { if (p == nullptr) return {result::applied_nothing, "PC tried to breathe the magic in the air. "}; - apply_effect(p); + std::string name = p->get_name(); + + apply_effect(std::move(p)); + + if (race == rt_800) + return {result::applied, + "PC gets rusty joints (-50 HP). "}; return {result::applied, - (std::string)"PC applied potion of " + p->get_name() + ". "}; + "PC applied potion of " + name + ". "}; } long_result player_base::attack(character *ch) { @@ -62,8 +68,15 @@ long_result player_base::move(level *lvl, } else if ((tmp = get_enemy_at(p, lvl->get_elist())) != nullptr) { auto res = attack((character *)tmp); - if (tmp->is_dead()) - tmp->dies(lvl, this); + if (tmp->is_dead()) { + int g = tmp->dies(lvl); + lvl->erase_enemy(tmp); + + if (g) + res.msg += "PC gains " + + std::to_string(g) + + " pieces of gold. "; + } return res; } @@ -112,19 +125,18 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { } else if (cmd >= move_north && cmd <= move_southwest) { auto res = move(lvl, pos + MOVE[cmd - move_north]); - if (res.res == result::moved) - start_turn(); - gold g = get_gold_at(pos, lvl->get_glist()); gold_cnt += g.amount; + + if (g.amount) + res.msg += "PC picked up " + std::to_string(g.amount) + + " pieces of gold. "; + return res; } else if (cmd >= apply_north && cmd <= apply_southwest) { - auto res = apply(get_potion_at(pos + MOVE[cmd - apply_north], - lvl->get_plist())); - - if (res.res == result::applied) - start_turn(); - + size_t idx = get_potion_at(pos + MOVE[cmd - apply_north], + lvl->get_plist()); + auto res = apply(lvl->detach_potion(idx)); return res; } else if (cmd == apply_panic) { return {result::fine, @@ -133,29 +145,29 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { enemy_base *tmp = get_enemy_at(pos + MOVE[cmd - attack_north], lvl->get_elist()); - if (tmp != nullptr) - start_turn(); - auto res = attack((character *)tmp); - if (tmp != nullptr && tmp->is_dead()) - tmp->dies(lvl, this); + if (tmp != nullptr && tmp->is_dead()) { + int g = tmp->dies(lvl); + lvl->erase_enemy(tmp); + + if (g) + res.msg += "PC gains " + + std::to_string(g) + + " pieces of gold. "; + } return res; } else if (cmd == up_stairs) { if (lvl->get_up_stairs() == pos && - enabled_features & FEATURE_REVISIT) { - start_turn(); + enabled_features & FEATURE_REVISIT) return {go_up, "PC went up the stairs. "}; - } return {result::fine, "PC tried to fly through the ceiling. "}; } else if (cmd == down_stairs) { - if (lvl->get_down_stairs() == pos) { - start_turn(); + if (lvl->get_down_stairs() == pos) return {go_down, "PC went down the stairs. "}; - } return {result::fine, "PC tried to dig through the floor. "}; diff --git a/src/player.h b/src/player.h index 0139bc4..bc6e2f8 100644 --- a/src/player.h +++ b/src/player.h @@ -8,13 +8,13 @@ enum game_command : int; class player_base: public character { protected: int gold_cnt; - potion_list potions; + potion_own_list potions; public: player_base(RNG *rng, const feature enabled_features, const enum race &nrace); virtual long_result move(level *lvl, const position &p); - virtual long_result apply(potion *p); + virtual long_result apply(std::unique_ptr p); virtual long_result attack(character *ch) override; virtual long_result get_hit(character *ch, const int tATK, diff --git a/src/player/t_800.cc b/src/player/t_800.cc new file mode 100644 index 0000000..f84380d --- /dev/null +++ b/src/player/t_800.cc @@ -0,0 +1,15 @@ +#include "t_800.h" + +#include "../constants.h" + +t_800::t_800(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, race::rt_800} {} + +const char *t_800::get_race_name() const { + return "T-800"; +} + +void t_800::apply_effect(std::unique_ptr p) { + HP -= RUSTY; + return; +} diff --git a/src/player/t_800.h b/src/player/t_800.h new file mode 100644 index 0000000..e5c41b5 --- /dev/null +++ b/src/player/t_800.h @@ -0,0 +1,15 @@ +#ifndef __T_800_H__ +#define __T_800_H__ + +#include "../player.h" +class potion; + +class t_800 final: public player_base { + static const int RUSTY = 50; +public: + t_800(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; + void apply_effect(std::unique_ptr p) override; +}; + +#endif diff --git a/src/potion.cc b/src/potion.cc index 95751e0..accd252 100644 --- a/src/potion.cc +++ b/src/potion.cc @@ -47,15 +47,10 @@ void potion::print(output *out) { out->print_char(pos, 'P', COLOR_PAIR(COLOR_GREEN)); } -potion *get_potion_at(const position &pos, potion_list &plist) { - potion *ret = nullptr; - +size_t get_potion_at(const position &pos, const potion_list &plist) { for (size_t i = 0; i < plist.size(); ++i) - if (plist[i]->get_pos() == pos) { - ret = plist[i]; - plist.erase(plist.begin() + i); - return ret; - } + if (plist[i]->get_pos() == pos) + return i; - return ret; + return plist.size(); } diff --git a/src/potion.h b/src/potion.h index 745e889..fd3de67 100644 --- a/src/potion.h +++ b/src/potion.h @@ -2,6 +2,7 @@ #define __POTION_H__ #include +#include #include "fraction.h" #include "output.h" @@ -40,7 +41,8 @@ public: }; typedef std::vector potion_list; +typedef std::vector > potion_own_list; -potion *get_potion_at(const position &pos, potion_list &plist); +size_t get_potion_at(const position &pos, const potion_list &plist); #endif diff --git a/src/potions.cc b/src/potions.cc index 4034745..ae8725e 100644 --- a/src/potions.cc +++ b/src/potions.cc @@ -8,34 +8,35 @@ #include "potions/wound_atk.h" #include "potions/wound_def.h" -void new_potion(std::unique_ptr &pp, potion_type type, - const position &pos) { +std::unique_ptr new_potion(potion_type type, const position &pos) { switch (type) { case restore_health: - pp = std::make_unique(pos); + return std::make_unique(pos); break; case boost_atk: - pp = std::make_unique(pos); + return std::make_unique(pos); break; case boost_def: - pp = std::make_unique(pos); + return std::make_unique(pos); break; case poison_health: - pp = std::make_unique(pos); + return std::make_unique(pos); break; case wound_atk: - pp = std::make_unique(pos); + return std::make_unique(pos); break; case wound_def: - pp = std::make_unique(pos); + return std::make_unique(pos); break; default: break; } + + return nullptr; } diff --git a/src/potions.h b/src/potions.h index ae83786..e4c8651 100644 --- a/src/potions.h +++ b/src/potions.h @@ -6,7 +6,6 @@ #include #include -void new_potion(std::unique_ptr &pp, potion_type type, - const position &pos); +std::unique_ptr new_potion(potion_type type, const position &pos); #endif