Merge branch 'master' of peisongxiao.com:~/cs246/a5
This commit is contained in:
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
};
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ cursor::cursor() {
|
||||
|
||||
cursor::~cursor() {
|
||||
endwin();
|
||||
refresh();
|
||||
}
|
||||
|
||||
int cursor::getcmd() const {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
20
src/enemy.cc
20
src/enemy.cc
@ -8,9 +8,7 @@ enemy_base::enemy_base(RNG *rng, const feature enabled_features,
|
||||
const enum race &nrace, const position &pos,
|
||||
const int gen_room_num, std::string abbrev):
|
||||
character{rng, enabled_features, nrace, pos},
|
||||
room_num{gen_room_num}, abbrev{abbrev} {
|
||||
base_hit_rate_reset = {1, 2};
|
||||
}
|
||||
room_num{gen_room_num}, abbrev{abbrev} {}
|
||||
|
||||
int enemy_base::get_room_num() const {
|
||||
return room_num;
|
||||
@ -88,27 +86,21 @@ long_result enemy_base::get_hit(character *ch, const int tATK,
|
||||
return {miss, "PC tried to hit " + abbrev + " but missed. "};
|
||||
}
|
||||
|
||||
void enemy_base::dies(level *lvl, character *pc) {
|
||||
auto &elist = lvl->get_elist();
|
||||
|
||||
for (size_t i = 0; i < elist.size(); ++i)
|
||||
if (elist[i] == this) {
|
||||
elist.erase(elist.begin() + i);
|
||||
break;
|
||||
}
|
||||
|
||||
int enemy_base::dies(level *lvl) {
|
||||
if (race == race::rdragon) {
|
||||
return;
|
||||
return 0;
|
||||
} else if (race == race::rmerchant) {
|
||||
lvl->add_gold({GOLD_MERCHANT, pos});
|
||||
return 0;
|
||||
} else if (race == race::rhuman) {
|
||||
lvl->add_gold({GOLD_NORMAL, pos});
|
||||
auto plist = lvl->get_available_around_all(pos);
|
||||
lvl->add_gold({GOLD_NORMAL,
|
||||
rng->get_rand_in_vector(plist)});
|
||||
return 0;
|
||||
}
|
||||
|
||||
((player_base *)pc)->add_gold(rand_gold_drop(rng));
|
||||
return rand_gold_drop(rng);
|
||||
}
|
||||
|
||||
enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) {
|
||||
|
@ -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;
|
||||
|
23
src/game.cc
23
src/game.cc
@ -15,7 +15,7 @@ game::game(const enum race starting_race,
|
||||
the_world = false;
|
||||
|
||||
// TODO: add the other races
|
||||
init_player(rng, player, enabled_features, starting_race);
|
||||
player = init_player(rng, enabled_features, starting_race);
|
||||
|
||||
if (enabled_features & FEATURE_EXTRA_LEVELS)
|
||||
max_level = rng->rand_between(MIN_LEVEL_CNT, MAX_LEVEL_CNT + 1);
|
||||
@ -73,8 +73,13 @@ character *game::move_enemies() {
|
||||
if (player->is_dead())
|
||||
return ch;
|
||||
|
||||
if (ch->is_dead())
|
||||
ch->dies(levels[curr_level].get(), player.get());
|
||||
if (ch->is_dead()) {
|
||||
int g = ch->dies(levels[curr_level].get());
|
||||
levels[curr_level]->erase_enemy(ch);
|
||||
|
||||
if (g)
|
||||
levels[curr_level]->add_gold({g, ch->get_pos()});
|
||||
}
|
||||
|
||||
msg += res.msg;
|
||||
}
|
||||
@ -105,6 +110,8 @@ game_result game::run() {
|
||||
std::to_string(curr_turn + 1) +
|
||||
" turns!"};
|
||||
|
||||
player->start_turn();
|
||||
|
||||
player->discard_level_effects();
|
||||
|
||||
++curr_level;
|
||||
@ -112,6 +119,9 @@ game_result game::run() {
|
||||
if (curr_level == levels.size())
|
||||
new_level();
|
||||
|
||||
if (enabled_features & FEATURE_REVISIT)
|
||||
player->set_pos(levels[curr_level]->get_up_stairs());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -123,6 +133,8 @@ game_result game::run() {
|
||||
std::to_string(curr_turn + 1) +
|
||||
" turns! Coward!"};
|
||||
|
||||
player->start_turn();
|
||||
|
||||
player->discard_level_effects();
|
||||
|
||||
--curr_level;
|
||||
@ -142,13 +154,15 @@ game_result game::run() {
|
||||
case unknown:
|
||||
case fine:
|
||||
case applied_nothing:
|
||||
++curr_turn;
|
||||
return {in_game, ""};
|
||||
|
||||
default:
|
||||
player->start_turn();
|
||||
break;
|
||||
}
|
||||
|
||||
++curr_turn;
|
||||
|
||||
player->calc_effects();
|
||||
|
||||
if (player->is_dead())
|
||||
@ -164,7 +178,6 @@ game_result game::run() {
|
||||
killer->get_race_name()};
|
||||
}
|
||||
|
||||
++curr_turn;
|
||||
return {in_game, ""};
|
||||
}
|
||||
|
||||
|
60
src/level.cc
60
src/level.cc
@ -7,6 +7,9 @@ level::level(character *player, RNG *rng, const feature enabled_features):
|
||||
player{player} {
|
||||
auto tiles = map.get_room_list();
|
||||
|
||||
for (size_t i = 0; i < tiles.size(); ++i)
|
||||
remove_from_list(tiles[i], player->get_pos());
|
||||
|
||||
for (size_t i = 0; i < tiles.size(); ++i)
|
||||
remove_from_list(tiles[i], map.get_down_stairs());
|
||||
|
||||
@ -26,6 +29,9 @@ level::level(const std::string &map_data, character *player, RNG *rng,
|
||||
map{player, map_data, rng, enabled_features}, player{player} {
|
||||
auto tiles = map.get_room_list();
|
||||
|
||||
for (size_t i = 0; i < tiles.size(); ++i)
|
||||
remove_from_list(tiles[i], player->get_pos());
|
||||
|
||||
for (size_t i = 0; i < tiles.size(); ++i)
|
||||
remove_from_list(tiles[i], map.get_down_stairs());
|
||||
|
||||
@ -66,18 +72,25 @@ void level::gen_enemies(RNG *rng, std::vector<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);
|
||||
}
|
||||
}
|
||||
|
@ -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
17
src/output.cc
Normal 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
35
src/output.h
Normal 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
|
100
src/output/console_output.cc
Normal file
100
src/output/console_output.cc
Normal 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];
|
||||
}
|
21
src/output/console_output.h
Normal file
21
src/output/console_output.h
Normal 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
|
38
src/output/curses_output.cc
Normal file
38
src/output/curses_output.cc
Normal 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};
|
||||
}
|
||||
}
|
23
src/output/curses_output.h
Normal file
23
src/output/curses_output.h
Normal 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
38
src/output/file_output.cc
Normal 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
20
src/output/file_output.h
Normal 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
|
21
src/pc.cc
21
src/pc.cc
@ -6,35 +6,40 @@
|
||||
#include "player/shade.h"
|
||||
#include "player/troll.h"
|
||||
#include "player/vampire.h"
|
||||
#include "player/t_800.h"
|
||||
|
||||
void init_player(RNG *rng, std::unique_ptr<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;
|
||||
}
|
||||
|
2
src/pc.h
2
src/pc.h
@ -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
|
||||
|
@ -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. "};
|
||||
|
@ -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
15
src/player/t_800.cc
Normal 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
15
src/player/t_800.h
Normal 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
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user