finished the bulk of game

This commit is contained in:
2024-07-14 21:32:41 -04:00
parent b3475b7530
commit af8bc4112c
110 changed files with 1876 additions and 1481 deletions

View File

@ -1,33 +0,0 @@
# Universal makefile for single C++ program
#
# Use gcc flag -MMD (user) or -MD (user/system) to generate dependencies among source files.
# Use make default rules for commonly used file-name suffixes and make variables names.
#
# % make [ a.out ]
########## Variables ##########
CXX = g++ # compiler
CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags
MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name
SOURCES = $(wildcard src/*.cc) # source files (*.cc)
OBJECTS = ${SOURCES:.cc=.o} # object files forming executable
DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d"
EXEC = bin/cc3k # executable name
########## Targets ##########
.PHONY : clean # not file names
${EXEC} : ${OBJECTS} # link step
${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
# make implicitly generates rules to compile C++ files that generate .o files
-include ${DEPENDS} # include *.d files containing program dependences
clean : # remove files that can be regenerated
rm -f ${DEPENDS} ${OBJECTS} ${EXEC}

View File

View File

@ -1,33 +0,0 @@
# Universal makefile for single C++ program
#
# Use gcc flag -MMD (user) or -MD (user/system) to generate dependencies among source files.
# Use make default rules for commonly used file-name suffixes and make variables names.
#
# % make [ a.out ]
########## Variables ##########
CXX = g++ # compiler
CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags
MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name
SOURCES = $(wildcard ../src/*.cc) # source files (*.cc)
OBJECTS = ${SOURCES:.cc=.o} # object files forming executable
DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d"
EXEC = cc3k # executable name
########## Targets ##########
.PHONY : clean # not file names
${EXEC} : ${OBJECTS} # link step
${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
# make implicitly generates rules to compile C++ files that generate .o files
-include ${DEPENDS} # include *.d files containing program dependences
clean : # remove files that can be regenerated
rm -f ${DEPENDS} ${OBJECTS} ${EXEC}

View File

@ -11,10 +11,10 @@ CXX = g++ # compiler
CXXFLAGS = -std=c++20 -g -Wall -MMD -O0 # compiler flags
MAKEFILE_NAME = ${firstword ${MAKEFILE_LIST}} # makefile name
SOURCES = $(wildcard *.cc) # source files (*.cc)
SOURCES = $(wildcard *.cc) $(wildcard */*.cc) # source files (*.cc)
OBJECTS = ${SOURCES:.cc=.o} # object files forming executable
DEPENDS = ${OBJECTS:.o=.d} # substitute ".o" with ".d"
EXEC = ../bin/cc3k # executable name
EXEC = cc3k # executable name
########## Targets ##########

View File

@ -5,19 +5,20 @@
#include <fstream>
#include <string>
#include <sstream>
#include "file_input.h"
#include "file_output.h"
#include "console_input.h"
#include "console_output.h"
#include "curses_input.h"
#include "curses_output.h"
#include "constants.h"
#include "input/file_input.h"
#include "display/file_output.h"
#include "input/console_input.h"
#include "display/console_output.h"
#include "input/curses_input.h"
#include "display/curses_output.h"
#include "rng.h"
feature proc_args(int argc, char **argv,
std::unique_ptr<cursor> &curse,
std::unique_ptr<input> &in,
std::unique_ptr<display> &out,
std::unique_ptr<logger> &log,
std::unique_ptr<RNG> &rng) {
feature result = 0;
std::string str;
@ -53,6 +54,8 @@ feature proc_args(int argc, char **argv,
result |= FEATURE_EXTRA_STUFF;
} else if (str == "-E") {
result |= FEATURE_EXTRA_LEVELS;
} else if (str == "-o") {
result |= FEATURE_WALK_OVER;
} else if (str == "-s") {
++i;
str = argv[i];
@ -62,15 +65,6 @@ feature proc_args(int argc, char **argv,
seed = str;
result |= FEATURE_SEED;
} else if (str == "-L") {
++i;
str = argv[i];
if (!fn_lout.empty() && fn_lout != str)
return FEATURE_CONFLICT | i;
fn_lout = str;
result |= FEATURE_LOG;
} else if (str == "-I") {
++i;
str = argv[i];
@ -116,15 +110,6 @@ feature proc_args(int argc, char **argv,
} else if (!(result & FEATURE_NCURSES))
out = std::make_unique<console_output>(std::cout);
if (result & FEATURE_LOG) {
std::ofstream lout(fn_lout);
if (!lout.good())
return FEATURE_PANIC_FILE | FEATURE_LOG;
log = std::make_unique<logger>(std::move(lout));
}
if (result & FEATURE_NCURSES) {
curse = std::make_unique<cursor>();
in = std::make_unique<curses_input>(curse.get());
@ -167,10 +152,6 @@ void panic_args(feature panic) {
cerr << "Cannot open specified output file!" << endl;
break;
case FEATURE_LOG:
cerr << "Cannot open specified log file!" << endl;
break;
default:
cerr << "Something must have went really, really, wrong..."
<< endl;
@ -184,20 +165,18 @@ void panic_args(feature panic) {
}
void print_args_list() {
static const char *ARGS_LIST = "-n : Use ncurses for I/O\n\
-r : Randomly generate maps\n\
-m : Enabled a main menu + options\n\
-c : Enemies chase the player (through doors and up stairs)\n\
-C : Give things better coloring\n\
-i : Enable inventory\n\
-i : Enable inventory (player can walk on potions)\n\
-t : Enable throw\n\
-R : Enable revisiting levels\n\
-e : Enable extra potions and races\n\
-E : Enable extra levels\n\
-o : Allows monsters to go over gold and potions\n\
-s [seed] : Sets initial seed to seed\n\
-L [file] : Enable logging to file\n\
-I [file] : Reads commands from file. CANNOT BE USED WITH -n.\n\
-O [file] : Outputs to file. CANNOT BE USED WITH -n.\n\
-h/--help : Displays options list (doesn't start a game)";

View File

@ -1,20 +1,18 @@
#ifndef __ARGUMENTS_H__
#define __ARGUMENTS_H__
#include <memory>
#include "log.h"
#include "cursor.h"
#include "display.h"
#include "input.h"
#include "rng.h"
#include "constants.h"
typedef unsigned int feature;
// IMPORTANT: Errors include the index that caused them (or'ed into them)
feature proc_args(int argc, char **argv,
std::unique_ptr<cursor> &curse,
std::unique_ptr<input> &in,
std::unique_ptr<display> &out,
std::unique_ptr<logger> &log,
std::unique_ptr<RNG> &rng);
void panic_args(feature panic);

View File

@ -1,161 +1,47 @@
#include "characters.h"
#include "constants.h"
#include "level.h"
#include <algorithm>
character::character(RNG *rng, const enum race &nrace, const position &pos):
rng{rng}, race{nrace}, HP{STARTING_HP[race]},
character::character(RNG *rng, const feature enabled_features,
const enum race &nrace, const position &pos):
rng{rng}, enabled_features{enabled_features},
race{nrace}, HP{STARTING_HP[race]}, pos{pos},
ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]},
base_hit_rate{1, 1}, pos{pos} {}
base_hit_rate{1, 1}, base_hit_rate_reset{1, 1} {}
void character::start_turn() {
ATK = STARTING_ATK[race];
DEF = STARTING_DEF[race];
base_hit_rate = {1, 1};
}
void character::print(display *out, bool player) {
out->print_char(pos, player ? '@' : CHARACTER_REP[race],
player ? COLOR_PAIR(COLOR_BLUE) : COLOR_PAIR(COLOR_RED));
base_hit_rate = base_hit_rate_reset;
}
enum race character::get_race() const {
return race;
}
position character::get_position() const {
position character::get_pos() const {
return pos;
}
int character::get_HP() const {
return HP;
}
int character::get_ATK() const {
return ATK;
}
int character::get_DEF() const {
return DEF;
}
int character::get_gold() const {
return gold;
}
float character::get_hitrate_real() const {
return base_hit_rate.real();
}
fraction character::get_hitrate() const {
return base_hit_rate;
}
int character::get_room_num() const {
return room_num;
}
<<< <<< < HEAD
== == == =
bool character::is_hostile() const {
return hostile;
}
>>> >>> > AL - races
void character::set_room_num(const int room) {
room_num = room;
}
void character::set_position(const position &npos) {
void character::set_pos(const position &npos) {
pos = npos;
}
void character::set_HP(const int nHP) {
HP = nHP;
void character::apply_effect(potion *effect) {
insert_effect(effect);
}
void character::set_ATK(const int nATK) {
ATK = nATK;
}
void character::set_DEF(const int nDEF) {
DEF = nDEF;
}
void character::set_gold(const int ngold) {
gold = ngold;
}
void character::set_hitrate(const fraction nhitrate) {
base_hit_rate = nhitrate;
}
<<< <<< < HEAD
== == == =
void character::set_hostile(const bool is_hostile) {
hostile = is_hostile;
}
void character::apply_buff(const stat_name statn, const int amount) {
// TODO: add checks for bounds
switch (statn) {
case stat_name::HP:
HP += amount;
break;
case stat_name::ATK:
ATK += amount;
break;
case stat_name::DEF:
DEF += amount;
break;
case stat_name::hostile: {
if (amount > 0)
hostile = true;
else
hostile = false;
break;
}
}
}
>>> >>> > AL - races
direction_list character::moveable(const position_list &available_positions)
const {
direction_list result;
for (int i = 0; i < DIRECTION_CNT; ++i)
if (find(available_positions, pos + MOVE[i])
!= available_positions.size())
result.push_back((direction)i);
return result;
}
character::apply_result character::apply(const direction &dir,
potion_list &plist) {
for (size_t i = 0; i < plist.size(); ++i)
if (pos + MOVE[dir] == plist[i]->get_pos()) {
apply_result res{applied, plist[i]};
insert_potion(plist[i]);
plist.erase(plist.begin() + i);
return res;
}
return {applied_nothing, nullptr};
}
void character::insert_potion(potion *p) {
effects.push_back(p);
void character::insert_effect(potion *effect) {
effects.push_back(effect);
for (int i = effects.size() - 1; i > 0; --i)
if (p->get_priority() < effects[i - 1]->get_priority())
if (effect->get_priority() < effects[i - 1]->get_priority())
std::swap(effects[i], effects[i - 1]);
}
result character::apply_effects() {
result character::calc_effects() {
potion_list tmp;
tmp.reserve(effects.size());
@ -190,17 +76,28 @@ void character::discard_level_effects() {
}
// IMPORTANT: remember to check if player is on the stairs
result character::move(const direction dir,
const position_list &available_positions) {
if (find(available_positions, pos + MOVE[dir])
!= available_positions.size()) {
pos += MOVE[dir];
return result::moved;
long_result character::move(level *lvl,
const position &p) {
if (lvl->is_available(p)) {
pos = p;
return {result::moved, ""};
}
return result::fine;
return {result::fine, ""};
}
int character::calc_dmg(const int ATK, const int DEF) {
int calc_dmg(const int ATK, const int DEF) {
return ceil((100.0f / (100.0f + DEF)) * ATK);
}
character *get_ch_at(const position &pos, const character_list &chlist) {
for (auto ch : chlist)
if (ch->get_pos() == pos)
return ch;
return nullptr;
}
bool character::is_dead() const {
return HP <= 0;
}

View File

@ -5,7 +5,6 @@
#include <memory>
#include <math.h>
#include "constants.h"
#include "display.h"
#include "fraction.h"
#include "position.h"
@ -13,62 +12,38 @@
#include "gold.h"
#include "rng.h"
class character; // forward declaration
// Note: player should not be in the character list
class level;
typedef unsigned int feature;
struct long_result;
enum result : int;
class character {
public:
character(RNG *rng, const race &nrace,
const position &pos); // fills out the starting stats
character(RNG *rng, const feature enabled_features,
const race &nrace, const position &pos); // fills out the starting stats
// usually I wouldn't do this but considering that the map has
// a super small size an O(n) solution is acceptable
// IMPORTANT: available_positions do NOT have characters in them
direction_list moveable(const position_list &available_positions) const;
virtual result move(const direction dir,
const position_list &available_positions);
virtual long_result move(level *lvl, const position &p);
struct attack_result {
result res;
std::string msg;
};
virtual long_result attack(character *ch) = 0;
virtual long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate) = 0;
struct apply_result {
result res;
std::string msg;
};
virtual result move(position_list spots, const direction &dir);
virtual void apply_effect(potion *effect);
virtual attack_result attack(const direction dir, character *ch) = 0;
// override for different types
virtual void print(display *out) = 0;
virtual apply_result apply(const direction &dir,
potion_list &potions);
virtual attack_result get_hit(const enum race &race, const int atk,
const fraction hitrate) = 0;
// overload for different races
virtual void print(display *out);
virtual result calc_effects();
result calc_effects();
void discard_level_effects();
virtual void start_turn();
virtual const char *get_race_name() const = 0;
enum race get_race() const;
position get_pos() const;
virtual std::string get_abbrev() const = 0;
void set_pos(const position &npos);
int get_HP() const;
int get_ATK() const;
int get_DEF() const;
fraction get_hitrate() const;
int get_room_num() const;
void set_HP(const int nHP);
void set_ATK(const int nATK);
void set_DEF(const int nDEF);
void set_hitrate(const fraction nhitrate);
void set_room_num(const int room);
bool is_dead() const;
protected:
RNG *rng;
@ -79,18 +54,22 @@ protected:
int HP;
position pos;
// IMPORTANT: keep track of ATK and DEF in game at turn time
// IMPORTANT: keep track at turn time
int ATK;
int DEF;
fraction base_hit_rate;
fraction base_hit_rate_reset;
potion_list effects;
int calc_dmg(const int ATK, const int DEF);
private:
void insert_effect(potion *effect);
};
int calc_dmg(const int ATK, const int DEF);
typedef std::vector<character *> character_list;
character *get_ch_at(const position &pos, const character_list &chlist);
#endif

View File

@ -1,42 +0,0 @@
#include "console_input.h"
#include <utility>
#include <string>
console_input::console_input(std::istream &cin):
in{cin} {}
game_command console_input::get_command() {
std::string cmd;
in >> cmd;
game_command tmp;
if (in.eof())
return game_command_terminate;
if (cmd == "q")
return game_command_terminate;
else if (cmd == "f")
return the_world;
else if (cmd == "r")
return game_restart;
else if (cmd == "u" || cmd == "a") {
bool use = cmd == "u";
in >> cmd;
if (in.eof())
return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) ==
game_command_panic
? tmp : tmp - move_north +
(use ? apply_north : attack_north));
} else {
auto tmp = get_direction(cmd);
if (tmp != game_command_panic)
return tmp;
}
return game_command_pass;
}

View File

@ -1,16 +0,0 @@
#ifndef __CONSOLE_INPUT_H__
#define __CONSOLE_INPUT_H__
#include <iostream>
#include "input.h"
class console_input final : public input {
private:
std::istream &in;
public:
// This is for cin
console_input(std::istream &cin);
game_command get_command() override;
};
#endif

View File

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

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

View File

@ -2,24 +2,28 @@
#define __CONSTANTS_H__
#include <vector>
#include <ncurses.h>
#include <string>
#include "position.h"
const int INF = 0x3F3F3F3F;
enum error {none};
static const int INF = 0x3F3F3F3F;
// TODO: update result to include subject
// fine will waste a turn
enum result {fine, died, go_down, go_up, hit, moved,
enum result : int {fine, died, go_down, go_up, hit, moved,
miss, terminate, applied, applied_nothing,
toggle_the_world, restart_game, unknown
};
enum game_status {terminated, main_menu, in_game, options,
dead, won, escaped, restart
struct long_result {
result res;
std::string msg;
};
enum game_command {game_command_terminate = 0,
enum game_status : int {terminated, main_menu, in_game, options,
dead, won, escaped, restart, skip_turn
};
enum game_command : int {game_command_terminate = 0,
move_north, move_south, move_east, move_west,
move_northeast, move_northwest,
move_southeast, move_southwest,
@ -32,70 +36,67 @@ enum game_command {game_command_terminate = 0,
attack_southeast, attack_southwest,
up_stairs, down_stairs,
the_world, game_restart,
game_command_pass, game_command_panic
game_command_pass, game_command_panic,
enter
};
// Character generation related
const int RACE_CNT = 12;
enum race {rshade = 0, rdrow, rvampire, rtroll,
static const int RACE_CNT = 12;
enum race : int {rshade = 0, rdrow, rvampire, rtroll,
rgoblin, rhuman, rdwarf, relf,
rorc, rmerchant, rdragon, rhalfling
};
const char *RACE_NAME[RACE_CNT] = {
"Shade", "Drow", "Vampire", "Troll",
"Goblin", "Human", "Dwarf", "Elf",
"Orc", "Merchant", "Dragon", "Halfling"
static const char CHAR_REP[RACE_CNT] = {
's', 'd', 'v', 't', 'g', 'H', 'W', 'E', 'O', 'M', 'D', 'L'
};
const int MAX_HP[RACE_CNT] = {
static const int MAX_HP[RACE_CNT] = {
125, 150, INF, 120, 110, 140, 100, 140, 180, 30, 150, 100
};
const int STARTING_HP[RACE_CNT] = {
static const int STARTING_HP[RACE_CNT] = {
125, 150, 50, 120, 110, 140, 100, 140, 180, 30, 150, 100
};
const int STARTING_ATK[RACE_CNT] = {
static const int STARTING_ATK[RACE_CNT] = {
25, 25, 25, 25, 15, 20, 20, 30, 30, 70, 20, 15
};
const int STARTING_DEF[RACE_CNT] = {
static const int STARTING_DEF[RACE_CNT] = {
25, 15, 25, 15, 20, 20, 30, 10, 25, 5, 20, 20
};
// Potion-related
const int POTION_TYPE_CNT = 6;
const int DEFAULT_POTION_TYPE_CNT = 6;
enum potion_type {restore_health = 0, boost_atk, boost_def,
static const int POTION_TYPE_CNT = 6;
static const int DEFAULT_POTION_TYPE_CNT = 6;
enum potion_type : int {restore_health = 0, boost_atk, boost_def,
poison_health, wound_atk, wound_def
};
const char *POTION_REAL_NAME[POTION_TYPE_CNT] = {
"RH", "BA", "BD", "PH", "WA", "WD"
};
// Calculation priorities
const int CALC_ADD_BASE = 0;
const int CALC_MUL_BASE = 1;
const int CALC_ADD_LATER = 2;
const int CALC_MUL_LATER = 3;
const int CALC_ADD_FIXED = 4;
static const int CALC_ADD_BASE = 0;
static const int CALC_MUL_BASE = 1;
static const int CALC_ADD_LATER = 2;
static const int CALC_MUL_LATER = 3;
static const int CALC_ADD_FIXED = 4;
const int DIRECTION_CNT = 8;
static const int DIRECTION_CNT = 8;
// IMPORTANT: east is positive for x and SOUTH is positive for y
// initializes all directions to an int
enum direction { north = 0, south, east, west, northeast,
enum direction : int { north = 0, south, east, west, northeast,
northwest, southeast, southwest
};
const position MOVE[DIRECTION_CNT] = {
static const position MOVE[DIRECTION_CNT] = {
{0, -1}, {0, 1}, {1, 0}, {-1, 0},
{1, -1}, {-1, -1}, {1, 1}, {-1, 1}
};
const int MAP_HEIGHT = 25;
const int MAP_WIDTH = 79;
const int DISPLAY_HEIGHT = 30;
const int DISPLAY_WIDTH = 79;
const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH +
static const int MAP_HEIGHT = 25;
static const int MAP_WIDTH = 79;
static const int DISPLAY_HEIGHT = 30;
static const int DISPLAY_WIDTH = 79;
static const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH +
DISPLAY_WIDTH * 3;
// TODO: list all extra features
@ -105,38 +106,35 @@ const int DISPLAY_BUFFER_SIZE = DISPLAY_HEIGHT * DISPLAY_WIDTH +
// check if feature is enabled:
// features & FEATURE_XXX
typedef unsigned int feature; // can extend to unsigned long (long) if needed
const feature FEATURE_NCURSES = 1 << 0;
const feature FEATURE_RAND_MAP = 1 << 1;
const feature FEATURE_ENEMIES_CHASE = 1 << 2;
const feature FEATURE_INVENTORY = 1 << 3;
const feature FEATURE_THROW = 1 << 4;
const feature FEATURE_REVISIT = 1 << 5;
const feature FEATURE_LOG = 1 << 6;
const feature FEATURE_SEED = 1 << 7;
const feature FEATURE_MENU = 1 << 8;
const feature FEATURE_IN_FILE = 1 << 9;
const feature FEATURE_OUT_FILE = 1 << 10;
const feature FEATURE_EXTRA_STUFF = 1 << 11;
const feature FEATURE_EXTRA_LEVELS = 1 << 12;
const feature FEATURE_COLORFUL = 1 << 13;
static const feature FEATURE_NCURSES = 1 << 0;
static const feature FEATURE_RAND_MAP = 1 << 1;
static const feature FEATURE_ENEMIES_CHASE = 1 << 2;
static const feature FEATURE_INVENTORY = 1 << 3;
static const feature FEATURE_THROW = 1 << 4;
static const feature FEATURE_REVISIT = 1 << 5;
static const feature FEATURE_WALK_OVER = 1 << 6;
static const feature FEATURE_SEED = 1 << 7;
static const feature FEATURE_MENU = 1 << 8;
static const feature FEATURE_IN_FILE = 1 << 9;
static const feature FEATURE_OUT_FILE = 1 << 10;
static const feature FEATURE_EXTRA_STUFF = 1 << 11;
static const feature FEATURE_EXTRA_LEVELS = 1 << 12;
static const feature FEATURE_COLORFUL = 1 << 13;
const feature FEATURE_PANIC_SEED = 1 << 27;
const feature FEATURE_PANIC_FILE = 1 << 28;
const feature FEATURE_PANIC = 1 << 29;
const feature FEATURE_CONFLICT = 1 << 30;
const feature FEATURE_LIST_ARGS = 1 << 31;
static const feature FEATURE_PANIC_SEED = 1 << 27;
static const feature FEATURE_PANIC_FILE = 1 << 28;
static const feature FEATURE_PANIC = 1 << 29;
static const feature FEATURE_CONFLICT = 1 << 30;
static const feature FEATURE_LIST_ARGS = 1 << 31;
const int RETURN_FINE = 0;
const int RETURN_PANICKED = 1;
static const int RETURN_FINE = 0;
static const int RETURN_PANICKED = 1;
const int COLOR_BLACK_ON_WHITE = 8;
static const int COLOR_BLACK_ON_WHITE = 8;
typedef std::vector<position> position_list;
typedef std::vector<direction> direction_list;
const int GOLD_SMALL = 1;
const int GOLD_NORMAL = 2;
const int GOLD_MERCHANT = 4;
const int GOLD_DRAGON = 6;
static const int GOLD_SMALL = 1;
static const int GOLD_NORMAL = 2;
static const int GOLD_MERCHANT = 4;
static const int GOLD_DRAGON = 6;
#endif

View File

@ -1,84 +0,0 @@
#include "curses_input.h"
curses_input::curses_input(cursor *new_curse):
curse{new_curse} {}
game_command curses_input::get_command() {
switch (curse->getcmd()) {
case 'h':
return game_command::move_west;
case 'j':
return game_command::move_south;
case 'k':
return game_command::move_north;
case 'l':
return game_command::move_east;
case 'y':
return game_command::move_northwest;
case 'u':
return game_command::move_northeast;
case 'b':
return game_command::move_southwest;
case 'n':
return game_command::move_southeast;
case 'a':
break; // wait for another command
case '<':
return game_command::up_stairs;
case '>':
return game_command::down_stairs;
case 'q':
return game_command_terminate;
case 'f':
return game_command::the_world;
case 'r':
return game_restart;
default:
return game_command_pass;
}
switch (curse->getcmd()) {
case 'h':
return game_command::apply_west;
case 'j':
return game_command::apply_south;
case 'k':
return game_command::apply_north;
case 'l':
return game_command::apply_east;
case 'y':
return game_command::apply_northwest;
case 'u':
return game_command::apply_northeast;
case 'b':
return game_command::apply_southwest;
case 'n':
return game_command::apply_southeast;
default:
return game_command::apply_panic;
}
return game_command::game_command_panic;
}

View File

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

View File

@ -1,5 +1,7 @@
#include "display.h"
#include "constants.h"
display::display():
contents{DISPLAY_BUFFER_SIZE, 0} {
for (int i = 0; i < DISPLAY_BUFFER_SIZE; ++i)

View File

@ -1,9 +1,9 @@
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <ncurses.h>
#include <vector>
#include "position.h"
#include "constants.h"
#include "cursor.h"
class display {

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 "../display.h"
class console_output final : public display {
private:
std::ostream &out;
std::string get_code(const int attrs);
public:
console_output(std::ostream &cout);
void render() override;
void print_char(const position &pos,
const char ch, const int attrs) override;
void print_str(const position &pos,
const std::string &str, const int attrs) override;
};
#endif

View File

@ -1,5 +1,7 @@
#include "curses_output.h"
#include "../constants.h"
curses_output::curses_output(cursor *new_curse):
curse{new_curse} {}

View File

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

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/display/file_output.h Normal file
View File

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

View File

@ -1,34 +0,0 @@
#include "dragon.h"
dragon::dragon(RNG *rng, const position &pos):
character{rng, race::rdragon, pos} {
gold = 0;
hostile = true;
}
result dragon::attack(const direction dir, character_list &chlist) {
position tmp{pos + MOVE[dir]};
for (auto &ch : chlist)
if (tmp == ch->get_position()) {
return ch->get_hit(race, ATK, base_hit_rate);
}
return result::fine;
}
result dragon::get_hit(const enum race &race, const int atk,
const fraction hitrate) {
if (rng->trial(hitrate)) // This is a hit!
HP = std::max(HP - calc_dmg(atk, DEF), 0);
if (HP == 0)
return result::died;
return result::hit;
}
result dragon::move(const direction dir,
const position_list &available_positions) {
return result::fine;
}

View File

@ -1,17 +0,0 @@
#ifndef __DRAGON_H__
#define __DRAGON_H__
#include "characters.h"
class dragon final: public character {
public:
dragon(RNG *rng, const position &pos);
virtual result attack(const direction dir,
character_list &chlist) override;
virtual result get_hit(const enum race &race, const int atk,
const fraction hit_rate) override;
virtual result move(const direction dir,
const position_list &avilable_positions) override;
};
#endif

View File

@ -1,31 +0,0 @@
#include "drow.h"
#include <algorithm>
#include <math.h>
drow::drow(RNG *rng, const position &pos):
character{rng, race::rdrow, pos} {
gold = 0;
hostile = true;
}
result drow::attack(const direction dir, character_list &chlist) {
position tmp{pos + MOVE[dir]};
for (auto &ch : chlist)
if (tmp == ch->get_position()) {
return ch->get_hit(race, ATK, base_hit_rate);
}
return result::fine;
}
result drow::get_hit(const enum race &race, const int atk,
const fraction hitrate) {
if (rng->trial(hitrate))
HP = std::max(HP - calc_dmg(atk, DEF), 0);
if (HP == 0)
return result::died;
return result::hit;
}

View File

@ -1,15 +0,0 @@
#ifndef __DROW_H__
#define __DROW_H__
#include "characters.h"
class drow final: public character {
public:
drow(RNG *rng,
const position &pos);
virtual result attack(const direction dir,
character_list &chlist) override;
virtual result get_hit(const enum race &race, const int atk,
const fraction hit_rate) override;
};
#endif

View File

@ -1,20 +1,85 @@
#include "enemies.h"
// TODO: implement after characters
void new_enemy(RNG *rng, std::unique_ptr<character> &pch, const position &pos,
bool extras) {
if (extras) {
#include "constants.h"
#include "enemies/dragon.h"
#include "enemies/dwarf.h"
#include "enemies/elf.h"
#include "enemies/halfling.h"
#include "enemies/human.h"
#include "enemies/merchant.h"
#include "enemies/orc.h"
} else {
}
}
void new_dragon(RNG *rng, std::unique_ptr<character> &pch, const position &pos,
const position &fallback) {
void new_dragon(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const position &fallback,
const feature enabled_features, int which_room) {
const position nil{0, 0};
if (pos != nil)
pch = std::make_unique<dragon>(rng, pos);
p = std::make_unique<dragon>(rng, enabled_features,
pos, which_room);
else
pch = std::make_unique<dragon>(rng, fallback);
p = std::make_unique<dragon>(rng, enabled_features,
fallback, which_room);
}
const int EXCNT = 6;
const race EXCHOICES[EXCNT] = {
rhuman, rdwarf, rhalfling, relf, rorc, rmerchant
};
const int CNT = 18;
const race CHOICES[CNT] = {
rhuman, rhuman, rhuman, rhuman,
rdwarf, rdwarf, rdwarf,
rhalfling, rhalfling, rhalfling, rhalfling, rhalfling,
relf, relf,
rorc, rorc,
rmerchant, rmerchant
};
enum race get_extra_race(RNG *rng) {
return EXCHOICES[rng->rand_under(EXCNT)];
}
enum race get_normal_race(RNG *rng) {
return CHOICES[rng->rand_under(CNT)];
}
void new_enemy(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const feature enabled_features,
int which_room) {
using std::make_unique;
enum race r;
if (enabled_features & FEATURE_EXTRA_STUFF)
r = get_extra_race(rng);
else
r = get_normal_race(rng);
p = nullptr;
switch (r) {
case rdwarf:
p = make_unique<dwarf>(rng, enabled_features, pos, which_room);
break;
case rhuman:
p = make_unique<human>(rng, enabled_features, pos, which_room);
break;
case relf:
p = make_unique<elf>(rng, enabled_features, pos, which_room);
break;
case rorc:
p = make_unique<orc>(rng, enabled_features, pos, which_room);
break;
case rmerchant:
p = make_unique<merchant>(rng, enabled_features, pos, which_room);
break;
default:
break;
}
}

View File

@ -2,13 +2,14 @@
#define __ENEMIES_H__
#include <memory>
#include "characters.h"
#include "races.h"
#include "enemy.h"
void new_enemy(RNG *rng, std::unique_ptr<character> &pch, const position &pos,
bool extras);
void new_dragon(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const position &fallback,
const feature enabled_features, int which_room);
void new_dragon(RNG *rng, std::unique_ptr<character> &pch, const position &pos,
const position &fallback);
void new_enemy(RNG *rng, std::unique_ptr<enemy_base> &p,
const position &pos, const feature enabled_features,
int which_room);
#endif

22
src/enemies/dragon.cc Normal file
View File

@ -0,0 +1,22 @@
#include "dragon.h"
#include "../constants.h"
dragon::dragon(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, rdragon, pos, gen_room_num, "D"} {}
void dragon::print(display *out) {
out->print_char(pos, 'D', COLOR_PAIR(COLOR_RED));
}
const char *dragon::get_race_name() const {
return "Dragon";
}
long_result dragon::act(level *lvl, character *pc, bool hostile) {
if (is_adjacent(pos, pc->get_pos()) && hostile)
return attack(pc);
return {fine, ""};
}

15
src/enemies/dragon.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __DRAGON_H__
#define __DRAGON_H__
#include "../enemy.h"
class dragon final: public enemy_base {
public:
dragon(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
long_result act(level *lvl, character *pc, bool hostile) override;
};
#endif

15
src/enemies/dwarf.cc Normal file
View File

@ -0,0 +1,15 @@
#include "dwarf.h"
#include "../constants.h"
dwarf::dwarf(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, rdwarf, pos, gen_room_num, "W"} {}
void dwarf::print(display *out) {
out->print_char(pos, 'W', COLOR_PAIR(COLOR_RED));
}
const char *dwarf::get_race_name() const {
return "Dwarf";
}

14
src/enemies/dwarf.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __DWARF_H__
#define __DWARF_H__
#include "../enemy.h"
class dwarf final: public enemy_base {
public:
dwarf(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
};
#endif

31
src/enemies/elf.cc Normal file
View File

@ -0,0 +1,31 @@
#include "elf.h"
#include "../constants.h"
elf::elf(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, relf, pos, gen_room_num, "H"} {}
void elf::print(display *out) {
out->print_char(pos, 'H', COLOR_PAIR(COLOR_RED));
}
const char *elf::get_race_name() const {
return "Elf";
}
long_result elf::attack(character *ch) {
auto res1 = ch->get_hit(this, ATK, base_hit_rate);
if (res1.res == result::died)
return res1;
auto res2 = ch->get_hit(this, ATK, base_hit_rate);
if (res1.res == miss && res2.res == miss)
return {miss, res1.msg + res2.msg};
else if (res2.res == died)
return {died, res1.msg + res2.msg};
else
return {hit, res1.msg + res2.msg};
}

15
src/enemies/elf.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __ELF_H__
#define __ELF_H__
#include "../enemy.h"
class elf final: public enemy_base {
public:
elf(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
long_result attack(character *ch) override;
};
#endif

41
src/enemies/halfling.cc Normal file
View File

@ -0,0 +1,41 @@
#include "halfling.h"
#include "../constants.h"
fraction halfling::HIT_RATE_MUL = {1, 2};
halfling::halfling(RNG *rng, const feature enabled_features,
const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, rhalfling, pos, gen_room_num, "L"} {}
void halfling::print(display *out) {
out->print_char(pos, 'L', COLOR_PAIR(COLOR_RED));
}
const char *halfling::get_race_name() const {
return "Halfling";
}
long_result halfling::get_hit(character *ch, const int tATK,
const fraction &hit_rate) {
auto nhit_rate = HIT_RATE_MUL * hit_rate;
if (rng->trial(nhit_rate)) {
int tmp = calc_dmg(tATK, DEF);
tmp = tmp > HP ? HP : tmp;
HP -= tmp;
if (HP == 0)
return {result::hit,
"PC deals " + std::to_string(tmp) +
" damage to " + abbrev + ". " + abbrev +
" is slain by PC. "};
return {result::hit, "PC deals " +
std::to_string(tmp) + " damage to " + abbrev};
}
return {miss, "PC tried to hit " + abbrev +
" but missed. The halfling laughed. "};
}

17
src/enemies/halfling.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __HALFLING_H__
#define __HALFLING_H__
#include "../enemy.h"
class halfling final: public enemy_base {
static fraction HIT_RATE_MUL;
public:
halfling(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate);
};
#endif

15
src/enemies/human.cc Normal file
View File

@ -0,0 +1,15 @@
#include "human.h"
#include "../constants.h"
human::human(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, rhuman, pos, gen_room_num, "H"} {}
void human::print(display *out) {
out->print_char(pos, 'H', COLOR_PAIR(COLOR_RED));
}
const char *human::get_race_name() const {
return "Human";
}

14
src/enemies/human.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __HUMAN_H__
#define __HUMAN_H__
#include "../enemy.h"
class human final: public enemy_base {
public:
human(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
};
#endif

15
src/enemies/merchant.cc Normal file
View File

@ -0,0 +1,15 @@
#include "merchant.h"
#include "../constants.h"
merchant::merchant(RNG *rng, const feature enabled_features,
const position &pos, const int gen_room_num):
enemy_base{rng, enabled_features, rmerchant, pos, gen_room_num, "M"} {}
void merchant::print(display *out) {
out->print_char(pos, 'M', COLOR_PAIR(COLOR_RED));
}
const char *merchant::get_race_name() const {
return "Merchant";
}

14
src/enemies/merchant.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __MERCHANT_H__
#define __MERCHANT_H__
#include "../enemy.h"
class merchant final: public enemy_base {
public:
merchant(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
};
#endif

15
src/enemies/orc.cc Normal file
View File

@ -0,0 +1,15 @@
#include "orc.h"
#include "../constants.h"
orc::orc(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num):
enemy_base{rng, enabled_features, rorc, pos, gen_room_num, "O"} {}
void orc::print(display *out) {
out->print_char(pos, 'O', COLOR_PAIR(COLOR_RED));
}
const char *orc::get_race_name() const {
return "Orc";
}

14
src/enemies/orc.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __ORC_H__
#define __ORC_H__
#include "../enemy.h"
class orc final: public enemy_base {
public:
orc(RNG *rng, const feature enabled_features, const position &pos,
const int gen_room_num);
void print(display *out) override;
const char *get_race_name() const override;
};
#endif

115
src/enemy.cc Normal file
View File

@ -0,0 +1,115 @@
#include "enemy.h"
#include "constants.h"
#include "player.h"
#include "level.h"
enemy_base::enemy_base(RNG *rng, const feature enabled_features,
const enum race &nrace, const position &pos,
const int gen_room_num, std::string abbrev):
character{rng, enabled_features, nrace, pos},
room_num{gen_room_num}, abbrev{abbrev} {
base_hit_rate_reset = {1, 2};
}
int enemy_base::get_room_num() const {
return room_num;
}
long_result enemy_base::act(level *lvl, character *pc, bool hostile) {
if (is_adjacent(pos, pc->get_pos()) && hostile)
return attack(pc);
if (enabled_features & FEATURE_ENEMIES_CHASE &&
distance_sqr(pos, pc->get_pos()) <= DIST_THRESHOLD_SQR) {
position tmp;
position target = {BIG_NUMBER, BIG_NUMBER};
for (int i = 0; i < DIRECTION_CNT; ++i) {
tmp = pos + MOVE[i];
if (lvl->get_room(tmp) == room_num &&
lvl->is_available(tmp) &&
distance_sqr(tmp, pc->get_pos()) <
distance_sqr(target, pc->get_pos()))
target = tmp;
}
pos = target;
return {fine, ""};
}
int choices_cnt = 0;
position choices[DIRECTION_CNT];
for (int i = 0; i < DIRECTION_CNT; ++i)
if (lvl->get_room(pos + MOVE[i]) == room_num &&
lvl->is_available(pos + MOVE[i])) {
choices[choices_cnt] = pos + MOVE[i];
++choices_cnt;
}
if (choices_cnt)
pos = choices[rng->rand_under(choices_cnt)];
return {fine, ""};
}
long_result enemy_base::attack(character *ch) {
return ch->get_hit(this, ATK, base_hit_rate);
}
std::string enemy_base::get_abbrev() const {
return abbrev;
}
long_result enemy_base::get_hit(character *ch, const int tATK,
const fraction &hit_rate) {
if (rng->trial(hit_rate)) {
int tmp = calc_dmg(tATK, DEF);
tmp = tmp > HP ? HP : tmp;
HP -= tmp;
if (HP == 0)
return {result::hit,
"PC deals " + std::to_string(tmp) +
" damage to " + abbrev + ". " + abbrev +
" is slain by PC. "};
return {result::hit, "PC deals " +
std::to_string(tmp) + " damage to " + abbrev};
}
return {miss, "PC tried to hit " + abbrev + " but missed. "};
}
void enemy_base::dies(level *lvl, character *pc) {
auto elist = lvl->get_elist();
for (size_t i = 0; i < elist.size(); ++i)
if (elist[i] == this) {
elist.erase(elist.begin() + i);
break;
}
if (race == race::rdragon) {
return;
} else if (race == race::rmerchant) {
lvl->add_gold({GOLD_MERCHANT, pos});
} else if (race == race::rhuman) {
lvl->add_gold({GOLD_NORMAL, pos});
auto plist = lvl->get_available_around_all(pos);
lvl->add_gold({GOLD_NORMAL,
rng->get_rand_in_vector(plist)});
}
((player_base *)pc)->add_gold(rand_gold_drop(rng));
}
enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) {
for (auto e : elist)
if (e->get_pos() == pos)
return e;
return nullptr;
}

36
src/enemy.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef __ENEMY_H__
#define __ENEMY_H__
#include "characters.h"
class enemy_base : public character {
private:
const static int BIG_NUMBER = 500;
const static int DIST_THRESHOLD_SQR = 8 * 8;
protected:
const int room_num;
std::string abbrev;
public:
enemy_base(RNG *rng, const feature enabled_features,
const enum race &nrace, const position &pos,
const int gen_room_num, std::string abbrev);
int get_room_num() const;
virtual long_result act(level *lvl, character *pc, bool hostile = true);
virtual long_result attack(character *ch) override;
virtual long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate) override;
virtual std::string get_abbrev() const override;
virtual void dies(level *lvl, character *pc);
};
typedef std::vector<enemy_base *> enemy_list;
enemy_base *get_enemy_at(const position &pos, const enemy_list &elist);
#endif

View File

@ -1,42 +0,0 @@
#include "file_input.h"
#include <utility>
#include <string>
file_input::file_input(std::ifstream &&ifs):
in{std::move(ifs)} {}
game_command file_input::get_command() {
std::string cmd;
in >> cmd;
game_command tmp;
if (in.eof())
return game_command_terminate;
if (cmd == "q")
return game_command_terminate;
else if (cmd == "f")
return the_world;
else if (cmd == "r")
return game_restart;
else if (cmd == "u" || cmd == "a") {
bool use = cmd == "u";
in >> cmd;
if (in.eof())
return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) ==
game_command_panic
? tmp : tmp - move_north +
(use ? apply_north : attack_north));
} else {
auto tmp = get_direction(cmd);
if (tmp != game_command_panic)
return tmp;
}
return game_command_pass;
}

View File

@ -1,16 +0,0 @@
#ifndef __FILE_INPUT_H__
#define __FILE_INPUT_H__
#include <fstream>
#include "input.h"
class file_input final : public input {
private:
std::ifstream in;
public:
// This is for cin
file_input(std::ifstream &&ifs);
game_command get_command() override;
};
#endif

View File

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

View File

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

View File

@ -42,3 +42,16 @@ int fraction::gcd(int a, int b) {
return gcd(b, a % b);
}
fraction &fraction::operator=(const fraction &frac) {
numerator = frac.numerator;
denominator = frac.denominator;
this->simplify();
return *this;
}
fraction &fraction::operator*=(const fraction &frac) {
numerator *= frac.numerator;
denominator *= frac.denominator;
return *this;
}

View File

@ -7,6 +7,8 @@ struct fraction final {
fraction operator+(const fraction &frac);
fraction operator*(const fraction &frac);
fraction &operator*=(const fraction &frac);
fraction &operator=(const fraction &frac);
bool operator==(const fraction &frac);
bool operator!=(const fraction &frac);
fraction &simplify();

View File

@ -1,25 +1,21 @@
#include "game.h"
#include "races.h"
#include "constants.h"
#include "pc.h"
game::game(const enum race starting_race,
const feature enabled_features,
input *new_in,
display *new_out,
logger *new_log,
RNG *new_rng):
enabled_features{enabled_features},
in{new_in}, out{new_out}, log{new_log}, rng{new_rng},
in{new_in}, out{new_out}, rng{new_rng},
curr_turn{0} {
const position nil{0, 0};
the_world = false;
// TODO: add the other races
switch (starting_race) {
case race::rshade:
player = std::make_unique<shade>(rng, nil);
break;
}
init_player(rng, player, enabled_features, starting_race);
if (enabled_features & FEATURE_EXTRA_LEVELS)
max_level = rng->rand_between(MIN_LEVEL_CNT, MAX_LEVEL_CNT + 1);
@ -45,118 +41,43 @@ void game::new_level() {
rng, enabled_features));
}
result game::player_moves(game_command cmd) {
if (cmd == game_command_terminate) {
return result::terminate;
} else if (cmd >= move_north && cmd <= move_southwest) { // Tried to move
if (enabled_features & FEATURE_NCURSES)
return player_move_or_attack((direction)(cmd - move_north));
return player->move((direction)(cmd - move_north),
levels[curr_level]->get_available_around(
player->get_position()));
} else if (cmd >= apply_north && cmd <= apply_southwest) {
auto res = player->apply((direction)(cmd - apply_north),
levels[curr_level]->get_plist());
if (res.res == applied) {
msg += "PC used potion ";
msg += POTION_REAL_NAME[res.which->get_type()];
msg += ". ";
} else {
msg += "PC tried to drink thin air. ";
}
return res.res;
} else if (cmd == apply_panic) {
msg += "PC tried to use in some non-existent direction. ";
return fine;
} else if (cmd >= attack_north && cmd <= attack_southwest) {
auto res = player->attack((direction)(cmd - attack_north),
levels[curr_level]->get_chlist());
if (res.res == hit) {
msg += "PC deals " + std::to_string(res.dmg_dealt) +
" damage to " +
CHARACTER_REP[res.which->get_race()] +
" (" + std::to_string(res.remaining_HP) +
" HP). ";
} else if (res.res == miss) {
msg += "PC missed ";
msg += CHARACTER_REP[res.which->get_race()];
msg += ". ";
} else {
msg += "PC tried to attack thin air. ";
}
if (res.which->get_race() == rmerchant)
hostile_merchants = true;
return res.res;
} else if (cmd == up_stairs) {
if (player->get_position() ==
levels[curr_level]->get_up_stairs())
return go_up;
msg += "PC tried to fly through the ceiling. ";
return fine;
} else if (cmd == down_stairs) {
if (player->get_position() ==
levels[curr_level]->get_down_stairs())
return go_down;
msg += "PC tried to dig through the floor. ";
return fine;
} else if (cmd == the_world) {
msg += "PC toggled Stand: The World! ";
return toggle_the_world;
} else if (cmd == game_restart) {
return restart_game;
} else if (cmd == game_command_pass) {
return fine;
} else if (cmd == game_command_panic) {
msg += "PC tried to produce some undefined behaviour. ";
return unknown;
}
msg += "PC tried to produce some undefined behaviour. ";
return unknown;
}
#include <algorithm>
bool compare_characters(character *&a, character *&b) {
return a->get_position() < b->get_position();
bool compare_characters(const character *a, const character *b) {
return a->get_pos() < b->get_pos();
}
void game::move_enemies() {
if (the_world)
return;
character_list enemies = levels[curr_level]->get_chlist();
std::sort(enemies.begin(), enemies.end(), &game::compare_characters);
auto enemies = levels[curr_level]->get_elist();
std::sort(enemies.begin(), enemies.end(), ::compare_characters);
for (auto ch : enemies) {
bool hostile = ch->get_race() == rmerchant ?
hostile_merchants : true;
ch->start_turn();
ch->calc_effects();
auto res =
ch->act(levels[curr_level].get(), player.get(), hostile);
if (player->is_dead())
return;
if (ch->is_dead())
ch->dies(levels[curr_level].get(), player.get());
msg += res.msg;
}
}
game_status game::run() {
msg = "";
auto res = player_moves(in->get_command());
player->start_turn();
auto res = player->interpret_command(levels[curr_level].get(),
in->get_command());
msg = res.msg;
if (!(enabled_features & FEATURE_NCURSES) &&
res == result::moved &&
(player->get_position() ==
levels[curr_level]->get_down_stairs()))
res = go_down;
if (!(enabled_features & FEATURE_NCURSES) &&
res == result::moved &&
(player->get_position() ==
levels[curr_level]->get_up_stairs()))
res = go_up;
switch (res) {
switch (res.res) {
case result::terminate:
return terminated;
@ -170,7 +91,6 @@ game_status game::run() {
player->discard_level_effects();
++curr_level;
new_level();
msg += "PC went down a floor. ";
break;
}
@ -180,8 +100,7 @@ game_status game::run() {
player->discard_level_effects();
--curr_level;
player->set_position(levels[curr_level]->get_down_stairs());
msg += "PC went up a floor. ";
player->set_pos(levels[curr_level]->get_down_stairs());
break;
}
@ -203,14 +122,14 @@ game_status game::run() {
}
player->start_turn();
player->apply_effects();
player->calc_effects();
if (player->get_HP() <= 0)
if (player->is_dead())
return game_status::dead;
move_enemies();
if (player->get_HP() <= 0)
if (player->is_dead())
return game_status::dead;
++curr_turn;
@ -223,8 +142,6 @@ const position STATUS_HP{0, 26};
const position STATUS_ATK{0, 27};
const position STATUS_DEF{0, 28};
const position STATUS_ACTION{0, 29};
const char *ASK_ATK_MERCHANT =
"Action: Do you really want to attack the peaceful merchant? [Y/N]";
size_t game::get_curr_turn() const {
return curr_turn;
@ -237,61 +154,15 @@ void game::print() {
}
levels[curr_level]->print(out);
player->print(out, true);
player->print(out);
std::string tmp = "Race: ";
tmp += RACE_NAME[player->get_race()];
tmp += player->get_race_name();
tmp += " Gold: " + std::to_string(player->get_gold());
out->print_str(STATUS_RACE, tmp);
tmp = "Floor " + std::to_string(curr_level + 1);
out->print_str(STATUS_FLOOR, tmp);
tmp = "Atk: " + std::to_string(player->get_ATK());
out->print_str(STATUS_ATK, tmp);
tmp = "Def: " + std::to_string(player->get_DEF());
out->print_str(STATUS_DEF, tmp);
tmp = "Action: " + msg;
out->print_str(STATUS_ACTION, tmp);
}
result game::player_move_or_attack(const direction &dir) {
auto avlbl = levels[curr_level]->get_available_around(
player->get_position());
if (find(avlbl, player->get_position() + MOVE[dir])
!= avlbl.size()) {
player->set_position(player->get_position() + MOVE[dir]);
return result::moved;
}
auto tmp = player->get_position() + MOVE[dir];
character *target;
for (auto ch : levels[curr_level]->get_chlist())
if (tmp == ch->get_position()) {
target = ch;
}
auto res = player->attack(dir, target);
if (target->get_race() == rmerchant)
hostile_merchants = true;
if (res.res == hit) {
msg += "PC deals " +
std::to_string(res.dmg_dealt) + " damage to " +
CHARACTER_REP[res.which->get_race()] + " (" +
std::to_string(res.remaining_HP) + " HP). ";
} else if (res.res == miss) {
msg += "PC missed ";
msg += CHARACTER_REP[res.which->get_race()];
msg += ". ";
} else {
msg += "PC tried to attack thin air. ";
}
return res.res;
out->print_str(STATUS_FLOOR, "Floor " + std::to_string(curr_level + 1));
out->print_str(STATUS_HP, "HP: " + std::to_string(player->get_HP()));
out->print_str(STATUS_ATK, "Atk: " + std::to_string(player->get_ATK()));
out->print_str(STATUS_DEF, "Def: " + std::to_string(player->get_DEF()));
out->print_str(STATUS_ACTION, "Action: " + msg);
}

View File

@ -2,13 +2,14 @@
#define __GAME_H__
#include <memory>
#include <vector>
#include "constants.h"
#include "display.h"
#include "input.h"
#include "rng.h"
#include "characters.h"
#include "level.h"
#include "log.h"
#include "player.h"
enum game_status : int;
class game final {
private:
@ -21,12 +22,11 @@ private:
input *in;
display *out;
logger *log;
RNG *rng;
unsigned int curr_turn;
std::unique_ptr<character> player;
std::unique_ptr<player_base> player;
std::vector<std::unique_ptr<level>> levels;
size_t max_level;
@ -42,7 +42,6 @@ public:
const feature enabled_features,
input *new_in,
display *new_out,
logger *new_log,
RNG *new_rng);
game_status run();
size_t get_curr_level() const;
@ -50,11 +49,8 @@ public:
const character *get_player() const;
void print();
private:
result player_moves(game_command cmd);
void new_level();
void move_enemies();
result player_move_or_attack(const direction &dir);
bool compare_characters(const character *&a, const character *&b);
};
#endif

View File

@ -1,35 +0,0 @@
#include "goblin.h"
goblin::goblin(RNG *rng, const position &pos):
character{rng, race::rgoblin, pos} {
gold = 0;
hostile = true;
}
result goblin::attack(const direction dir, character_list &chlist) {
position tmp{pos + MOVE[dir]};
for (auto &ch : chlist)
if (tmp == ch->get_position()) {
auto res = ch->get_hit(race, ATK, base_hit_rate);
if (res == result::died) {
gold += GAIN_GOLD;
}
return res;
}
return result::fine;
}
result goblin::get_hit(const enum race &race, const int atk,
const fraction hitrate) {
if (rng->trial(hitrate))
HP = std::max(HP - calc_dmg(atk, DEF), 0);
if (HP == 0)
return result::died;
return result::hit;
}

View File

@ -1,16 +0,0 @@
#ifndef __GOBLIN_H__
#define __GOBLIN_H__
#include "characters.h"
class goblin final: public character {
static const int GAIN_GOLD = 5;
public:
goblin(RNG *rng, const position &pos);
virtual result attack(const direction dir,
character_list &chlist) override;
virtual result get_hit(const enum race &race, const int atk,
const fraction hit_rate) override;
};
#endif

View File

@ -1,5 +1,7 @@
#include "gold.h"
#include "constants.h"
int rand_gold_pile(RNG *rng) {
const int denominator = 8;
const int normal = 5;
@ -16,3 +18,23 @@ int rand_gold_pile(RNG *rng) {
return 0;
}
gold get_gold_at(const position &pos, gold_list &glist) {
gold ret = {0, {0, 0}};
for (size_t i = 0; i < glist.size(); ++i)
if (glist[i].pos == pos) {
ret = glist[i];
glist.erase(i + glist.begin());
return ret;
}
return ret;
}
int rand_gold_drop(RNG *rng) {
if (rng->coin_flip())
return GOLD_NORMAL;
return GOLD_SMALL;
}

View File

@ -2,7 +2,6 @@
#define __GOLD_H__
#include <vector>
#include "constants.h"
#include "position.h"
#include "rng.h"
@ -13,6 +12,8 @@ struct gold {
typedef std::vector<gold> gold_list;
gold get_gold_at(const position &pos, gold_list &glist);
int rand_gold_pile(RNG *rng);
int rand_gold_drop(RNG *rng);
#endif

View File

@ -1,5 +1,7 @@
#include "input.h"
#include "constants.h"
const char *COMMANDS[] = {
"no", "so", "ea", "we", "ne", "nw", "se", "sw"
};

View File

@ -1,7 +1,8 @@
#ifndef __INPUT_H__
#define __INPUT_H__
#include <string>
#include "constants.h"
enum game_command : int;
class input {
public:

View File

@ -0,0 +1,46 @@
#include "console_input.h"
#include <utility>
#include <string>
#include "../constants.h"
console_input::console_input(std::istream &cin):
in{cin} {}
game_command console_input::get_command() {
std::string cmd;
in >> cmd;
game_command tmp;
if (in.eof())
return game_command_terminate;
if (cmd == "q")
return game_command_terminate;
else if (cmd == "f")
return the_world;
else if (cmd == "r")
return game_restart;
else if (cmd == "u" || cmd == "a") {
bool use = cmd == "u";
in >> cmd;
if (in.eof())
return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) ==
game_command_panic
? tmp : tmp - move_north +
(use ? apply_north : attack_north));
} else if (cmd == "yes") {
return game_command::enter;
} else {
auto tmp = get_direction(cmd);
if (tmp != game_command_panic)
return tmp;
}
return game_command_pass;
}

16
src/input/console_input.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __CONSOLE_INPUT_H__
#define __CONSOLE_INPUT_H__
#include <iostream>
#include "../input.h"
class console_input final : public input {
private:
std::istream &in;
public:
// This is for cin
console_input(std::istream &cin);
game_command get_command() override;
};
#endif

89
src/input/curses_input.cc Normal file
View File

@ -0,0 +1,89 @@
#include "curses_input.h"
#include "../constants.h"
curses_input::curses_input(cursor *new_curse):
curse{new_curse} {}
game_command curses_input::get_command() {
switch (curse->getcmd()) {
case 'h':
return game_command::move_west;
case 'j':
return game_command::move_south;
case 'k':
return game_command::move_north;
case 'l':
return game_command::move_east;
case 'y':
return game_command::move_northwest;
case 'u':
return game_command::move_northeast;
case 'b':
return game_command::move_southwest;
case 'n':
return game_command::move_southeast;
case 'a':
break; // wait for another command
case '<':
return game_command::up_stairs;
case '>':
return game_command::down_stairs;
case 'q':
return game_command_terminate;
case 'f':
return game_command::the_world;
case 'r':
return game_restart;
case KEY_ENTER:
return game_command::enter;
default:
return game_command_pass;
}
switch (curse->getcmd()) {
case 'h':
return game_command::apply_west;
case 'j':
return game_command::apply_south;
case 'k':
return game_command::apply_north;
case 'l':
return game_command::apply_east;
case 'y':
return game_command::apply_northwest;
case 'u':
return game_command::apply_northeast;
case 'b':
return game_command::apply_southwest;
case 'n':
return game_command::apply_southeast;
default:
return game_command::apply_panic;
}
return game_command::game_command_panic;
}

View File

@ -2,9 +2,8 @@
#define __CURSES_INPUT_H__
#include <memory>
#include "input.h"
#include "constants.h"
#include "cursor.h"
#include "../input.h"
#include "../cursor.h"
class curses_input final: public input {
private:

46
src/input/file_input.cc Normal file
View File

@ -0,0 +1,46 @@
#include "file_input.h"
#include <utility>
#include <string>
#include "../constants.h"
file_input::file_input(std::ifstream &&ifs):
in{std::move(ifs)} {}
game_command file_input::get_command() {
std::string cmd;
in >> cmd;
game_command tmp;
if (in.eof())
return game_command_terminate;
if (cmd == "q")
return game_command_terminate;
else if (cmd == "f")
return the_world;
else if (cmd == "r")
return game_restart;
else if (cmd == "u" || cmd == "a") {
bool use = cmd == "u";
in >> cmd;
if (in.eof())
return game_command_panic;
return (game_command)((tmp = get_direction(cmd)) ==
game_command_panic
? tmp : tmp - move_north +
(use ? apply_north : attack_north));
} else if (cmd == "yes") {
return game_command::enter;
} else {
auto tmp = get_direction(cmd);
if (tmp != game_command_panic)
return tmp;
}
return game_command_pass;
}

16
src/input/file_input.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __FILE_INPUT_H__
#define __FILE_INPUT_H__
#include <fstream>
#include "../input.h"
class file_input final : public input {
private:
std::ifstream in;
public:
// This is for cin
file_input(std::ifstream &&ifs);
game_command get_command() override;
};
#endif

View File

@ -1,5 +1,7 @@
#include "level.h"
#include "constants.h"
level::level(character *player, RNG *rng, const feature enabled_features):
enabled_features{enabled_features}, map{player, rng, enabled_features},
player{player} {
@ -34,24 +36,29 @@ void level::gen_enemies(RNG *rng, std::vector<position_list> &tiles) {
rng->rand_between(MIN_ENEMIE_CNT, MAX_ENEMIE_CNT + 1) :
MIN_ENEMIE_CNT + dhoard.size();
chlist.reserve(cnt);
pchlist.reserve(cnt);
elist.reserve(cnt);
pelist.reserve(cnt);
for (size_t i = 0; i < dhoard.size(); ++i) {
auto spots = get_available_around(dhoard[i].pos);
pchlist.push_back(nullptr);
new_dragon(rng, pchlist[i], rng->get_rand_in_vector(spots),
dhoard[i].pos);
chlist.push_back(pchlist[i].get());
position_list spots;
for (int i = 0; i < DIRECTION_CNT; ++i)
if (map.which_room(dhoard[i].pos + MOVE[i]) != -1)
spots.push_back(dhoard[i].pos + MOVE[i]);
pelist.push_back(nullptr);
new_dragon(rng, pelist[i], rng->get_rand_in_vector(spots),
dhoard[i].pos, enabled_features,
map.which_room(dhoard[i].pos));
elist.push_back(pelist[i].get());
}
for (int i = dhoard.size(); i < cnt; ++i) {
pchlist.push_back(nullptr);
new_enemy(rng, pchlist[i], get_rand_pos(rng, tiles),
enabled_features & FEATURE_EXTRA_STUFF);
pchlist[i]->set_room_num(map.which_room(pchlist[i]->get_position()));
chlist.push_back(pchlist[i].get());
pelist.push_back(nullptr);
auto p = get_rand_pos(rng, tiles);
new_enemy(rng, pelist[i], p, enabled_features, map.which_room(p));
elist.push_back(pelist[i].get());
}
}
@ -69,6 +76,10 @@ position level::get_rand_pos(RNG *rng, std::vector<position_list> &tiles) {
return pos;
}
void level::add_gold(gold g) {
glist.push_back(g);
}
void level::gen_gold(RNG *rng, std::vector<position_list> &tiles) {
glist.reserve(GOLD_CNT);
@ -103,7 +114,7 @@ void level::gen_potions(RNG *rng, std::vector<position_list> &tiles) {
void level::print(display *out) const {
map.print(out);
for (auto ch : chlist)
for (auto ch : elist)
ch->print(out);
for (auto p : plist)
@ -113,21 +124,50 @@ void level::print(display *out) const {
out->print_char(gold.pos, 'G', COLOR_PAIR(COLOR_YELLOW));
}
bool level::is_available(const position &pos) const {
bool level::is_available(const position &pos, bool is_player) const {
if (!map.is_available(pos))
return false;
for (auto ch : chlist)
if (pos == ch->get_position())
for (auto ch : elist)
if (pos == ch->get_pos())
return false;
if (!(enabled_features & FEATURE_WALK_OVER) && !is_player) {
for (auto p : plist)
if (pos == p->get_pos())
return false;
for (auto g : glist)
if (pos == g.pos)
return false;
}
return true;
}
bool level::is_available_all(const position &pos) const {
if (!map.is_available(pos))
return false;
for (auto ch : elist)
if (pos == ch->get_pos())
return false;
for (auto p : plist)
if (pos == p->get_pos())
return false;
for (auto g : glist)
if (pos == g.pos)
return false;
return true;
}
int level::get_room(const position &pos) const {
return map.which_room(pos);
}
position_list level::get_available_around(const position &pos) const {
position_list result;
@ -138,6 +178,16 @@ position_list level::get_available_around(const position &pos) const {
return result;
}
position_list level::get_available_around_all(const position &pos) const {
position_list result;
for (int i = 0; i < DIRECTION_CNT; ++i)
if (is_available_all(pos + MOVE[i]))
result.push_back(pos + MOVE[i]);
return result;
}
position level::get_up_stairs() const {
return map.get_up_stairs();
}
@ -146,8 +196,8 @@ position level::get_down_stairs() const {
return map.get_down_stairs();
}
character_list &level::get_chlist() {
return chlist;
enemy_list &level::get_elist() {
return elist;
}
potion_list &level::get_plist() {

View File

@ -7,8 +7,8 @@
#include "display.h"
#include "gold.h"
#include "enemies.h"
#include "enemy.h"
#include "potions.h"
#include "constants.h"
#include "map.h"
class level {
@ -22,9 +22,9 @@ private:
const feature enabled_features;
game_map map;
character *player;
std::vector<std::unique_ptr<character>>pchlist;
std::vector<std::unique_ptr<enemy_base>>pelist;
std::vector<std::unique_ptr<potion>>pplist;
character_list chlist;
enemy_list elist;
potion_list plist;
gold_list glist;
public:
@ -35,15 +35,21 @@ public:
const feature enabled_features);
void print(display *out) const;
bool is_available(const position &pos) const;
bool is_available(const position &pos, bool is_player = false) const;
bool is_available_all(const position &pos) const;
int get_room(const position &pos) const;
position_list get_available_around(const position &pos) const;
position_list get_available_around_all(const position &pos) const;
position get_up_stairs() const;
position get_down_stairs() const;
// you can delete the pointers to the stuff
character_list &get_chlist();
enemy_list &get_elist();
potion_list &get_plist();
gold_list &get_glist();
void add_gold(gold g);
private:
// every gen will delete the positions in tiles
void gen_potions(RNG *rng, std::vector<position_list> &tiles);

View File

@ -1,10 +0,0 @@
#include "log.h"
#include <utility>
logger::logger(std::ofstream &&new_out):
out{std::move(new_out)} {}
void logger::render() {
}

View File

@ -1,28 +0,0 @@
#ifndef __LOG_H__
#define __LOG_H__
#include <fstream>
#include <string>
#include <vector>
#include <memory>
#include "constants.h"
#include "characters.h"
class logger final {
private:
std::ofstream out;
std::vector<char> contents;
public:
logger(std::ofstream &&new_out);
// called once every turn
void render();
void print_char(const position &pos, const char ch);
void print_str(const position &pos, const std::string &str);
void print_turn(const unsigned turn);
void print_player(const character &player);
void print_chlist(const character_list &chlist);
void print_gold(const gold_list &gold_piles);
void print_potions(const potion_list &potions);
};
#endif

View File

@ -2,16 +2,15 @@
#include "cc3k.h"
#include "arguments.h"
#include "constants.h"
int main(int argc, char **argv) {
std::unique_ptr<cursor> curse;
std::unique_ptr<input> in;
std::unique_ptr<display> out;
std::unique_ptr<logger> log;
std::unique_ptr<RNG> rng;
feature enabled_features = proc_args(argc, argv,
curse, in, out, log, rng);
feature enabled_features = proc_args(argc, argv, curse, in, out, rng);
if (enabled_features &
(FEATURE_PANIC | FEATURE_PANIC_FILE |

View File

@ -1,9 +1,10 @@
#include "map.h"
#include <algorithm>
#include <iostream>
#include "constants.h"
game_map::game_map(character *player, RNG *rng, const feature enabled_features):
enabled_features{enabled_features} {
map.reserve(MAP_HEIGHT * MAP_WIDTH);
@ -25,7 +26,6 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features):
gen_path(layer_data, rng);
rooms_tile_list.reserve(room_data.size());
for (size_t i = 0; i < room_data.size(); ++i)
@ -41,7 +41,7 @@ game_map::game_map(character *player, RNG *rng, const feature enabled_features):
rooms_tile_list.shrink_to_fit();
int player_room = rng->rand_under(room_data.size());
player->set_position(rng->get_rand_in_vector(rooms_tile_list[player_room]));
player->set_pos(rng->get_rand_in_vector(rooms_tile_list[player_room]));
gen_stairs(player_room, rng);
}
@ -96,7 +96,7 @@ game_map::game_map(character *player, const std::string &map_data,
room_data.push_back({0, 0, 0, 0});
int player_room = rng->rand_under(room_data.size());
player->set_position(rng->get_rand_in_vector(rooms_tile_list[player_room]));
player->set_pos(rng->get_rand_in_vector(rooms_tile_list[player_room]));
gen_stairs(player_room, rng);
@ -557,3 +557,11 @@ bool game_map::hit_room(const position &a, const room &r) {
return a.y >= r.top - 1 && a.y <= r.top + r.height &&
a.x >= r.left - 1 && a.x <= r.left + r.width;
}
position game_map::remap_index(const int idx) const {
return {idx % MAP_WIDTH, idx / MAP_WIDTH};
}
int game_map::remap_position(const position &pos) const {
return pos.y * MAP_WIDTH + pos.x;
}

View File

@ -3,11 +3,12 @@
#include <vector>
#include <string>
#include <utility>
#include "constants.h"
#include "display.h"
#include "position.h"
#include "rng.h"
#include "fraction.h"
#include "player.h"
#include "characters.h"
class game_map final {
@ -18,8 +19,8 @@ private:
static const int MAX_ROOM_WIDTH = 20;
static const int MAX_ROOM_HEIGHT = 5;
// setup safezones
static const int ACTUAL_MAP_WIDTH = MAP_WIDTH - 6;
static const int ACTUAL_MAP_HEIGHT = MAP_HEIGHT - 5;
static const int ACTUAL_MAP_WIDTH = 73;
static const int ACTUAL_MAP_HEIGHT = 20;
static const int MAP_PADDING = 3;
static const int MIN_ROOM_SPACING = 3;
static const int WIDTH_RESERVED = 6;
@ -72,16 +73,10 @@ public:
int get_down_stairs_room() const;
int get_room_cnt() const;
room get_room(std::size_t idx) const;
private:
std::vector<room> room_data;
position remap_index(const int idx) const {
return {idx % MAP_WIDTH, idx / MAP_WIDTH};
}
int remap_position(const position &pos) const {
return pos.y * MAP_WIDTH + pos.x;
}
position remap_index(const int idx) const;
int remap_position(const position &pos) const;
position random_size(int min_width, int min_height,
int max_width, int max_height,
@ -113,6 +108,7 @@ private:
bool hit_room(const position &a, const room &r);
void gen_stairs(int player_room, RNG *rng);
room get_room(std::size_t idx) const;
};
const std::string default_map =

40
src/pc.cc Normal file
View File

@ -0,0 +1,40 @@
#include "pc.h"
#include "constants.h"
#include "player/goblin.h"
#include "player/drow.h"
#include "player/shade.h"
#include "player/troll.h"
#include "player/vampire.h"
void init_player(RNG *rng, std::unique_ptr<player_base> &pc,
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);
break;
case rdrow:
pc = make_unique<drow>(rng, enabled_features);
break;
case rshade:
pc = make_unique<shade>(rng, enabled_features);
break;
case rtroll:
pc = make_unique<troll>(rng, enabled_features);
break;
case rvampire:
pc = make_unique<vampire>(rng, enabled_features);
break;
default:
break;
}
}

10
src/pc.h Normal file
View File

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

151
src/player.cc Normal file
View File

@ -0,0 +1,151 @@
#include "player.h"
#include "constants.h"
#include "enemy.h"
#include "level.h"
player_base::player_base(RNG *rng, const feature enabled_features,
const enum race &nrace):
character{rng, enabled_features, nrace, {0, 0}}, gold_cnt(0) {}
void player_base::print(display *out) {
out->print_char(pos, '@', COLOR_PAIR(COLOR_BLUE));
}
std::string player_base::get_abbrev() const {
return "PC";
}
int player_base::get_gold() const {
return this->gold_cnt;
}
int player_base::get_ATK() const {
return this->ATK;
}
int player_base::get_DEF() const {
return this->DEF;
}
int player_base::get_HP() const {
return this->HP;
}
long_result player_base::apply(potion *p) {
if (p == nullptr)
return {result::applied_nothing,
"PC tried to breathe the magic in the air. "};
apply_effect(p);
return {result::applied,
(std::string)"PC applied potion of " + p->get_name()};
}
long_result player_base::attack(character *ch) {
if (ch == nullptr)
return {result::fine,
"PC tried to attack thin air. "};
return ch->get_hit(this, ATK, base_hit_rate);
}
long_result player_base::move(level *lvl,
const position &p) {
if (enabled_features & FEATURE_NCURSES) {
enemy_base *tmp = nullptr;
if (lvl->is_available(p, true)) {
pos = p;
return {result::moved, ""};
} else if ((tmp = get_enemy_at(p, lvl->get_elist())) != nullptr) {
auto res = attack((character *)tmp);
if (tmp->is_dead())
tmp->dies(lvl, this);
return res;
}
} else if (lvl->get_up_stairs() == p &&
enabled_features & FEATURE_REVISIT) {
return {result::go_up, "PC went up the stairs. "};
} else if (lvl->get_down_stairs() == p) {
return {result::go_down, "PC went down the stairs. "};
} else if (lvl->is_available(p, true)) {
pos = p;
return {result::moved, ""};
}
return {result::fine, "PC tried to move into non-existent space. "};
}
long_result player_base::get_hit(character *ch, const int tATK,
const fraction &hit_rate) {
if (rng->trial(hit_rate)) {
int tmp = calc_dmg(tATK, DEF);
tmp = tmp > HP ? HP : tmp;
HP -= tmp;
if (HP == 0)
return {result::died,
ch->get_abbrev() + " deals " +
std::to_string(tmp) +
" damage to PC. PC is slain by " +
ch->get_abbrev() + ". "};
return {result::hit, ch->get_abbrev() + " deals " +
std::to_string(tmp) +
" damage to PC. "};
}
return {result::miss, ch->get_abbrev() + " tried to hit PC but missed. "};
}
void player_base::add_gold(int amount) {
gold_cnt += amount;
}
long_result player_base::interpret_command(level *lvl, game_command cmd) {
if (cmd == game_command_terminate) {
return {result::terminate, ""};
} else if (cmd >= move_north && cmd <= move_southwest) {
return move(lvl, pos + MOVE[cmd - move_north]);
} else if (cmd >= apply_north && cmd <= apply_southwest) {
return apply(get_potion_at(pos + MOVE[cmd - apply_north],
lvl->get_plist()));
} else if (cmd == apply_panic) {
return {result::fine,
"PC tried to use in some non-existent direction. "};
} else if (cmd >= attack_north && cmd <= attack_southwest) {
enemy_base *tmp = get_enemy_at(pos + MOVE[cmd - attack_north],
lvl->get_elist());
auto res = attack((character *)tmp);
if (tmp->is_dead())
tmp->dies(lvl, this);
return res;
} else if (cmd == up_stairs) {
if (lvl->get_up_stairs() == pos &&
enabled_features & FEATURE_REVISIT)
return {go_up, "PC went up the stairs. "};
return {result::fine,
"PC tried to fly through the ceiling. "};
} else if (cmd == down_stairs) {
if (lvl->get_down_stairs() == pos)
return {go_down, "PC went down the stairs. "};
return {result::fine,
"PC tried to dig through the floor. "};
} else if (cmd == the_world) {
return{toggle_the_world, "PC toggled Stand: The World! "};
} else if (cmd == game_restart) {
return {restart_game, ""};
} else if (cmd == game_command_pass) {
return {result::fine, ""};
}
return {unknown, "PC tried to produce some undefined behaviour. "};
}

38
src/player.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include "characters.h"
enum game_command : int;
class player_base: public character {
protected:
int gold_cnt;
potion_list potions;
public:
player_base(RNG *rng, const feature enabled_features,
const enum race &nrace);
virtual long_result move(level *lvl,
const position &p) override;
virtual long_result apply(potion *p);
virtual long_result attack(character *ch) override;
virtual long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate) override;
virtual void add_gold(int amount);
void print(display *out) override;
std::string get_abbrev() const override;
long_result interpret_command(level *lvl, game_command cmd);
int get_gold() const;
int get_ATK() const;
int get_DEF() const;
int get_HP() const;
};
#endif

10
src/player/drow.cc Normal file
View File

@ -0,0 +1,10 @@
#include "drow.h"
#include "../constants.h"
drow::drow(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, race::rdrow} {}
const char *drow::get_race_name() const {
return "Drow";
}

12
src/player/drow.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef __DROW_H__
#define __DROW_H__
#include "../player.h"
class drow final: public player_base {
public:
drow(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
};
#endif

53
src/player/goblin.cc Normal file
View File

@ -0,0 +1,53 @@
#include "goblin.h"
#include "../constants.h"
goblin::goblin(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, race::rgoblin} {};
const char *goblin::get_race_name() const {
return "Goblin";
}
long_result goblin::attack(character *ch) {
if (ch == nullptr)
return {result::fine,
"PC tried to attack thin air. "};
auto res = ch->get_hit(this, ATK, base_hit_rate);
if (ch->is_dead()) {
gold_cnt += GAIN_GOLD;
res.msg += "PC feels greedy. ";
}
return res;
}
long_result goblin::get_hit(character *ch, const int tATK,
const fraction &hit_rate) {
if (rng->trial(hit_rate)) {
int tmp = calc_dmg(tATK, DEF);
std::string msg = "";
if (ch->get_race() == rorc) {
msg += "PC feels scared. ";
tmp *= ORC_DMG_MUL;
}
tmp = tmp > HP ? HP : tmp;
HP -= tmp;
msg += ch->get_abbrev() + " deals " +
std::to_string(tmp) +
" damage to PC. ";
if (HP == 0)
return {result::died,
msg + "PC is slain by " +
ch->get_abbrev() + ". "};
return {result::hit, msg};
}
return {result::miss, ch->get_abbrev() + " tried to hit PC but missed. "};
}

17
src/player/goblin.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __GOBLIN_H__
#define __GOBLIN_H__
#include "../player.h"
class goblin final: public player_base {
static const int GAIN_GOLD = 5;
constexpr static const float ORC_DMG_MUL = 1.5f;
public:
goblin(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
long_result attack(character *ch) override;
long_result get_hit(character *ch, const int tATK,
const fraction &hit_rate) override;
};
#endif

10
src/player/shade.cc Normal file
View File

@ -0,0 +1,10 @@
#include "shade.h"
#include "../constants.h"
shade::shade(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, race::rshade} {}
const char *shade::get_race_name() const {
return "Shade";
}

12
src/player/shade.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef __SHADE_H__
#define __SHADE_H__
#include "../player.h"
class shade final: public player_base {
public:
shade(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
};
#endif

17
src/player/troll.cc Normal file
View File

@ -0,0 +1,17 @@
#include "troll.h"
#include "../constants.h"
troll::troll(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, rtroll} {}
const char *troll::get_race_name() const {
return "Troll";
}
void troll::start_turn() {
ATK = STARTING_ATK[rtroll];
DEF = STARTING_DEF[rtroll];
base_hit_rate = {1, 1};
HP = HP + GAIN_HP < MAX_HP[rtroll] ? HP + GAIN_HP : MAX_HP[rtroll];
}

14
src/player/troll.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __TROLL_H__
#define __TROLL_H__
#include "../player.h"
class troll final: public player_base {
static const int GAIN_HP = 5;
public:
troll(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
void start_turn() override;
};
#endif

33
src/player/vampire.cc Normal file
View File

@ -0,0 +1,33 @@
#include "vampire.h"
#include "../constants.h"
vampire::vampire(RNG *rng, const feature enabled_features):
player_base{rng, enabled_features, race::rvampire} {};
const char *vampire::get_race_name() const {
return "Vampire";
}
long_result vampire::attack(character *ch) {
if (ch == nullptr)
return {result::fine,
"PC tried to drain thin air. "};
auto res = ch->get_hit(this, ATK, base_hit_rate);
if (res.res == miss)
return {miss, res.msg + "PC is unhappy. "};
if (ch->get_race() == rdwarf) {
HP -= GAIN_HP;
if (is_dead())
return {died, ""};
return {hit, res.msg + "PC accidentally drained themselves. "};
}
HP += GAIN_HP;
return {hit, res.msg + "PC is happy. "};
}

14
src/player/vampire.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __VAMPIRE_H__
#define __VAMPIRE_H__
#include "../player.h"
class vampire final: public player_base {
static const int GAIN_HP = 5;
public:
vampire(RNG *rng, const feature enabled_features);
const char *get_race_name() const override;
long_result attack(character *ch) override;
};
#endif

View File

@ -17,14 +17,15 @@ typedef struct position {
bool operator<(const position &other) const;
} position;
std::size_t find(const std::vector<position> &sorted_list,
typedef std::vector<position> position_list;
std::size_t find(const position_list &sorted_list,
const position &target);
// requires: all elements of excluded are in sorted_positions
std::vector<position> remove_from_list(const std::vector<position>
&sorted_positions,
std::vector<position> excluded);
void remove_from_list(std::vector<position> &sorted_positions,
std::vector<position> remove_from_list(const position_list &sorted_positions,
position_list excluded);
void remove_from_list(position_list &sorted_positions,
position &excluded);
float distance(const position &a, const position &b);

View File

@ -1,5 +1,7 @@
#include "potion.h"
#include "constants.h"
potion::potion(const potion_type type, const int duration, const position &pos):
type{type}, remaining_duration{duration}, pos{pos} {}
@ -44,3 +46,16 @@ potion &potion::operator=(potion &&p) {
void potion::print(display *out) {
out->print_char(pos, 'P', COLOR_PAIR(COLOR_GREEN));
}
potion *get_potion_at(const position &pos, potion_list &plist) {
potion *ret = nullptr;
for (size_t i = 0; i < plist.size(); ++i)
if (plist[i]->get_pos() == pos) {
ret = plist[i];
plist.erase(plist.begin() + i);
return ret;
}
return ret;
}

View File

@ -2,10 +2,12 @@
#define __POTION_H__
#include <vector>
#include "constants.h"
#include "fraction.h"
#include "display.h"
enum potion_type : int;
enum race : int;
// IMPORTANT: pop all potions of duration == 0, and when entering a
// new level, pop all potions of duration == -1
@ -32,10 +34,13 @@ public:
int get_duration() const;
position get_pos() const;
void set_pos(const position &npos);
virtual const char *get_name() const = 0;
virtual void print(display *out);
};
typedef std::vector<potion *> potion_list;
potion *get_potion_at(const position &pos, potion_list &plist);
#endif

View File

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

View File

@ -2,7 +2,6 @@
#define __POTIONS_H__
#include "potion.h"
#include "restore_health.h"
#include <memory>
#include <utility>

View File

@ -1,6 +1,7 @@
#include "boost_atk.h"
#include <algorithm>
#include "../constants.h"
const int BOOST_ATK = 5;
const int BOOST_ATK_DROW = 7;
@ -23,3 +24,7 @@ void boost_atk::apply(const enum race &race, int &HP, int &ATK, int &DEF,
int boost_atk::get_priority() const {
return CALC_ADD_BASE;
}
const char *boost_atk::get_name() const {
return "BA";
}

View File

@ -1,7 +1,7 @@
#ifndef __BOOST_ATK_H__
#define __BOOST_ATK_H__
#include "potion.h"
#include "../potion.h"
class boost_atk final: public potion {
public:
@ -9,6 +9,7 @@ public:
void apply(const enum race &race, int &HP, int &ATK, int &DEF,
fraction &base_hit_rate) override;
int get_priority() const override;
const char *get_name() const override;
};
#endif

View File

@ -1,6 +1,7 @@
#include "boost_def.h"
#include <algorithm>
#include "../constants.h"
// TODO: move into class def as static constants
const int BOOST_DEF = 5;
@ -24,3 +25,7 @@ void boost_def::apply(const enum race &race, int &HP, int &ATK, int &DEF,
int boost_def::get_priority() const {
return CALC_ADD_BASE;
}
const char *boost_def::get_name() const {
return "BD";
}

View File

@ -1,7 +1,7 @@
#ifndef __BOOST_DEF_H__
#define __BOOST_DEF_H__
#include "potion.h"
#include "../potion.h"
class boost_def final: public potion {
public:
@ -9,6 +9,7 @@ public:
void apply(const enum race &race, int &HP, int &ATK, int &DEF,
fraction &base_hit_rate) override;
int get_priority() const override;
const char *get_name() const override;
};
#endif

View File

@ -1,6 +1,7 @@
#include "poison_health.h"
#include <algorithm>
#include "../constants.h"
poison_health::poison_health(const position &pos):
potion{potion_type::poison_health, 1, pos} {}
@ -20,3 +21,7 @@ void poison_health::apply(const enum race &race, int &HP, int &ATK, int &DEF,
int poison_health::get_priority() const {
return CALC_ADD_BASE;
}
const char *poison_health::get_name() const {
return "PH";
}

View File

@ -1,7 +1,7 @@
#ifndef __POISON_HEALTH_H__
#define __POISON_HEALTH_H__
#include "potion.h"
#include "../potion.h"
class poison_health final: public potion {
public:
@ -9,6 +9,7 @@ public:
void apply(const enum race &race, int &HP, int &ATK, int &DEF,
fraction &base_hit_rate) override;
int get_priority() const override;
const char *get_name() const override;
};
#endif

View File

@ -1,6 +1,7 @@
#include "restore_health.h"
#include <algorithm>
#include "../constants.h"
restore_health::restore_health(const position &pos):
potion{potion_type::restore_health, -1, pos} {}
@ -20,3 +21,7 @@ void restore_health::apply(const enum race &race, int &HP, int &ATK, int &DEF,
int restore_health::get_priority() const {
return CALC_ADD_BASE;
}
const char *restore_health::get_name() const {
return "RH";
}

View File

@ -1,7 +1,7 @@
#ifndef __RESTORE_HEALTH_H__
#define __RESTORE_HEALTH_H__
#include "potion.h"
#include "../potion.h"
class restore_health final: public potion {
public:
@ -9,6 +9,7 @@ public:
void apply(const enum race &race, int &HP, int &ATK, int &DEF,
fraction &base_hit_rate) override;
int get_priority() const override;
const char *get_name() const override;
};
#endif

View File

@ -1,6 +1,7 @@
#include "wound_atk.h"
#include <algorithm>
#include "../constants.h"
const int WOUND_ATK = 5;
const int WOUND_ATK_DROW = 7;
@ -23,3 +24,7 @@ void wound_atk::apply(const enum race &race, int &HP, int &ATK, int &DEF,
int wound_atk::get_priority() const {
return CALC_ADD_BASE;
}
const char *wound_atk::get_name() const {
return "WA";
}

Some files were not shown because too many files have changed in this diff Show More