diff --git a/src/constants.h b/src/constants.h index ef45771..d98f396 100644 --- a/src/constants.h +++ b/src/constants.h @@ -58,7 +58,7 @@ enum race : int {rshade = 0, rdrow, rvampire, rtroll, static const char CHAR_REP[RACE_CNT] = { 's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L', 't', 'g', 'm', 'b', 'a', - 'V', 'S', 'l', 'W', 'h', 'B' + 'V', 'S', 'l', 'Z', 'h', 'B' }; static const int MAX_HP[RACE_CNT] = { @@ -68,13 +68,13 @@ static const int MAX_HP[RACE_CNT] = { }; static const int STARTING_HP[RACE_CNT] = { 125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100, - 800, 130, 150, 120, 100, + 800, 130, 100, 120, 100, 150, 100, 60, 100, 90, 140 }; static const int STARTING_ATK[RACE_CNT] = { 25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15, - 40, 25, 80, 15, 30, - 30, 25, 10, 20, 15, 25 + 40, 25, 70, 15, 30, + 30, 25, 10, 20, 15, 20 }; static const int STARTING_DEF[RACE_CNT] = { 25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20, diff --git a/src/enemies.cc b/src/enemies.cc index 0ed2561..7b9c036 100644 --- a/src/enemies.cc +++ b/src/enemies.cc @@ -8,6 +8,12 @@ #include "enemies/human.h" #include "enemies/merchant.h" #include "enemies/orc.h" +#include "enemies/viking.h" +#include "enemies/swordsman.h" +#include "enemies/leprechaun.h" +#include "enemies/witch.h" +#include "enemies/hacker.h" +#include "enemies/baby_dragon.h" std::unique_ptr new_dragon(RNG *rng, const position &pos, const position &fallback, @@ -23,9 +29,10 @@ std::unique_ptr new_dragon(RNG *rng, const position &pos, fallback, which_room); } -const int EXCNT = 6; +const int EXCNT = 12; const race EXCHOICES[EXCNT] = { - rhuman, rdwarf, rhalfling, relf, rorc, rmerchant + rhuman, rdwarf, rhalfling, relf, rorc, rmerchant, + rviking, rswordsman, rleprechaun, rwitch, rhacker, rbaby_dragon }; const int CNT = 18; const race CHOICES[CNT] = { @@ -61,32 +68,50 @@ std::unique_ptr new_enemy(RNG *rng, const position &pos, case rdwarf: return make_unique(rng, enabled_features, pos, which_room); - break; case rhuman: return make_unique(rng, enabled_features, pos, which_room); - break; case relf: return make_unique(rng, enabled_features, pos, which_room); - break; case rorc: return make_unique(rng, enabled_features, pos, which_room); - break; case rmerchant: return make_unique(rng, enabled_features, pos, which_room); - break; case rhalfling: return make_unique(rng, enabled_features, pos, which_room); - break; + + case rviking: + return make_unique(rng, enabled_features, + pos, which_room); + + case rswordsman: + return make_unique(rng, enabled_features, + pos, which_room); + + case rleprechaun: + return make_unique(rng, enabled_features, + pos, which_room); + + case rwitch: + return make_unique(rng, enabled_features, + pos, which_room); + + case rhacker: + return make_unique(rng, enabled_features, + pos, which_room); + + case rbaby_dragon: + return make_unique(rng, enabled_features, + pos, which_room); default: break; diff --git a/src/enemies/baby_dragon.cc b/src/enemies/baby_dragon.cc new file mode 100644 index 0000000..b121f47 --- /dev/null +++ b/src/enemies/baby_dragon.cc @@ -0,0 +1,15 @@ +#include "baby_dragon.h" + +#include "../constants.h" + +baby_dragon::baby_dragon(RNG *rng, const feature enabled_features, + const position &pos, const int gen_room_num): + enemy_base{rng, enabled_features, rbaby_dragon, pos, gen_room_num, "B"} {} + +const char *baby_dragon::get_race_name() const { + return "Baby_Dragon"; +} + +void baby_dragon::apply_effect(std::unique_ptr effect) { + return; +} diff --git a/src/enemies/baby_dragon.h b/src/enemies/baby_dragon.h new file mode 100644 index 0000000..f582f4d --- /dev/null +++ b/src/enemies/baby_dragon.h @@ -0,0 +1,14 @@ +#ifndef __BABY_DRAGON_H__ +#define __BABY_DRAGON_H__ + +#include "../enemy.h" + +class baby_dragon final: public enemy_base { +public: + baby_dragon(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + const char *get_race_name() const override; + void apply_effect(std::unique_ptr effect) override; +}; + +#endif diff --git a/src/enemies/hacker.cc b/src/enemies/hacker.cc new file mode 100644 index 0000000..d4ba6a4 --- /dev/null +++ b/src/enemies/hacker.cc @@ -0,0 +1,40 @@ +#include "hacker.h" + +#include "../constants.h" + +hacker::hacker(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rhacker, pos, gen_room_num, "h"} {} + +const char *hacker::get_race_name() const { + return "Hacker"; +} + +long_result hacker::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, rand_str()}; + + return {result::hit, rand_str()}; + } + + return {miss, rand_str()}; +} + +std::string hacker::rand_str() { + size_t len = rng->rand_between(RAND_STR_LEN_MIN, RAND_STR_LEN_MAX); + std::string result{""}; + + for (size_t i = 0; i < len; ++i) + result += static_cast(rng->rand_between(PRINT_CHAR_MIN, + PRINT_CHAR_MAX + 1)); + + result += ' '; + + return result; +} diff --git a/src/enemies/hacker.h b/src/enemies/hacker.h new file mode 100644 index 0000000..3ea590f --- /dev/null +++ b/src/enemies/hacker.h @@ -0,0 +1,20 @@ +#ifndef __HACKER_H__ +#define __HACKER_H__ + +#include "../enemy.h" + +class hacker final: public enemy_base { + static const int RAND_STR_LEN_MIN = 10; + static const int RAND_STR_LEN_MAX = 30; + static const int PRINT_CHAR_MIN = 33; + static const int PRINT_CHAR_MAX = 126; + std::string rand_str(); +public: + hacker(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + const char *get_race_name() const override; + long_result get_hit(character *ch, const int tATK, + const fraction &hit_rate) override; +}; + +#endif diff --git a/src/enemies/halfling.cc b/src/enemies/halfling.cc index 47d74d2..f757e8d 100644 --- a/src/enemies/halfling.cc +++ b/src/enemies/halfling.cc @@ -25,11 +25,13 @@ long_result halfling::get_hit(character *ch, const int tATK, if (HP == 0) return {result::hit, "PC deals " + std::to_string(tmp) + - " damage to " + abbrev + ". " + abbrev + - " is slain by PC. "}; + " damage to " + abbrev + " (" + + std::to_string(HP) + " HP). " + + abbrev + " is slain by PC. "}; return {result::hit, "PC deals " + - std::to_string(tmp) + " damage to " + abbrev + ". "}; + std::to_string(tmp) + " damage to " + abbrev + " (" + + std::to_string(HP) + " HP). "}; } return {miss, "PC tried to hit " + abbrev + diff --git a/src/enemies/leprechaun.cc b/src/enemies/leprechaun.cc index e82dbba..552d7e1 100644 --- a/src/enemies/leprechaun.cc +++ b/src/enemies/leprechaun.cc @@ -2,11 +2,13 @@ #include "../constants.h" #include "../player.h" +#include "../level.h" leprechaun::leprechaun(RNG *rng, const feature enabled_features, const position &pos, const int gen_room_num): - enemy_base{rng, enabled_features, rleprechaun, pos, gen_room_num, "l"} {} + enemy_base{rng, enabled_features, rleprechaun, pos, gen_room_num, "l"}, + gold_cnt{0} {} const char *leprechaun::get_race_name() const { return "Leprechaun"; @@ -25,8 +27,14 @@ long_result leprechaun::attack(character *ch) { res.msg += "l steals " + std::to_string(STEAL_GOLD) + " pieces of gold from PC. "; static_cast(ch)->add_gold(-STEAL_GOLD); + gold_cnt += STEAL_GOLD; } } return res; } + +int leprechaun::dies(level *lvl) { + lvl->add_gold(gold{pos, gold_cnt}); + return 0; +} diff --git a/src/enemies/leprechaun.h b/src/enemies/leprechaun.h index c102a15..e3fc694 100644 --- a/src/enemies/leprechaun.h +++ b/src/enemies/leprechaun.h @@ -12,7 +12,8 @@ public: leprechaun(RNG *rng, const feature enabled_features, const position &pos, const int gen_room_num); const char *get_race_name() const override; - virtual long_result attack(character *ch) override; + long_result attack(character *ch) override; + int dies(level *lvl) override; }; #endif diff --git a/src/enemies/witch.cc b/src/enemies/witch.cc new file mode 100644 index 0000000..c501133 --- /dev/null +++ b/src/enemies/witch.cc @@ -0,0 +1,27 @@ +#include "witch.h" + +#include "../constants.h" + +witch::witch(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num): + enemy_base{rng, enabled_features, rwitch, pos, gen_room_num, "Z"} {} + +const char *witch::get_race_name() const { + return "Witch"; +} + +long_result witch::attack(character *ch) { + auto res = ch->get_hit(this, ATK, base_hit_rate); + + if (res.res == hit && rng->trial(POTION_RATE)) { + res.msg += "W's potion spilled onto PC. "; + potion_type type = + static_cast(rng->rand_under( + (enabled_features & FEATURE_EXTRA_STUFF) ? + POTION_TYPE_CNT : + DEFAULT_POTION_TYPE_CNT)); + ch->apply_effect(new_potion(type, {0, 0})); + } + + return res; +} diff --git a/src/enemies/witch.h b/src/enemies/witch.h new file mode 100644 index 0000000..062478e --- /dev/null +++ b/src/enemies/witch.h @@ -0,0 +1,15 @@ +#ifndef __WITCH_H__ +#define __WITCH_H__ + +#include "../enemy.h" + +class witch final: public enemy_base { + constexpr static const fraction POTION_RATE = {1, 5}; +public: + witch(RNG *rng, const feature enabled_features, const position &pos, + const int gen_room_num); + const char *get_race_name() const override; + long_result attack(character *ch) override; +}; + +#endif diff --git a/src/enemy.cc b/src/enemy.cc index 5ca39f8..29df745 100644 --- a/src/enemy.cc +++ b/src/enemy.cc @@ -75,8 +75,7 @@ long_result enemy_base::get_hit(character *ch, const int tATK, "PC deals " + std::to_string(tmp) + " damage to " + abbrev + " (" + std::to_string(HP) + " HP). " + - abbrev + - " is slain by PC. "}; + abbrev + " is slain by PC. "}; return {result::hit, "PC deals " + std::to_string(tmp) + " damage to " + abbrev + " (" + diff --git a/src/game.cc b/src/game.cc index ae7b23f..92cf0bd 100644 --- a/src/game.cc +++ b/src/game.cc @@ -92,7 +92,7 @@ game_result game::run() { in->get_command()); msg = res.msg; - if (!hostile_merchants && res.msg.find('M') != std::string::npos) { + if (!hostile_merchants && res.msg.find(" M ") != std::string::npos) { hostile_merchants = true; msg += "PC has angered the merchants. "; } diff --git a/src/player.cc b/src/player.cc index 3af4cbf..64a5e64 100644 --- a/src/player.cc +++ b/src/player.cc @@ -82,6 +82,8 @@ long_result player_base::move(level *lvl, res.msg += "PC gains " + std::to_string(g) + " pieces of gold. "; + + gold_cnt += g; } return res; @@ -180,13 +182,17 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { } else if (cmd >= move_north && cmd <= move_southwest) { auto res = move(lvl, pos + MOVE[cmd - move_north]); - gold g = get_gold_at(pos, lvl->get_glist()); - gold_cnt += g.get_amount(); + gold g{{0, 0}, 0}; + int gold_tmp = 0; - if (g.get_amount()) - res.msg += "PC picked up " + std::to_string(g.get_amount()) + + while ((g = get_gold_at(pos, lvl->get_glist())).get_amount() != 0) + gold_tmp += g.get_amount(); + + if (gold_tmp) + res.msg += "PC picked up " + std::to_string(gold_tmp) + " pieces of gold. "; + if (enabled_features & FEATURE_INVENTORY) { size_t idx = get_potion_at(pos, lvl->get_plist()); @@ -223,6 +229,8 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) { res.msg += "PC gains " + std::to_string(g) + " pieces of gold. "; + + gold_cnt += g; } return res;