diff --git a/src/arguments.cc b/src/arguments.cc index f9d27f1..bcfe2f2 100644 --- a/src/arguments.cc +++ b/src/arguments.cc @@ -49,6 +49,8 @@ feature proc_args(int argc, char **argv, result |= FEATURE_REVISIT; } else if (str == "-e") { result |= FEATURE_EXTRA_STUFF; + } else if (str == "-E") { + result |= FEATURE_EXTRA_LEVELS; } else if (str == "-s") { ++i; str = argv[i]; @@ -168,6 +170,7 @@ void panic_args(feature panic) { default: cerr << "Something must have went really, really, wrong..." << endl; + break; } } else if (panic & FEATURE_PANIC_SEED) { cerr << "Invalid seed" << endl; @@ -175,3 +178,23 @@ void panic_args(feature panic) { cerr << "Something must have went really, really, wrong..." << endl; } + +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\ +-i : Enable inventory\n\ +-t : Enable throw\n\ +-R : Enable revisiting levels\n\ +-e : Enable extra potions and races\n\ +-E : Enable extra levels\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)"; + std::cout << ARGS_LIST << std::endl; +} diff --git a/src/arguments.h b/src/arguments.h index 0c0d088..76ef406 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -10,22 +10,6 @@ #include "constants.h" -/* Arguments --n : Use ncurses for I/O --r : Randomly generate maps --m : Enabled a main menu + options --c : Enemies chase the player (through doors and up stairs) --i : Enable inventory --t : Enable throw --R : Enable revisiting levels --e : Enable extra potions and races --s [seed] : Sets initial seed to seed --L [file] : Enable logging to file --I [file] : Reads commands from file. CANNOT BE USED WITH -n. --O [file] : Outputs to file. CANNOT BE USED WITH -n. --h/--help : Displays options list (doesn't start a game) - */ - // IMPORTANT: Errors include the index that caused them (or'ed into them) feature proc_args(int argc, char **argv, std::unique_ptr &curse, @@ -36,4 +20,6 @@ feature proc_args(int argc, char **argv, void panic_args(feature panic); +void print_args_list(); + #endif diff --git a/src/characters.cc b/src/characters.cc index 13602db..704a058 100644 --- a/src/characters.cc +++ b/src/characters.cc @@ -2,10 +2,17 @@ #include -character::character(RNG &rng, const enum race &nrace): +character::character(RNG *rng, const enum race &nrace): rng{rng}, race{nrace}, HP{STARTING_HP[race]}, - ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]} {} + ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]}, + base_hit_rate{1, 1} {} + +void character::start_turn() { + ATK = STARTING_ATK[race]; + DEF = STARTING_DEF[race]; + base_hit_rate = {1, 1}; +} enum race character::get_race() const { return race; @@ -31,7 +38,11 @@ int character::get_gold() const { return gold; } -float character::get_hitrate() const { +float character::get_hitrate_real() const { + return base_hit_rate.real(); +} + +fraction character::get_hitrate() const { return base_hit_rate; } @@ -59,7 +70,7 @@ void character::set_gold(const int ngold) { gold = ngold; } -void character::set_hitrate(const float nhitrate) { +void character::set_hitrate(const fraction nhitrate) { base_hit_rate = nhitrate; } @@ -105,11 +116,57 @@ const { return result; } -result character::apply(direction &dir, const potion_list &potions) { - // TODO: implement this after implementing potions +void character::apply(direction &dir, potion_list &plist) { + for (size_t i = 0; i < plist.size(); ++i) + if (pos + MOVE[dir] == plist[i]->get_pos()) { + insert_potion(plist[i]); + plist.erase(plist.begin() + i); + return; + } +} + +void character::insert_potion(potion *p) { + effects.push_back(p); + + for (int i = effects.size() - 1; i > 0; --i) + if (p->get_priority() < effects[i - 1]->get_priority()) + std::swap(effects[i], effects[i - 1]); +} + +result character::apply_effects() { + potion_list tmp; + tmp.reserve(effects.size()); + + for (auto p : effects) { + p->apply(this->race, HP, ATK, DEF, base_hit_rate); + + if (HP <= 0) + return result::died; + + if (p->get_duration() != 0) + tmp.push_back(p); + } + + tmp.shrink_to_fit(); + + std::swap(tmp, effects); + return result::fine; } +void character::discard_level_effects() { + potion_list tmp; + tmp.reserve(effects.size()); + + for (auto p : effects) + if (p->get_duration() != -1) + tmp.push_back(p); + + tmp.shrink_to_fit(); + + std::swap(tmp, effects); +} + // IMPORTANT: remember to check if player is on the stairs result character::move(const direction dir, const position_list &available_positions) { diff --git a/src/characters.h b/src/characters.h index 9d8ac68..e23719f 100644 --- a/src/characters.h +++ b/src/characters.h @@ -6,13 +6,12 @@ #include #include "constants.h" +#include "fraction.h" #include "position.h" -#include "potion.h" +#include "potions.h" #include "gold.h" #include "rng.h" -// #include "inventory.h" // Reserved for later - class character; // forward declaration // Note: player should not be in the character list @@ -20,7 +19,7 @@ typedef std::vector character_list; class character { public: - character(RNG &rng, const race &nrace); // fills out the starting stats + character(RNG *rng, const race &nrace); // 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 @@ -33,10 +32,13 @@ public: virtual result move_or_attack(const direction dir, const position_list &available_positions, character_list &chlist); - virtual result apply(direction &dir, - const potion_list &potions); + virtual void apply(direction &dir, + potion_list &potions); virtual result get_hit(const enum race &race, const int atk, - const float hitrate) = 0; + const fraction hitrate) = 0; + result apply_effects(); + void discard_level_effects(); + void start_turn(); enum race get_race() const; position get_position() const; @@ -44,7 +46,8 @@ public: int get_ATK() const; int get_DEF() const; int get_gold() const; - float get_hitrate() const; + float get_hitrate_real() const; + fraction get_hitrate() const; bool is_hostile() const; void set_position(const position &npos); @@ -52,7 +55,7 @@ public: void set_ATK(const int nATK); void set_DEF(const int nDEF); void set_gold(const int ngold); - void set_hitrate(const float nhitrate); + void set_hitrate(const fraction nhitrate); void set_hostile(const bool is_hostile); // if stat is hostile, positive to set it to hostile, @@ -61,7 +64,7 @@ public: // void apply_buff(const stat_name statn, const float mul); // reserved for later protected: - RNG &rng; + RNG *rng; const enum race race; int HP; @@ -69,15 +72,17 @@ protected: // IMPORTANT: keep track of ATK and DEF in game at turn time int ATK; int DEF; + fraction base_hit_rate; position pos; int gold; // characters spawn with gold - potion_list potions;// inventory inventory; // Reserved - - float base_hit_rate; // requires: between [0,1] + potion_list potions; // inventory + potion_list effects; // applied potions bool hostile; +private: + void insert_potion(potion *p); }; int calc_dmg(const int ATK, const int DEF); diff --git a/src/constants.h b/src/constants.h index 7161acb..25590e8 100644 --- a/src/constants.h +++ b/src/constants.h @@ -93,6 +93,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_PANIC_SEED = 1 << 27; const feature FEATURE_PANIC_FILE = 1 << 28; diff --git a/src/fraction.cc b/src/fraction.cc index e7c1b57..c31f061 100644 --- a/src/fraction.cc +++ b/src/fraction.cc @@ -32,7 +32,7 @@ fraction &fraction::simplify() { return *this; } -float fraction::real() { +float fraction::real() const { return (float)numerator / denominator; } diff --git a/src/fraction.h b/src/fraction.h index 59cd82c..5c5a61e 100644 --- a/src/fraction.h +++ b/src/fraction.h @@ -10,7 +10,7 @@ struct fraction final { bool operator==(const fraction &frac); bool operator!=(const fraction &frac); fraction &simplify(); - float real(); + float real() const; private: int gcd(int a, int b); }; diff --git a/src/goblin.cc b/src/goblin.cc index 5431227..3155232 100644 --- a/src/goblin.cc +++ b/src/goblin.cc @@ -2,9 +2,9 @@ #include #include -goblin::goblin(RNG &rng, const position_list &available_positions): +goblin::goblin(RNG *rng, const position_list &available_positions): character{rng, race::rshade} { - pos = available_positions[rng.rand_under(available_positions.size())]; + pos = available_positions[rng->rand_under(available_positions.size())]; gold = 0; hostile = true; } @@ -27,8 +27,8 @@ result goblin::attack(const direction dir, character_list &chlist) { } result goblin::get_hit(const enum race &race, const int atk, - const float hitrate) { - if (rng.rand_num() <= hitrate * (float)RAND_MAX) + const fraction hitrate) { + if (rng->trial(hitrate)) HP = std::max(HP - calc_dmg(atk, DEF), 0); if (HP == 0) diff --git a/src/goblin.h b/src/goblin.h index 9f85e84..c416a4f 100644 --- a/src/goblin.h +++ b/src/goblin.h @@ -6,12 +6,12 @@ const int GAIN_GOLD = 5; class goblin final: public character { public: - goblin(RNG &rng, + goblin(RNG *rng, const position_list &available_positions); virtual result attack(const direction dir, character_list &chlist) override; virtual result get_hit(const enum race &race, const int atk, - const float hit_rate) override; + const fraction hit_rate) override; }; #endif diff --git a/src/main.cc b/src/main.cc index ab2e35a..f33033e 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,3 +1,5 @@ +#include + #include "cc3k.h" #include "arguments.h" @@ -17,6 +19,9 @@ int main(int argc, char **argv) { FEATURE_CONFLICT | FEATURE_PANIC_SEED)) { panic_args(enabled_features); return RETURN_PANICKED; + } else if (enabled_features & FEATURE_LIST_ARGS) { + print_args_list(); + return RETURN_FINE; } // CC3K game_proc(enabled_features, *in, *out, *log, *rng); diff --git a/src/map.cc b/src/map.cc index 0977f92..6401513 100644 --- a/src/map.cc +++ b/src/map.cc @@ -87,16 +87,25 @@ game_map::game_map(const std::string &map_data, room_data.push_back({0, 0, 0, 0}); } -const position_list game_map::get_available_positions() const { - return empty_spots; -} - bool game_map::is_available(const position &pos) const { + if (pos.x < 0 || pos.x >= MAP_WIDTH || pos.y < 0 || pos.y >= MAP_HEIGHT) + return false; + int idx = remap_position(pos); return map[idx] < MAX_ROOM_CNT || map[idx] == '\\' || map[idx] == '>' || map[idx] == '<' || map[idx] == '+' || map[idx] == '#'; } +position_list game_map::get_available_around(const position &pos) const { + position_list result; + + for (int i = 0; i < DIRECTION_CNT; ++i) + if (is_available(pos + MOVE[i])) + result.push_back(pos + MOVE[i]); + + return result; +} + void game_map::print(display *out) const { for (std::size_t i = 0; i < map.size(); ++i) out->print_char(remap_index(i), map[i] <= 9 ? '.' : map[i], @@ -132,6 +141,20 @@ position_list game_map::get_room_list(int idx) const { return result; } +std::vector game_map::get_room_list() const { + std::vector result; + result.reserve(room_data.size()); + + for (size_t i = 0; i < room_data.size(); ++i) + result.push_back({}); + + for (size_t i = 0; i < map.size(); ++i) + if (map[i] >= 0 && map[i] < MAX_ROOM_CNT) + result[map[i]].push_back(remap_index(i)); + + return result; +} + std::vector> game_map::gen_room_dims(RNG *rng) { position_list room_dim_list; room_dim_list.reserve(MAX_ROOM_CNT); diff --git a/src/map.h b/src/map.h index ee55ad5..f96b529 100644 --- a/src/map.h +++ b/src/map.h @@ -56,10 +56,11 @@ public: game_map(const std::string &map_data, RNG *rng, const feature enabled_features); - const position_list get_available_positions() const; bool is_available(const position &pos) const; + position_list get_available_around(const position &pos) const; position_list get_room_list(int idx) const; + std::vector get_room_list() const; // IMPORTANT: always print a map before anything else void print(display *out) const; diff --git a/src/potion.cc b/src/potion.cc index 2f9fab6..b2e5e36 100644 --- a/src/potion.cc +++ b/src/potion.cc @@ -1,7 +1,7 @@ #include "potion.h" -potion::potion(const potion_type type, const int duration): - type{type}, remaining_duration{duration} {} +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; @@ -10,3 +10,33 @@ potion_type potion::get_type() const { int potion::get_duration() const { return remaining_duration; } + +position potion::get_pos() const { + return pos; +} + +void potion::set_pos(const position &npos) { + pos = npos; +} + +potion::potion(const potion &p): + type(p.type), remaining_duration(p.remaining_duration), + pos{p.pos} {} + +potion::potion(potion &&p): + type(p.type), remaining_duration(p.remaining_duration), + pos{p.pos} {} + +potion &potion::operator=(const potion &p) { + type = p.type; + remaining_duration = p.remaining_duration; + pos = p.pos; + return *this; +} + +potion &potion::operator=(potion &&p) { + type = p.type; + remaining_duration = p.remaining_duration; + pos = p.pos; + return *this; +} diff --git a/src/potion.h b/src/potion.h index 00396bb..64d6786 100644 --- a/src/potion.h +++ b/src/potion.h @@ -1,8 +1,9 @@ -#ifndef __POTIONS_H__ -#define __POTIONS_H__ +#ifndef __POTION_H__ +#define __POTION_H__ #include #include "constants.h" +#include "fraction.h" // IMPORTANT: pop all potions of duration == 0, and when entering a // new level, pop all potions of duration == -1 @@ -12,19 +13,26 @@ protected: // Use -1 to denote that the effect will last until leaving the level // Otherwise, use a positive number // Single use potions have a starting duration of 1 - const potion_type type; + potion_type type; int remaining_duration; + position pos; public: - potion(const potion_type type, const int duration); + potion(const potion &p); + potion(potion &&p); + potion &operator=(const potion &p); + potion &operator=(potion &&p); + potion(const potion_type type, const int duration, const position &pos); // apply decrements remaining_duration if it's positive, and // won't do anything if it's non-negative - virtual void apply(enum race &race, int &HP, int &ATK, int &DEF, - float &base_hit_rate) = 0; + virtual void apply(const enum race &race, int &HP, int &ATK, int &DEF, + fraction &base_hit_rate) = 0; virtual int get_priority() const = 0; potion_type get_type() const; int get_duration() const; + position get_pos() const; + void set_pos(const position &npos); }; -typedef std::vector potion_list; +typedef std::vector potion_list; #endif diff --git a/src/restore_health.cc b/src/restore_health.cc index 6d725c0..b1fa352 100644 --- a/src/restore_health.cc +++ b/src/restore_health.cc @@ -3,10 +3,10 @@ #include restore_health::restore_health(): - potion{potion_type::restore_health, -1} {} + potion{potion_type::restore_health, -1, {0, 0}} {} -void restore_health::apply(enum race &race, int &HP, int &ATK, int &DEF, - float &base_hit_rate) { +void restore_health::apply(const enum race &race, int &HP, int &ATK, int &DEF, + fraction &base_hit_rate) { if (remaining_duration > 0) { if (race == rdrow) HP = std::min(HP + 7, MAX_HP[race]); diff --git a/src/restore_health.h b/src/restore_health.h index 1e7a594..30916af 100644 --- a/src/restore_health.h +++ b/src/restore_health.h @@ -6,8 +6,8 @@ class restore_health final: public potion { public: restore_health(); - void apply(enum race &race, int &HP, int &ATK, int &DEF, - float &base_hit_rate) override; + void apply(const enum race &race, int &HP, int &ATK, int &DEF, + fraction &base_hit_rate) override; int get_priority() const override; }; diff --git a/src/rng.cc b/src/rng.cc index ee6e224..07a0105 100644 --- a/src/rng.cc +++ b/src/rng.cc @@ -32,7 +32,7 @@ int RNG::get_curr_rand_num() const { return curr_rand_num; } -bool RNG::trial(fraction &psuccess) { +bool RNG::trial(const fraction &psuccess) { return (rand() % psuccess.denominator) < psuccess.numerator; } diff --git a/src/rng.h b/src/rng.h index a1fac0b..4283770 100644 --- a/src/rng.h +++ b/src/rng.h @@ -26,7 +26,7 @@ public: unsigned int get_init_seed() const; int get_curr_rand_num() const; - bool trial(fraction &psuccess); + bool trial(const fraction &psuccess); bool coin_flip() const; diff --git a/src/shade.cc b/src/shade.cc index 1e915fe..38bbe96 100644 --- a/src/shade.cc +++ b/src/shade.cc @@ -3,9 +3,9 @@ #include #include -shade::shade(RNG &rng, const position_list &available_positions): +shade::shade(RNG *rng, const position_list &available_positions): character{rng, race::rshade} { - pos = available_positions[rng.rand_under(available_positions.size())]; + pos = available_positions[rng->rand_under(available_positions.size())]; gold = 0; hostile = true; } @@ -22,8 +22,8 @@ result shade::attack(const direction dir, character_list &chlist) { } result shade::get_hit(const enum race &race, const int atk, - const float hitrate) { - if (rng.rand_num() <= hitrate * (float)RAND_MAX) // This is a hit! + const fraction hitrate) { + if (rng->trial(hitrate)) // This is a hit! HP = std::max(HP - calc_dmg(atk, DEF), 0); if (HP == 0) diff --git a/src/shade.h b/src/shade.h index 838d437..9848045 100644 --- a/src/shade.h +++ b/src/shade.h @@ -5,12 +5,12 @@ class shade final: public character { public: - shade(RNG &rng, const position_list + shade(RNG *rng, const position_list &available_positions); // spawn at a random place virtual result attack(const direction dir, character_list &chlist) override; virtual result get_hit(const enum race &race, const int atk, - const float hit_rate) override; + const fraction hit_rate) override; }; #endif diff --git a/src/vampire.cc b/src/vampire.cc index 2d5657f..2528977 100644 --- a/src/vampire.cc +++ b/src/vampire.cc @@ -2,9 +2,9 @@ #include #include -vampire::vampire(RNG &rng, const position_list &available_positions): +vampire::vampire(RNG *rng, const position_list &available_positions): character{rng, race::rshade} { - pos = available_positions[rng.rand_under(available_positions.size())]; + pos = available_positions[rng->rand_under(available_positions.size())]; gold = 0; hostile = true; } @@ -27,8 +27,8 @@ result vampire::attack(const direction dir, character_list &chlist) { } result vampire::get_hit(const enum race &race, const int atk, - const float hitrate) { - if (rng.rand_num() <= hitrate * (float)RAND_MAX) + const fraction hitrate) { + if (rng->trial(hitrate)) HP = std::max(HP - calc_dmg(atk, DEF), 0); if (HP == 0) diff --git a/src/vampire.h b/src/vampire.h index 04a89de..b74900c 100644 --- a/src/vampire.h +++ b/src/vampire.h @@ -6,12 +6,12 @@ const int GAIN_HP = 5; class vampire final: public character { public: - vampire(RNG &rng, + vampire(RNG *rng, const position_list &available_positions); virtual result attack(const direction dir, character_list &chlist) override; virtual result get_hit(const enum race &race, const int atk, - const float hit_rate) override; + const fraction hit_rate) override; }; #endif