Merge branch 'master' of peisongxiao.com:~/cs246/a5

This commit is contained in:
2024-07-17 12:01:01 -04:00
32 changed files with 544 additions and 145 deletions

View File

@ -117,9 +117,8 @@ feature proc_args(int argc, char **argv,
if (result & FEATURE_SEED) {
std::istringstream iss {seed};
unsigned int tmp;
iss >> tmp;
if (!iss.good())
if (!(iss >> tmp))
return FEATURE_PANIC_SEED;
rng = std::make_unique<RNG>(tmp);

View File

@ -18,8 +18,10 @@ game_status CC3K::run() {
case main_menu: {
auto tmp = curr_menu->run(in);
if (tmp == -2)
if (tmp == -2) {
gresult.status = terminated;
return terminated;
}
if (tmp != -1) {
curr_menu = nullptr;

View File

@ -9,12 +9,12 @@ character::character(RNG *rng, const feature enabled_features,
rng{rng}, enabled_features{enabled_features},
race{nrace}, HP{STARTING_HP[race]}, pos{pos},
ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]},
base_hit_rate{1, 1}, base_hit_rate_reset{1, 1} {}
base_hit_rate{STARTING_HR[race]} {}
void character::start_turn() {
ATK = STARTING_ATK[race];
DEF = STARTING_DEF[race];
base_hit_rate = base_hit_rate_reset;
base_hit_rate = STARTING_HR[race];
}
enum race character::get_race() const {
@ -29,12 +29,12 @@ void character::set_pos(const position &npos) {
pos = npos;
}
void character::apply_effect(potion *effect) {
insert_effect(effect);
void character::apply_effect(std::unique_ptr<potion> effect) {
insert_effect(std::move(effect));
}
void character::insert_effect(potion *effect) {
effects.push_back(effect);
void character::insert_effect(std::unique_ptr<potion> effect) {
effects.push_back(std::move(effect));
for (int i = effects.size() - 1; i > 0 &&
effect->get_priority() < effects[i - 1]->get_priority(); --i)
@ -42,17 +42,17 @@ void character::insert_effect(potion *effect) {
}
result character::calc_effects() {
potion_list tmp;
potion_own_list tmp;
tmp.reserve(effects.size());
for (auto p : effects) {
p->apply(this->race, HP, ATK, DEF, base_hit_rate);
for (size_t i = 0; i < effects.size(); ++i) {
effects[i]->apply(this->race, HP, ATK, DEF, base_hit_rate);
if (HP <= 0)
return result::died;
if (p->get_duration() != 0)
tmp.push_back(p);
if (effects[i]->get_duration() != 0)
tmp.push_back(std::move(effects[i]));
}
tmp.shrink_to_fit();
@ -63,12 +63,12 @@ result character::calc_effects() {
}
void character::discard_level_effects() {
potion_list tmp;
potion_own_list tmp;
tmp.reserve(effects.size());
for (auto p : effects)
if (p->get_duration() > 0)
tmp.push_back(p);
for (size_t i = 0; i < effects.size(); ++i)
if (effects[i]->get_duration() > 0)
tmp.push_back(std::move(effects[i]));
tmp.shrink_to_fit();

View File

@ -26,7 +26,7 @@ public:
virtual long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate) = 0;
virtual void apply_effect(potion *effect);
virtual void apply_effect(std::unique_ptr<potion> effect);
// override for different types
virtual void print(output *out) = 0;
@ -56,12 +56,11 @@ protected:
int ATK;
int DEF;
fraction base_hit_rate;
fraction base_hit_rate_reset;
potion_list effects;
potion_own_list effects;
private:
void insert_effect(potion *effect);
void insert_effect(std::unique_ptr<potion> effect);
};
int calc_dmg(const int ATK, const int DEF);

View File

@ -4,6 +4,7 @@
#include <ncurses.h>
#include <string>
#include "position.h"
#include "fraction.h"
static const int INF = 0x3F3F3F3F;
@ -41,27 +42,33 @@ enum game_command : int {game_command_terminate = 0,
};
// Character generation related
static const int RACE_CNT = 12;
static const int RACE_CNT = 13;
enum race : int {rshade = 0, rdrow, rvampire, rtroll,
rgoblin, rhuman, rdwarf, relf,
rorc, rmerchant, rdragon, rhalfling
rorc, rmerchant, rdragon, rhalfling,
rt_800
};
static const char CHAR_REP[RACE_CNT] = {
's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L'
's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L', 't'
};
static const int MAX_HP[RACE_CNT] = {
125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100
125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100, 500
};
static const int STARTING_HP[RACE_CNT] = {
125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100
125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100, 500
};
static const int STARTING_ATK[RACE_CNT] = {
25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15
25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15, 40
};
static const int STARTING_DEF[RACE_CNT] = {
25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20
25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20, 50
};
static const fraction STARTING_HR[RACE_CNT] = {
{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},
{1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
{1, 1}
};

View File

@ -19,6 +19,7 @@ cursor::cursor() {
cursor::~cursor() {
endwin();
refresh();
}
int cursor::getcmd() const {

View File

@ -9,16 +9,17 @@
#include "enemies/merchant.h"
#include "enemies/orc.h"
void new_dragon(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const position &fallback,
const feature enabled_features, int which_room) {
std::unique_ptr<enemy_base> new_dragon(RNG *rng, const position &pos,
const position &fallback,
const feature enabled_features,
int which_room) {
const position nil{0, 0};
if (pos != nil)
p = std::make_unique<dragon>(rng, enabled_features,
return std::make_unique<dragon>(rng, enabled_features,
pos, which_room);
else
p = std::make_unique<dragon>(rng, enabled_features,
return std::make_unique<dragon>(rng, enabled_features,
fallback, which_room);
}
@ -44,8 +45,8 @@ enum race get_normal_race(RNG *rng) {
return CHOICES[rng->rand_under(CNT)];
}
void new_enemy(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const feature enabled_features,
std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
const feature enabled_features,
int which_room) {
using std::make_unique;
@ -56,34 +57,40 @@ void new_enemy(RNG *rng, std::unique_ptr<enemy_base> &p,
else
r = get_normal_race(rng);
p = nullptr;
switch (r) {
case rdwarf:
p = make_unique<dwarf>(rng, enabled_features, pos, which_room);
return make_unique<dwarf>(rng, enabled_features,
pos, which_room);
break;
case rhuman:
p = make_unique<human>(rng, enabled_features, pos, which_room);
return make_unique<human>(rng, enabled_features,
pos, which_room);
break;
case relf:
p = make_unique<elf>(rng, enabled_features, pos, which_room);
return make_unique<elf>(rng, enabled_features,
pos, which_room);
break;
case rorc:
p = make_unique<orc>(rng, enabled_features, pos, which_room);
return make_unique<orc>(rng, enabled_features,
pos, which_room);
break;
case rmerchant:
p = make_unique<merchant>(rng, enabled_features, pos, which_room);
return make_unique<merchant>(rng, enabled_features,
pos, which_room);
break;
case rhalfling:
p = make_unique<halfling>(rng, enabled_features, pos, which_room);
return make_unique<halfling>(rng, enabled_features,
pos, which_room);
break;
default:
break;
}
return nullptr;
}

View File

@ -4,12 +4,13 @@
#include <memory>
#include "enemy.h"
void new_dragon(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const position &fallback,
const feature enabled_features, int which_room);
std::unique_ptr<enemy_base> new_dragon(RNG *rng, const position &pos,
const position &fallback,
const feature enabled_features,
int which_room);
void new_enemy(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const feature enabled_features,
std::unique_ptr<enemy_base> new_enemy(RNG *rng, const position &pos,
const feature enabled_features,
int which_room);
#endif

View File

@ -20,6 +20,9 @@ long_result elf::attack(character *ch) {
if (res1.res == result::died)
return res1;
if (ch->get_race() == rdrow)
return res1;
auto res2 = ch->get_hit(this, ATK, base_hit_rate);
if (res1.res == miss && res2.res == miss)

View File

@ -8,9 +8,7 @@ enemy_base::enemy_base(RNG *rng, const feature enabled_features,
const enum race &nrace, const position &pos,
const int gen_room_num, std::string abbrev):
character{rng, enabled_features, nrace, pos},
room_num{gen_room_num}, abbrev{abbrev} {
base_hit_rate_reset = {1, 2};
}
room_num{gen_room_num}, abbrev{abbrev} {}
int enemy_base::get_room_num() const {
return room_num;
@ -88,27 +86,21 @@ long_result enemy_base::get_hit(character *ch, const int tATK,
return {miss, "PC tried to hit " + abbrev + " but missed. "};
}
void enemy_base::dies(level *lvl, character *pc) {
auto &elist = lvl->get_elist();
for (size_t i = 0; i < elist.size(); ++i)
if (elist[i] == this) {
elist.erase(elist.begin() + i);
break;
}
int enemy_base::dies(level *lvl) {
if (race == race::rdragon) {
return;
return 0;
} else if (race == race::rmerchant) {
lvl->add_gold({GOLD_MERCHANT, pos});
return 0;
} else if (race == race::rhuman) {
lvl->add_gold({GOLD_NORMAL, pos});
auto plist = lvl->get_available_around_all(pos);
lvl->add_gold({GOLD_NORMAL,
rng->get_rand_in_vector(plist)});
return 0;
}
((player_base *)pc)->add_gold(rand_gold_drop(rng));
return rand_gold_drop(rng);
}
enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) {

View File

@ -26,7 +26,7 @@ public:
virtual std::string get_abbrev() const override;
virtual void dies(level *lvl, character *pc);
virtual int dies(level *lvl);
};
typedef std::vector<enemy_base *> enemy_list;

View File

@ -15,7 +15,7 @@ game::game(const enum race starting_race,
the_world = false;
// TODO: add the other races
init_player(rng, player, enabled_features, starting_race);
player = init_player(rng, enabled_features, starting_race);
if (enabled_features & FEATURE_EXTRA_LEVELS)
max_level = rng->rand_between(MIN_LEVEL_CNT, MAX_LEVEL_CNT + 1);
@ -73,8 +73,13 @@ character *game::move_enemies() {
if (player->is_dead())
return ch;
if (ch->is_dead())
ch->dies(levels[curr_level].get(), player.get());
if (ch->is_dead()) {
int g = ch->dies(levels[curr_level].get());
levels[curr_level]->erase_enemy(ch);
if (g)
levels[curr_level]->add_gold({g, ch->get_pos()});
}
msg += res.msg;
}
@ -105,6 +110,8 @@ game_result game::run() {
std::to_string(curr_turn + 1) +
" turns!"};
player->start_turn();
player->discard_level_effects();
++curr_level;
@ -112,6 +119,9 @@ game_result game::run() {
if (curr_level == levels.size())
new_level();
if (enabled_features & FEATURE_REVISIT)
player->set_pos(levels[curr_level]->get_up_stairs());
break;
}
@ -123,6 +133,8 @@ game_result game::run() {
std::to_string(curr_turn + 1) +
" turns! Coward!"};
player->start_turn();
player->discard_level_effects();
--curr_level;
@ -142,13 +154,15 @@ game_result game::run() {
case unknown:
case fine:
case applied_nothing:
++curr_turn;
return {in_game, ""};
default:
player->start_turn();
break;
}
++curr_turn;
player->calc_effects();
if (player->is_dead())
@ -164,7 +178,6 @@ game_result game::run() {
killer->get_race_name()};
}
++curr_turn;
return {in_game, ""};
}

View File

@ -7,6 +7,9 @@ level::level(character *player, RNG *rng, const feature enabled_features):
player{player} {
auto tiles = map.get_room_list();
for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], player->get_pos());
for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], map.get_down_stairs());
@ -26,6 +29,9 @@ level::level(const std::string &map_data, character *player, RNG *rng,
map{player, map_data, rng, enabled_features}, player{player} {
auto tiles = map.get_room_list();
for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], player->get_pos());
for (size_t i = 0; i < tiles.size(); ++i)
remove_from_list(tiles[i], map.get_down_stairs());
@ -66,18 +72,25 @@ void level::gen_enemies(RNG *rng, std::vector<position_list> &tiles) {
spots.push_back(dhoard[i].pos + MOVE[i]);
}
pelist.push_back(nullptr);
new_dragon(rng, pelist[i], rng->get_rand_in_vector(spots),
dhoard[i].pos, enabled_features,
map.which_room(dhoard[i].pos));
auto pos = spots.size() ? rng->get_rand_in_vector(spots) :
dhoard[i].pos;
pelist.push_back(new_dragon(rng, pos, dhoard[i].pos,
enabled_features,
map.which_room(dhoard[i].pos)));
int room = map.which_room(pos);
remove_from_list(tiles[room], pos);
if (!tiles[room].size())
tiles.erase(tiles.begin() + room);
elist.push_back(pelist[i].get());
}
for (int i = dhoard.size(); i < cnt; ++i) {
pelist.push_back(nullptr);
auto p = get_rand_pos(rng, tiles);
new_enemy(rng, pelist[i], p, enabled_features, map.which_room(p));
pelist.push_back(new_enemy(rng, p, enabled_features,
map.which_room(p)));
elist.push_back(pelist[i].get());
}
}
@ -123,10 +136,8 @@ void level::gen_potions(RNG *rng, std::vector<position_list> &tiles) {
pplist.reserve(cnt);
for (int i = 0; i < cnt; ++i) {
pplist.push_back(nullptr);
new_potion(pplist[i],
(potion_type)rng->rand_under(max_type),
get_rand_pos(rng, tiles));
pplist.push_back(new_potion((potion_type)rng->rand_under(max_type),
get_rand_pos(rng, tiles)));
plist.push_back(pplist[i].get());
}
}
@ -172,6 +183,10 @@ bool level::is_available_all(const position &pos) const {
if (!map.is_available(pos))
return false;
if (!(enabled_features & FEATURE_DOORS) &&
map.which_room(pos) == -1)
return false;
if (player->get_pos() == pos)
return false;
@ -222,14 +237,35 @@ position level::get_down_stairs() const {
return map.get_down_stairs();
}
enemy_list &level::get_elist() {
const enemy_list &level::get_elist() {
return elist;
}
potion_list &level::get_plist() {
const potion_list &level::get_plist() {
return plist;
}
gold_list &level::get_glist() {
return glist;
}
std::unique_ptr<potion> level::detach_potion(size_t idx) {
if (idx >= plist.size())
return nullptr;
auto tmp = std::move(pplist[idx]);
plist.erase(plist.begin() + idx);
pplist.erase(pplist.begin() + idx);
return tmp;
}
void level::erase_enemy(character *ch) {
if (ch == nullptr)
return;
for (size_t i = 0; i < elist.size(); ++i)
if (elist[i] == ch) {
elist.erase(elist.begin() + i);
pelist.erase(pelist.begin() + i);
}
}

View File

@ -45,11 +45,14 @@ public:
position get_down_stairs() const;
// you can delete the pointers to the stuff
enemy_list &get_elist();
potion_list &get_plist();
const enemy_list &get_elist();
const potion_list &get_plist();
gold_list &get_glist();
void add_gold(gold g);
void erase_enemy(character *ch);
std::unique_ptr<potion> detach_potion(size_t idx);
private:
// every gen will delete the positions in tiles
void gen_potions(RNG *rng, std::vector<position_list> &tiles);

17
src/output.cc Normal file
View File

@ -0,0 +1,17 @@
#include "output.h"
#include "constants.h"
output::output():
contents{DISPLAY_BUFFER_SIZE, 0} {
for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i)
contents.push_back(0);
}
void output::clear() {
contents.clear();
contents.reserve(DISPLAY_BUFFER_SIZE);
for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i)
contents.push_back(0);
}

35
src/output.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <ncurses.h>
#include <vector>
#include "position.h"
#include "cursor.h"
class output {
protected:
// use an array of ints to keep track of what's on the screen
// This will have a bit of buffer for the last line
// just in case it overflows
std::vector<int> contents;
public:
output();
virtual ~output() = default;
// Only call this to refresh the entire output
virtual void render() = 0;
// Clears the contents buffer
virtual void clear();
virtual void print_char(const position &pos, const char ch,
const int attrs =
A_NORMAL | COLOR_PAIR(COLOR_WHITE)) = 0;
virtual void print_str(const position &pos, const std::string &str,
const int attrs =
A_NORMAL | COLOR_PAIR(COLOR_WHITE)) = 0;
// default arguments are to be set in the base class's declaration
};
#endif

View File

@ -0,0 +1,100 @@
#include "console_output.h"
#include <string>
#include <utility>
#include <ncurses.h>
#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];
}

View File

@ -0,0 +1,21 @@
#ifndef __CONSOLE_OUTPUT_H__
#define __CONSOLE_OUTPUT_H__
#include <iostream>
#include "../output.h"
class console_output final : public output {
private:
std::ostream &out;
std::string get_code(const int attrs);
public:
console_output(std::ostream &cout);
void render() override;
void print_char(const position &pos,
const char ch, const int attrs) override;
void print_str(const position &pos,
const std::string &str, const int attrs) override;
};
#endif

View File

@ -0,0 +1,38 @@
#include "curses_output.h"
#include "../constants.h"
curses_output::curses_output(cursor *new_curse):
curse{new_curse} {}
void curses_output::render() {
curse->show();
}
void curses_output::clear() {
curse->clear();
}
void curses_output::print_char(const position &pos, const char ch,
const int attrs) {
if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
return;
curse->print_char(pos, ch, attrs);
}
void curses_output::print_str(const position &pos, const std::string &str,
const int attrs) {
if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
return;
position tmp = pos;
for (std::size_t i = 0; i < str.length(); ++i) {
curse->print_char(tmp, str[i], attrs);
tmp += {1, 0};
if (tmp.x >= DISPLAY_WIDTH)
tmp = {0, tmp.y + 1};
}
}

View File

@ -0,0 +1,23 @@
#ifndef __CURSES_OUTPUT_H__
#define __CURSES_OUTPUT_H__
#include <utility>
#include <memory>
#include "../cursor.h"
#include "../output.h"
class curses_output final : public output {
private:
cursor *curse;
public:
curses_output(cursor *new_curse);
void render() override;
void clear() override;
void print_char(const position &pos,
const char ch, const int attrs) override;
void print_str(const position &pos,
const std::string &str, const int attrs) override;
};
#endif

38
src/output/file_output.cc Normal file
View File

@ -0,0 +1,38 @@
#include "file_output.h"
#include <utility>
#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];
}

20
src/output/file_output.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef __FILE_OUTPUT_H__
#define __FILE_OUTPUT_H__
#include <fstream>
#include "../output.h"
class file_output final : public output {
private:
std::ofstream out;
public:
file_output(std::ofstream &&ofs);
void render() override;
void print_char(const position &pos,
const char ch, const int attrs) override;
void print_str(const position &pos,
const std::string &str, const int attrs) override;
};
#endif

View File

@ -6,35 +6,40 @@
#include "player/shade.h"
#include "player/troll.h"
#include "player/vampire.h"
#include "player/t_800.h"
void init_player(RNG *rng, std::unique_ptr<player_base> &pc,
std::unique_ptr<player_base> init_player(RNG *rng,
const feature enabled_features, const enum race &r) {
using std::make_unique;
pc = nullptr;
switch (r) {
case rgoblin:
pc = make_unique<goblin>(rng, enabled_features);
return make_unique<goblin>(rng, enabled_features);
break;
case rdrow:
pc = make_unique<drow>(rng, enabled_features);
return make_unique<drow>(rng, enabled_features);
break;
case rshade:
pc = make_unique<shade>(rng, enabled_features);
return make_unique<shade>(rng, enabled_features);
break;
case rtroll:
pc = make_unique<troll>(rng, enabled_features);
return make_unique<troll>(rng, enabled_features);
break;
case rvampire:
pc = make_unique<vampire>(rng, enabled_features);
return make_unique<vampire>(rng, enabled_features);
break;
case rt_800:
return make_unique<t_800>(rng, enabled_features);
break;
default:
break;
}
return nullptr;
}

View File

@ -4,7 +4,7 @@
#include <memory>
#include "player.h"
void init_player(RNG *rng, std::unique_ptr<player_base> &pc,
std::unique_ptr<player_base> init_player(RNG *rng,
const feature enabled_features, const enum race &r);
#endif

View File

@ -32,15 +32,21 @@ int player_base::get_HP() const {
return this->HP;
}
long_result player_base::apply(potion *p) {
long_result player_base::apply(std::unique_ptr<potion> p) {
if (p == nullptr)
return {result::applied_nothing,
"PC tried to breathe the magic in the air. "};
apply_effect(p);
std::string name = p->get_name();
apply_effect(std::move(p));
if (race == rt_800)
return {result::applied,
"PC gets rusty joints (-50 HP). "};
return {result::applied,
(std::string)"PC applied potion of " + p->get_name() + ". "};
"PC applied potion of " + name + ". "};
}
long_result player_base::attack(character *ch) {
@ -62,8 +68,15 @@ long_result player_base::move(level *lvl,
} else if ((tmp = get_enemy_at(p, lvl->get_elist())) != nullptr) {
auto res = attack((character *)tmp);
if (tmp->is_dead())
tmp->dies(lvl, this);
if (tmp->is_dead()) {
int g = tmp->dies(lvl);
lvl->erase_enemy(tmp);
if (g)
res.msg += "PC gains " +
std::to_string(g) +
" pieces of gold. ";
}
return res;
}
@ -112,19 +125,18 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) {
} else if (cmd >= move_north && cmd <= move_southwest) {
auto res = move(lvl, pos + MOVE[cmd - move_north]);
if (res.res == result::moved)
start_turn();
gold g = get_gold_at(pos, lvl->get_glist());
gold_cnt += g.amount;
if (g.amount)
res.msg += "PC picked up " + std::to_string(g.amount) +
" pieces of gold. ";
return res;
} else if (cmd >= apply_north && cmd <= apply_southwest) {
auto res = apply(get_potion_at(pos + MOVE[cmd - apply_north],
lvl->get_plist()));
if (res.res == result::applied)
start_turn();
size_t idx = get_potion_at(pos + MOVE[cmd - apply_north],
lvl->get_plist());
auto res = apply(lvl->detach_potion(idx));
return res;
} else if (cmd == apply_panic) {
return {result::fine,
@ -133,29 +145,29 @@ long_result player_base::interpret_command(level *lvl, game_command cmd) {
enemy_base *tmp = get_enemy_at(pos + MOVE[cmd - attack_north],
lvl->get_elist());
if (tmp != nullptr)
start_turn();
auto res = attack((character *)tmp);
if (tmp != nullptr && tmp->is_dead())
tmp->dies(lvl, this);
if (tmp != nullptr && tmp->is_dead()) {
int g = tmp->dies(lvl);
lvl->erase_enemy(tmp);
if (g)
res.msg += "PC gains " +
std::to_string(g) +
" pieces of gold. ";
}
return res;
} else if (cmd == up_stairs) {
if (lvl->get_up_stairs() == pos &&
enabled_features & FEATURE_REVISIT) {
start_turn();
enabled_features & FEATURE_REVISIT)
return {go_up, "PC went up the stairs. "};
}
return {result::fine,
"PC tried to fly through the ceiling. "};
} else if (cmd == down_stairs) {
if (lvl->get_down_stairs() == pos) {
start_turn();
if (lvl->get_down_stairs() == pos)
return {go_down, "PC went down the stairs. "};
}
return {result::fine,
"PC tried to dig through the floor. "};

View File

@ -8,13 +8,13 @@ enum game_command : int;
class player_base: public character {
protected:
int gold_cnt;
potion_list potions;
potion_own_list potions;
public:
player_base(RNG *rng, const feature enabled_features,
const enum race &nrace);
virtual long_result move(level *lvl, const position &p);
virtual long_result apply(potion *p);
virtual long_result apply(std::unique_ptr<potion> p);
virtual long_result attack(character *ch) override;
virtual long_result get_hit(character *ch, const int tATK,

15
src/player/t_800.cc Normal file
View File

@ -0,0 +1,15 @@
#include "t_800.h"
#include "../constants.h"
t_800::t_800(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, race::rt_800} {}
const char *t_800::get_race_name() const {
return "T-800";
}
void t_800::apply_effect(std::unique_ptr<potion> p) {
HP -= RUSTY;
return;
}

15
src/player/t_800.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __T_800_H__
#define __T_800_H__
#include "../player.h"
class potion;
class t_800 final: public player_base {
static const int RUSTY = 50;
public:
t_800(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
void apply_effect(std::unique_ptr<potion> p) override;
};
#endif

View File

@ -47,15 +47,10 @@ void potion::print(output *out) {
out->print_char(pos, 'P', COLOR_PAIR(COLOR_GREEN));
}
potion *get_potion_at(const position &pos, potion_list &plist) {
potion *ret = nullptr;
size_t get_potion_at(const position &pos, const potion_list &plist) {
for (size_t i = 0; i < plist.size(); ++i)
if (plist[i]->get_pos() == pos) {
ret = plist[i];
plist.erase(plist.begin() + i);
return ret;
}
if (plist[i]->get_pos() == pos)
return i;
return ret;
return plist.size();
}

View File

@ -2,6 +2,7 @@
#define __POTION_H__
#include <vector>
#include <memory>
#include "fraction.h"
#include "output.h"
@ -40,7 +41,8 @@ public:
};
typedef std::vector<potion *> potion_list;
typedef std::vector<std::unique_ptr<potion> > potion_own_list;
potion *get_potion_at(const position &pos, potion_list &plist);
size_t get_potion_at(const position &pos, const potion_list &plist);
#endif

View File

@ -8,34 +8,35 @@
#include "potions/wound_atk.h"
#include "potions/wound_def.h"
void new_potion(std::unique_ptr<potion> &pp, potion_type type,
const position &pos) {
std::unique_ptr<potion> new_potion(potion_type type, const position &pos) {
switch (type) {
case restore_health:
pp = std::make_unique<class restore_health>(pos);
return std::make_unique<class restore_health>(pos);
break;
case boost_atk:
pp = std::make_unique<class boost_atk>(pos);
return std::make_unique<class boost_atk>(pos);
break;
case boost_def:
pp = std::make_unique<class boost_def>(pos);
return std::make_unique<class boost_def>(pos);
break;
case poison_health:
pp = std::make_unique<class poison_health>(pos);
return std::make_unique<class poison_health>(pos);
break;
case wound_atk:
pp = std::make_unique<class wound_atk>(pos);
return std::make_unique<class wound_atk>(pos);
break;
case wound_def:
pp = std::make_unique<class wound_def>(pos);
return std::make_unique<class wound_def>(pos);
break;
default:
break;
}
return nullptr;
}

View File

@ -6,7 +6,6 @@
#include <memory>
#include <utility>
void new_potion(std::unique_ptr<potion> &pp, potion_type type,
const position &pos);
std::unique_ptr<potion> new_potion(potion_type type, const position &pos);
#endif