diff --git a/Makefile b/Makefile deleted file mode 100644 index fdca348..0000000 --- a/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# Universal makefile for single C++ program -# -# Use gcc flag -MMD (user) or -MD (user/system) to generate dependencies among source files. -# Use make default rules for commonly used file-name suffixes and make variables names. -# -# % make [ a.out ] - -########## Variables ########## - -CXX = g++ # compiler -CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags -MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name - -SOURCES = $(wildcard src/*.cc) # source files (*.cc) -OBJECTS = ${SOURCES:.cc=.o} # object files forming executable -DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d" -EXEC = bin/cc3k # executable name - -########## Targets ########## - -.PHONY : clean # not file names - -${EXEC} : ${OBJECTS} # link step - ${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^ - -${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile - -# make implicitly generates rules to compile C++ files that generate .o files - --include ${DEPENDS} # include *.d files containing program dependences - -clean : # remove files that can be regenerated - rm -f ${DEPENDS} ${OBJECTS} ${EXEC} diff --git a/bin/.keep_dir b/bin/.keep_dir deleted file mode 100644 index e69de29..0000000 diff --git a/bin/Makefile b/bin/Makefile deleted file mode 100644 index 54ad7e2..0000000 --- a/bin/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# Universal makefile for single C++ program -# -# Use gcc flag -MMD (user) or -MD (user/system) to generate dependencies among source files. -# Use make default rules for commonly used file-name suffixes and make variables names. -# -# % make [ a.out ] - -########## Variables ########## - -CXX = g++ # compiler -CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags -MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name - -SOURCES = $(wildcard ../src/*.cc) # source files (*.cc) -OBJECTS = ${SOURCES:.cc=.o} # object files forming executable -DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d" -EXEC = cc3k # executable name - -########## Targets ########## - -.PHONY : clean # not file names - -${EXEC} : ${OBJECTS} # link step - ${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^ - -${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile - -# make implicitly generates rules to compile C++ files that generate .o files - --include ${DEPENDS} # include *.d files containing program dependences - -clean : # remove files that can be regenerated - rm -f ${DEPENDS} ${OBJECTS} ${EXEC} diff --git a/src/Makefile b/src/Makefile index c00e7cc..897e856 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,17 +11,17 @@ CXX = g++ # compiler CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name -SOURCES = $(wildcard *.cc) # source files (*.cc) +SOURCES = $(wildcard *.cc) $(wildcard */*.cc) # source files (*.cc) OBJECTS = ${SOURCES:.cc=.o} # object files forming executable DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d" -EXEC = ../bin/cc3k # executable name +EXEC = cc3k # executable name ########## Targets ########## .PHONY : clean # not file names ${EXEC} : ${OBJECTS} # link step - ${CXX} ${CXXFLAGS} $^ -o $@ -lncurses # additional object files before $^ + ${CXX} ${CXXFLAGS} $^ -o $@ -lncurses # additional object files before $^ ${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile diff --git a/src/arguments.cc b/src/arguments.cc index 6e22392..8e30406 100644 --- a/src/arguments.cc +++ b/src/arguments.cc @@ -5,19 +5,20 @@ #include #include #include -#include "file_input.h" -#include "file_output.h" -#include "console_input.h" -#include "console_output.h" -#include "curses_input.h" -#include "curses_output.h" + +#include "constants.h" +#include "input/file_input.h" +#include "display/file_output.h" +#include "input/console_input.h" +#include "display/console_output.h" +#include "input/curses_input.h" +#include "display/curses_output.h" #include "rng.h" feature proc_args(int argc, char **argv, std::unique_ptr &curse, std::unique_ptr &in, std::unique_ptr &out, - std::unique_ptr &log, std::unique_ptr &rng) { feature result = 0; std::string str; @@ -53,6 +54,8 @@ feature proc_args(int argc, char **argv, result |= FEATURE_EXTRA_STUFF; } else if (str == "-E") { result |= FEATURE_EXTRA_LEVELS; + } else if (str == "-o") { + result |= FEATURE_WALK_OVER; } else if (str == "-s") { ++i; str = argv[i]; @@ -62,15 +65,6 @@ feature proc_args(int argc, char **argv, seed = str; result |= FEATURE_SEED; - } else if (str == "-L") { - ++i; - str = argv[i]; - - if (!fn_lout.empty() && fn_lout != str) - return FEATURE_CONFLICT | i; - - fn_lout = str; - result |= FEATURE_LOG; } else if (str == "-I") { ++i; str = argv[i]; @@ -116,15 +110,6 @@ feature proc_args(int argc, char **argv, } else if (!(result & FEATURE_NCURSES)) out = std::make_unique(std::cout); - if (result & FEATURE_LOG) { - std::ofstream lout(fn_lout); - - if (!lout.good()) - return FEATURE_PANIC_FILE | FEATURE_LOG; - - log = std::make_unique(std::move(lout)); - } - if (result & FEATURE_NCURSES) { curse = std::make_unique(); in = std::make_unique(curse.get()); @@ -167,10 +152,6 @@ void panic_args(feature panic) { cerr << "Cannot open specified output file!" << endl; break; - case FEATURE_LOG: - cerr << "Cannot open specified log file!" << endl; - break; - default: cerr << "Something must have went really, really, wrong..." << endl; @@ -184,20 +165,18 @@ void panic_args(feature panic) { } void print_args_list() { - - static const char *ARGS_LIST = "-n : Use ncurses for I/O\n\ -r : Randomly generate maps\n\ -m : Enabled a main menu + options\n\ -c : Enemies chase the player (through doors and up stairs)\n\ -C : Give things better coloring\n\ --i : Enable inventory\n\ +-i : Enable inventory (player can walk on potions)\n\ -t : Enable throw\n\ -R : Enable revisiting levels\n\ -e : Enable extra potions and races\n\ -E : Enable extra levels\n\ +-o : Allows monsters to go over gold and potions\n\ -s [seed] : Sets initial seed to seed\n\ --L [file] : Enable logging to file\n\ -I [file] : Reads commands from file. CANNOT BE USED WITH -n.\n\ -O [file] : Outputs to file. CANNOT BE USED WITH -n.\n\ -h/--help : Displays options list (doesn't start a game)"; diff --git a/src/arguments.h b/src/arguments.h index e4e5837..beb71d8 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -1,20 +1,18 @@ #ifndef __ARGUMENTS_H__ #define __ARGUMENTS_H__ #include -#include "log.h" #include "cursor.h" #include "display.h" #include "input.h" #include "rng.h" -#include "constants.h" +typedef unsigned int feature; // IMPORTANT: Errors include the index that caused them (or'ed into them) feature proc_args(int argc, char **argv, std::unique_ptr &curse, std::unique_ptr &in, std::unique_ptr &out, - std::unique_ptr &log, std::unique_ptr &rng); void panic_args(feature panic); diff --git a/src/characters.cc b/src/characters.cc index a2dcd91..7c5f0c1 100644 --- a/src/characters.cc +++ b/src/characters.cc @@ -1,161 +1,47 @@ #include "characters.h" +#include "constants.h" +#include "level.h" #include -character::character(RNG *rng, const enum race &nrace, const position &pos): - rng{rng}, race{nrace}, HP{STARTING_HP[race]}, +character::character(RNG *rng, const feature enabled_features, + const enum race &nrace, const position &pos): + 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}, pos{pos} {} + base_hit_rate{1, 1}, base_hit_rate_reset{1, 1} {} void character::start_turn() { ATK = STARTING_ATK[race]; DEF = STARTING_DEF[race]; - base_hit_rate = {1, 1}; -} - -void character::print(display *out, bool player) { - out->print_char(pos, player ? '@' : CHARACTER_REP[race], - player ? COLOR_PAIR(COLOR_BLUE) : COLOR_PAIR(COLOR_RED)); + base_hit_rate = base_hit_rate_reset; } enum race character::get_race() const { return race; } -position character::get_position() const { +position character::get_pos() const { return pos; } -int character::get_HP() const { - return HP; -} - -int character::get_ATK() const { - return ATK; -} - -int character::get_DEF() const { - return DEF; -} - -int character::get_gold() const { - return gold; -} - -float character::get_hitrate_real() const { - return base_hit_rate.real(); -} - -fraction character::get_hitrate() const { - return base_hit_rate; -} - -int character::get_room_num() const { - return room_num; -} - -<<< <<< < HEAD -== == == = -bool character::is_hostile() const { - return hostile; -} - ->>> >>> > AL - races -void character::set_room_num(const int room) { - room_num = room; -} - -void character::set_position(const position &npos) { +void character::set_pos(const position &npos) { pos = npos; } -void character::set_HP(const int nHP) { - HP = nHP; +void character::apply_effect(potion *effect) { + insert_effect(effect); } -void character::set_ATK(const int nATK) { - ATK = nATK; -} - -void character::set_DEF(const int nDEF) { - DEF = nDEF; -} - -void character::set_gold(const int ngold) { - gold = ngold; -} - -void character::set_hitrate(const fraction nhitrate) { - base_hit_rate = nhitrate; -} - -<<< <<< < HEAD -== == == = -void character::set_hostile(const bool is_hostile) { - hostile = is_hostile; -} - -void character::apply_buff(const stat_name statn, const int amount) { - // TODO: add checks for bounds - switch (statn) { - case stat_name::HP: - HP += amount; - break; - - case stat_name::ATK: - ATK += amount; - break; - - case stat_name::DEF: - DEF += amount; - break; - - case stat_name::hostile: { - if (amount > 0) - hostile = true; - else - hostile = false; - - break; - } - } -} - ->>> >>> > AL - races -direction_list character::moveable(const position_list &available_positions) -const { - direction_list result; - - for (int i = 0; i < DIRECTION_CNT; ++i) - if (find(available_positions, pos + MOVE[i]) - != available_positions.size()) - result.push_back((direction)i); - - return result; -} - -character::apply_result character::apply(const direction &dir, - potion_list &plist) { - for (size_t i = 0; i < plist.size(); ++i) - if (pos + MOVE[dir] == plist[i]->get_pos()) { - apply_result res{applied, plist[i]}; - insert_potion(plist[i]); - plist.erase(plist.begin() + i); - return res; - } - - return {applied_nothing, nullptr}; -} - -void character::insert_potion(potion *p) { - effects.push_back(p); +void character::insert_effect(potion *effect) { + effects.push_back(effect); for (int i = effects.size() - 1; i > 0; --i) - if (p->get_priority() < effects[i - 1]->get_priority()) + if (effect->get_priority() < effects[i - 1]->get_priority()) std::swap(effects[i], effects[i - 1]); } -result character::apply_effects() { +result character::calc_effects() { potion_list tmp; tmp.reserve(effects.size()); @@ -190,17 +76,28 @@ void character::discard_level_effects() { } // IMPORTANT: remember to check if player is on the stairs -result character::move(const direction dir, - const position_list &available_positions) { - if (find(available_positions, pos + MOVE[dir]) - != available_positions.size()) { - pos += MOVE[dir]; - return result::moved; +long_result character::move(level *lvl, + const position &p) { + if (lvl->is_available(p)) { + pos = p; + return {result::moved, ""}; } - return result::fine; + return {result::fine, ""}; } -int character::calc_dmg(const int ATK, const int DEF) { +int calc_dmg(const int ATK, const int DEF) { return ceil((100.0f / (100.0f + DEF)) * ATK); } + +character *get_ch_at(const position &pos, const character_list &chlist) { + for (auto ch : chlist) + if (ch->get_pos() == pos) + return ch; + + return nullptr; +} + +bool character::is_dead() const { + return HP <= 0; +} diff --git a/src/characters.h b/src/characters.h index e9554de..ab162c7 100644 --- a/src/characters.h +++ b/src/characters.h @@ -5,7 +5,6 @@ #include #include -#include "constants.h" #include "display.h" #include "fraction.h" #include "position.h" @@ -13,62 +12,38 @@ #include "gold.h" #include "rng.h" -class character; // forward declaration - -// Note: player should not be in the character list - +class level; +typedef unsigned int feature; +struct long_result; +enum result : int; class character { public: - character(RNG *rng, const race &nrace, - const position &pos); // fills out the starting stats + character(RNG *rng, const feature enabled_features, + const race &nrace, const position &pos); // fills out the starting stats - // usually I wouldn't do this but considering that the map has - // a super small size an O(n) solution is acceptable - // IMPORTANT: available_positions do NOT have characters in them - direction_list moveable(const position_list &available_positions) const; - virtual result move(const direction dir, - const position_list &available_positions); + virtual long_result move(level *lvl, const position &p); - struct attack_result { - result res; - std::string msg; - }; + virtual long_result attack(character *ch) = 0; + virtual long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate) = 0; - struct apply_result { - result res; - std::string msg; - }; - virtual result move(position_list spots, const direction &dir); + virtual void apply_effect(potion *effect); - virtual attack_result attack(const direction dir, character *ch) = 0; + // override for different types + virtual void print(display *out) = 0; - virtual apply_result apply(const direction &dir, - potion_list &potions); - virtual attack_result get_hit(const enum race &race, const int atk, - const fraction hitrate) = 0; - - // overload for different races - virtual void print(display *out); - - virtual result calc_effects(); + result calc_effects(); void discard_level_effects(); virtual void start_turn(); + virtual const char *get_race_name() const = 0; enum race get_race() const; position get_pos() const; + virtual std::string get_abbrev() const = 0; void set_pos(const position &npos); - int get_HP() const; - int get_ATK() const; - int get_DEF() const; - fraction get_hitrate() const; - int get_room_num() const; - void set_HP(const int nHP); - void set_ATK(const int nATK); - void set_DEF(const int nDEF); - void set_hitrate(const fraction nhitrate); - void set_room_num(const int room); + bool is_dead() const; protected: RNG *rng; @@ -79,18 +54,22 @@ protected: int HP; position pos; - // IMPORTANT: keep track of ATK and DEF in game at turn time + // IMPORTANT: keep track at turn time int ATK; int DEF; fraction base_hit_rate; + fraction base_hit_rate_reset; potion_list effects; - int calc_dmg(const int ATK, const int DEF); private: void insert_effect(potion *effect); }; +int calc_dmg(const int ATK, const int DEF); + typedef std::vector character_list; +character *get_ch_at(const position &pos, const character_list &chlist); + #endif diff --git a/src/console_input.cc b/src/console_input.cc deleted file mode 100644 index 0de1201..0000000 --- a/src/console_input.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "console_input.h" -#include -#include - -console_input::console_input(std::istream &cin): - in{cin} {} - -game_command console_input::get_command() { - std::string cmd; - in >> cmd; - game_command tmp; - - if (in.eof()) - return game_command_terminate; - - if (cmd == "q") - return game_command_terminate; - else if (cmd == "f") - return the_world; - else if (cmd == "r") - return game_restart; - else if (cmd == "u" || cmd == "a") { - bool use = cmd == "u"; - - in >> cmd; - - if (in.eof()) - return game_command_panic; - - return (game_command)((tmp = get_direction(cmd)) == - game_command_panic - ? tmp : tmp - move_north + - (use ? apply_north : attack_north)); - } else { - auto tmp = get_direction(cmd); - - if (tmp != game_command_panic) - return tmp; - } - - return game_command_pass; -} diff --git a/src/console_input.h b/src/console_input.h deleted file mode 100644 index c2c9b46..0000000 --- a/src/console_input.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __CONSOLE_INPUT_H__ -#define __CONSOLE_INPUT_H__ - -#include -#include "input.h" - -class console_input final : public input { -private: - std::istream ∈ -public: - // This is for cin - console_input(std::istream &cin); - game_command get_command() override; -}; - -#endif diff --git a/src/console_output.cc b/src/console_output.cc deleted file mode 100644 index 2be7fef..0000000 --- a/src/console_output.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include "console_output.h" - -#include -#include -#include - -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/console_output.h b/src/console_output.h deleted file mode 100644 index 91a8138..0000000 --- a/src/console_output.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __CONSOLE_OUTPUT_H__ -#define __CONSOLE_OUTPUT_H__ - -#include -#include "display.h" - -class console_output final : public display { -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/constants.h b/src/constants.h index 89014eb..d7668cd 100644 --- a/src/constants.h +++ b/src/constants.h @@ -2,101 +2,102 @@ #define __CONSTANTS_H__ #include #include +#include #include "position.h" -const int INF = 0x3F3F3F3F; - -enum error {none}; +static const int INF = 0x3F3F3F3F; // TODO: update result to include subject // fine will waste a turn -enum result {fine, died, go_down, go_up, hit, moved, - miss, terminate, applied, applied_nothing, - toggle_the_world, restart_game, unknown - }; - -enum game_status {terminated, main_menu, in_game, options, - dead, won, escaped, restart - }; - -enum game_command {game_command_terminate = 0, - move_north, move_south, move_east, move_west, - move_northeast, move_northwest, - move_southeast, move_southwest, - apply_north, apply_south, apply_east, apply_west, - apply_northeast, apply_northwest, - apply_southeast, apply_southwest, - apply_panic, // for curses_input specifically - attack_north, attack_south, attack_east, attack_west, - attack_northeast, attack_northwest, - attack_southeast, attack_southwest, - up_stairs, down_stairs, - the_world, game_restart, - game_command_pass, game_command_panic +enum result : int {fine, died, go_down, go_up, hit, moved, + miss, terminate, applied, applied_nothing, + toggle_the_world, restart_game, unknown }; -// Character generation related -const int RACE_CNT = 12; -enum race {rshade = 0, rdrow, rvampire, rtroll, - rgoblin, rhuman, rdwarf, relf, - rorc, rmerchant, rdragon, rhalfling - }; -const char *RACE_NAME[RACE_CNT] = { - "Shade", "Drow", "Vampire", "Troll", - "Goblin", "Human", "Dwarf", "Elf", - "Orc", "Merchant", "Dragon", "Halfling" +struct long_result { + result res; + std::string msg; }; -const int MAX_HP[RACE_CNT] = { +enum game_status : int {terminated, main_menu, in_game, options, + dead, won, escaped, restart, skip_turn + }; + +enum game_command : int {game_command_terminate = 0, + move_north, move_south, move_east, move_west, + move_northeast, move_northwest, + move_southeast, move_southwest, + apply_north, apply_south, apply_east, apply_west, + apply_northeast, apply_northwest, + apply_southeast, apply_southwest, + apply_panic, // for curses_input specifically + attack_north, attack_south, attack_east, attack_west, + attack_northeast, attack_northwest, + attack_southeast, attack_southwest, + up_stairs, down_stairs, + the_world, game_restart, + game_command_pass, game_command_panic, + enter + }; + +// Character generation related +static const int RACE_CNT = 12; +enum race : int {rshade = 0, rdrow, rvampire, rtroll, + rgoblin, rhuman, rdwarf, relf, + rorc, rmerchant, rdragon, rhalfling + }; + +static const char CHAR_REP[RACE_CNT] = { + 's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L' +}; + +static const int MAX_HP[RACE_CNT] = { 125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100 }; -const int STARTING_HP[RACE_CNT] = { +static const int STARTING_HP[RACE_CNT] = { 125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100 }; -const int STARTING_ATK[RACE_CNT] = { +static const int STARTING_ATK[RACE_CNT] = { 25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15 }; -const int STARTING_DEF[RACE_CNT] = { +static const int STARTING_DEF[RACE_CNT] = { 25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20 }; // Potion-related -const int POTION_TYPE_CNT = 6; -const int DEFAULT_POTION_TYPE_CNT = 6; -enum potion_type {restore_health = 0, boost_atk, boost_def, - poison_health, wound_atk, wound_def - }; -const char *POTION_REAL_NAME[POTION_TYPE_CNT] = { - "RH", "BA", "BD", "PH", "WA", "WD" -}; +static const int POTION_TYPE_CNT = 6; +static const int DEFAULT_POTION_TYPE_CNT = 6; +enum potion_type : int {restore_health = 0, boost_atk, boost_def, + poison_health, wound_atk, wound_def + }; // Calculation priorities -const int CALC_ADD_BASE = 0; -const int CALC_MUL_BASE = 1; -const int CALC_ADD_LATER = 2; -const int CALC_MUL_LATER = 3; -const int CALC_ADD_FIXED = 4; +static const int CALC_ADD_BASE = 0; +static const int CALC_MUL_BASE = 1; +static const int CALC_ADD_LATER = 2; +static const int CALC_MUL_LATER = 3; +static const int CALC_ADD_FIXED = 4; -const int DIRECTION_CNT = 8; +static const int DIRECTION_CNT = 8; // IMPORTANT: east is positive for x and SOUTH is positive for y // initializes all directions to an int -enum direction { north = 0, south, east, west, northeast, - northwest, southeast, southwest - }; +enum direction : int { north = 0, south, east, west, northeast, + northwest, southeast, southwest + }; -const position MOVE[DIRECTION_CNT] = { +static const position MOVE[DIRECTION_CNT] = { {0, -1}, {0, 1}, {1, 0}, {-1, 0}, {1, -1}, {-1, -1}, {1, 1}, {-1, 1} }; -const int MAP_HEIGHT = 25; -const int MAP_WIDTH = 79; -const int DISPLAY_HEIGHT = 30; -const int DISPLAY_WIDTH = 79; -const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH + - DISPLAY_WIDTH * 3; +static const int MAP_HEIGHT = 25; +static const int MAP_WIDTH = 79; +static const int DISPLAY_HEIGHT = 30; +static const int DISPLAY_WIDTH = 79; +static const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH + + DISPLAY_WIDTH * 3; // TODO: list all extra features // using constants to keep track of features @@ -105,38 +106,35 @@ const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH + // check if feature is enabled: // features & FEATURE_XXX typedef unsigned int feature; // can extend to unsigned long (long) if needed -const feature FEATURE_NCURSES = 1 << 0; -const feature FEATURE_RAND_MAP = 1 << 1; -const feature FEATURE_ENEMIES_CHASE = 1 << 2; -const feature FEATURE_INVENTORY = 1 << 3; -const feature FEATURE_THROW = 1 << 4; -const feature FEATURE_REVISIT = 1 << 5; -const feature FEATURE_LOG = 1 << 6; -const feature FEATURE_SEED = 1 << 7; -const feature FEATURE_MENU = 1 << 8; -const feature FEATURE_IN_FILE = 1 << 9; -const feature FEATURE_OUT_FILE = 1 << 10; -const feature FEATURE_EXTRA_STUFF = 1 << 11; -const feature FEATURE_EXTRA_LEVELS = 1 << 12; -const feature FEATURE_COLORFUL = 1 << 13; +static const feature FEATURE_NCURSES = 1 << 0; +static const feature FEATURE_RAND_MAP = 1 << 1; +static const feature FEATURE_ENEMIES_CHASE = 1 << 2; +static const feature FEATURE_INVENTORY = 1 << 3; +static const feature FEATURE_THROW = 1 << 4; +static const feature FEATURE_REVISIT = 1 << 5; +static const feature FEATURE_WALK_OVER = 1 << 6; +static const feature FEATURE_SEED = 1 << 7; +static const feature FEATURE_MENU = 1 << 8; +static const feature FEATURE_IN_FILE = 1 << 9; +static const feature FEATURE_OUT_FILE = 1 << 10; +static const feature FEATURE_EXTRA_STUFF = 1 << 11; +static const feature FEATURE_EXTRA_LEVELS = 1 << 12; +static const feature FEATURE_COLORFUL = 1 << 13; -const feature FEATURE_PANIC_SEED = 1 << 27; -const feature FEATURE_PANIC_FILE = 1 << 28; -const feature FEATURE_PANIC = 1 << 29; -const feature FEATURE_CONFLICT = 1 << 30; -const feature FEATURE_LIST_ARGS = 1 << 31; +static const feature FEATURE_PANIC_SEED = 1 << 27; +static const feature FEATURE_PANIC_FILE = 1 << 28; +static const feature FEATURE_PANIC = 1 << 29; +static const feature FEATURE_CONFLICT = 1 << 30; +static const feature FEATURE_LIST_ARGS = 1 << 31; -const int RETURN_FINE = 0; -const int RETURN_PANICKED = 1; +static const int RETURN_FINE = 0; +static const int RETURN_PANICKED = 1; -const int COLOR_BLACK_ON_WHITE = 8; +static const int COLOR_BLACK_ON_WHITE = 8; -typedef std::vector position_list; -typedef std::vector direction_list; - -const int GOLD_SMALL = 1; -const int GOLD_NORMAL = 2; -const int GOLD_MERCHANT = 4; -const int GOLD_DRAGON = 6; +static const int GOLD_SMALL = 1; +static const int GOLD_NORMAL = 2; +static const int GOLD_MERCHANT = 4; +static const int GOLD_DRAGON = 6; #endif diff --git a/src/curses_input.cc b/src/curses_input.cc deleted file mode 100644 index d6da0ce..0000000 --- a/src/curses_input.cc +++ /dev/null @@ -1,84 +0,0 @@ -#include "curses_input.h" - -curses_input::curses_input(cursor *new_curse): - curse{new_curse} {} - -game_command curses_input::get_command() { - switch (curse->getcmd()) { - case 'h': - return game_command::move_west; - - case 'j': - return game_command::move_south; - - case 'k': - return game_command::move_north; - - case 'l': - return game_command::move_east; - - case 'y': - return game_command::move_northwest; - - case 'u': - return game_command::move_northeast; - - case 'b': - return game_command::move_southwest; - - case 'n': - return game_command::move_southeast; - - case 'a': - break; // wait for another command - - case '<': - return game_command::up_stairs; - - case '>': - return game_command::down_stairs; - - case 'q': - return game_command_terminate; - - case 'f': - return game_command::the_world; - - case 'r': - return game_restart; - - default: - return game_command_pass; - } - - switch (curse->getcmd()) { - case 'h': - return game_command::apply_west; - - case 'j': - return game_command::apply_south; - - case 'k': - return game_command::apply_north; - - case 'l': - return game_command::apply_east; - - case 'y': - return game_command::apply_northwest; - - case 'u': - return game_command::apply_northeast; - - case 'b': - return game_command::apply_southwest; - - case 'n': - return game_command::apply_southeast; - - default: - return game_command::apply_panic; - } - - return game_command::game_command_panic; -} diff --git a/src/curses_output.h b/src/curses_output.h deleted file mode 100644 index 13dbd06..0000000 --- a/src/curses_output.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __CURSES_OUTPUT_H__ -#define __CURSES_OUTPUT_H__ - -#include -#include -#include "cursor.h" -#include "display.h" - -class curses_output final : public display { -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/display.cc b/src/display.cc index 5fd0463..8f223fc 100644 --- a/src/display.cc +++ b/src/display.cc @@ -1,15 +1,17 @@ #include "display.h" +#include "constants.h" + display::display(): - contents{DISPLAY_BUFFER_SIZE, 0} { - for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) - contents.push_back(0); + contents{DISPLAY_BUFFER_SIZE, 0} { + for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) + contents.push_back(0); } void display::clear() { - contents.clear(); - contents.reserve(DISPLAY_BUFFER_SIZE); + contents.clear(); + contents.reserve(DISPLAY_BUFFER_SIZE); - for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) - contents.push_back(0); + for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i) + contents.push_back(0); } diff --git a/src/display.h b/src/display.h index 9a4a4d5..9bd4ee2 100644 --- a/src/display.h +++ b/src/display.h @@ -1,35 +1,35 @@ #ifndef __DISPLAY_H__ #define __DISPLAY_H__ +#include #include #include "position.h" -#include "constants.h" #include "cursor.h" class display { 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; + // 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: - display(); + display(); - virtual ~display() = default; + virtual ~display() = default; - // Only call this to refresh the entire output - virtual void render() = 0; + // Only call this to refresh the entire output + virtual void render() = 0; - // Clears the contents buffer - virtual void clear(); + // 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 + 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/display/console_output.cc b/src/display/console_output.cc new file mode 100644 index 0000000..beff4c2 --- /dev/null +++ b/src/display/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/display/console_output.h b/src/display/console_output.h new file mode 100644 index 0000000..a4eaf62 --- /dev/null +++ b/src/display/console_output.h @@ -0,0 +1,21 @@ +#ifndef __CONSOLE_OUTPUT_H__ +#define __CONSOLE_OUTPUT_H__ + +#include +#include "../display.h" + +class console_output final : public display { +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/curses_output.cc b/src/display/curses_output.cc similarity index 66% rename from src/curses_output.cc rename to src/display/curses_output.cc index 06409a7..bee3adf 100644 --- a/src/curses_output.cc +++ b/src/display/curses_output.cc @@ -1,5 +1,7 @@ #include "curses_output.h" +#include "../constants.h" + curses_output::curses_output(cursor *new_curse): curse{new_curse} {} @@ -13,24 +15,24 @@ void curses_output::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; + 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; + if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT) + return; - position tmp = pos; + 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}; - } + if (tmp.x >= DISPLAY_WIDTH) + tmp = {0, tmp.y + 1}; + } } diff --git a/src/display/curses_output.h b/src/display/curses_output.h new file mode 100644 index 0000000..22a8cda --- /dev/null +++ b/src/display/curses_output.h @@ -0,0 +1,23 @@ +#ifndef __CURSES_OUTPUT_H__ +#define __CURSES_OUTPUT_H__ + +#include +#include +#include "../cursor.h" +#include "../display.h" + +class curses_output final : public display { +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/display/file_output.cc b/src/display/file_output.cc new file mode 100644 index 0000000..5408220 --- /dev/null +++ b/src/display/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/display/file_output.h b/src/display/file_output.h new file mode 100644 index 0000000..c53e4be --- /dev/null +++ b/src/display/file_output.h @@ -0,0 +1,20 @@ +#ifndef __FILE_OUTPUT_H__ +#define __FILE_OUTPUT_H__ + +#include +#include "../display.h" + +class file_output final : public display { +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/dragon.cc b/src/dragon.cc deleted file mode 100644 index 7c99396..0000000 --- a/src/dragon.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "dragon.h" - -dragon::dragon(RNG *rng, const position &pos): - character{rng, race::rdragon, pos} { - gold = 0; - hostile = true; -} - -result dragon::attack(const direction dir, character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - return ch->get_hit(race, ATK, base_hit_rate); - } - - return result::fine; -} - -result dragon::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) // This is a hit! - HP = std::max(HP - calc_dmg(atk, DEF), 0); - - if (HP == 0) - return result::died; - - return result::hit; -} - -result dragon::move(const direction dir, - const position_list &available_positions) { - return result::fine; -} diff --git a/src/dragon.h b/src/dragon.h deleted file mode 100644 index f6b34b9..0000000 --- a/src/dragon.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __DRAGON_H__ -#define __DRAGON_H__ - -#include "characters.h" - -class dragon final: public character { -public: - dragon(RNG *rng, const position &pos); - virtual result attack(const direction dir, - character_list &chlist) override; - virtual result get_hit(const enum race &race, const int atk, - const fraction hit_rate) override; - virtual result move(const direction dir, - const position_list &avilable_positions) override; -}; - -#endif diff --git a/src/drow.cc b/src/drow.cc deleted file mode 100644 index 6557362..0000000 --- a/src/drow.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "drow.h" -#include -#include - -drow::drow(RNG *rng, const position &pos): - character{rng, race::rdrow, pos} { - gold = 0; - hostile = true; -} - -result drow::attack(const direction dir, character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - return ch->get_hit(race, ATK, base_hit_rate); - } - - return result::fine; -} - -result drow::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) - HP = std::max(HP - calc_dmg(atk, DEF), 0); - - if (HP == 0) - return result::died; - - return result::hit; -} diff --git a/src/drow.h b/src/drow.h deleted file mode 100644 index 6e7b39e..0000000 --- a/src/drow.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __DROW_H__ -#define __DROW_H__ -#include "characters.h" - -class drow final: public character { -public: - drow(RNG *rng, - const position &pos); - virtual result attack(const direction dir, - character_list &chlist) override; - virtual result get_hit(const enum race &race, const int atk, - const fraction hit_rate) override; -}; - -#endif diff --git a/src/enemies.cc b/src/enemies.cc index 1f3ac4f..a6b3d75 100644 --- a/src/enemies.cc +++ b/src/enemies.cc @@ -1,20 +1,85 @@ #include "enemies.h" -// TODO: implement after characters -void new_enemy(RNG *rng, std::unique_ptr &pch, const position &pos, - bool extras) { - if (extras) { +#include "constants.h" +#include "enemies/dragon.h" +#include "enemies/dwarf.h" +#include "enemies/elf.h" +#include "enemies/halfling.h" +#include "enemies/human.h" +#include "enemies/merchant.h" +#include "enemies/orc.h" - } else { - } -} - -void new_dragon(RNG *rng, std::unique_ptr &pch, const position &pos, - const position &fallback) { +void new_dragon(RNG *rng, std::unique_ptr &p, + const position &pos, const position &fallback, + const feature enabled_features, int which_room) { const position nil{0, 0}; if (pos != nil) - pch = std::make_unique(rng, pos); + p = std::make_unique(rng, enabled_features, + pos, which_room); else - pch = std::make_unique(rng, fallback); + p = std::make_unique(rng, enabled_features, + fallback, which_room); +} + +const int EXCNT = 6; +const race EXCHOICES[EXCNT] = { + rhuman, rdwarf, rhalfling, relf, rorc, rmerchant +}; +const int CNT = 18; +const race CHOICES[CNT] = { + rhuman, rhuman, rhuman, rhuman, + rdwarf, rdwarf, rdwarf, + rhalfling, rhalfling, rhalfling, rhalfling, rhalfling, + relf, relf, + rorc, rorc, + rmerchant, rmerchant +}; + +enum race get_extra_race(RNG *rng) { + return EXCHOICES[rng->rand_under(EXCNT)]; +} + +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) { + using std::make_unique; + + enum race r; + + if (enabled_features & FEATURE_EXTRA_STUFF) + r = get_extra_race(rng); + else + r = get_normal_race(rng); + + p = nullptr; + + switch (r) { + case rdwarf: + p = make_unique(rng, enabled_features, pos, which_room); + break; + + case rhuman: + p = make_unique(rng, enabled_features, pos, which_room); + break; + + case relf: + p = make_unique(rng, enabled_features, pos, which_room); + break; + + case rorc: + p = make_unique(rng, enabled_features, pos, which_room); + break; + + case rmerchant: + p = make_unique(rng, enabled_features, pos, which_room); + break; + + default: + break; + } } diff --git a/src/enemies.h b/src/enemies.h index 5c3ac92..ea44300 100644 --- a/src/enemies.h +++ b/src/enemies.h @@ -2,13 +2,14 @@ #define __ENEMIES_H__ #include -#include "characters.h" -#include "races.h" +#include "enemy.h" -void new_enemy(RNG *rng, std::unique_ptr &pch, const position &pos, - bool extras); +void new_dragon(RNG *rng, std::unique_ptr &p, + const position &pos, const position &fallback, + const feature enabled_features, int which_room); -void new_dragon(RNG *rng, std::unique_ptr &pch, const position &pos, - const position &fallback); +void new_enemy(RNG *rng, std::unique_ptr &p, + const position &pos, const feature enabled_features, + int which_room); #endif diff --git a/src/enemies/dragon.cc b/src/enemies/dragon.cc new file mode 100644 index 0000000..c8c21b9 --- /dev/null +++ b/src/enemies/dragon.cc @@ -0,0 +1,22 @@ +#include "dragon.h" + +#include "../constants.h" + +dragon::dragon(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rdragon, pos, gen_room_num, "D"} {} + +void dragon::print(display *out) { + out->print_char(pos, 'D', COLOR_PAIR(COLOR_RED)); +} + +const char *dragon::get_race_name() const { + return "Dragon"; +} + +long_result dragon::act(level *lvl, character *pc, bool hostile) { + if (is_adjacent(pos, pc->get_pos()) && hostile) + return attack(pc); + + return {fine, ""}; +} diff --git a/src/enemies/dragon.h b/src/enemies/dragon.h new file mode 100644 index 0000000..535e54f --- /dev/null +++ b/src/enemies/dragon.h @@ -0,0 +1,15 @@ +#ifndef __DRAGON_H__ +#define __DRAGON_H__ + +#include "../enemy.h" + +class dragon final: public enemy_base { +public: + dragon(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; + long_result act(level *lvl, character *pc, bool hostile) override; +}; + +#endif diff --git a/src/enemies/dwarf.cc b/src/enemies/dwarf.cc new file mode 100644 index 0000000..c04f82c --- /dev/null +++ b/src/enemies/dwarf.cc @@ -0,0 +1,15 @@ +#include "dwarf.h" + +#include "../constants.h" + +dwarf::dwarf(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rdwarf, pos, gen_room_num, "W"} {} + +void dwarf::print(display *out) { + out->print_char(pos, 'W', COLOR_PAIR(COLOR_RED)); +} + +const char *dwarf::get_race_name() const { + return "Dwarf"; +} diff --git a/src/enemies/dwarf.h b/src/enemies/dwarf.h new file mode 100644 index 0000000..850f9a9 --- /dev/null +++ b/src/enemies/dwarf.h @@ -0,0 +1,14 @@ +#ifndef __DWARF_H__ +#define __DWARF_H__ + +#include "../enemy.h" + +class dwarf final: public enemy_base { +public: + dwarf(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; +}; + +#endif diff --git a/src/enemies/elf.cc b/src/enemies/elf.cc new file mode 100644 index 0000000..90117b0 --- /dev/null +++ b/src/enemies/elf.cc @@ -0,0 +1,31 @@ +#include "elf.h" + +#include "../constants.h" + +elf::elf(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, relf, pos, gen_room_num, "H"} {} + +void elf::print(display *out) { + out->print_char(pos, 'H', COLOR_PAIR(COLOR_RED)); +} + +const char *elf::get_race_name() const { + return "Elf"; +} + +long_result elf::attack(character *ch) { + auto res1 = ch->get_hit(this, ATK, base_hit_rate); + + if (res1.res == result::died) + return res1; + + auto res2 = ch->get_hit(this, ATK, base_hit_rate); + + if (res1.res == miss && res2.res == miss) + return {miss, res1.msg + res2.msg}; + else if (res2.res == died) + return {died, res1.msg + res2.msg}; + else + return {hit, res1.msg + res2.msg}; +} diff --git a/src/enemies/elf.h b/src/enemies/elf.h new file mode 100644 index 0000000..2e7f9f5 --- /dev/null +++ b/src/enemies/elf.h @@ -0,0 +1,15 @@ +#ifndef __ELF_H__ +#define __ELF_H__ + +#include "../enemy.h" + +class elf final: public enemy_base { +public: + elf(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; + long_result attack(character *ch) override; +}; + +#endif diff --git a/src/enemies/halfling.cc b/src/enemies/halfling.cc new file mode 100644 index 0000000..e0ead49 --- /dev/null +++ b/src/enemies/halfling.cc @@ -0,0 +1,41 @@ +#include "halfling.h" + +#include "../constants.h" + +fraction halfling::HIT_RATE_MUL = {1, 2}; + +halfling::halfling(RNG *rng, const feature enabled_features, + const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rhalfling, pos, gen_room_num, "L"} {} + +void halfling::print(display *out) { + out->print_char(pos, 'L', COLOR_PAIR(COLOR_RED)); +} + +const char *halfling::get_race_name() const { + return "Halfling"; +} + +long_result halfling::get_hit(character *ch, const int tATK, + const fraction &hit_rate) { + auto nhit_rate = HIT_RATE_MUL * hit_rate; + + if (rng->trial(nhit_rate)) { + int tmp = calc_dmg(tATK, DEF); + tmp = tmp > HP ? HP : tmp; + HP -= tmp; + + if (HP == 0) + return {result::hit, + "PC deals " + std::to_string(tmp) + + " damage to " + abbrev + ". " + abbrev + + " is slain by PC. "}; + + return {result::hit, "PC deals " + + std::to_string(tmp) + " damage to " + abbrev}; + } + + return {miss, "PC tried to hit " + abbrev + + " but missed. The halfling laughed. "}; +} diff --git a/src/enemies/halfling.h b/src/enemies/halfling.h new file mode 100644 index 0000000..5413646 --- /dev/null +++ b/src/enemies/halfling.h @@ -0,0 +1,17 @@ +#ifndef __HALFLING_H__ +#define __HALFLING_H__ + +#include "../enemy.h" + +class halfling final: public enemy_base { + static fraction HIT_RATE_MUL; +public: + halfling(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; + long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate); +}; + +#endif diff --git a/src/enemies/human.cc b/src/enemies/human.cc new file mode 100644 index 0000000..a6976ca --- /dev/null +++ b/src/enemies/human.cc @@ -0,0 +1,15 @@ +#include "human.h" + +#include "../constants.h" + +human::human(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rhuman, pos, gen_room_num, "H"} {} + +void human::print(display *out) { + out->print_char(pos, 'H', COLOR_PAIR(COLOR_RED)); +} + +const char *human::get_race_name() const { + return "Human"; +} diff --git a/src/enemies/human.h b/src/enemies/human.h new file mode 100644 index 0000000..3eb6dbe --- /dev/null +++ b/src/enemies/human.h @@ -0,0 +1,14 @@ +#ifndef __HUMAN_H__ +#define __HUMAN_H__ + +#include "../enemy.h" + +class human final: public enemy_base { +public: + human(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; +}; + +#endif diff --git a/src/enemies/merchant.cc b/src/enemies/merchant.cc new file mode 100644 index 0000000..aa91c3a --- /dev/null +++ b/src/enemies/merchant.cc @@ -0,0 +1,15 @@ +#include "merchant.h" + +#include "../constants.h" + +merchant::merchant(RNG *rng, const feature enabled_features, + const position &pos, const int gen_room_num): + enemy_base{rng, enabled_features, rmerchant, pos, gen_room_num, "M"} {} + +void merchant::print(display *out) { + out->print_char(pos, 'M', COLOR_PAIR(COLOR_RED)); +} + +const char *merchant::get_race_name() const { + return "Merchant"; +} diff --git a/src/enemies/merchant.h b/src/enemies/merchant.h new file mode 100644 index 0000000..befbac6 --- /dev/null +++ b/src/enemies/merchant.h @@ -0,0 +1,14 @@ +#ifndef __MERCHANT_H__ +#define __MERCHANT_H__ + +#include "../enemy.h" + +class merchant final: public enemy_base { +public: + merchant(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; +}; + +#endif diff --git a/src/enemies/orc.cc b/src/enemies/orc.cc new file mode 100644 index 0000000..ebc61a8 --- /dev/null +++ b/src/enemies/orc.cc @@ -0,0 +1,15 @@ +#include "orc.h" + +#include "../constants.h" + +orc::orc(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rorc, pos, gen_room_num, "O"} {} + +void orc::print(display *out) { + out->print_char(pos, 'O', COLOR_PAIR(COLOR_RED)); +} + +const char *orc::get_race_name() const { + return "Orc"; +} diff --git a/src/enemies/orc.h b/src/enemies/orc.h new file mode 100644 index 0000000..42ae94c --- /dev/null +++ b/src/enemies/orc.h @@ -0,0 +1,14 @@ +#ifndef __ORC_H__ +#define __ORC_H__ + +#include "../enemy.h" + +class orc final: public enemy_base { +public: + orc(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + void print(display *out) override; + const char *get_race_name() const override; +}; + +#endif diff --git a/src/enemy.cc b/src/enemy.cc new file mode 100644 index 0000000..179d995 --- /dev/null +++ b/src/enemy.cc @@ -0,0 +1,115 @@ +#include "enemy.h" + +#include "constants.h" +#include "player.h" +#include "level.h" + +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}; +} + +int enemy_base::get_room_num() const { + return room_num; +} + +long_result enemy_base::act(level *lvl, character *pc, bool hostile) { + if (is_adjacent(pos, pc->get_pos()) && hostile) + return attack(pc); + + if (enabled_features & FEATURE_ENEMIES_CHASE && + distance_sqr(pos, pc->get_pos()) <= DIST_THRESHOLD_SQR) { + position tmp; + position target = {BIG_NUMBER, BIG_NUMBER}; + + for (int i = 0; i < DIRECTION_CNT; ++i) { + tmp = pos + MOVE[i]; + + if (lvl->get_room(tmp) == room_num && + lvl->is_available(tmp) && + distance_sqr(tmp, pc->get_pos()) < + distance_sqr(target, pc->get_pos())) + target = tmp; + } + + pos = target; + return {fine, ""}; + } + + int choices_cnt = 0; + position choices[DIRECTION_CNT]; + + for (int i = 0; i < DIRECTION_CNT; ++i) + if (lvl->get_room(pos + MOVE[i]) == room_num && + lvl->is_available(pos + MOVE[i])) { + choices[choices_cnt] = pos + MOVE[i]; + ++choices_cnt; + } + + if (choices_cnt) + pos = choices[rng->rand_under(choices_cnt)]; + + return {fine, ""}; +} + +long_result enemy_base::attack(character *ch) { + return ch->get_hit(this, ATK, base_hit_rate); +} + +std::string enemy_base::get_abbrev() const { + return abbrev; +} + +long_result enemy_base::get_hit(character *ch, const int tATK, + const fraction &hit_rate) { + if (rng->trial(hit_rate)) { + int tmp = calc_dmg(tATK, DEF); + tmp = tmp > HP ? HP : tmp; + HP -= tmp; + + if (HP == 0) + return {result::hit, + "PC deals " + std::to_string(tmp) + + " damage to " + abbrev + ". " + abbrev + + " is slain by PC. "}; + + return {result::hit, "PC deals " + + std::to_string(tmp) + " damage to " + abbrev}; + } + + 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; + } + + if (race == race::rdragon) { + return; + } else if (race == race::rmerchant) { + lvl->add_gold({GOLD_MERCHANT, pos}); + } 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)}); + } + + ((player_base *)pc)->add_gold(rand_gold_drop(rng)); +} + +enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) { + for (auto e : elist) + if (e->get_pos() == pos) + return e; + + return nullptr; +} diff --git a/src/enemy.h b/src/enemy.h new file mode 100644 index 0000000..2f11c63 --- /dev/null +++ b/src/enemy.h @@ -0,0 +1,36 @@ +#ifndef __ENEMY_H__ +#define __ENEMY_H__ + +#include "characters.h" + +class enemy_base : public character { +private: + const static int BIG_NUMBER = 500; + const static int DIST_THRESHOLD_SQR = 8 * 8; +protected: + const int room_num; + + std::string abbrev; +public: + enemy_base(RNG *rng, const feature enabled_features, + const enum race &nrace, const position &pos, + const int gen_room_num, std::string abbrev); + + int get_room_num() const; + + virtual long_result act(level *lvl, character *pc, bool hostile = true); + + virtual long_result attack(character *ch) override; + virtual long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate) override; + + virtual std::string get_abbrev() const override; + + virtual void dies(level *lvl, character *pc); +}; + +typedef std::vector enemy_list; + +enemy_base *get_enemy_at(const position &pos, const enemy_list &elist); + +#endif diff --git a/src/file_input.cc b/src/file_input.cc deleted file mode 100644 index 5cbfc86..0000000 --- a/src/file_input.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "file_input.h" -#include -#include - -file_input::file_input(std::ifstream &&ifs): - in{std::move(ifs)} {} - -game_command file_input::get_command() { - std::string cmd; - in >> cmd; - game_command tmp; - - if (in.eof()) - return game_command_terminate; - - if (cmd == "q") - return game_command_terminate; - else if (cmd == "f") - return the_world; - else if (cmd == "r") - return game_restart; - else if (cmd == "u" || cmd == "a") { - bool use = cmd == "u"; - - in >> cmd; - - if (in.eof()) - return game_command_panic; - - return (game_command)((tmp = get_direction(cmd)) == - game_command_panic - ? tmp : tmp - move_north + - (use ? apply_north : attack_north)); - } else { - auto tmp = get_direction(cmd); - - if (tmp != game_command_panic) - return tmp; - } - - return game_command_pass; -} diff --git a/src/file_input.h b/src/file_input.h deleted file mode 100644 index b3b92f4..0000000 --- a/src/file_input.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __FILE_INPUT_H__ -#define __FILE_INPUT_H__ - -#include -#include "input.h" - -class file_input final : public input { -private: - std::ifstream in; -public: - // This is for cin - file_input(std::ifstream &&ifs); - game_command get_command() override; -}; - -#endif diff --git a/src/file_output.cc b/src/file_output.cc deleted file mode 100644 index 710b00a..0000000 --- a/src/file_output.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include "file_output.h" - -#include - -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/file_output.h b/src/file_output.h deleted file mode 100644 index 8acf522..0000000 --- a/src/file_output.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __FILE_OUTPUT_H__ -#define __FILE_OUTPUT_H__ - -#include -#include "display.h" - -class file_output final : public display { -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/fraction.cc b/src/fraction.cc index 595d33f..7910b86 100644 --- a/src/fraction.cc +++ b/src/fraction.cc @@ -1,35 +1,35 @@ #include "fraction.h" fraction fraction::operator+(const fraction &frac) { - fraction tmp = *this; - tmp.numerator = tmp.numerator * frac.denominator + - tmp.denominator * frac.numerator; - tmp.denominator = tmp.denominator * frac.denominator; - return tmp.simplify(); + fraction tmp = *this; + tmp.numerator = tmp.numerator * frac.denominator + + tmp.denominator * frac.numerator; + tmp.denominator = tmp.denominator * frac.denominator; + return tmp.simplify(); } fraction fraction::operator*(const fraction &frac) { - fraction tmp = *this; - tmp.numerator = tmp.numerator * frac.numerator; - tmp.denominator = tmp.denominator * frac.denominator; - return tmp.simplify(); + fraction tmp = *this; + tmp.numerator = tmp.numerator * frac.numerator; + tmp.denominator = tmp.denominator * frac.denominator; + return tmp.simplify(); } bool fraction::operator==(const fraction &frac) { - this->simplify(); - return gcd(frac.numerator, this->numerator) == this->numerator && - gcd(frac.denominator, this->denominator) == this->denominator; + this->simplify(); + return gcd(frac.numerator, this->numerator) == this->numerator && + gcd(frac.denominator, this->denominator) == this->denominator; } bool fraction::operator!=(const fraction &frac) { - return !(*this == frac); + return !(*this == frac); } fraction &fraction::simplify() { - int g = gcd(numerator, denominator); - numerator /= g; - denominator /= g; - return *this; + int g = gcd(numerator, denominator); + numerator /= g; + denominator /= g; + return *this; } float fraction::real() const { @@ -37,8 +37,21 @@ float fraction::real() const { } int fraction::gcd(int a, int b) { - if (a % b == 0) - return b; + if (a % b == 0) + return b; - return gcd(b, a % b); + return gcd(b, a % b); +} + +fraction &fraction::operator=(const fraction &frac) { + numerator = frac.numerator; + denominator = frac.denominator; + this->simplify(); + return *this; +} + +fraction &fraction::operator*=(const fraction &frac) { + numerator *= frac.numerator; + denominator *= frac.denominator; + return *this; } diff --git a/src/fraction.h b/src/fraction.h index 65e686e..f0b779f 100644 --- a/src/fraction.h +++ b/src/fraction.h @@ -2,17 +2,19 @@ #define __FRACTION_H__ struct fraction final { - int numerator; - int denominator; + int numerator; + int denominator; fraction operator+(const fraction &frac); fraction operator*(const fraction &frac); + fraction &operator*=(const fraction &frac); + fraction &operator=(const fraction &frac); bool operator==(const fraction &frac); bool operator!=(const fraction &frac); fraction &simplify(); float real() const; private: - int gcd(int a, int b); + int gcd(int a, int b); }; #endif diff --git a/src/game.cc b/src/game.cc index 246973a..cc835d8 100644 --- a/src/game.cc +++ b/src/game.cc @@ -1,25 +1,21 @@ #include "game.h" -#include "races.h" +#include "constants.h" +#include "pc.h" game::game(const enum race starting_race, const feature enabled_features, input *new_in, display *new_out, - logger *new_log, RNG *new_rng): enabled_features{enabled_features}, - in{new_in}, out{new_out}, log{new_log}, rng{new_rng}, + in{new_in}, out{new_out}, rng{new_rng}, curr_turn{0} { const position nil{0, 0}; the_world = false; // TODO: add the other races - switch (starting_race) { - case race::rshade: - player = std::make_unique(rng, nil); - break; - } + init_player(rng, player, enabled_features, starting_race); if (enabled_features & FEATURE_EXTRA_LEVELS) max_level = rng->rand_between(MIN_LEVEL_CNT, MAX_LEVEL_CNT + 1); @@ -45,118 +41,43 @@ void game::new_level() { rng, enabled_features)); } -result game::player_moves(game_command cmd) { - if (cmd == game_command_terminate) { - return result::terminate; - } else if (cmd >= move_north && cmd <= move_southwest) { // Tried to move - if (enabled_features & FEATURE_NCURSES) - return player_move_or_attack((direction)(cmd - move_north)); - - return player->move((direction)(cmd - move_north), - levels[curr_level]->get_available_around( - player->get_position())); - } else if (cmd >= apply_north && cmd <= apply_southwest) { - auto res = player->apply((direction)(cmd - apply_north), - levels[curr_level]->get_plist()); - - if (res.res == applied) { - msg += "PC used potion "; - msg += POTION_REAL_NAME[res.which->get_type()]; - msg += ". "; - } else { - msg += "PC tried to drink thin air. "; - } - - return res.res; - } else if (cmd == apply_panic) { - msg += "PC tried to use in some non-existent direction. "; - return fine; - } else if (cmd >= attack_north && cmd <= attack_southwest) { - auto res = player->attack((direction)(cmd - attack_north), - levels[curr_level]->get_chlist()); - - if (res.res == hit) { - msg += "PC deals " + std::to_string(res.dmg_dealt) + - " damage to " + - CHARACTER_REP[res.which->get_race()] + - " (" + std::to_string(res.remaining_HP) + - " HP). "; - } else if (res.res == miss) { - msg += "PC missed "; - msg += CHARACTER_REP[res.which->get_race()]; - msg += ". "; - } else { - msg += "PC tried to attack thin air. "; - } - - if (res.which->get_race() == rmerchant) - hostile_merchants = true; - - return res.res; - } else if (cmd == up_stairs) { - if (player->get_position() == - levels[curr_level]->get_up_stairs()) - return go_up; - - msg += "PC tried to fly through the ceiling. "; - return fine; - } else if (cmd == down_stairs) { - if (player->get_position() == - levels[curr_level]->get_down_stairs()) - return go_down; - - msg += "PC tried to dig through the floor. "; - return fine; - } else if (cmd == the_world) { - msg += "PC toggled Stand: The World! "; - return toggle_the_world; - } else if (cmd == game_restart) { - return restart_game; - } else if (cmd == game_command_pass) { - return fine; - } else if (cmd == game_command_panic) { - msg += "PC tried to produce some undefined behaviour. "; - return unknown; - } - - msg += "PC tried to produce some undefined behaviour. "; - return unknown; -} - #include -bool compare_characters(character *&a, character *&b) { - return a->get_position() < b->get_position(); +bool compare_characters(const character *a, const character *b) { + return a->get_pos() < b->get_pos(); } void game::move_enemies() { if (the_world) return; - character_list enemies = levels[curr_level]->get_chlist(); - std::sort(enemies.begin(), enemies.end(), &game::compare_characters); + auto enemies = levels[curr_level]->get_elist(); + std::sort(enemies.begin(), enemies.end(), ::compare_characters); for (auto ch : enemies) { + bool hostile = ch->get_race() == rmerchant ? + hostile_merchants : true; + ch->start_turn(); + ch->calc_effects(); + auto res = + ch->act(levels[curr_level].get(), player.get(), hostile); + if (player->is_dead()) + return; + + if (ch->is_dead()) + ch->dies(levels[curr_level].get(), player.get()); + + msg += res.msg; } } game_status game::run() { - msg = ""; - auto res = player_moves(in->get_command()); + player->start_turn(); + auto res = player->interpret_command(levels[curr_level].get(), + in->get_command()); + msg = res.msg; - if (!(enabled_features & FEATURE_NCURSES) && - res == result::moved && - (player->get_position() == - levels[curr_level]->get_down_stairs())) - res = go_down; - - if (!(enabled_features & FEATURE_NCURSES) && - res == result::moved && - (player->get_position() == - levels[curr_level]->get_up_stairs())) - res = go_up; - - switch (res) { + switch (res.res) { case result::terminate: return terminated; @@ -170,7 +91,6 @@ game_status game::run() { player->discard_level_effects(); ++curr_level; new_level(); - msg += "PC went down a floor. "; break; } @@ -180,8 +100,7 @@ game_status game::run() { player->discard_level_effects(); --curr_level; - player->set_position(levels[curr_level]->get_down_stairs()); - msg += "PC went up a floor. "; + player->set_pos(levels[curr_level]->get_down_stairs()); break; } @@ -203,14 +122,14 @@ game_status game::run() { } player->start_turn(); - player->apply_effects(); + player->calc_effects(); - if (player->get_HP() <= 0) + if (player->is_dead()) return game_status::dead; move_enemies(); - if (player->get_HP() <= 0) + if (player->is_dead()) return game_status::dead; ++curr_turn; @@ -223,8 +142,6 @@ const position STATUS_HP{0, 26}; const position STATUS_ATK{0, 27}; const position STATUS_DEF{0, 28}; const position STATUS_ACTION{0, 29}; -const char *ASK_ATK_MERCHANT = - "Action: Do you really want to attack the peaceful merchant? [Y/N]"; size_t game::get_curr_turn() const { return curr_turn; @@ -237,61 +154,15 @@ void game::print() { } levels[curr_level]->print(out); - player->print(out, true); + player->print(out); std::string tmp = "Race: "; - tmp += RACE_NAME[player->get_race()]; + tmp += player->get_race_name(); tmp += " Gold: " + std::to_string(player->get_gold()); out->print_str(STATUS_RACE, tmp); - - tmp = "Floor " + std::to_string(curr_level + 1); - out->print_str(STATUS_FLOOR, tmp); - - tmp = "Atk: " + std::to_string(player->get_ATK()); - out->print_str(STATUS_ATK, tmp); - - tmp = "Def: " + std::to_string(player->get_DEF()); - out->print_str(STATUS_DEF, tmp); - - tmp = "Action: " + msg; - out->print_str(STATUS_ACTION, tmp); -} - -result game::player_move_or_attack(const direction &dir) { - auto avlbl = levels[curr_level]->get_available_around( - player->get_position()); - - if (find(avlbl, player->get_position() + MOVE[dir]) - != avlbl.size()) { - player->set_position(player->get_position() + MOVE[dir]); - return result::moved; - } - - auto tmp = player->get_position() + MOVE[dir]; - character *target; - - for (auto ch : levels[curr_level]->get_chlist()) - if (tmp == ch->get_position()) { - target = ch; - } - - auto res = player->attack(dir, target); - - if (target->get_race() == rmerchant) - hostile_merchants = true; - - if (res.res == hit) { - msg += "PC deals " + - std::to_string(res.dmg_dealt) + " damage to " + - CHARACTER_REP[res.which->get_race()] + " (" + - std::to_string(res.remaining_HP) + " HP). "; - } else if (res.res == miss) { - msg += "PC missed "; - msg += CHARACTER_REP[res.which->get_race()]; - msg += ". "; - } else { - msg += "PC tried to attack thin air. "; - } - - return res.res; + out->print_str(STATUS_FLOOR, "Floor " + std::to_string(curr_level + 1)); + out->print_str(STATUS_HP, "HP: " + std::to_string(player->get_HP())); + out->print_str(STATUS_ATK, "Atk: " + std::to_string(player->get_ATK())); + out->print_str(STATUS_DEF, "Def: " + std::to_string(player->get_DEF())); + out->print_str(STATUS_ACTION, "Action: " + msg); } diff --git a/src/game.h b/src/game.h index 65fdd52..5f92fab 100644 --- a/src/game.h +++ b/src/game.h @@ -2,13 +2,14 @@ #define __GAME_H__ #include #include -#include "constants.h" #include "display.h" #include "input.h" #include "rng.h" #include "characters.h" #include "level.h" -#include "log.h" +#include "player.h" + +enum game_status : int; class game final { private: @@ -21,12 +22,11 @@ private: input *in; display *out; - logger *log; RNG *rng; unsigned int curr_turn; - std::unique_ptr player; + std::unique_ptr player; std::vector> levels; size_t max_level; @@ -42,7 +42,6 @@ public: const feature enabled_features, input *new_in, display *new_out, - logger *new_log, RNG *new_rng); game_status run(); size_t get_curr_level() const; @@ -50,11 +49,8 @@ public: const character *get_player() const; void print(); private: - result player_moves(game_command cmd); void new_level(); void move_enemies(); - result player_move_or_attack(const direction &dir); - bool compare_characters(const character *&a, const character *&b); }; #endif diff --git a/src/goblin.cc b/src/goblin.cc deleted file mode 100644 index fa0a745..0000000 --- a/src/goblin.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "goblin.h" - -goblin::goblin(RNG *rng, const position &pos): - character{rng, race::rgoblin, pos} { - gold = 0; - hostile = true; -} - -result goblin::attack(const direction dir, character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - auto res = ch->get_hit(race, ATK, base_hit_rate); - - if (res == result::died) { - gold += GAIN_GOLD; - } - - return res; - } - - return result::fine; -} - -result goblin::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) - HP = std::max(HP - calc_dmg(atk, DEF), 0); - - if (HP == 0) - return result::died; - - return result::hit; -} diff --git a/src/goblin.h b/src/goblin.h deleted file mode 100644 index 460fe9e..0000000 --- a/src/goblin.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __GOBLIN_H__ -#define __GOBLIN_H__ - -#include "characters.h" - -class goblin final: public character { - static const int GAIN_GOLD = 5; -public: - goblin(RNG *rng, const position &pos); - virtual result attack(const direction dir, - character_list &chlist) override; - virtual result get_hit(const enum race &race, const int atk, - const fraction hit_rate) override; -}; - -#endif diff --git a/src/gold.cc b/src/gold.cc index bad64a2..05e8846 100644 --- a/src/gold.cc +++ b/src/gold.cc @@ -1,5 +1,7 @@ #include "gold.h" +#include "constants.h" + int rand_gold_pile(RNG *rng) { const int denominator = 8; const int normal = 5; @@ -16,3 +18,23 @@ int rand_gold_pile(RNG *rng) { return 0; } + +gold get_gold_at(const position &pos, gold_list &glist) { + gold ret = {0, {0, 0}}; + + for (size_t i = 0; i < glist.size(); ++i) + if (glist[i].pos == pos) { + ret = glist[i]; + glist.erase(i + glist.begin()); + return ret; + } + + return ret; +} + +int rand_gold_drop(RNG *rng) { + if (rng->coin_flip()) + return GOLD_NORMAL; + + return GOLD_SMALL; +} diff --git a/src/gold.h b/src/gold.h index 6ada545..9eaf053 100644 --- a/src/gold.h +++ b/src/gold.h @@ -2,7 +2,6 @@ #define __GOLD_H__ #include -#include "constants.h" #include "position.h" #include "rng.h" @@ -13,6 +12,8 @@ struct gold { typedef std::vector gold_list; +gold get_gold_at(const position &pos, gold_list &glist); int rand_gold_pile(RNG *rng); +int rand_gold_drop(RNG *rng); #endif diff --git a/src/input.cc b/src/input.cc index 59f0368..7dddf6a 100644 --- a/src/input.cc +++ b/src/input.cc @@ -1,14 +1,16 @@ #include "input.h" +#include "constants.h" + const char *COMMANDS[] = { - "no", "so", "ea", "we", "ne", "nw", "se", "sw" + "no", "so", "ea", "we", "ne", "nw", "se", "sw" }; const int COMMANDS_CNT = 8; game_command get_direction(std::string &str) { - for (int i = 0; i < COMMANDS_CNT; ++i) - if (str == COMMANDS[i]) - return (game_command)i; + for (int i = 0; i < COMMANDS_CNT; ++i) + if (str == COMMANDS[i]) + return (game_command)i; - return game_command_panic; + return game_command_panic; } diff --git a/src/input.h b/src/input.h index aa9d120..6958cba 100644 --- a/src/input.h +++ b/src/input.h @@ -1,12 +1,13 @@ #ifndef __INPUT_H__ #define __INPUT_H__ #include -#include "constants.h" + +enum game_command : int; class input { public: - virtual ~input() = default; - virtual game_command get_command() = 0; + virtual ~input() = default; + virtual game_command get_command() = 0; }; game_command get_direction(std::string &str); diff --git a/src/input/console_input.cc b/src/input/console_input.cc new file mode 100644 index 0000000..561bf60 --- /dev/null +++ b/src/input/console_input.cc @@ -0,0 +1,46 @@ +#include "console_input.h" + +#include +#include +#include "../constants.h" + +console_input::console_input(std::istream &cin): + in{cin} {} + +game_command console_input::get_command() { + std::string cmd; + in >> cmd; + game_command tmp; + + if (in.eof()) + return game_command_terminate; + + if (cmd == "q") + return game_command_terminate; + else if (cmd == "f") + return the_world; + else if (cmd == "r") + return game_restart; + else if (cmd == "u" || cmd == "a") { + bool use = cmd == "u"; + + in >> cmd; + + if (in.eof()) + return game_command_panic; + + return (game_command)((tmp = get_direction(cmd)) == + game_command_panic + ? tmp : tmp - move_north + + (use ? apply_north : attack_north)); + } else if (cmd == "yes") { + return game_command::enter; + } else { + auto tmp = get_direction(cmd); + + if (tmp != game_command_panic) + return tmp; + } + + return game_command_pass; +} diff --git a/src/input/console_input.h b/src/input/console_input.h new file mode 100644 index 0000000..4888d0c --- /dev/null +++ b/src/input/console_input.h @@ -0,0 +1,16 @@ +#ifndef __CONSOLE_INPUT_H__ +#define __CONSOLE_INPUT_H__ + +#include +#include "../input.h" + +class console_input final : public input { +private: + std::istream ∈ +public: + // This is for cin + console_input(std::istream &cin); + game_command get_command() override; +}; + +#endif diff --git a/src/input/curses_input.cc b/src/input/curses_input.cc new file mode 100644 index 0000000..5c9065f --- /dev/null +++ b/src/input/curses_input.cc @@ -0,0 +1,89 @@ +#include "curses_input.h" + +#include "../constants.h" + +curses_input::curses_input(cursor *new_curse): + curse{new_curse} {} + +game_command curses_input::get_command() { + switch (curse->getcmd()) { + case 'h': + return game_command::move_west; + + case 'j': + return game_command::move_south; + + case 'k': + return game_command::move_north; + + case 'l': + return game_command::move_east; + + case 'y': + return game_command::move_northwest; + + case 'u': + return game_command::move_northeast; + + case 'b': + return game_command::move_southwest; + + case 'n': + return game_command::move_southeast; + + case 'a': + break; // wait for another command + + case '<': + return game_command::up_stairs; + + case '>': + return game_command::down_stairs; + + case 'q': + return game_command_terminate; + + case 'f': + return game_command::the_world; + + case 'r': + return game_restart; + + case KEY_ENTER: + return game_command::enter; + + default: + return game_command_pass; + } + + switch (curse->getcmd()) { + case 'h': + return game_command::apply_west; + + case 'j': + return game_command::apply_south; + + case 'k': + return game_command::apply_north; + + case 'l': + return game_command::apply_east; + + case 'y': + return game_command::apply_northwest; + + case 'u': + return game_command::apply_northeast; + + case 'b': + return game_command::apply_southwest; + + case 'n': + return game_command::apply_southeast; + + default: + return game_command::apply_panic; + } + + return game_command::game_command_panic; +} diff --git a/src/curses_input.h b/src/input/curses_input.h similarity index 80% rename from src/curses_input.h rename to src/input/curses_input.h index 3d34d5c..9f6b978 100644 --- a/src/curses_input.h +++ b/src/input/curses_input.h @@ -2,9 +2,8 @@ #define __CURSES_INPUT_H__ #include -#include "input.h" -#include "constants.h" -#include "cursor.h" +#include "../input.h" +#include "../cursor.h" class curses_input final: public input { private: diff --git a/src/input/file_input.cc b/src/input/file_input.cc new file mode 100644 index 0000000..2e6bb0c --- /dev/null +++ b/src/input/file_input.cc @@ -0,0 +1,46 @@ +#include "file_input.h" + +#include +#include +#include "../constants.h" + +file_input::file_input(std::ifstream &&ifs): + in{std::move(ifs)} {} + +game_command file_input::get_command() { + std::string cmd; + in >> cmd; + game_command tmp; + + if (in.eof()) + return game_command_terminate; + + if (cmd == "q") + return game_command_terminate; + else if (cmd == "f") + return the_world; + else if (cmd == "r") + return game_restart; + else if (cmd == "u" || cmd == "a") { + bool use = cmd == "u"; + + in >> cmd; + + if (in.eof()) + return game_command_panic; + + return (game_command)((tmp = get_direction(cmd)) == + game_command_panic + ? tmp : tmp - move_north + + (use ? apply_north : attack_north)); + } else if (cmd == "yes") { + return game_command::enter; + } else { + auto tmp = get_direction(cmd); + + if (tmp != game_command_panic) + return tmp; + } + + return game_command_pass; +} diff --git a/src/input/file_input.h b/src/input/file_input.h new file mode 100644 index 0000000..48fc9f5 --- /dev/null +++ b/src/input/file_input.h @@ -0,0 +1,16 @@ +#ifndef __FILE_INPUT_H__ +#define __FILE_INPUT_H__ + +#include +#include "../input.h" + +class file_input final : public input { +private: + std::ifstream in; +public: + // This is for cin + file_input(std::ifstream &&ifs); + game_command get_command() override; +}; + +#endif diff --git a/src/level.cc b/src/level.cc index e63db10..03b40b7 100644 --- a/src/level.cc +++ b/src/level.cc @@ -1,5 +1,7 @@ #include "level.h" +#include "constants.h" + level::level(character *player, RNG *rng, const feature enabled_features): enabled_features{enabled_features}, map{player, rng, enabled_features}, player{player} { @@ -34,24 +36,29 @@ void level::gen_enemies(RNG *rng, std::vector &tiles) { rng->rand_between(MIN_ENEMIE_CNT, MAX_ENEMIE_CNT + 1) : MIN_ENEMIE_CNT + dhoard.size(); - chlist.reserve(cnt); - pchlist.reserve(cnt); + elist.reserve(cnt); + pelist.reserve(cnt); for (size_t i = 0; i < dhoard.size(); ++i) { - auto spots = get_available_around(dhoard[i].pos); - pchlist.push_back(nullptr); - new_dragon(rng, pchlist[i], rng->get_rand_in_vector(spots), - dhoard[i].pos); - chlist.push_back(pchlist[i].get()); + position_list spots; + + for (int i = 0; i < DIRECTION_CNT; ++i) + if (map.which_room(dhoard[i].pos + MOVE[i]) != -1) + 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)); + elist.push_back(pelist[i].get()); } for (int i = dhoard.size(); i < cnt; ++i) { - pchlist.push_back(nullptr); - new_enemy(rng, pchlist[i], get_rand_pos(rng, tiles), - enabled_features & FEATURE_EXTRA_STUFF); - pchlist[i]->set_room_num(map.which_room(pchlist[i]->get_position())); - chlist.push_back(pchlist[i].get()); + pelist.push_back(nullptr); + auto p = get_rand_pos(rng, tiles); + new_enemy(rng, pelist[i], p, enabled_features, map.which_room(p)); + elist.push_back(pelist[i].get()); } } @@ -69,6 +76,10 @@ position level::get_rand_pos(RNG *rng, std::vector &tiles) { return pos; } +void level::add_gold(gold g) { + glist.push_back(g); +} + void level::gen_gold(RNG *rng, std::vector &tiles) { glist.reserve(GOLD_CNT); @@ -103,7 +114,7 @@ void level::gen_potions(RNG *rng, std::vector &tiles) { void level::print(display *out) const { map.print(out); - for (auto ch : chlist) + for (auto ch : elist) ch->print(out); for (auto p : plist) @@ -113,21 +124,50 @@ void level::print(display *out) const { out->print_char(gold.pos, 'G', COLOR_PAIR(COLOR_YELLOW)); } -bool level::is_available(const position &pos) const { +bool level::is_available(const position &pos, bool is_player) const { if (!map.is_available(pos)) return false; - for (auto ch : chlist) - if (pos == ch->get_position()) + for (auto ch : elist) + if (pos == ch->get_pos()) + return false; + + if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) { + for (auto p : plist) + if (pos == p->get_pos()) + return false; + + for (auto g : glist) + if (pos == g.pos) + return false; + } + + return true; +} + +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()) return false; for (auto p : plist) if (pos == p->get_pos()) return false; + for (auto g : glist) + if (pos == g.pos) + return false; + return true; } +int level::get_room(const position &pos) const { + return map.which_room(pos); +} + position_list level::get_available_around(const position &pos) const { position_list result; @@ -138,6 +178,16 @@ position_list level::get_available_around(const position &pos) const { return result; } +position_list level::get_available_around_all(const position &pos) const { + position_list result; + + for (int i = 0; i < DIRECTION_CNT; ++i) + if (is_available_all(pos + MOVE[i])) + result.push_back(pos + MOVE[i]); + + return result; +} + position level::get_up_stairs() const { return map.get_up_stairs(); } @@ -146,8 +196,8 @@ position level::get_down_stairs() const { return map.get_down_stairs(); } -character_list &level::get_chlist() { - return chlist; +enemy_list &level::get_elist() { + return elist; } potion_list &level::get_plist() { diff --git a/src/level.h b/src/level.h index 1ec91bf..08e0c6d 100644 --- a/src/level.h +++ b/src/level.h @@ -7,8 +7,8 @@ #include "display.h" #include "gold.h" #include "enemies.h" +#include "enemy.h" #include "potions.h" -#include "constants.h" #include "map.h" class level { @@ -22,9 +22,9 @@ private: const feature enabled_features; game_map map; character *player; - std::vector>pchlist; + std::vector>pelist; std::vector>pplist; - character_list chlist; + enemy_list elist; potion_list plist; gold_list glist; public: @@ -35,15 +35,21 @@ public: const feature enabled_features); void print(display *out) const; - bool is_available(const position &pos) const; + bool is_available(const position &pos, bool is_player = false) const; + bool is_available_all(const position &pos) const; + + int get_room(const position &pos) const; position_list get_available_around(const position &pos) const; + position_list get_available_around_all(const position &pos) const; position get_up_stairs() const; position get_down_stairs() const; // you can delete the pointers to the stuff - character_list &get_chlist(); + enemy_list &get_elist(); potion_list &get_plist(); gold_list &get_glist(); + + void add_gold(gold g); private: // every gen will delete the positions in tiles void gen_potions(RNG *rng, std::vector &tiles); diff --git a/src/log.cc b/src/log.cc deleted file mode 100644 index fe31d0b..0000000 --- a/src/log.cc +++ /dev/null @@ -1,10 +0,0 @@ -#include "log.h" - -#include - -logger::logger(std::ofstream &&new_out): - out{std::move(new_out)} {} - -void logger::render() { - -} diff --git a/src/log.h b/src/log.h deleted file mode 100644 index 887dcd7..0000000 --- a/src/log.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __LOG_H__ -#define __LOG_H__ -#include -#include -#include -#include -#include "constants.h" -#include "characters.h" - -class logger final { -private: - std::ofstream out; - std::vector contents; -public: - logger(std::ofstream &&new_out); - - // called once every turn - void render(); - void print_char(const position &pos, const char ch); - void print_str(const position &pos, const std::string &str); - void print_turn(const unsigned turn); - void print_player(const character &player); - void print_chlist(const character_list &chlist); - void print_gold(const gold_list &gold_piles); - void print_potions(const potion_list &potions); -}; - -#endif diff --git a/src/main.cc b/src/main.cc index 5cff79f..1e76615 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,18 +2,17 @@ #include "cc3k.h" #include "arguments.h" +#include "constants.h" int main(int argc, char **argv) { - std::unique_ptr curse; - std::unique_ptr in; - std::unique_ptr out; - std::unique_ptr log; - std::unique_ptr rng; + std::unique_ptr curse; + std::unique_ptr in; + std::unique_ptr out; + std::unique_ptr rng; - feature enabled_features = proc_args(argc, argv, - curse, in, out, log, rng); + feature enabled_features = proc_args(argc, argv, curse, in, out, rng); - if (enabled_features & + if (enabled_features & (FEATURE_PANIC | FEATURE_PANIC_FILE | FEATURE_CONFLICT | FEATURE_PANIC_SEED)) { panic_args(enabled_features); @@ -30,5 +29,5 @@ int main(int argc, char **argv) { // out->clear(); // } - return RETURN_FINE; + return RETURN_FINE; } diff --git a/src/map.cc b/src/map.cc index 03d30bb..09ff711 100644 --- a/src/map.cc +++ b/src/map.cc @@ -1,9 +1,10 @@ #include "map.h" #include - #include +#include "constants.h" + game_map::game_map(character *player, RNG *rng, const feature enabled_features): enabled_features{enabled_features} { map.reserve(MAP_HEIGHT * MAP_WIDTH); @@ -25,7 +26,6 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features): gen_path(layer_data, rng); - rooms_tile_list.reserve(room_data.size()); for (size_t i = 0; i < room_data.size(); ++i) @@ -41,7 +41,7 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features): rooms_tile_list.shrink_to_fit(); int player_room = rng->rand_under(room_data.size()); - player->set_position(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); } @@ -96,7 +96,7 @@ game_map::game_map(character *player, const std::string &map_data, room_data.push_back({0, 0, 0, 0}); int player_room = rng->rand_under(room_data.size()); - player->set_position(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); @@ -277,7 +277,7 @@ std::vector game_map::distr_rooms(RNG *rng, if (l == r) { result.push_back({ 0, rng->rand_under(ACTUAL_MAP_WIDTH - - room_dims[l].first.x + 1), + room_dims[l].first.x + 1), room_dims[l].first.x, room_dims[l].first.y, {0, 0}, {0, 0}}); continue; @@ -557,3 +557,11 @@ bool game_map::hit_room(const position &a, const room &r) { return a.y >= r.top - 1 && a.y <= r.top + r.height && a.x >= r.left - 1 && a.x <= r.left + r.width; } + +position game_map::remap_index(const int idx) const { + return {idx % MAP_WIDTH, idx / MAP_WIDTH}; +} + +int game_map::remap_position(const position &pos) const { + return pos.y * MAP_WIDTH + pos.x; +} diff --git a/src/map.h b/src/map.h index e4652b0..717cad2 100644 --- a/src/map.h +++ b/src/map.h @@ -3,11 +3,12 @@ #include #include #include -#include "constants.h" + #include "display.h" #include "position.h" #include "rng.h" #include "fraction.h" +#include "player.h" #include "characters.h" class game_map final { @@ -18,8 +19,8 @@ private: static const int MAX_ROOM_WIDTH = 20; static const int MAX_ROOM_HEIGHT = 5; // setup safezones - static const int ACTUAL_MAP_WIDTH = MAP_WIDTH - 6; - static const int ACTUAL_MAP_HEIGHT = MAP_HEIGHT - 5; + static const int ACTUAL_MAP_WIDTH = 73; + static const int ACTUAL_MAP_HEIGHT = 20; static const int MAP_PADDING = 3; static const int MIN_ROOM_SPACING = 3; static const int WIDTH_RESERVED = 6; @@ -72,16 +73,10 @@ public: int get_down_stairs_room() const; int get_room_cnt() const; - room get_room(std::size_t idx) const; private: std::vector room_data; - position remap_index(const int idx) const { - return {idx % MAP_WIDTH, idx / MAP_WIDTH}; - } - - int remap_position(const position &pos) const { - return pos.y * MAP_WIDTH + pos.x; - } + position remap_index(const int idx) const; + int remap_position(const position &pos) const; position random_size(int min_width, int min_height, int max_width, int max_height, @@ -113,6 +108,7 @@ private: bool hit_room(const position &a, const room &r); void gen_stairs(int player_room, RNG *rng); + room get_room(std::size_t idx) const; }; const std::string default_map = diff --git a/src/pc.cc b/src/pc.cc new file mode 100644 index 0000000..a5df804 --- /dev/null +++ b/src/pc.cc @@ -0,0 +1,40 @@ +#include "pc.h" + +#include "constants.h" +#include "player/goblin.h" +#include "player/drow.h" +#include "player/shade.h" +#include "player/troll.h" +#include "player/vampire.h" + +void init_player(RNG *rng, std::unique_ptr &pc, + const feature enabled_features, const enum race &r) { + using std::make_unique; + + pc = nullptr; + + switch (r) { + case rgoblin: + pc = make_unique(rng, enabled_features); + break; + + case rdrow: + pc = make_unique(rng, enabled_features); + break; + + case rshade: + pc = make_unique(rng, enabled_features); + break; + + case rtroll: + pc = make_unique(rng, enabled_features); + break; + + case rvampire: + pc = make_unique(rng, enabled_features); + break; + + default: + break; + } +} diff --git a/src/pc.h b/src/pc.h new file mode 100644 index 0000000..7c67f2c --- /dev/null +++ b/src/pc.h @@ -0,0 +1,10 @@ +#ifndef __PC_H__ +#define __PC_H__ + +#include +#include "player.h" + +void init_player(RNG *rng, std::unique_ptr &pc, + const feature enabled_features, const enum race &r); + +#endif diff --git a/src/player.cc b/src/player.cc new file mode 100644 index 0000000..f94b32d --- /dev/null +++ b/src/player.cc @@ -0,0 +1,151 @@ +#include "player.h" + +#include "constants.h" +#include "enemy.h" +#include "level.h" + +player_base::player_base(RNG *rng, const feature enabled_features, + const enum race &nrace): + character{rng, enabled_features, nrace, {0, 0}}, gold_cnt(0) {} + +void player_base::print(display *out) { + out->print_char(pos, '@', COLOR_PAIR(COLOR_BLUE)); +} + +std::string player_base::get_abbrev() const { + return "PC"; +} + +int player_base::get_gold() const { + return this->gold_cnt; +} + +int player_base::get_ATK() const { + return this->ATK; +} + +int player_base::get_DEF() const { + return this->DEF; +} + +int player_base::get_HP() const { + return this->HP; +} + +long_result player_base::apply(potion *p) { + if (p == nullptr) + return {result::applied_nothing, + "PC tried to breathe the magic in the air. "}; + + apply_effect(p); + + return {result::applied, + (std::string)"PC applied potion of " + p->get_name()}; +} + +long_result player_base::attack(character *ch) { + if (ch == nullptr) + return {result::fine, + "PC tried to attack thin air. "}; + + return ch->get_hit(this, ATK, base_hit_rate); +} + +long_result player_base::move(level *lvl, + const position &p) { + if (enabled_features & FEATURE_NCURSES) { + enemy_base *tmp = nullptr; + + if (lvl->is_available(p, true)) { + pos = p; + return {result::moved, ""}; + } else if ((tmp = get_enemy_at(p, lvl->get_elist())) != nullptr) { + auto res = attack((character *)tmp); + + if (tmp->is_dead()) + tmp->dies(lvl, this); + + return res; + } + } else if (lvl->get_up_stairs() == p && + enabled_features & FEATURE_REVISIT) { + return {result::go_up, "PC went up the stairs. "}; + } else if (lvl->get_down_stairs() == p) { + return {result::go_down, "PC went down the stairs. "}; + } else if (lvl->is_available(p, true)) { + pos = p; + return {result::moved, ""}; + } + + return {result::fine, "PC tried to move into non-existent space. "}; +} + +long_result player_base::get_hit(character *ch, const int tATK, + const fraction &hit_rate) { + if (rng->trial(hit_rate)) { + int tmp = calc_dmg(tATK, DEF); + tmp = tmp > HP ? HP : tmp; + HP -= tmp; + + if (HP == 0) + return {result::died, + ch->get_abbrev() + " deals " + + std::to_string(tmp) + + " damage to PC. PC is slain by " + + ch->get_abbrev() + ". "}; + + return {result::hit, ch->get_abbrev() + " deals " + + std::to_string(tmp) + + " damage to PC. "}; + } + + return {result::miss, ch->get_abbrev() + " tried to hit PC but missed. "}; +} + +void player_base::add_gold(int amount) { + gold_cnt += amount; +} + +long_result player_base::interpret_command(level *lvl, game_command cmd) { + if (cmd == game_command_terminate) { + return {result::terminate, ""}; + } else if (cmd >= move_north && cmd <= move_southwest) { + return move(lvl, pos + MOVE[cmd - move_north]); + } else if (cmd >= apply_north && cmd <= apply_southwest) { + return apply(get_potion_at(pos + MOVE[cmd - apply_north], + lvl->get_plist())); + } else if (cmd == apply_panic) { + return {result::fine, + "PC tried to use in some non-existent direction. "}; + } else if (cmd >= attack_north && cmd <= attack_southwest) { + enemy_base *tmp = get_enemy_at(pos + MOVE[cmd - attack_north], + lvl->get_elist()); + auto res = attack((character *)tmp); + + if (tmp->is_dead()) + tmp->dies(lvl, this); + + return res; + } else if (cmd == up_stairs) { + if (lvl->get_up_stairs() == pos && + 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) + return {go_down, "PC went down the stairs. "}; + + return {result::fine, + "PC tried to dig through the floor. "}; + } else if (cmd == the_world) { + return{toggle_the_world, "PC toggled Stand: The World! "}; + } else if (cmd == game_restart) { + return {restart_game, ""}; + } else if (cmd == game_command_pass) { + return {result::fine, ""}; + } + + return {unknown, "PC tried to produce some undefined behaviour. "}; +} diff --git a/src/player.h b/src/player.h new file mode 100644 index 0000000..00a9398 --- /dev/null +++ b/src/player.h @@ -0,0 +1,38 @@ +#ifndef __PLAYER_H__ +#define __PLAYER_H__ + +#include "characters.h" + +enum game_command : int; + +class player_base: public character { +protected: + int gold_cnt; + potion_list potions; +public: + player_base(RNG *rng, const feature enabled_features, + const enum race &nrace); + virtual long_result move(level *lvl, + const position &p) override; + + virtual long_result apply(potion *p); + + virtual long_result attack(character *ch) override; + virtual long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate) override; + + virtual void add_gold(int amount); + + void print(display *out) override; + + std::string get_abbrev() const override; + + long_result interpret_command(level *lvl, game_command cmd); + + int get_gold() const; + int get_ATK() const; + int get_DEF() const; + int get_HP() const; +}; + +#endif diff --git a/src/player/drow.cc b/src/player/drow.cc new file mode 100644 index 0000000..5337a6a --- /dev/null +++ b/src/player/drow.cc @@ -0,0 +1,10 @@ +#include "drow.h" + +#include "../constants.h" + +drow::drow(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, race::rdrow} {} + +const char *drow::get_race_name() const { + return "Drow"; +} diff --git a/src/player/drow.h b/src/player/drow.h new file mode 100644 index 0000000..4e67295 --- /dev/null +++ b/src/player/drow.h @@ -0,0 +1,12 @@ +#ifndef __DROW_H__ +#define __DROW_H__ + +#include "../player.h" + +class drow final: public player_base { +public: + drow(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; +}; + +#endif diff --git a/src/player/goblin.cc b/src/player/goblin.cc new file mode 100644 index 0000000..3ca3756 --- /dev/null +++ b/src/player/goblin.cc @@ -0,0 +1,53 @@ +#include "goblin.h" + +#include "../constants.h" + +goblin::goblin(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, race::rgoblin} {}; + +const char *goblin::get_race_name() const { + return "Goblin"; +} + +long_result goblin::attack(character *ch) { + if (ch == nullptr) + return {result::fine, + "PC tried to attack thin air. "}; + + auto res = ch->get_hit(this, ATK, base_hit_rate); + + if (ch->is_dead()) { + gold_cnt += GAIN_GOLD; + res.msg += "PC feels greedy. "; + } + + return res; +} + +long_result goblin::get_hit(character *ch, const int tATK, + const fraction &hit_rate) { + if (rng->trial(hit_rate)) { + int tmp = calc_dmg(tATK, DEF); + std::string msg = ""; + + if (ch->get_race() == rorc) { + msg += "PC feels scared. "; + tmp *= ORC_DMG_MUL; + } + + tmp = tmp > HP ? HP : tmp; + HP -= tmp; + msg += ch->get_abbrev() + " deals " + + std::to_string(tmp) + + " damage to PC. "; + + if (HP == 0) + return {result::died, + msg + "PC is slain by " + + ch->get_abbrev() + ". "}; + + return {result::hit, msg}; + } + + return {result::miss, ch->get_abbrev() + " tried to hit PC but missed. "}; +} diff --git a/src/player/goblin.h b/src/player/goblin.h new file mode 100644 index 0000000..92ed0db --- /dev/null +++ b/src/player/goblin.h @@ -0,0 +1,17 @@ +#ifndef __GOBLIN_H__ +#define __GOBLIN_H__ + +#include "../player.h" + +class goblin final: public player_base { + static const int GAIN_GOLD = 5; + constexpr static const float ORC_DMG_MUL = 1.5f; +public: + goblin(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; + long_result attack(character *ch) override; + long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate) override; +}; + +#endif diff --git a/src/player/shade.cc b/src/player/shade.cc new file mode 100644 index 0000000..3b9fffb --- /dev/null +++ b/src/player/shade.cc @@ -0,0 +1,10 @@ +#include "shade.h" + +#include "../constants.h" + +shade::shade(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, race::rshade} {} + +const char *shade::get_race_name() const { + return "Shade"; +} diff --git a/src/player/shade.h b/src/player/shade.h new file mode 100644 index 0000000..7f05edc --- /dev/null +++ b/src/player/shade.h @@ -0,0 +1,12 @@ +#ifndef __SHADE_H__ +#define __SHADE_H__ + +#include "../player.h" + +class shade final: public player_base { +public: + shade(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; +}; + +#endif diff --git a/src/player/troll.cc b/src/player/troll.cc new file mode 100644 index 0000000..aaca9b9 --- /dev/null +++ b/src/player/troll.cc @@ -0,0 +1,17 @@ +#include "troll.h" + +#include "../constants.h" + +troll::troll(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, rtroll} {} + +const char *troll::get_race_name() const { + return "Troll"; +} + +void troll::start_turn() { + ATK = STARTING_ATK[rtroll]; + DEF = STARTING_DEF[rtroll]; + base_hit_rate = {1, 1}; + HP = HP + GAIN_HP < MAX_HP[rtroll] ? HP + GAIN_HP : MAX_HP[rtroll]; +} diff --git a/src/player/troll.h b/src/player/troll.h new file mode 100644 index 0000000..2f60257 --- /dev/null +++ b/src/player/troll.h @@ -0,0 +1,14 @@ +#ifndef __TROLL_H__ +#define __TROLL_H__ + +#include "../player.h" + +class troll final: public player_base { + static const int GAIN_HP = 5; +public: + troll(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; + void start_turn() override; +}; + +#endif diff --git a/src/player/vampire.cc b/src/player/vampire.cc new file mode 100644 index 0000000..874e064 --- /dev/null +++ b/src/player/vampire.cc @@ -0,0 +1,33 @@ +#include "vampire.h" + +#include "../constants.h" + +vampire::vampire(RNG *rng, const feature enabled_features): + player_base{rng, enabled_features, race::rvampire} {}; + +const char *vampire::get_race_name() const { + return "Vampire"; +} + +long_result vampire::attack(character *ch) { + if (ch == nullptr) + return {result::fine, + "PC tried to drain thin air. "}; + + auto res = ch->get_hit(this, ATK, base_hit_rate); + + if (res.res == miss) + return {miss, res.msg + "PC is unhappy. "}; + + if (ch->get_race() == rdwarf) { + HP -= GAIN_HP; + + if (is_dead()) + return {died, ""}; + + return {hit, res.msg + "PC accidentally drained themselves. "}; + } + + HP += GAIN_HP; + return {hit, res.msg + "PC is happy. "}; +} diff --git a/src/player/vampire.h b/src/player/vampire.h new file mode 100644 index 0000000..7a9ac87 --- /dev/null +++ b/src/player/vampire.h @@ -0,0 +1,14 @@ +#ifndef __VAMPIRE_H__ +#define __VAMPIRE_H__ + +#include "../player.h" + +class vampire final: public player_base { + static const int GAIN_HP = 5; +public: + vampire(RNG *rng, const feature enabled_features); + const char *get_race_name() const override; + long_result attack(character *ch) override; +}; + +#endif diff --git a/src/position.h b/src/position.h index e468144..e05e984 100644 --- a/src/position.h +++ b/src/position.h @@ -3,28 +3,29 @@ #include typedef struct position { - int x; - int y; + int x; + int y; - position(); - position(int nx, int ny); + position(); + position(int nx, int ny); - position operator+(const position &other) const; - position &operator=(const position &other); - position &operator+=(const position &other); - bool operator==(const position &other) const; - bool operator!=(const position &other) const; - bool operator<(const position &other) const; + position operator+(const position &other) const; + position &operator=(const position &other); + position &operator+=(const position &other); + bool operator==(const position &other) const; + bool operator!=(const position &other) const; + bool operator<(const position &other) const; } position; -std::size_t find(const std::vector &sorted_list, +typedef std::vector position_list; + +std::size_t find(const position_list &sorted_list, const position &target); // requires: all elements of excluded are in sorted_positions -std::vector remove_from_list(const std::vector - &sorted_positions, - std::vector excluded); -void remove_from_list(std::vector &sorted_positions, +std::vector remove_from_list(const position_list &sorted_positions, + position_list excluded); +void remove_from_list(position_list &sorted_positions, position &excluded); float distance(const position &a, const position &b); diff --git a/src/potion.cc b/src/potion.cc index 4326a0c..3e4c67b 100644 --- a/src/potion.cc +++ b/src/potion.cc @@ -1,14 +1,16 @@ #include "potion.h" +#include "constants.h" + potion::potion(const potion_type type, const int duration, const position &pos): type{type}, remaining_duration{duration}, pos{pos} {} potion_type potion::get_type() const { - return type; + return type; } int potion::get_duration() const { - return remaining_duration; + return remaining_duration; } position potion::get_pos() const { @@ -44,3 +46,16 @@ potion &potion::operator=(potion &&p) { void potion::print(display *out) { out->print_char(pos, 'P', COLOR_PAIR(COLOR_GREEN)); } + +potion *get_potion_at(const position &pos, potion_list &plist) { + potion *ret = nullptr; + + 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; + } + + return ret; +} diff --git a/src/potion.h b/src/potion.h index 1ad618f..ec62b43 100644 --- a/src/potion.h +++ b/src/potion.h @@ -2,10 +2,12 @@ #define __POTION_H__ #include -#include "constants.h" #include "fraction.h" #include "display.h" +enum potion_type : int; +enum race : int; + // IMPORTANT: pop all potions of duration == 0, and when entering a // new level, pop all potions of duration == -1 @@ -32,10 +34,13 @@ public: int get_duration() const; position get_pos() const; void set_pos(const position &npos); + virtual const char *get_name() const = 0; virtual void print(display *out); }; typedef std::vector potion_list; +potion *get_potion_at(const position &pos, potion_list &plist); + #endif diff --git a/src/potions.cc b/src/potions.cc index 74a1c1a..4034745 100644 --- a/src/potions.cc +++ b/src/potions.cc @@ -1,12 +1,40 @@ #include "potions.h" +#include "constants.h" +#include "potions/restore_health.h" +#include "potions/boost_atk.h" +#include "potions/boost_def.h" +#include "potions/poison_health.h" +#include "potions/wound_atk.h" +#include "potions/wound_def.h" + void new_potion(std::unique_ptr &pp, potion_type type, const position &pos) { switch (type) { - case potion_type::restore_health: + case restore_health: pp = std::make_unique(pos); break; + case boost_atk: + pp = std::make_unique(pos); + break; + + case boost_def: + pp = std::make_unique(pos); + break; + + case poison_health: + pp = std::make_unique(pos); + break; + + case wound_atk: + pp = std::make_unique(pos); + break; + + case wound_def: + pp = std::make_unique(pos); + break; + default: break; } diff --git a/src/potions.h b/src/potions.h index e0a5ad6..ae83786 100644 --- a/src/potions.h +++ b/src/potions.h @@ -2,7 +2,6 @@ #define __POTIONS_H__ #include "potion.h" -#include "restore_health.h" #include #include diff --git a/src/boost_atk.cc b/src/potions/boost_atk.cc similarity index 87% rename from src/boost_atk.cc rename to src/potions/boost_atk.cc index 18da5d9..f788b25 100644 --- a/src/boost_atk.cc +++ b/src/potions/boost_atk.cc @@ -1,6 +1,7 @@ #include "boost_atk.h" #include +#include "../constants.h" const int BOOST_ATK = 5; const int BOOST_ATK_DROW = 7; @@ -23,3 +24,7 @@ void boost_atk::apply(const enum race &race, int &HP, int &ATK, int &DEF, int boost_atk::get_priority() const { return CALC_ADD_BASE; } + +const char *boost_atk::get_name() const { + return "BA"; +} diff --git a/src/boost_atk.h b/src/potions/boost_atk.h similarity index 81% rename from src/boost_atk.h rename to src/potions/boost_atk.h index bd54e9e..c13e109 100644 --- a/src/boost_atk.h +++ b/src/potions/boost_atk.h @@ -1,7 +1,7 @@ #ifndef __BOOST_ATK_H__ #define __BOOST_ATK_H__ -#include "potion.h" +#include "../potion.h" class boost_atk final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/boost_def.cc b/src/potions/boost_def.cc similarity index 87% rename from src/boost_def.cc rename to src/potions/boost_def.cc index d4b1f38..c740a1a 100644 --- a/src/boost_def.cc +++ b/src/potions/boost_def.cc @@ -1,6 +1,7 @@ #include "boost_def.h" #include +#include "../constants.h" // TODO: move into class def as static constants const int BOOST_DEF = 5; @@ -24,3 +25,7 @@ void boost_def::apply(const enum race &race, int &HP, int &ATK, int &DEF, int boost_def::get_priority() const { return CALC_ADD_BASE; } + +const char *boost_def::get_name() const { + return "BD"; +} diff --git a/src/boost_def.h b/src/potions/boost_def.h similarity index 81% rename from src/boost_def.h rename to src/potions/boost_def.h index 1a5eb74..14eca61 100644 --- a/src/boost_def.h +++ b/src/potions/boost_def.h @@ -1,7 +1,7 @@ #ifndef __BOOST_DEF_H__ #define __BOOST_DEF_H__ -#include "potion.h" +#include "../potion.h" class boost_def final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/poison_health.cc b/src/potions/poison_health.cc similarity index 86% rename from src/poison_health.cc rename to src/potions/poison_health.cc index b6f4fbb..995be0a 100644 --- a/src/poison_health.cc +++ b/src/potions/poison_health.cc @@ -1,6 +1,7 @@ #include "poison_health.h" #include +#include "../constants.h" poison_health::poison_health(const position &pos): potion{potion_type::poison_health, 1, pos} {} @@ -20,3 +21,7 @@ void poison_health::apply(const enum race &race, int &HP, int &ATK, int &DEF, int poison_health::get_priority() const { return CALC_ADD_BASE; } + +const char *poison_health::get_name() const { + return "PH"; +} diff --git a/src/poison_health.h b/src/potions/poison_health.h similarity index 82% rename from src/poison_health.h rename to src/potions/poison_health.h index f970f7c..83cdf85 100644 --- a/src/poison_health.h +++ b/src/potions/poison_health.h @@ -1,7 +1,7 @@ #ifndef __POISON_HEALTH_H__ #define __POISON_HEALTH_H__ -#include "potion.h" +#include "../potion.h" class poison_health final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/restore_health.cc b/src/potions/restore_health.cc similarity index 76% rename from src/restore_health.cc rename to src/potions/restore_health.cc index 0734331..b427891 100644 --- a/src/restore_health.cc +++ b/src/potions/restore_health.cc @@ -1,6 +1,7 @@ #include "restore_health.h" #include +#include "../constants.h" restore_health::restore_health(const position &pos): potion{potion_type::restore_health, -1, pos} {} @@ -13,10 +14,14 @@ void restore_health::apply(const enum race &race, int &HP, int &ATK, int &DEF, else HP = std::min(HP + 5, MAX_HP[race]); - --remaining_duration; - } + --remaining_duration; + } } int restore_health::get_priority() const { - return CALC_ADD_BASE; + return CALC_ADD_BASE; +} + +const char *restore_health::get_name() const { + return "RH"; } diff --git a/src/restore_health.h b/src/potions/restore_health.h similarity index 82% rename from src/restore_health.h rename to src/potions/restore_health.h index 4f00a75..d4670f1 100644 --- a/src/restore_health.h +++ b/src/potions/restore_health.h @@ -1,7 +1,7 @@ #ifndef __RESTORE_HEALTH_H__ #define __RESTORE_HEALTH_H__ -#include "potion.h" +#include "../potion.h" class restore_health final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/wound_atk.cc b/src/potions/wound_atk.cc similarity index 87% rename from src/wound_atk.cc rename to src/potions/wound_atk.cc index e30ffd2..9ed62c5 100644 --- a/src/wound_atk.cc +++ b/src/potions/wound_atk.cc @@ -1,6 +1,7 @@ #include "wound_atk.h" #include +#include "../constants.h" const int WOUND_ATK = 5; const int WOUND_ATK_DROW = 7; @@ -23,3 +24,7 @@ void wound_atk::apply(const enum race &race, int &HP, int &ATK, int &DEF, int wound_atk::get_priority() const { return CALC_ADD_BASE; } + +const char *wound_atk::get_name() const { + return "WA"; +} diff --git a/src/wound_atk.h b/src/potions/wound_atk.h similarity index 81% rename from src/wound_atk.h rename to src/potions/wound_atk.h index 1d95411..a8ea133 100644 --- a/src/wound_atk.h +++ b/src/potions/wound_atk.h @@ -1,7 +1,7 @@ #ifndef __WOUND_ATK_H__ #define __WOUND_ATK_H__ -#include "potion.h" +#include "../potion.h" class wound_atk final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/wound_def.cc b/src/potions/wound_def.cc similarity index 87% rename from src/wound_def.cc rename to src/potions/wound_def.cc index 1c3561f..ebfdabf 100644 --- a/src/wound_def.cc +++ b/src/potions/wound_def.cc @@ -1,6 +1,7 @@ #include "wound_def.h" #include +#include "../constants.h" const int WOUND_DEF = 5; const int WOUND_DEF_DROW = 7; @@ -23,3 +24,7 @@ void wound_def::apply(const enum race &race, int &HP, int &ATK, int &DEF, int wound_def::get_priority() const { return CALC_ADD_BASE; } + +const char *wound_def::get_name() const { + return "WD"; +} diff --git a/src/wound_def.h b/src/potions/wound_def.h similarity index 81% rename from src/wound_def.h rename to src/potions/wound_def.h index 62cd528..a4d8564 100644 --- a/src/wound_def.h +++ b/src/potions/wound_def.h @@ -1,7 +1,7 @@ #ifndef __WOUND_DEF_H__ #define __WOUND_DEF_H__ -#include "potion.h" +#include "../potion.h" class wound_def final: public potion { public: @@ -9,6 +9,7 @@ public: void apply(const enum race &race, int &HP, int &ATK, int &DEF, fraction &base_hit_rate) override; int get_priority() const override; + const char *get_name() const override; }; #endif diff --git a/src/races.h b/src/races.h deleted file mode 100644 index 55b5a8a..0000000 --- a/src/races.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __RACES_H__ -#define __RACES_H__ - -#include "shade.h" -#include "vampire.h" -#include "troll.h" -#include "goblin.h" -#include "dragon.h" - -#endif diff --git a/src/shade.cc b/src/shade.cc deleted file mode 100644 index d639c6b..0000000 --- a/src/shade.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "shade.h" - -shade::shade(RNG *rng, const position &pos): - character{rng, race::rshade, pos} { - gold = 0; -} - -character::attack_result shade::attack(const direction dir, - character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - auto res = ch->get_hit(race, ATK, base_hit_rate); - return {res.res, res.dmg_dealt, res.remaining_HP, ch}; - } - - return {fine, 0, 0, nullptr}; -} - -character::attack_result shade::attack(const direction dir, character *ch) { - position tmp{pos + MOVE[dir]}; - - if (tmp == ch->get_position()) { - auto res = ch->get_hit(race, ATK, base_hit_rate); - return {res.res, res.dmg_dealt, res.remaining_HP, ch}; - } - - return {fine, 0, 0, nullptr}; -} - -character::hit_result shade::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) { - int tmp = calc_dmg(atk, DEF); - - if (tmp > HP) - tmp = HP; - - HP -= tmp; - - if (HP == 0) - return {result::died, tmp, HP}; - - return {result::hit, tmp, HP}; - } - - return {result::miss, 0, HP}; -} diff --git a/src/shade.h b/src/shade.h deleted file mode 100644 index 94636d4..0000000 --- a/src/shade.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __SHADE_H__ -#define __SHADE_H__ - -#include "characters.h" - -class shade final: public character { -public: - shade(RNG *rng, const position &pos); // spawn at a random place - virtual attack_result attack(const direction dir, - character_list &chlist) override; - virtual hit_result get_hit(const enum race &race, const int atk, - const fraction hit_rate) override; - virtual attack_result attack(const direction dir, character *ch) override; -}; - -#endif diff --git a/src/troll.cc b/src/troll.cc deleted file mode 100644 index 61a5b22..0000000 --- a/src/troll.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include "troll.h" - -troll::troll(RNG *rng, const position &pos): - character{rng, race::rtroll, pos} { - gold = 0; - hostile = true; -} - -result troll::attack(const direction dir, character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - if (HP + REGAIN_HP > HP_CAP) { - HP = HP_CAP; - } else { - HP += REGAIN_HP; - } - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - auto res = ch->get_hit(race, ATK, base_hit_rate); - return res; - } - - return result::fine; -} - -result troll::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) - HP = std::max(HP - calc_dmg(atk, DEF), 0); - - if (HP == 0) - return result::died; - - return result::hit; -} diff --git a/src/troll.h b/src/troll.h deleted file mode 100644 index d144db3..0000000 --- a/src/troll.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __TROLL_H__ -#define __TROLL_H__ - -#include "characters.h" - -class troll final: public character { - static const int REGAIN_HP = 5; - static const int HP_CAP = 120; -public: - troll(RNG *rng, const position &pos); - virtual result attack(const direction dir, - character_list &chlist) override; - virtual result get_hit(const enum race &race, const int afk, - const fraction hit_rate) override; - -}; - -#endif diff --git a/src/vampire.cc b/src/vampire.cc deleted file mode 100644 index 208a405..0000000 --- a/src/vampire.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "vampire.h" - -vampire::vampire(RNG *rng, const position &pos): - character{rng, race::rvampire, pos} { - gold = 0; - hostile = true; -} - -result vampire::attack(const direction dir, character_list &chlist) { - position tmp{pos + MOVE[dir]}; - - for (auto &ch : chlist) - if (tmp == ch->get_position()) { - auto res = ch->get_hit(race, ATK, base_hit_rate); - - if (res != result::miss) { - HP += GAIN_HP; - } - - return res; - } - - return result::fine; -} - -result vampire::get_hit(const enum race &race, const int atk, - const fraction hitrate) { - if (rng->trial(hitrate)) - HP = std::max(HP - calc_dmg(atk, DEF), 0); - - if (HP == 0) - return result::died; - - return result::hit; -} diff --git a/src/vampire.h b/src/vampire.h deleted file mode 100644 index b7ccf6c..0000000 --- a/src/vampire.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __VAMPIRE_H__ -#define __VAMPIRE_H__ - -#include "characters.h" - -class vampire final: public character { - static const int GAIN_HP = 5; -public: - vampire(RNG *rng, const position &pos); - virtual result attack(const direction dir, - character_list &chlist) override; - virtual result get_hit(const enum race &race, const int atk, - const fraction hit_rate) override; -}; - -#endif