Merge branch 'paul' into AL/races-potions
This commit is contained in:
@ -21,7 +21,7 @@ EXEC = ../bin/cc3k # executable name
|
|||||||
.PHONY : clean # not file names
|
.PHONY : clean # not file names
|
||||||
|
|
||||||
${EXEC} : ${OBJECTS} # link step
|
${EXEC} : ${OBJECTS} # link step
|
||||||
${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^
|
${CXX} ${CXXFLAGS} $^ -o $@ -lncurses # additional object files before $^
|
||||||
|
|
||||||
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
|
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
|
||||||
|
|
||||||
|
@ -34,22 +34,28 @@ feature proc_args(int argc, char **argv,
|
|||||||
if (result & (FEATURE_IN_FILE | FEATURE_OUT_FILE))
|
if (result & (FEATURE_IN_FILE | FEATURE_OUT_FILE))
|
||||||
return FEATURE_CONFLICT | i;
|
return FEATURE_CONFLICT | i;
|
||||||
|
|
||||||
result |= FEATURE_NCURSES;
|
result |= FEATURE_NCURSES;
|
||||||
} else if (str == "-r") {
|
} else if (str == "-r") {
|
||||||
result |= FEATURE_RAND_MAP;
|
result |= FEATURE_RAND_MAP;
|
||||||
} else if (str == "-m") {
|
} else if (str == "-m") {
|
||||||
result |= FEATURE_MENU;
|
result |= FEATURE_MENU;
|
||||||
} else if (str == "-c") {
|
} else if (str == "-c") {
|
||||||
result |= FEATURE_ENEMIES_CHASE;
|
result |= FEATURE_ENEMIES_CHASE;
|
||||||
} else if (str == "-i") {
|
} else if (str == "-C") {
|
||||||
result |= FEATURE_INVENTORY;
|
result |= FEATURE_COLORFUL;
|
||||||
} else if (str == "-t") {
|
} else if (str == "-i") {
|
||||||
result |= FEATURE_THROW;
|
result |= FEATURE_INVENTORY;
|
||||||
} else if (str == "-R") {
|
} else if (str == "-t") {
|
||||||
result |= FEATURE_REVISIT;
|
result |= FEATURE_THROW;
|
||||||
} else if (str == "-s") {
|
} else if (str == "-R") {
|
||||||
++i;
|
result |= FEATURE_REVISIT;
|
||||||
str = argv[i];
|
} else if (str == "-e") {
|
||||||
|
result |= FEATURE_EXTRA_STUFF;
|
||||||
|
} else if (str == "-E") {
|
||||||
|
result |= FEATURE_EXTRA_LEVELS;
|
||||||
|
} else if (str == "-s") {
|
||||||
|
++i;
|
||||||
|
str = argv[i];
|
||||||
|
|
||||||
if (!seed.empty() && seed != str)
|
if (!seed.empty() && seed != str)
|
||||||
return FEATURE_CONFLICT | i;
|
return FEATURE_CONFLICT | i;
|
||||||
@ -119,11 +125,11 @@ feature proc_args(int argc, char **argv,
|
|||||||
log = std::make_unique<logger>(std::move(lout));
|
log = std::make_unique<logger>(std::move(lout));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result & FEATURE_NCURSES) {
|
if (result & FEATURE_NCURSES) {
|
||||||
curse = std::make_unique<cursor>();
|
curse = std::make_unique<cursor>();
|
||||||
in = std::make_unique<curses_input>(*curse);
|
in = std::make_unique<curses_input>(curse.get());
|
||||||
out = std::make_unique<curses_output>(*curse);
|
out = std::make_unique<curses_output>(curse.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result & FEATURE_SEED) {
|
if (result & FEATURE_SEED) {
|
||||||
std::istringstream iss {seed};
|
std::istringstream iss {seed};
|
||||||
@ -163,13 +169,35 @@ void panic_args(feature panic) {
|
|||||||
cerr << "Cannot open specified log file!" << endl;
|
cerr << "Cannot open specified log file!" << endl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cerr << "Something must have went really, really, wrong..."
|
cerr << "Something must have went really, really, wrong..."
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
break;
|
||||||
} else if (panic & FEATURE_PANIC_SEED) {
|
}
|
||||||
cerr << "Invalid seed" << endl;
|
} else if (panic & FEATURE_PANIC_SEED) {
|
||||||
} else
|
cerr << "Invalid seed" << endl;
|
||||||
cerr << "Something must have went really, really, wrong..."
|
} else
|
||||||
<< endl;
|
cerr << "Something must have went really, really, wrong..."
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_args_list() {
|
||||||
|
|
||||||
|
|
||||||
|
static const char *ARGS_LIST = "-n : Use ncurses for I/O\n\
|
||||||
|
-r : Randomly generate maps\n\
|
||||||
|
-m : Enabled a main menu + options\n\
|
||||||
|
-c : Enemies chase the player (through doors and up stairs)\n\
|
||||||
|
-C : Give things better coloring\n\
|
||||||
|
-i : Enable inventory\n\
|
||||||
|
-t : Enable throw\n\
|
||||||
|
-R : Enable revisiting levels\n\
|
||||||
|
-e : Enable extra potions and races\n\
|
||||||
|
-E : Enable extra levels\n\
|
||||||
|
-s [seed] : Sets initial seed to seed\n\
|
||||||
|
-L [file] : Enable logging to file\n\
|
||||||
|
-I [file] : Reads commands from file. CANNOT BE USED WITH -n.\n\
|
||||||
|
-O [file] : Outputs to file. CANNOT BE USED WITH -n.\n\
|
||||||
|
-h/--help : Displays options list (doesn't start a game)";
|
||||||
|
std::cout << ARGS_LIST << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,6 @@
|
|||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
/* Arguments
|
|
||||||
-n : Use ncurses for I/O
|
|
||||||
-r : Randomly generate maps
|
|
||||||
-m : Enabled a main menu + options
|
|
||||||
-c : Enemies chase the player (through doors and up stairs)
|
|
||||||
-i : Enable inventory
|
|
||||||
-t : Enable throw
|
|
||||||
-R : Enable revisiting levels
|
|
||||||
-s [seed] : Sets initial seed to seed
|
|
||||||
-L [file] : Enable logging to file
|
|
||||||
-I [file] : Reads commands from file. CANNOT BE USED WITH -n.
|
|
||||||
-O [file] : Outputs to file. CANNOT BE USED WITH -n.
|
|
||||||
-h/--help : Displays options list (doesn't start a game)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// IMPORTANT: Errors include the index that caused them (or'ed into them)
|
// IMPORTANT: Errors include the index that caused them (or'ed into them)
|
||||||
feature proc_args(int argc, char **argv,
|
feature proc_args(int argc, char **argv,
|
||||||
std::unique_ptr<cursor> &curse,
|
std::unique_ptr<cursor> &curse,
|
||||||
@ -34,4 +19,6 @@ feature proc_args(int argc, char **argv,
|
|||||||
|
|
||||||
void panic_args(feature panic);
|
void panic_args(feature panic);
|
||||||
|
|
||||||
|
void print_args_list();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
6
src/cc3k.h
Normal file
6
src/cc3k.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __CC3K_H__
|
||||||
|
#define __CC3K_H__
|
||||||
|
|
||||||
|
class CC3K;
|
||||||
|
|
||||||
|
#endif
|
@ -2,10 +2,21 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
character::character(RNG &rng, const enum race &nrace):
|
character::character(RNG *rng, const enum race &nrace, const position &pos):
|
||||||
rng{rng},
|
rng{rng}, race{nrace}, HP{STARTING_HP[race]},
|
||||||
race{nrace}, HP{STARTING_HP[race]},
|
ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]},
|
||||||
ATK{STARTING_ATK[race]}, DEF{STARTING_DEF[race]} {}
|
base_hit_rate{1, 1}, pos{pos} {}
|
||||||
|
|
||||||
|
void character::start_turn() {
|
||||||
|
ATK = STARTING_ATK[race];
|
||||||
|
DEF = STARTING_DEF[race];
|
||||||
|
base_hit_rate = {1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
void character::print(display *out, bool color, bool player) {
|
||||||
|
out->print_char(pos, player ? '@' : CHARACTER_REP[race],
|
||||||
|
COLOR_PAIR(COLOR_BLACK_ON_WHITE));
|
||||||
|
}
|
||||||
|
|
||||||
enum race character::get_race() const {
|
enum race character::get_race() const {
|
||||||
return race;
|
return race;
|
||||||
@ -31,14 +42,26 @@ int character::get_gold() const {
|
|||||||
return gold;
|
return gold;
|
||||||
}
|
}
|
||||||
|
|
||||||
float character::get_hitrate() const {
|
float character::get_hitrate_real() const {
|
||||||
return base_hit_rate;
|
return base_hit_rate.real();
|
||||||
|
}
|
||||||
|
|
||||||
|
fraction character::get_hitrate() const {
|
||||||
|
return base_hit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int character::get_room_num() const {
|
||||||
|
return room_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool character::is_hostile() const {
|
bool character::is_hostile() const {
|
||||||
return hostile;
|
return hostile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void character::set_room_num(const int room) {
|
||||||
|
room_num = room;
|
||||||
|
}
|
||||||
|
|
||||||
void character::set_position(const position &npos) {
|
void character::set_position(const position &npos) {
|
||||||
pos = npos;
|
pos = npos;
|
||||||
}
|
}
|
||||||
@ -59,8 +82,8 @@ void character::set_gold(const int ngold) {
|
|||||||
gold = ngold;
|
gold = ngold;
|
||||||
}
|
}
|
||||||
|
|
||||||
void character::set_hitrate(const float nhitrate) {
|
void character::set_hitrate(const fraction nhitrate) {
|
||||||
base_hit_rate = nhitrate;
|
base_hit_rate = nhitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void character::set_hostile(const bool is_hostile) {
|
void character::set_hostile(const bool is_hostile) {
|
||||||
@ -105,27 +128,55 @@ const {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result character::apply(direction &dir, const potion_list &potions) {
|
void character::apply(direction &dir, potion_list &plist) {
|
||||||
// TODO: implement this after implementing potions
|
for (size_t i = 0; i < plist.size(); ++i)
|
||||||
return result::fine;
|
if (pos + MOVE[dir] == plist[i]->get_pos()) {
|
||||||
|
insert_potion(plist[i]);
|
||||||
|
plist.erase(plist.begin() + i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
position_list remove_from_list(const position_list &sorted_positions,
|
void character::insert_potion(potion *p) {
|
||||||
position_list excluded) {
|
effects.push_back(p);
|
||||||
std::sort(excluded.begin(), excluded.end());
|
|
||||||
|
|
||||||
position_list result{sorted_positions.size() - excluded.size()};
|
for (int i = effects.size() - 1; i > 0; --i)
|
||||||
|
if (p->get_priority() < effects[i - 1]->get_priority())
|
||||||
|
std::swap(effects[i], effects[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
auto exc = excluded.begin();
|
result character::apply_effects() {
|
||||||
|
potion_list tmp;
|
||||||
|
tmp.reserve(effects.size());
|
||||||
|
|
||||||
for (auto src : sorted_positions) {
|
for (auto p : effects) {
|
||||||
if (exc != excluded.end() && src == *exc)
|
p->apply(this->race, HP, ATK, DEF, base_hit_rate);
|
||||||
++exc;
|
|
||||||
else
|
|
||||||
result.push_back(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
if (HP <= 0)
|
||||||
|
return result::died;
|
||||||
|
|
||||||
|
if (p->get_duration() != 0)
|
||||||
|
tmp.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.shrink_to_fit();
|
||||||
|
|
||||||
|
std::swap(tmp, effects);
|
||||||
|
|
||||||
|
return result::fine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void character::discard_level_effects() {
|
||||||
|
potion_list tmp;
|
||||||
|
tmp.reserve(effects.size());
|
||||||
|
|
||||||
|
for (auto p : effects)
|
||||||
|
if (p->get_duration() != -1)
|
||||||
|
tmp.push_back(p);
|
||||||
|
|
||||||
|
tmp.shrink_to_fit();
|
||||||
|
|
||||||
|
std::swap(tmp, effects);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT: remember to check if player is on the stairs
|
// IMPORTANT: remember to check if player is on the stairs
|
||||||
|
115
src/characters.h
115
src/characters.h
@ -1,9 +1,3 @@
|
|||||||
/*
|
|
||||||
* CS 246 Final Project
|
|
||||||
* File: map.h
|
|
||||||
* Purpose: handles all characters
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __CHARACTERS_H__
|
#ifndef __CHARACTERS_H__
|
||||||
#define __CHARACTERS_H__
|
#define __CHARACTERS_H__
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -12,53 +6,65 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "fraction.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "potion.h"
|
#include "potions.h"
|
||||||
|
#include "gold.h"
|
||||||
#include "rng.h"
|
#include "rng.h"
|
||||||
|
|
||||||
// #include "inventory.h" // Reserved for later
|
|
||||||
|
|
||||||
class character; // forward declaration
|
class character; // forward declaration
|
||||||
|
|
||||||
// Note: player should not be in the character list
|
// Note: player should not be in the character list
|
||||||
typedef std::vector<character> character_list;
|
typedef std::vector<character *> character_list;
|
||||||
|
|
||||||
class character {
|
class character {
|
||||||
public:
|
public:
|
||||||
character(RNG &rng, const race &nrace); // fills out the starting stats
|
character(RNG *rng, const race &nrace,
|
||||||
|
const position &pos); // fills out the starting stats
|
||||||
|
|
||||||
// usually I wouldn't do this but considering that the map has
|
// usually I wouldn't do this but considering that the map has
|
||||||
// a super small size an O(n) solution is acceptable
|
// a super small size an O(n) solution is acceptable
|
||||||
// IMPORTANT: available_positions do NOT have characters in them
|
// IMPORTANT: available_positions do NOT have characters in them
|
||||||
direction_list moveable(const position_list &available_positions) const;
|
direction_list moveable(const position_list &available_positions) const;
|
||||||
virtual result move(const direction dir,
|
virtual result move(const direction dir,
|
||||||
const position_list &available_positions);
|
const position_list &available_positions);
|
||||||
virtual result attack(const direction dir,
|
virtual result attack(const direction dir,
|
||||||
character_list &chlist) = 0;
|
character_list &chlist) = 0;
|
||||||
virtual result move_or_attack(const direction dir,
|
virtual result move_or_attack(const direction dir,
|
||||||
const position_list &available_positions,
|
const position_list &available_positions,
|
||||||
character_list &chlist);
|
character_list &chlist);
|
||||||
virtual result apply(direction &dir,
|
virtual void apply(direction &dir,
|
||||||
const potion_list &potions);
|
potion_list &potions);
|
||||||
virtual result get_hit(const enum race &race, const int atk,
|
virtual result get_hit(const enum race &race, const int atk,
|
||||||
const float hitrate) = 0;
|
const fraction hitrate) = 0;
|
||||||
|
|
||||||
enum race get_race() const;
|
// overload for different races
|
||||||
position get_position() const;
|
virtual void print(display *out, bool color = false, bool player = false);
|
||||||
int get_HP() const;
|
|
||||||
int get_ATK() const;
|
|
||||||
int get_DEF() const;
|
|
||||||
int get_gold() const;
|
|
||||||
float get_hitrate() const;
|
|
||||||
bool is_hostile() const;
|
|
||||||
|
|
||||||
void set_position(const position &npos);
|
result apply_effects();
|
||||||
void set_HP(const int nHP);
|
void discard_level_effects();
|
||||||
void set_ATK(const int nATK);
|
void start_turn();
|
||||||
void set_DEF(const int nDEF);
|
|
||||||
void set_gold(const int ngold);
|
enum race get_race() const;
|
||||||
void set_hitrate(const float nhitrate);
|
position get_position() const;
|
||||||
void set_hostile(const bool is_hostile);
|
int get_HP() const;
|
||||||
|
int get_ATK() const;
|
||||||
|
int get_DEF() const;
|
||||||
|
int get_gold() const;
|
||||||
|
float get_hitrate_real() const;
|
||||||
|
fraction get_hitrate() const;
|
||||||
|
bool is_hostile() const;
|
||||||
|
int get_room_num() const;
|
||||||
|
|
||||||
|
void set_position(const position &npos);
|
||||||
|
void set_HP(const int nHP);
|
||||||
|
void set_ATK(const int nATK);
|
||||||
|
void set_DEF(const int nDEF);
|
||||||
|
void set_gold(const int ngold);
|
||||||
|
void set_hitrate(const fraction nhitrate);
|
||||||
|
virtual void set_hostile(const bool is_hostile);
|
||||||
|
void set_room_num(const int room);
|
||||||
|
|
||||||
// if stat is hostile, positive to set it to hostile,
|
// if stat is hostile, positive to set it to hostile,
|
||||||
// negative to set it to peaceful
|
// negative to set it to peaceful
|
||||||
@ -66,29 +72,28 @@ public:
|
|||||||
// void apply_buff(const stat_name statn, const float mul);
|
// void apply_buff(const stat_name statn, const float mul);
|
||||||
// reserved for later
|
// reserved for later
|
||||||
protected:
|
protected:
|
||||||
RNG &rng;
|
RNG *rng;
|
||||||
const enum race race;
|
const enum race race;
|
||||||
|
|
||||||
int HP;
|
int HP;
|
||||||
|
|
||||||
// IMPORTANT: keep track of ATK and DEF in game at turn time
|
// IMPORTANT: keep track of ATK and DEF in game at turn time
|
||||||
int ATK;
|
int ATK;
|
||||||
int DEF;
|
int DEF;
|
||||||
|
fraction base_hit_rate;
|
||||||
|
|
||||||
position pos;
|
position pos;
|
||||||
|
|
||||||
int gold; // characters spawn with gold
|
int gold; // characters spawn with gold
|
||||||
potion_list potions;// inventory inventory; // Reserved
|
potion_list potions; // inventory
|
||||||
|
potion_list effects; // applied potions
|
||||||
|
|
||||||
float base_hit_rate; // requires: between [0,1]
|
bool hostile;
|
||||||
|
int room_num;
|
||||||
bool hostile;
|
private:
|
||||||
|
void insert_potion(potion *p);
|
||||||
};
|
};
|
||||||
|
|
||||||
// requires: all elements of excluded are in sorted_positions
|
|
||||||
position_list remove_from_list(const position_list &sorted_positions,
|
|
||||||
position_list excluded);
|
|
||||||
|
|
||||||
int calc_dmg(const int ATK, const int DEF);
|
int calc_dmg(const int ATK, const int DEF);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,17 +31,19 @@ enum game_command {game_command_terminate = 0,
|
|||||||
|
|
||||||
enum stat_name {HP, ATK, DEF, hostile};
|
enum stat_name {HP, ATK, DEF, hostile};
|
||||||
|
|
||||||
const int RACE_CNT = 4; // TODO: update as you go
|
const int RACE_CNT = 5; // TODO: update as you go
|
||||||
|
|
||||||
enum race {rshade = 0, rvampire, rgoblin, rdrow /* TODO: fill out the other races (including enemies) */};
|
enum race {rshade = 0, rvampire, rgoblin, rdrow, rdragon /* TODO: fill out the other races (including enemies) */};
|
||||||
|
|
||||||
// TODO: fill out the other races (including enemies)
|
// TODO: fill out the other races (including enemies)
|
||||||
const int MAX_HP[RACE_CNT] = {125, INF, 110, 150};
|
const int MAX_HP[RACE_CNT] = {125, INF, 110, 150, 150};
|
||||||
const int STARTING_HP[RACE_CNT] = {125, 50, 110, 150};
|
const int STARTING_HP[RACE_CNT] = {125, 50, 110, 150, 150};
|
||||||
const int STARTING_ATK[RACE_CNT] = {25, 25, 15, 25};
|
const int STARTING_ATK[RACE_CNT] = {25, 25, 15, 25, 20};
|
||||||
const int STARTING_DEF[RACE_CNT] = {25, 25, 20, 15};
|
const int STARTING_DEF[RACE_CNT] = {25, 25, 20, 15, 20};
|
||||||
const char CHARACTER_REP[RACE_CNT] = {'S', 'V', 'G', 'D'};
|
const char CHARACTER_REP[RACE_CNT] = {'S', 'V', 'G', 'd', 'D'};
|
||||||
|
|
||||||
|
const int POTION_TYPE_CNT = 6;
|
||||||
|
const int DEFAULT_POTION_TYPE_CNT = 6;
|
||||||
enum potion_type {restore_health = 0, boost_atk, boost_def,
|
enum potion_type {restore_health = 0, boost_atk, boost_def,
|
||||||
poison_health, wound_atk, wound_def
|
poison_health, wound_atk, wound_def
|
||||||
};
|
};
|
||||||
@ -90,6 +92,9 @@ const feature FEATURE_SEED = 1 << 7;
|
|||||||
const feature FEATURE_MENU = 1 << 8;
|
const feature FEATURE_MENU = 1 << 8;
|
||||||
const feature FEATURE_IN_FILE = 1 << 9;
|
const feature FEATURE_IN_FILE = 1 << 9;
|
||||||
const feature FEATURE_OUT_FILE = 1 << 10;
|
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;
|
||||||
|
|
||||||
const feature FEATURE_PANIC_SEED = 1 << 27;
|
const feature FEATURE_PANIC_SEED = 1 << 27;
|
||||||
const feature FEATURE_PANIC_FILE = 1 << 28;
|
const feature FEATURE_PANIC_FILE = 1 << 28;
|
||||||
@ -104,6 +109,10 @@ const int COLOR_BLACK_ON_WHITE = 8;
|
|||||||
|
|
||||||
typedef std::vector<position> position_list;
|
typedef std::vector<position> position_list;
|
||||||
typedef std::vector<direction> direction_list;
|
typedef std::vector<direction> direction_list;
|
||||||
typedef std::vector<int> gold_list;
|
|
||||||
|
const int GOLD_SMALL = 1;
|
||||||
|
const int GOLD_NORMAL = 2;
|
||||||
|
const int GOLD_MERCHANT = 4;
|
||||||
|
const int GOLD_DRAGON = 6;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#include "curses_input.h"
|
#include "curses_input.h"
|
||||||
|
|
||||||
curses_input::curses_input(cursor &new_curse):
|
curses_input::curses_input(cursor *new_curse):
|
||||||
curse{new_curse} {}
|
curse{new_curse} {}
|
||||||
|
|
||||||
game_command curses_input::get_command() {
|
game_command curses_input::get_command() {
|
||||||
switch (curse.getcmd()) {
|
switch (curse->getcmd()) {
|
||||||
case 'h':
|
case 'h':
|
||||||
return game_command::move_west;
|
return game_command::move_west;
|
||||||
|
|
||||||
case 'j':
|
case 'j':
|
||||||
return game_command::move_south;
|
return game_command::move_south;
|
||||||
@ -51,9 +51,9 @@ game_command curses_input::get_command() {
|
|||||||
return game_command_pass;
|
return game_command_pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (curse.getcmd()) {
|
switch (curse->getcmd()) {
|
||||||
case 'h':
|
case 'h':
|
||||||
return game_command::apply_west;
|
return game_command::apply_west;
|
||||||
|
|
||||||
case 'j':
|
case 'j':
|
||||||
return game_command::apply_south;
|
return game_command::apply_south;
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
class curses_input final: public input {
|
class curses_input final: public input {
|
||||||
private:
|
private:
|
||||||
cursor &curse;
|
cursor *curse;
|
||||||
public:
|
public:
|
||||||
curses_input(cursor &new_curse);
|
curses_input(cursor *new_curse);
|
||||||
game_command get_command() override;
|
game_command get_command() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include "curses_output.h"
|
#include "curses_output.h"
|
||||||
|
|
||||||
curses_output::curses_output(cursor &new_curse):
|
curses_output::curses_output(cursor *new_curse):
|
||||||
curse{new_curse} {}
|
curse{new_curse} {}
|
||||||
|
|
||||||
void curses_output::render() {
|
void curses_output::render() {
|
||||||
curse.show();
|
curse->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void curses_output::clear() {
|
void curses_output::clear() {
|
||||||
curse.clear();
|
curse->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void curses_output::print_char(const position &pos, const char ch,
|
void curses_output::print_char(const position &pos, const char ch,
|
||||||
@ -16,7 +16,7 @@ void curses_output::print_char(const position &pos, const char ch,
|
|||||||
if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
|
if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
curse.print_char(pos, ch, attrs);
|
curse->print_char(pos, ch, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void curses_output::print_str(const position &pos, const std::string &str,
|
void curses_output::print_str(const position &pos, const std::string &str,
|
||||||
@ -26,9 +26,9 @@ void curses_output::print_str(const position &pos, const std::string &str,
|
|||||||
|
|
||||||
position tmp = pos;
|
position tmp = pos;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < str.length(); ++i) {
|
for (std::size_t i = 0; i < str.length(); ++i) {
|
||||||
curse.print_char(tmp, str[i], attrs);
|
curse->print_char(tmp, str[i], attrs);
|
||||||
tmp += {1, 0};
|
tmp += {1, 0};
|
||||||
|
|
||||||
if (tmp.x >= DISPLAY_WIDTH)
|
if (tmp.x >= DISPLAY_WIDTH)
|
||||||
tmp = {0, tmp.y + 1};
|
tmp = {0, tmp.y + 1};
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
class curses_output final : public display {
|
class curses_output final : public display {
|
||||||
private:
|
private:
|
||||||
cursor &curse;
|
cursor *curse;
|
||||||
public:
|
public:
|
||||||
curses_output(cursor &new_curse);
|
curses_output(cursor *new_curse);
|
||||||
|
|
||||||
void render() override;
|
void render() override;
|
||||||
void clear() override;
|
void clear() override;
|
||||||
|
@ -32,8 +32,8 @@ fraction &fraction::simplify() {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
float fraction::real() {
|
float fraction::real() const {
|
||||||
return (float)numerator / denominator;
|
return (float)numerator / denominator;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fraction::gcd(int a, int b) {
|
int fraction::gcd(int a, int b) {
|
||||||
|
@ -5,12 +5,12 @@ struct fraction final {
|
|||||||
int numerator;
|
int numerator;
|
||||||
int denominator;
|
int denominator;
|
||||||
|
|
||||||
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);
|
||||||
bool operator!=(const fraction &frac);
|
bool operator!=(const fraction &frac);
|
||||||
fraction &simplify();
|
fraction &simplify();
|
||||||
float real();
|
float real() const;
|
||||||
private:
|
private:
|
||||||
int gcd(int a, int b);
|
int gcd(int a, int b);
|
||||||
};
|
};
|
||||||
|
26
src/game.h
26
src/game.h
@ -12,19 +12,21 @@
|
|||||||
|
|
||||||
class game final {
|
class game final {
|
||||||
private:
|
private:
|
||||||
feature features;
|
feature features;
|
||||||
input ∈
|
input *in;
|
||||||
display &out;
|
display *out;
|
||||||
logger &log;
|
logger *log;
|
||||||
RNG &rng;
|
RNG *rng;
|
||||||
std::unique_ptr<character> player;
|
|
||||||
|
// IMPORTANT: during player generation,
|
||||||
|
std::unique_ptr<character> player;
|
||||||
public:
|
public:
|
||||||
game(const feature enabled_features,
|
game(const feature enabled_features,
|
||||||
input &new_in,
|
input *new_in,
|
||||||
display &new_out,
|
display *new_out,
|
||||||
logger &new_log,
|
logger *new_log,
|
||||||
RNG &new_rng);
|
RNG *new_rng);
|
||||||
game_status run();
|
game_status run();
|
||||||
private:
|
private:
|
||||||
int getcmd() const;
|
int getcmd() const;
|
||||||
};
|
};
|
||||||
|
@ -2,19 +2,18 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
goblin::goblin(RNG &rng, const position_list &available_positions):
|
goblin::goblin(RNG *rng, const position &pos):
|
||||||
character{rng, race::rshade} {
|
character{rng, race::rshade, pos} {
|
||||||
pos = available_positions[rng.rand_under(available_positions.size())];
|
gold = 0;
|
||||||
gold = 0;
|
hostile = true;
|
||||||
hostile = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result goblin::attack(const direction dir, character_list &chlist) {
|
result goblin::attack(const direction dir, character_list &chlist) {
|
||||||
position tmp{pos + MOVE[dir]};
|
position tmp{pos + MOVE[dir]};
|
||||||
|
|
||||||
for (auto &ch : chlist)
|
for (auto &ch : chlist)
|
||||||
if (tmp == ch.get_position()) {
|
if (tmp == ch->get_position()) {
|
||||||
auto res = ch.get_hit(race, ATK, base_hit_rate);
|
auto res = ch->get_hit(race, ATK, base_hit_rate);
|
||||||
|
|
||||||
if (res == result::died) {
|
if (res == result::died) {
|
||||||
gold += GAIN_GOLD;
|
gold += GAIN_GOLD;
|
||||||
@ -27,9 +26,15 @@ result goblin::attack(const direction dir, character_list &chlist) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result goblin::get_hit(const enum race &race, const int atk,
|
result goblin::get_hit(const enum race &race, const int atk,
|
||||||
|
<<<<<<< HEAD
|
||||||
const float hitrate) {
|
const float hitrate) {
|
||||||
if (rng.rand_num() <= hitrate * (float)RAND_MAX)
|
if (rng.rand_num() <= hitrate * (float)RAND_MAX)
|
||||||
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
||||||
|
=======
|
||||||
|
const fraction hitrate) {
|
||||||
|
if (rng->trial(hitrate))
|
||||||
|
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
||||||
|
>>>>>>> paul
|
||||||
|
|
||||||
if (HP == 0)
|
if (HP == 0)
|
||||||
return result::died;
|
return result::died;
|
||||||
|
14
src/goblin.h
14
src/goblin.h
@ -2,16 +2,14 @@
|
|||||||
#define __GOBLIN_H__
|
#define __GOBLIN_H__
|
||||||
#include "characters.h"
|
#include "characters.h"
|
||||||
|
|
||||||
const int GAIN_GOLD = 5;
|
|
||||||
|
|
||||||
class goblin final: public character {
|
class goblin final: public character {
|
||||||
|
static const int GAIN_GOLD = 5;
|
||||||
public:
|
public:
|
||||||
goblin(RNG &rng,
|
goblin(RNG *rng, const position &pos);
|
||||||
const position_list &available_positions);
|
virtual result attack(const direction dir,
|
||||||
virtual result attack(const direction dir,
|
character_list &chlist) override;
|
||||||
character_list &chlist) override;
|
virtual result get_hit(const enum race &race, const int atk,
|
||||||
virtual result get_hit(const enum race &race, const int atk,
|
const fraction hit_rate) override;
|
||||||
const float hit_rate) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
33
src/gold.h
Normal file
33
src/gold.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef __GOLD_H__
|
||||||
|
#define __GOLD_H__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "constants.h"
|
||||||
|
#include "position.h"
|
||||||
|
#include "rng.h"
|
||||||
|
|
||||||
|
struct gold {
|
||||||
|
int amount;
|
||||||
|
position pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<gold> gold_list;
|
||||||
|
|
||||||
|
int rand_gold_pile(RNG *rng) {
|
||||||
|
const int denominator = 8;
|
||||||
|
const int normal = 5;
|
||||||
|
const int dragon = 6;
|
||||||
|
|
||||||
|
int tmp = rng->rand_under(denominator);
|
||||||
|
|
||||||
|
if (tmp < normal)
|
||||||
|
return GOLD_NORMAL;
|
||||||
|
else if (tmp < dragon)
|
||||||
|
return GOLD_DRAGON;
|
||||||
|
else
|
||||||
|
return GOLD_SMALL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
53
src/level.h
53
src/level.h
@ -1,6 +1,57 @@
|
|||||||
#ifndef __LEVEL_H__
|
#ifndef __LEVEL_H__
|
||||||
#define __LEVEL_H__
|
#define __LEVEL_H__
|
||||||
|
|
||||||
class level;
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include "display.h"
|
||||||
|
#include "enemies.h"
|
||||||
|
#include "potions.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "gold.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
class level {
|
||||||
|
private:
|
||||||
|
static const int MAX_POTION_CNT = 15;
|
||||||
|
static const int MAX_ENEMIE_CNT = 30;
|
||||||
|
static const int MIN_POTION_CNT = 10;
|
||||||
|
static const int MIN_ENEMIE_CNT = 20;
|
||||||
|
static const int GOLD_CNT = 10;
|
||||||
|
|
||||||
|
const feature enabled_features;
|
||||||
|
game_map map;
|
||||||
|
character *player;
|
||||||
|
std::vector<std::unique_ptr<character>>pchlist;
|
||||||
|
std::vector<std::unique_ptr<potion>>pplist;
|
||||||
|
character_list chlist;
|
||||||
|
potion_list plist;
|
||||||
|
gold_list glist;
|
||||||
|
public:
|
||||||
|
// randomly generate a map
|
||||||
|
level(character *player, RNG *rng, const feature enabled_features);
|
||||||
|
// read map from a string
|
||||||
|
level(const std::string &map_data, character *player, RNG *rng,
|
||||||
|
const feature enabled_features);
|
||||||
|
void print(display *out) const;
|
||||||
|
|
||||||
|
bool is_available(const position &pos) const;
|
||||||
|
position_list get_available_around(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();
|
||||||
|
potion_list &get_plist();
|
||||||
|
gold_list &get_glist();
|
||||||
|
private:
|
||||||
|
// every gen will delete the positions in tiles
|
||||||
|
void gen_potions(RNG *rng, std::vector<position_list> &tiles);
|
||||||
|
void gen_gold(RNG *rng, std::vector<position_list> &tiles);
|
||||||
|
void gen_enemies(RNG *rng, std::vector<position_list> &tiles);
|
||||||
|
|
||||||
|
position get_rand_pos(RNG *rng, std::vector<position_list> &tiles);
|
||||||
|
gold_list dragon_hoard();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
21
src/main.cc
21
src/main.cc
@ -1,4 +1,6 @@
|
|||||||
#include "game.h"
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "cc3k.h"
|
||||||
#include "arguments.h"
|
#include "arguments.h"
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@ -14,14 +16,19 @@ int main(int argc, char **argv) {
|
|||||||
if (enabled_features &
|
if (enabled_features &
|
||||||
(FEATURE_PANIC | FEATURE_PANIC_FILE |
|
(FEATURE_PANIC | FEATURE_PANIC_FILE |
|
||||||
FEATURE_CONFLICT | FEATURE_PANIC_SEED)) {
|
FEATURE_CONFLICT | FEATURE_PANIC_SEED)) {
|
||||||
panic_args(enabled_features);
|
panic_args(enabled_features);
|
||||||
return RETURN_PANICKED;
|
return RETURN_PANICKED;
|
||||||
}
|
} else if (enabled_features & FEATURE_LIST_ARGS) {
|
||||||
|
print_args_list();
|
||||||
|
return RETURN_FINE;
|
||||||
|
}
|
||||||
|
|
||||||
game game_proc(enabled_features, *in, *out, *log, *rng);
|
// CC3K game_proc(enabled_features, *in, *out, *log, *rng);
|
||||||
|
|
||||||
while (game_proc.run() != game_status::terminated)
|
// while (game_proc.run() != game_status::terminated) {
|
||||||
out->render();
|
// out->render();
|
||||||
|
// out->clear();
|
||||||
|
// }
|
||||||
|
|
||||||
return RETURN_FINE;
|
return RETURN_FINE;
|
||||||
}
|
}
|
||||||
|
577
src/map.cc
577
src/map.cc
@ -1,68 +1,559 @@
|
|||||||
#include "map.h"
|
#include "map.h"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
game_map::game_map(int lvl) {
|
#include <iostream>
|
||||||
level = lvl;
|
|
||||||
// TODO: randomly generate a map
|
game_map::game_map(character *player, RNG *rng, const feature enabled_features):
|
||||||
|
enabled_features{enabled_features} {
|
||||||
|
map.reserve(MAP_HEIGHT * MAP_WIDTH);
|
||||||
|
|
||||||
|
for (int i = MAP_HEIGHT * MAP_WIDTH; i > 0; --i)
|
||||||
|
map.push_back(' ');
|
||||||
|
|
||||||
|
// Note: during generation, walls DO NOT count as being in the rooms
|
||||||
|
std::vector<std::pair<position, int>> room_dims = gen_room_dims(rng);
|
||||||
|
|
||||||
|
std::vector<std::pair<position, int>> layer_data;
|
||||||
|
// width height top left
|
||||||
|
room_data = distr_rooms(rng, room_dims, layer_data);
|
||||||
|
|
||||||
|
for (size_t r = 0; r < room_data.size(); ++r)
|
||||||
|
fill_room(room_data[r], r);
|
||||||
|
|
||||||
|
fill_outline();
|
||||||
|
|
||||||
|
gen_path(layer_data, rng);
|
||||||
|
|
||||||
|
|
||||||
|
rooms_tile_list.reserve(room_data.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < room_data.size(); ++i)
|
||||||
|
rooms_tile_list.push_back({});
|
||||||
|
|
||||||
|
for (size_t i = 0; i < map.size(); ++i)
|
||||||
|
if (map[i] >= 0 && map[i] < MAX_ROOM_CNT)
|
||||||
|
rooms_tile_list[map[i]].push_back(remap_index(i));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rooms_tile_list.size(); ++i)
|
||||||
|
rooms_tile_list[i].shrink_to_fit();
|
||||||
|
|
||||||
|
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]));
|
||||||
|
|
||||||
|
gen_stairs(player_room, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
game_map::game_map(const std::string &map_data, int lvl) {
|
void game_map::gen_stairs(int player_room, RNG *rng) {
|
||||||
level = lvl;
|
down_stairs = rng->get_rand_in_vector(rooms_tile_list[
|
||||||
std::istringstream iss{map_data};
|
rng->exclude_middle(0, room_data.size(), player_room)]);
|
||||||
|
|
||||||
std::string line;
|
if (enabled_features & FEATURE_REVISIT) {
|
||||||
|
up_stairs = rng->get_rand_in_vector(rooms_tile_list[player_room]);
|
||||||
|
|
||||||
|
map[remap_position(up_stairs)] = '<';
|
||||||
|
map[remap_position(down_stairs)] = '>';
|
||||||
|
} else {
|
||||||
|
map[remap_position(down_stairs)] = '\\';
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAP_HEIGHT; ++i) {
|
|
||||||
getline(iss, line);
|
|
||||||
map[i] = line;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
game_map::~game_map() {}
|
position game_map::random_size(int min_width, int min_height,
|
||||||
|
int max_width, int max_height,
|
||||||
int game_map::get_level() const {
|
RNG *rng) {
|
||||||
return this->level;
|
return {rng->rand_between(min_width, max_width + 1),
|
||||||
|
rng->rand_between(min_height, max_height + 1)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<position> game_map::get_available_positions() const {
|
game_map::game_map(character *player, const std::string &map_data,
|
||||||
std::vector<position> result;
|
RNG *rng, const feature enabled_features):
|
||||||
|
enabled_features{enabled_features} {
|
||||||
|
int map_size = MAP_HEIGHT * MAP_WIDTH;
|
||||||
|
map.reserve(map_size);
|
||||||
|
rooms_tile_list.reserve(MAX_ROOM_CNT);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ROOM_CNT; ++i)
|
||||||
|
rooms_tile_list.push_back({});
|
||||||
|
|
||||||
|
int max_room_num = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < map_size; ++i) {
|
||||||
|
if (map_data[i] >= '0' && map_data[i] <= '9') {
|
||||||
|
map.push_back(map_data[i] - '0');
|
||||||
|
max_room_num = std::max(max_room_num, map_data[i] - '0');
|
||||||
|
} else {
|
||||||
|
map.push_back(map_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map_data[i] >= '0' && map_data[i] <= '9')
|
||||||
|
rooms_tile_list[map_data[i] - '0'].push_back(remap_index(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= max_room_num; ++i)
|
||||||
|
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]));
|
||||||
|
|
||||||
|
gen_stairs(player_room, rng);
|
||||||
|
|
||||||
for (int line = 0; line < MAP_HEIGHT; ++line)
|
|
||||||
for (int x = 0; x < MAP_WIDTH; ++x)
|
|
||||||
if (map[line][x] == '.' ||
|
|
||||||
map[line][x] == '#' ||
|
|
||||||
map[line][x] == '+')
|
|
||||||
result.push_back(position{x, line});
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::print() const {
|
bool game_map::is_available(const position &pos) const {
|
||||||
|
if (pos.x < 0 || pos.x >= MAP_WIDTH || pos.y < 0 || pos.y >= MAP_HEIGHT)
|
||||||
|
return false;
|
||||||
|
|
||||||
// TODO: write a print function using ncurses
|
int idx = remap_position(pos);
|
||||||
|
return map[idx] < MAX_ROOM_CNT || map[idx] == '\\' || map[idx] == '>' ||
|
||||||
|
map[idx] == '<' || map[idx] == '+' || map[idx] == '#';
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::print(display &display) const {
|
position_list game_map::get_available_around(const position &pos) const {
|
||||||
for (int i = 0; i < MAP_HEIGHT; ++i)
|
position_list result;
|
||||||
display.print_str(position{0, i}, map[i]);
|
|
||||||
|
for (int i = 0; i < DIRECTION_CNT; ++i)
|
||||||
|
if (is_available(pos + MOVE[i]))
|
||||||
|
result.push_back(pos + MOVE[i]);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::apply_potion(character *who, const stat_name statn,
|
void game_map::print(display *out) const {
|
||||||
const int amount) {
|
for (std::size_t i = 0; i < map.size(); ++i)
|
||||||
effects.push_back(effect{who, statn, amount});
|
out->print_char(remap_index(i), map[i] <= 9 ? '.' : map[i],
|
||||||
who->apply_buff(statn, amount);
|
map[i] == '\\' || map[i] == '>' ||
|
||||||
|
map[i] == '<' ? COLOR_PAIR(COLOR_BLUE) : A_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::enter_level(character *who) {
|
int game_map::which_room(const position &pos) const {
|
||||||
for (auto eff : effects)
|
if (pos.y > MAP_HEIGHT || pos.x > MAP_WIDTH || pos.x < 0 || pos.y < 0)
|
||||||
if (eff.who == who)
|
return -1;
|
||||||
who->apply_buff(eff.statn, eff.amount);
|
|
||||||
|
char tmp = map[remap_position(pos)];
|
||||||
|
return tmp < MAX_ROOM_CNT ? tmp : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::leave_level(character *who) {
|
position game_map::get_up_stairs() const {
|
||||||
who->set_ATK(STARTING_ATK[who->get_race()]);
|
return up_stairs;
|
||||||
who->set_DEF(STARTING_DEF[who->get_race()]);
|
}
|
||||||
|
|
||||||
|
position game_map::get_down_stairs() const {
|
||||||
|
return down_stairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int game_map::get_up_stairs_room() const {
|
||||||
|
return map[remap_position(up_stairs)];
|
||||||
|
}
|
||||||
|
|
||||||
|
int game_map::get_down_stairs_room() const {
|
||||||
|
return map[remap_position(down_stairs)];
|
||||||
|
}
|
||||||
|
|
||||||
|
position_list game_map::get_room_list(int idx) const {
|
||||||
|
return rooms_tile_list[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<position_list> game_map::get_room_list() const {
|
||||||
|
return rooms_tile_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<position, int>> game_map::gen_room_dims(RNG *rng) {
|
||||||
|
position_list room_dim_list;
|
||||||
|
room_dim_list.reserve(MAX_ROOM_CNT);
|
||||||
|
std::vector<std::pair<position, int>> result;
|
||||||
|
result.reserve(MAX_ROOM_CNT);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ROOM_CNT; ++i)
|
||||||
|
room_dim_list.push_back(random_size(MIN_ROOM_WIDTH, MIN_ROOM_HEIGHT,
|
||||||
|
MAX_ROOM_WIDTH, MAX_ROOM_HEIGHT,
|
||||||
|
rng));
|
||||||
|
|
||||||
|
int curr_top = 0;
|
||||||
|
int curr_left = 0;
|
||||||
|
// index in result
|
||||||
|
int curr_layer_idx = 0;
|
||||||
|
int curr_layer_cnt = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ROOM_CNT &&
|
||||||
|
curr_top <= ACTUAL_MAP_HEIGHT &&
|
||||||
|
curr_left <= ACTUAL_MAP_WIDTH; ++i) {
|
||||||
|
// DO NOT update curr_top until curr_left is exhausted
|
||||||
|
if (curr_top + MIN_ROOM_SPACING +
|
||||||
|
room_dim_list[i].y + HEIGHT_RESERVED <= ACTUAL_MAP_HEIGHT &&
|
||||||
|
curr_left + MIN_ROOM_SPACING +
|
||||||
|
room_dim_list[i].x + WIDTH_RESERVED <= ACTUAL_MAP_WIDTH) {
|
||||||
|
result.push_back({room_dim_list[i], curr_layer_cnt});
|
||||||
|
curr_left += MIN_ROOM_SPACING + room_dim_list[i].x;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tmp = curr_top;
|
||||||
|
|
||||||
|
for (std::size_t j = curr_layer_idx; j < result.size(); ++j)
|
||||||
|
tmp = std::max(curr_top + result[j].first.y + MIN_ROOM_SPACING,
|
||||||
|
tmp);
|
||||||
|
|
||||||
|
curr_top = tmp;
|
||||||
|
curr_layer_idx = result.size();
|
||||||
|
curr_left = 0;
|
||||||
|
++curr_layer_cnt;
|
||||||
|
|
||||||
|
if (curr_top + MIN_ROOM_SPACING +
|
||||||
|
room_dim_list[i].y + HEIGHT_RESERVED <= ACTUAL_MAP_HEIGHT &&
|
||||||
|
curr_left + MIN_ROOM_SPACING +
|
||||||
|
room_dim_list[i].x + WIDTH_RESERVED <= ACTUAL_MAP_WIDTH) {
|
||||||
|
result.push_back({room_dim_list[i], curr_layer_cnt});
|
||||||
|
curr_left += MIN_ROOM_SPACING + room_dim_list[i].x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.shrink_to_fit();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
position random_size(int min_width, int min_height,
|
||||||
|
int max_width, int max_height,
|
||||||
|
RNG *rng) {
|
||||||
|
return {rng->rand_between(min_width, max_width + 1),
|
||||||
|
rng->rand_between(min_height, max_height + 1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::fill_room(const room &r, const int num) {
|
||||||
|
for (int x = 0; x < r.width; ++x)
|
||||||
|
for (int y = 0; y < r.height; ++y)
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top + y})] = num;
|
||||||
|
|
||||||
|
for (int x = 0; x < r.width; ++x) {
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top - 1})] = '-';
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top +
|
||||||
|
r.height})] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = -1; y <= r.height; ++y) {
|
||||||
|
map[remap_position({r.left - 1,
|
||||||
|
r.top + y})] = '|';
|
||||||
|
map[remap_position({r.left +
|
||||||
|
r.width,
|
||||||
|
r.top + y})] = '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<game_map::room> game_map::distr_rooms(RNG *rng,
|
||||||
|
std::vector<std::pair<position, int>> &room_dims,
|
||||||
|
std::vector<std::pair<position, int>> &layer_data) {
|
||||||
|
std::vector<room> result;
|
||||||
|
result.reserve(room_dims.size());
|
||||||
|
|
||||||
|
int max_layer = room_dims[room_dims.size() - 1].second;
|
||||||
|
// left, right, height
|
||||||
|
layer_data.reserve(max_layer + 1);
|
||||||
|
|
||||||
|
// distributing rooms horizontally
|
||||||
|
for (int layer = 0; layer <= max_layer; ++layer) {
|
||||||
|
int l = INF;
|
||||||
|
int r = 0;
|
||||||
|
int layer_height = 0;
|
||||||
|
|
||||||
|
// get the interval for the current layer
|
||||||
|
// and the max height of the layer
|
||||||
|
for (std::size_t i = 0; i < room_dims.size(); ++i)
|
||||||
|
if (room_dims[i].second == layer) {
|
||||||
|
l = std::min(l, (int)i);
|
||||||
|
r = std::max(r, (int)i);
|
||||||
|
layer_height = std::max(layer_height,
|
||||||
|
room_dims[i].first.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_data.push_back({{l, r}, layer_height});
|
||||||
|
|
||||||
|
// distribute the current layer
|
||||||
|
if (l == r) {
|
||||||
|
result.push_back({
|
||||||
|
0, rng->rand_under(ACTUAL_MAP_WIDTH -
|
||||||
|
room_dims[l].first.x + 1),
|
||||||
|
room_dims[l].first.x,
|
||||||
|
room_dims[l].first.y, {0, 0}, {0, 0}});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int left = 0;
|
||||||
|
int right = ACTUAL_MAP_WIDTH;
|
||||||
|
|
||||||
|
// every time, distribute the last one first
|
||||||
|
for (int i = l; i < r; ++i)
|
||||||
|
left += MIN_ROOM_SPACING + room_dims[i].first.x;
|
||||||
|
|
||||||
|
for (int i = r; i >= l; --i) {
|
||||||
|
int offset = rng->rand_between(left, right - room_dims[i].first.x + 1);
|
||||||
|
result.push_back({0, offset,
|
||||||
|
room_dims[i].first.x,
|
||||||
|
room_dims[i].first.y, {0, 0}, {0, 0}});
|
||||||
|
|
||||||
|
right = offset - MIN_ROOM_SPACING;
|
||||||
|
|
||||||
|
if (i != l)
|
||||||
|
left -= MIN_ROOM_SPACING + room_dims[i - 1].first.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// distributing rooms vertically
|
||||||
|
int top = 0;
|
||||||
|
int bottom = ACTUAL_MAP_HEIGHT;
|
||||||
|
|
||||||
|
for (int i = 0; i < max_layer; ++i)
|
||||||
|
top += MIN_ROOM_SPACING + layer_data[i].second;
|
||||||
|
|
||||||
|
for (int i = max_layer; i >= 0; --i) {
|
||||||
|
int offset = rng->rand_between(top,
|
||||||
|
bottom - layer_data[i].second + 1);
|
||||||
|
|
||||||
|
for (int j = layer_data[i].first.x;
|
||||||
|
j <= layer_data[i].first.y; ++j)
|
||||||
|
result[j].top = offset;
|
||||||
|
|
||||||
|
bottom = offset - MIN_ROOM_SPACING;
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
top -= MIN_ROOM_SPACING + layer_data[i - 1].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add some vertical jitter
|
||||||
|
jitter(rng, result);
|
||||||
|
|
||||||
|
//add padding
|
||||||
|
for (std::size_t i = 0; i < result.size(); ++i) {
|
||||||
|
result[i].top += MAP_PADDING;
|
||||||
|
result[i].left += MAP_PADDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::jitter(RNG *rng,
|
||||||
|
std::vector<room> &rooms) {
|
||||||
|
for (int n = rooms.size() - 1; n >= 0; --n) {
|
||||||
|
int t = 0;
|
||||||
|
int b = ACTUAL_MAP_HEIGHT - 1;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < rooms.size(); ++i) {
|
||||||
|
if (rooms[i] == rooms[n])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (overlap_x(rooms[n], rooms[i])) {
|
||||||
|
if (rooms[n].top > rooms[i].top)
|
||||||
|
t = std::max(t,
|
||||||
|
rooms[i].top +
|
||||||
|
rooms[i].height +
|
||||||
|
MIN_ROOM_SPACING);
|
||||||
|
else
|
||||||
|
b = std::min(b,
|
||||||
|
rooms[i].top -
|
||||||
|
MIN_ROOM_SPACING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t < b - rooms[n].height + 1)
|
||||||
|
rooms[n].top = rng->rand_between(t,
|
||||||
|
b - rooms[n].height + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_map::overlap_x(room &room1,
|
||||||
|
room &room2) {
|
||||||
|
return (room1.left >= room2.left &&
|
||||||
|
room1.left - MIN_ROOM_SPACING <=
|
||||||
|
room2.left + room2.width) ||
|
||||||
|
(room2.left >= room1.left &&
|
||||||
|
room2.left - MIN_ROOM_SPACING <=
|
||||||
|
room1.left + room1.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_map::overlap_y(room &room1,
|
||||||
|
room &room2) {
|
||||||
|
return (room1.top >= room2.top &&
|
||||||
|
room1.top - MIN_ROOM_SPACING <=
|
||||||
|
room2.top + room2.height) ||
|
||||||
|
(room2.top >= room1.top &&
|
||||||
|
room2.top - MIN_ROOM_SPACING <=
|
||||||
|
room1.top + room1.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::fill_outline() {
|
||||||
|
for (int x = 0; x < MAP_WIDTH; ++x) {
|
||||||
|
map[remap_position({x, 0})] = '-';
|
||||||
|
map[remap_position({x, MAP_HEIGHT - 1})] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int y = 0; y < MAP_HEIGHT; ++y) {
|
||||||
|
map[remap_position({0, y})] = '|';
|
||||||
|
map[remap_position({MAP_WIDTH - 1, y})] = '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int game_map::get_room_cnt() const {
|
||||||
|
return room_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
game_map::room game_map::get_room(std::size_t idx) const {
|
||||||
|
if (idx < room_data.size())
|
||||||
|
return room_data[idx];
|
||||||
|
|
||||||
|
return {0, 0, 0, 0, {0, 0}, {0, 0}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::gen_path(std::vector<std::pair<position, int>> &layer_data,
|
||||||
|
RNG *rng) {
|
||||||
|
for (std::size_t l = 0; l < layer_data.size(); ++l) {
|
||||||
|
for (int i = layer_data[l].first.x; i < layer_data[l].first.y; ++i)
|
||||||
|
connect_x(room_data[i + 1], room_data[i], rng);
|
||||||
|
|
||||||
|
if (l < layer_data.size() - 1) {
|
||||||
|
connect_y(room_data[layer_data[l].first.x],
|
||||||
|
room_data[layer_data[l + 1].first.x], rng);
|
||||||
|
connect_y(room_data[layer_data[l].first.y],
|
||||||
|
room_data[layer_data[l + 1].first.y], rng);
|
||||||
|
connect_y(room_data[(layer_data[l].first.x +
|
||||||
|
layer_data[l].first.y) / 2],
|
||||||
|
room_data[(1 + layer_data[l + 1].first.x +
|
||||||
|
layer_data[l + 1].first.y) / 2], rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::connect_x(room &room1, room &room2, RNG *rng) {
|
||||||
|
room1.rdoor = {room1.left + room1.width,
|
||||||
|
rng->rand_between(room1.top, room1.top + room1.height)
|
||||||
|
};
|
||||||
|
room2.ldoor = {room2.left - 1,
|
||||||
|
rng->rand_between(room2.top, room2.top + room2.height)
|
||||||
|
};
|
||||||
|
map[remap_position(room1.rdoor)] = '+';
|
||||||
|
map[remap_position(room2.ldoor)] = '+';
|
||||||
|
connect({room1.rdoor.x + 1, room1.rdoor.y},
|
||||||
|
{room2.ldoor.x - 1, room2.ldoor.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::connect_y(room &room1, room &room2, RNG *rng) {
|
||||||
|
room1.bdoor = {rng->rand_between(room1.left, room1.left + room1.width),
|
||||||
|
room1.top + room1.height
|
||||||
|
};
|
||||||
|
room2.tdoor = {rng->rand_between(room2.left, room2.left + room2.width),
|
||||||
|
room2.top - 1
|
||||||
|
};
|
||||||
|
map[remap_position(room1.bdoor)] = '+';
|
||||||
|
map[remap_position(room2.tdoor)] = '+';
|
||||||
|
connect({room1.bdoor.x, room1.bdoor.y + 1},
|
||||||
|
{room2.tdoor.x, room2.tdoor.y - 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// a is to the left of b
|
||||||
|
void game_map::connect(position s, position t) {
|
||||||
|
if (s.x > t.x) {
|
||||||
|
connect(t, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
map[remap_position(t)] = '#';
|
||||||
|
|
||||||
|
const room nil{0, 0, 0, 0};
|
||||||
|
|
||||||
|
while (s.x < t.x) {
|
||||||
|
auto r = hit_room({s.x + 1, s.y});
|
||||||
|
|
||||||
|
if (r == nil) {
|
||||||
|
++s.x;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.top + r.height - s.y < r.top - 1) {
|
||||||
|
while (hit_room({s.x + 1, s.y}, r)) {
|
||||||
|
++s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (hit_room({s.x + 1, s.y}, r)) {
|
||||||
|
--s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.y < t.y) {
|
||||||
|
while (s.y < t.y) {
|
||||||
|
auto r = hit_room({s.x, s.y + 1});
|
||||||
|
|
||||||
|
if (r == nil) {
|
||||||
|
++s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x, s.y + 1}, r)) {
|
||||||
|
++s.x;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x - 1, s.y + 1}, r)) {
|
||||||
|
++s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x, s.y - 1}, r) && s.x > t.x) {
|
||||||
|
--s.x;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
++s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (s.y > t.y) {
|
||||||
|
auto r = hit_room({s.x, s.y - 1});
|
||||||
|
|
||||||
|
if (r == nil) {
|
||||||
|
--s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x, s.y - 1}, r)) {
|
||||||
|
++s.x;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x - 1, s.y - 1}, r)) {
|
||||||
|
--s.y;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hit_room({s.x, s.y + 1}, r) && s.x > t.x) {
|
||||||
|
--s.x;
|
||||||
|
map[remap_position(s)] = '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game_map::room game_map::hit_room(const position &a) {
|
||||||
|
for (auto r : room_data) {
|
||||||
|
if (hit_room(a, r))
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
const room nil{0, 0, 0, 0};
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
160
src/map.h
160
src/map.h
@ -1,60 +1,150 @@
|
|||||||
/*
|
|
||||||
* CS 246 Final Project
|
|
||||||
* File: map.h
|
|
||||||
* Purpose: handles map functionality
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __MAP_H__
|
#ifndef __MAP_H__
|
||||||
#define __MAP_H__
|
#define __MAP_H__
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "fraction.h"
|
||||||
#include "characters.h"
|
#include "characters.h"
|
||||||
|
|
||||||
class game_map final {
|
class game_map final {
|
||||||
|
private:
|
||||||
|
static const int MAX_ROOM_CNT = 20;
|
||||||
|
static const int MIN_ROOM_WIDTH = 3;
|
||||||
|
static const int MIN_ROOM_HEIGHT = 3;
|
||||||
|
static const int MAX_ROOM_WIDTH = 20;
|
||||||
|
static const int MAX_ROOM_HEIGHT = 5;
|
||||||
|
// setup safezones
|
||||||
|
static const int ACTUAL_MAP_WIDTH = MAP_WIDTH - 5;
|
||||||
|
static const int ACTUAL_MAP_HEIGHT = MAP_HEIGHT - 5;
|
||||||
|
static const int MAP_PADDING = 3;
|
||||||
|
static const int MIN_ROOM_SPACING = 3;
|
||||||
|
static const int WIDTH_RESERVED = 6;
|
||||||
|
static const int HEIGHT_RESERVED = 3;
|
||||||
|
|
||||||
|
const feature enabled_features;
|
||||||
|
std::vector<char> map;
|
||||||
|
position up_stairs;
|
||||||
|
position down_stairs;
|
||||||
|
std::vector<position_list> rooms_tile_list;
|
||||||
public:
|
public:
|
||||||
game_map(int lvl = 0); // randomly generate one
|
struct room {
|
||||||
// initialize using stored data
|
int top;
|
||||||
game_map(const std::string &map_data, int lvl);
|
int left;
|
||||||
~game_map(); // placeholder
|
int width;
|
||||||
|
int height;
|
||||||
|
position ldoor;
|
||||||
|
position rdoor;
|
||||||
|
position tdoor;
|
||||||
|
position bdoor;
|
||||||
|
bool operator!=(const room &r) {
|
||||||
|
return !(*this == r);
|
||||||
|
}
|
||||||
|
bool operator==(const room &r) {
|
||||||
|
return top == r.top &&
|
||||||
|
left == r.left &&
|
||||||
|
width == r.width &&
|
||||||
|
height == r.height;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// randomly generate a map
|
||||||
|
game_map(character *player, RNG *rng, const feature enabled_features);
|
||||||
|
// read map from a string
|
||||||
|
game_map(character *player, const std::string &map_data, RNG *rng,
|
||||||
|
const feature enabled_features);
|
||||||
|
|
||||||
int get_level() const;
|
bool is_available(const position &pos) const;
|
||||||
|
position_list get_available_around(const position &pos) const;
|
||||||
|
int which_room(const position &pos) const;
|
||||||
|
|
||||||
position_list get_available_positions() const;
|
position_list get_room_list(int idx) const;
|
||||||
|
std::vector<position_list> get_room_list() const;
|
||||||
|
// IMPORTANT: always print a map before anything else
|
||||||
|
void print(display *out) const;
|
||||||
|
|
||||||
// IMPORTANT: always print a map before anything else
|
position get_up_stairs() const;
|
||||||
|
position get_down_stairs() const;
|
||||||
|
|
||||||
// prints using ncurses
|
int get_up_stairs_room() const;
|
||||||
void print() const;
|
int get_down_stairs_room() const;
|
||||||
|
|
||||||
// prints to a string
|
int get_room_cnt() const;
|
||||||
void print(display &display) 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};
|
||||||
|
}
|
||||||
|
|
||||||
// This is implemented this way to do two things:
|
int remap_position(const position &pos) const {
|
||||||
// 1. avoid directly accessing heap memory (bonus points)
|
return pos.y * MAP_WIDTH + pos.x;
|
||||||
// 2. make a level revisitable
|
}
|
||||||
void apply_potion(character *who, const stat_name statn, const int amount);
|
|
||||||
|
|
||||||
// just in case you want to retain the potion effects
|
position random_size(int min_width, int min_height,
|
||||||
void enter_level(character *who);
|
int max_width, int max_height,
|
||||||
void leave_level(character *who);
|
RNG *rng);
|
||||||
public:
|
|
||||||
struct effect {
|
|
||||||
character *who;
|
|
||||||
stat_name statn;
|
|
||||||
int amount;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string map[MAP_HEIGHT];
|
std::vector<std::pair<position, int>> gen_room_dims(RNG *rng);
|
||||||
int level;
|
std::vector<room> distr_rooms(RNG *rng,
|
||||||
|
std::vector<std::pair<position, int>> &room_dims,
|
||||||
|
std::vector<std::pair<position, int>> &layer_data);
|
||||||
|
|
||||||
// use this to remember every applied potion
|
bool overlap_x(room &room1,
|
||||||
std::vector<effect> effects;
|
room &room2);
|
||||||
|
bool overlap_y(room &room1,
|
||||||
|
room &room2);
|
||||||
|
// has anything between the two
|
||||||
|
bool between_x(room &room1,
|
||||||
|
room &room2);
|
||||||
|
bool between_y(room &room1,
|
||||||
|
room &room2);
|
||||||
|
|
||||||
|
void jitter(RNG *rng, std::vector<room> &rooms);
|
||||||
|
|
||||||
|
void fill_room(const room &r, const int num);
|
||||||
|
void fill_outline();
|
||||||
|
|
||||||
|
void gen_path(std::vector<std::pair<position, int>> &layer_data, RNG *rng);
|
||||||
|
// IMPORTANT: assumes room1 is to the left of room 2
|
||||||
|
void connect_x(room &room1, room &room2, RNG *rng);
|
||||||
|
// IMPORTANT: assumes room1 is under room2
|
||||||
|
void connect_y(room &room1, room &room2, RNG *rng);
|
||||||
|
|
||||||
|
void connect(position s, position t);
|
||||||
|
room hit_room(const position &a);
|
||||||
|
bool hit_room(const position &a, const room &r);
|
||||||
|
|
||||||
|
void gen_stairs(int player_room, RNG *rng);
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string default_map =
|
const std::string default_map =
|
||||||
"|-----------------------------------------------------------------------------|\n| |\n| |--------------------------| |-----------------------| |\n| |..........................| |.......................| |\n| |..........................+########+.......................|-------| |\n| |..........................| # |...............................|--| |\n| |..........................| # |..................................|--| |\n| |----------+---------------| # |----+----------------|...............| |\n| # ############# |...............| |\n| # # |-----+------| |...............| |\n| # # |............| |...............| |\n| ################### |............| ######+...............| |\n| # # |............| # |...............| |\n| # # |-----+------| # |--------+------| |\n| |---------+-----------| # # # # |\n| |.....................| # # # |----+------| |\n| |.....................| ######################## |...........| |\n| |.....................| # # |...........| |\n| |.....................| # |------+--------------------|...........| |\n| |.....................| # |.......................................| |\n| |.....................+##########+.......................................| |\n| |.....................| |.......................................| |\n| |---------------------| |---------------------------------------| |\n| |\n|-----------------------------------------------------------------------------|";
|
"|-----------------------------------------------------------------------------|\
|
||||||
|
| |\
|
||||||
|
| |--------------------------| |-----------------------| |\
|
||||||
|
| |11111111111111111111111111| |33333333333333333333333| |\
|
||||||
|
| |11111111111111111111111111+########+33333333333333333333333|-------| |\
|
||||||
|
| |11111111111111111111111111| # |3333333333333333333333333333333|--| |\
|
||||||
|
| |11111111111111111111111111| # |3333333333333333333333333333333333|--| |\
|
||||||
|
| |----------+---------------| # |----+----------------|333333333333333| |\
|
||||||
|
| # ############# |333333333333333| |\
|
||||||
|
| # # |-----+------| |333333333333333| |\
|
||||||
|
| # # |444444444444| |333333333333333| |\
|
||||||
|
| ################### |444444444444| ######+333333333333333| |\
|
||||||
|
| # # |444444444444| # |333333333333333| |\
|
||||||
|
| # # |-----+------| # |--------+------| |\
|
||||||
|
| |---------+-----------| # # # # |\
|
||||||
|
| |222222222222222222222| # # # |----+------| |\
|
||||||
|
| |222222222222222222222| ######################## |00000000000| |\
|
||||||
|
| |222222222222222222222| # # |00000000000| |\
|
||||||
|
| |222222222222222222222| # |------+--------------------|00000000000| |\
|
||||||
|
| |222222222222222222222| # |000000000000000000000000000000000000000| |\
|
||||||
|
| |222222222222222222222+##########+000000000000000000000000000000000000000| |\
|
||||||
|
| |222222222222222222222| |000000000000000000000000000000000000000| |\
|
||||||
|
| |---------------------| |---------------------------------------| |\
|
||||||
|
| |\
|
||||||
|
|-----------------------------------------------------------------------------|";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,3 +43,34 @@ size_t find(const std::vector<position> &sorted_list,
|
|||||||
|
|
||||||
return sorted_list.size();
|
return sorted_list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
std::vector<position> remove_from_list(const std::vector<position>
|
||||||
|
&sorted_positions,
|
||||||
|
std::vector<position> excluded) {
|
||||||
|
std::sort(excluded.begin(), excluded.end());
|
||||||
|
|
||||||
|
std::vector<position> result{sorted_positions.size() - excluded.size()};
|
||||||
|
|
||||||
|
auto exc = excluded.begin();
|
||||||
|
|
||||||
|
for (auto src : sorted_positions) {
|
||||||
|
if (exc != excluded.end() && src == *exc)
|
||||||
|
++exc;
|
||||||
|
else
|
||||||
|
result.push_back(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_from_list(std::vector<position>
|
||||||
|
&positions,
|
||||||
|
position &excluded) {
|
||||||
|
for (auto i = positions.begin(); i != positions.end(); ++i)
|
||||||
|
if (*i == excluded) {
|
||||||
|
positions.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
/*
|
|
||||||
* CS 246 Final Project
|
|
||||||
* File: map.h
|
|
||||||
* Purpose: handles map functionality
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __POSITION_H__
|
#ifndef __POSITION_H__
|
||||||
#define __POSITION_H__
|
#define __POSITION_H__
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -26,4 +20,10 @@ typedef struct position {
|
|||||||
std::size_t find(const std::vector<position> &sorted_list,
|
std::size_t find(const std::vector<position> &sorted_list,
|
||||||
const position &target);
|
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,
|
||||||
|
position &excluded);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "potion.h"
|
#include "potion.h"
|
||||||
|
|
||||||
potion::potion(const potion_type type, const int duration):
|
potion::potion(const potion_type type, const int duration, const position &pos):
|
||||||
type{type}, remaining_duration{duration} {}
|
type{type}, remaining_duration{duration}, pos{pos} {}
|
||||||
|
|
||||||
potion_type potion::get_type() const {
|
potion_type potion::get_type() const {
|
||||||
return type;
|
return type;
|
||||||
@ -10,3 +10,37 @@ potion_type potion::get_type() const {
|
|||||||
int potion::get_duration() const {
|
int potion::get_duration() const {
|
||||||
return remaining_duration;
|
return remaining_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position potion::get_pos() const {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void potion::set_pos(const position &npos) {
|
||||||
|
pos = npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
potion::potion(const potion &p):
|
||||||
|
type(p.type), remaining_duration(p.remaining_duration),
|
||||||
|
pos{p.pos} {}
|
||||||
|
|
||||||
|
potion::potion(potion &&p):
|
||||||
|
type(p.type), remaining_duration(p.remaining_duration),
|
||||||
|
pos{p.pos} {}
|
||||||
|
|
||||||
|
potion &potion::operator=(const potion &p) {
|
||||||
|
type = p.type;
|
||||||
|
remaining_duration = p.remaining_duration;
|
||||||
|
pos = p.pos;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
potion &potion::operator=(potion &&p) {
|
||||||
|
type = p.type;
|
||||||
|
remaining_duration = p.remaining_duration;
|
||||||
|
pos = p.pos;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void potion::print(display *out, bool color) {
|
||||||
|
out->print_char(pos, 'P');
|
||||||
|
}
|
||||||
|
43
src/potion.h
43
src/potion.h
@ -1,30 +1,41 @@
|
|||||||
#ifndef __POTIONS_H__
|
#ifndef __POTION_H__
|
||||||
#define __POTIONS_H__
|
#define __POTION_H__
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include "fraction.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
// IMPORTANT: pop all potions of duration == 0, and when entering a
|
// IMPORTANT: pop all potions of duration == 0, and when entering a
|
||||||
// new level, pop all potions of duration == -1
|
// new level, pop all potions of duration == -1
|
||||||
|
|
||||||
class potion {
|
class potion {
|
||||||
protected:
|
protected:
|
||||||
// Use -1 to denote that the effect will last until leaving the level
|
// Use -1 to denote that the effect will last until leaving the level
|
||||||
// Otherwise, use a positive number
|
// Otherwise, use a positive number
|
||||||
// Single use potions have a starting duration of 1
|
// Single use potions have a starting duration of 1
|
||||||
const potion_type type;
|
potion_type type;
|
||||||
int remaining_duration;
|
int remaining_duration;
|
||||||
|
position pos;
|
||||||
public:
|
public:
|
||||||
potion(const potion_type type, const int duration);
|
potion(const potion &p);
|
||||||
// apply decrements remaining_duration if it's positive, and
|
potion(potion &&p);
|
||||||
// won't do anything if it's non-negative
|
potion &operator=(const potion &p);
|
||||||
virtual void apply(enum race &race, int &HP, int &ATK, int &DEF,
|
potion &operator=(potion &&p);
|
||||||
float &base_hit_rate) = 0;
|
potion(const potion_type type, const int duration, const position &pos);
|
||||||
virtual int get_priority() const = 0;
|
// apply decrements remaining_duration if it's positive, and
|
||||||
potion_type get_type() const;
|
// won't do anything if it's non-negative
|
||||||
int get_duration() const;
|
virtual void apply(const enum race &race, int &HP, int &ATK, int &DEF,
|
||||||
|
fraction &base_hit_rate) = 0;
|
||||||
|
virtual int get_priority() const = 0;
|
||||||
|
potion_type get_type() const;
|
||||||
|
int get_duration() const;
|
||||||
|
position get_pos() const;
|
||||||
|
void set_pos(const position &npos);
|
||||||
|
|
||||||
|
virtual void print(display *out, bool color = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<potion> potion_list;
|
typedef std::vector<potion *> potion_list;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
/*
|
|
||||||
* CS 246 Final Project
|
|
||||||
* File: map.h
|
|
||||||
* Purpose: handles map functionality
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __RACES_H__
|
#ifndef __RACES_H__
|
||||||
#define __RACES_H__
|
#define __RACES_H__
|
||||||
|
|
||||||
#include "shade.h"
|
#include "shade.h"
|
||||||
#include "goblin.h"
|
#include "goblin.h"
|
||||||
#include "vampire.h"
|
#include "vampire.h"
|
||||||
|
#include "dragon.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
restore_health::restore_health():
|
restore_health::restore_health(const position &pos):
|
||||||
potion{potion_type::restore_health, 1} {}
|
potion{potion_type::restore_health, -1, pos} {}
|
||||||
|
|
||||||
void restore_health::apply(enum race &race, int &HP, int &ATK, int &DEF,
|
void restore_health::apply(const enum race &race, int &HP, int &ATK, int &DEF,
|
||||||
float &base_hit_rate) {
|
fraction &base_hit_rate) {
|
||||||
if (remaining_duration > 0) {
|
if (remaining_duration > 0) {
|
||||||
if (race == rdrow)
|
if (race == rdrow)
|
||||||
HP = std::min(HP + 7, MAX_HP[race]);
|
HP = std::min(HP + 7, MAX_HP[race]);
|
||||||
else
|
else
|
||||||
HP = std::min(HP + 5, MAX_HP[race]);
|
HP = std::min(HP + 5, MAX_HP[race]);
|
||||||
|
|
||||||
--remaining_duration;
|
--remaining_duration;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
class restore_health final: public potion {
|
class restore_health final: public potion {
|
||||||
public:
|
public:
|
||||||
restore_health();
|
restore_health(const position &pos);
|
||||||
void apply(enum race &race, int &HP, int &ATK, int &DEF,
|
void apply(const enum race &race, int &HP, int &ATK, int &DEF,
|
||||||
float &base_hit_rate) override;
|
fraction &base_hit_rate) override;
|
||||||
int get_priority() const override;
|
int get_priority() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
33
src/rng.cc
33
src/rng.cc
@ -32,12 +32,37 @@ int RNG::get_curr_rand_num() const {
|
|||||||
return curr_rand_num;
|
return curr_rand_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RNG::trial(fraction &psuccess) {
|
bool RNG::trial(const fraction &psuccess) {
|
||||||
return (rand() % psuccess.denominator) < psuccess.numerator;
|
return ((curr_rand_num = rand()) % psuccess.denominator) <
|
||||||
|
psuccess.numerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T> T &RNG::get_rand_in_vector(const std::vector<T> &vec) {
|
bool RNG::coin_flip() {
|
||||||
curr_rand_num = rand();
|
return (curr_rand_num = rand()) % 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RNG::exclude_middle(const int lower_bound, const int upper_bound,
|
||||||
|
const int excluded) {
|
||||||
|
curr_rand_num = rand();
|
||||||
|
int tmp = (curr_rand_num % (upper_bound - lower_bound - 1));
|
||||||
|
return tmp >= excluded ? tmp + 1 : tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> T RNG::rand_exclude(std::vector<T> &vec, T &target) {
|
||||||
|
std::size_t idx = 0;
|
||||||
|
|
||||||
|
for (; idx < vec.size(); ++idx)
|
||||||
|
if (vec[idx] == target)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return vec[exclude_middle(0, vec.size(), idx)];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> T RNG::get_rand_in_vector(std::vector<T> &vec) {
|
||||||
|
if (!vec.size())
|
||||||
|
return T{};
|
||||||
|
|
||||||
|
curr_rand_num = rand();
|
||||||
|
|
||||||
return vec[curr_rand_num % vec.size()];
|
return vec[curr_rand_num % vec.size()];
|
||||||
}
|
}
|
||||||
|
11
src/rng.h
11
src/rng.h
@ -26,9 +26,16 @@ public:
|
|||||||
unsigned int get_init_seed() const;
|
unsigned int get_init_seed() const;
|
||||||
int get_curr_rand_num() const;
|
int get_curr_rand_num() const;
|
||||||
|
|
||||||
bool trial(fraction &psuccess);
|
bool trial(const fraction &psuccess);
|
||||||
|
|
||||||
template<class T> T &get_rand_in_vector(const std::vector<T> &vec);
|
bool coin_flip();
|
||||||
|
|
||||||
|
int exclude_middle(const int lower_bound, const int upper_bound,
|
||||||
|
const int excluded);
|
||||||
|
|
||||||
|
template<class T> T rand_exclude(std::vector<T> &vec, T &target);
|
||||||
|
|
||||||
|
template<class T> T get_rand_in_vector(std::vector<T> &vec);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
23
src/shade.cc
23
src/shade.cc
@ -3,28 +3,27 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
shade::shade(RNG &rng, const position_list &available_positions):
|
shade::shade(RNG *rng, const position &pos):
|
||||||
character{rng, race::rshade} {
|
character{rng, race::rshade, pos} {
|
||||||
pos = available_positions[rng.rand_under(available_positions.size())];
|
gold = 0;
|
||||||
gold = 0;
|
hostile = true;
|
||||||
hostile = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result shade::attack(const direction dir, character_list &chlist) {
|
result shade::attack(const direction dir, character_list &chlist) {
|
||||||
position tmp{pos + MOVE[dir]};
|
position tmp{pos + MOVE[dir]};
|
||||||
|
|
||||||
for (auto &ch : chlist)
|
for (auto &ch : chlist)
|
||||||
if (tmp == ch.get_position()) {
|
if (tmp == ch->get_position()) {
|
||||||
return ch.get_hit(race, ATK, base_hit_rate);
|
return ch->get_hit(race, ATK, base_hit_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result::fine;
|
return result::fine;
|
||||||
}
|
}
|
||||||
|
|
||||||
result shade::get_hit(const enum race &race, const int atk,
|
result shade::get_hit(const enum race &race, const int atk,
|
||||||
const float hitrate) {
|
const fraction hitrate) {
|
||||||
if (rng.rand_num() <= hitrate * (float)RAND_MAX) // This is a hit!
|
if (rng->trial(hitrate)) // This is a hit!
|
||||||
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
||||||
|
|
||||||
if (HP == 0)
|
if (HP == 0)
|
||||||
return result::died;
|
return result::died;
|
||||||
|
11
src/shade.h
11
src/shade.h
@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
class shade final: public character {
|
class shade final: public character {
|
||||||
public:
|
public:
|
||||||
shade(RNG &rng, const position_list
|
shade(RNG *rng, const position &pos); // spawn at a random place
|
||||||
&available_positions); // spawn at a random place
|
virtual result attack(const direction dir,
|
||||||
virtual result attack(const direction dir,
|
character_list &chlist) override;
|
||||||
character_list &chlist) override;
|
virtual result get_hit(const enum race &race, const int atk,
|
||||||
virtual result get_hit(const enum race &race, const int atk,
|
const fraction hit_rate) override;
|
||||||
const float hit_rate) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,19 +2,18 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
vampire::vampire(RNG &rng, const position_list &available_positions):
|
vampire::vampire(RNG *rng, const position &pos):
|
||||||
character{rng, race::rvampire} {
|
character{rng, race::rshade, pos} {
|
||||||
pos = available_positions[rng.rand_under(available_positions.size())];
|
gold = 0;
|
||||||
gold = 0;
|
hostile = true;
|
||||||
hostile = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result vampire::attack(const direction dir, character_list &chlist) {
|
result vampire::attack(const direction dir, character_list &chlist) {
|
||||||
position tmp{pos + MOVE[dir]};
|
position tmp{pos + MOVE[dir]};
|
||||||
|
|
||||||
for (auto &ch : chlist)
|
for (auto &ch : chlist)
|
||||||
if (tmp == ch.get_position()) {
|
if (tmp == ch->get_position()) {
|
||||||
auto res = ch.get_hit(race, ATK, base_hit_rate);
|
auto res = ch->get_hit(race, ATK, base_hit_rate);
|
||||||
|
|
||||||
if (res != result::miss) {
|
if (res != result::miss) {
|
||||||
HP += GAIN_HP;
|
HP += GAIN_HP;
|
||||||
@ -27,9 +26,9 @@ result vampire::attack(const direction dir, character_list &chlist) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result vampire::get_hit(const enum race &race, const int atk,
|
result vampire::get_hit(const enum race &race, const int atk,
|
||||||
const float hitrate) {
|
const fraction hitrate) {
|
||||||
if (rng.rand_num() <= hitrate * (float)RAND_MAX)
|
if (rng->trial(hitrate))
|
||||||
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
HP = std::max(HP - calc_dmg(atk, DEF), 0);
|
||||||
|
|
||||||
if (HP == 0)
|
if (HP == 0)
|
||||||
return result::died;
|
return result::died;
|
||||||
|
@ -6,12 +6,11 @@ const int GAIN_HP = 5;
|
|||||||
|
|
||||||
class vampire final: public character {
|
class vampire final: public character {
|
||||||
public:
|
public:
|
||||||
vampire(RNG &rng,
|
vampire(RNG *rng, const position &pos);
|
||||||
const position_list &available_positions);
|
virtual result attack(const direction dir,
|
||||||
virtual result attack(const direction dir,
|
character_list &chlist) override;
|
||||||
character_list &chlist) override;
|
virtual result get_hit(const enum race &race, const int atk,
|
||||||
virtual result get_hit(const enum race &race, const int atk,
|
const fraction hit_rate) override;
|
||||||
const float hit_rate) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user