Compare commits
10 Commits
2686764cb3
...
c008ffc6a3
Author | SHA1 | Date | |
---|---|---|---|
c008ffc6a3 | |||
965a76000b | |||
15075e33af | |||
a8ab13f978 | |||
4d7183f380 | |||
ef522be8ef | |||
843606bcbf | |||
89b4bce734 | |||
25ba63cea9 | |||
35dcee85a0 |
7
ADG5.cpp
7
ADG5.cpp
@ -14,7 +14,8 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
auto console = tcod::Console(80, 50);
|
auto console = tcod::Console(80, 50);
|
||||||
auto params = TCOD_ContextParams{};
|
auto params = TCOD_ContextParams{};
|
||||||
|
auto tileset = tcod::load_tilesheet("imgs/terminal10x16_gs_ro.png", { 16, 16 }, tcod::CHARMAP_CP437);
|
||||||
|
params.tileset = tileset.get();
|
||||||
params.console = console.get();
|
params.console = console.get();
|
||||||
params.window_title = "Andrew's Dungeon Game 5";
|
params.window_title = "Andrew's Dungeon Game 5";
|
||||||
params.sdl_window_flags = SDL_WINDOW_RESIZABLE;
|
params.sdl_window_flags = SDL_WINDOW_RESIZABLE;
|
||||||
@ -24,12 +25,14 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
auto context = tcod::Context(params);
|
auto context = tcod::Context(params);
|
||||||
engine = new Engine(80, 50, &context, &console);
|
engine = new Engine(80, 50, &context, &console);
|
||||||
engine->init();
|
engine->load();
|
||||||
|
|
||||||
while (engine->update()) {
|
while (engine->update()) {
|
||||||
engine->render();
|
engine->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
engine->save();
|
||||||
|
|
||||||
delete engine;
|
delete engine;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
63
Actor.cpp
63
Actor.cpp
@ -11,12 +11,17 @@
|
|||||||
|
|
||||||
Actor::Actor(int x, int y, std::string_view ch, std::string name, const TCOD_ColorRGB& col) :
|
Actor::Actor(int x, int y, std::string_view ch, std::string name, const TCOD_ColorRGB& col) :
|
||||||
x(x), y(y), ch(ch), col(col), name(name),
|
x(x), y(y), ch(ch), col(col), name(name),
|
||||||
blocks(true), attacker(nullptr), destructible(nullptr), ai(nullptr), pickable(nullptr), container(nullptr) {
|
blocks(true), attacker(nullptr), destructible(nullptr), ai(nullptr), pickable(nullptr), container(nullptr), fovOnly(true) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::render(TCOD_Console& cons) const {
|
void Actor::render(TCOD_Console& cons) const {
|
||||||
tcod::print(cons, { x, y }, ch, col, std::nullopt);
|
int offset_x = engine->player->x - Engine::VIEW_WIDTH / 2;
|
||||||
|
int offset_y = engine->player->y - Engine::VIEW_HEIGHT / 2;
|
||||||
|
if (offset_x > x || Engine::VIEW_WIDTH + offset_x < x || offset_y > y || Engine::VIEW_HEIGHT + offset_y < y) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tcod::print(cons, { x - offset_x, y - offset_y}, ch, col, std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::update() {
|
void Actor::update() {
|
||||||
@ -36,5 +41,57 @@ Actor::~Actor() {
|
|||||||
float Actor::getDistance(int cx, int cy) const {
|
float Actor::getDistance(int cx, int cy) const {
|
||||||
int dx = x - cx;
|
int dx = x - cx;
|
||||||
int dy = y - cy;
|
int dy = y - cy;
|
||||||
return sqrtf(dx * dx + dy * dy);
|
return sqrtf((float)(dx * dx + dy * dy));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::save(TCODZip& zip) {
|
||||||
|
zip.putInt(x);
|
||||||
|
zip.putInt(y);
|
||||||
|
zip.putString(std::string(ch).c_str());
|
||||||
|
zip.putInt(col.r);
|
||||||
|
zip.putInt(col.g);
|
||||||
|
zip.putInt(col.b);
|
||||||
|
zip.putString(name.c_str());
|
||||||
|
zip.putInt(blocks);
|
||||||
|
zip.putInt(attacker != NULL);
|
||||||
|
zip.putInt(destructible != NULL);
|
||||||
|
zip.putInt(ai != NULL);
|
||||||
|
zip.putInt(pickable != NULL);
|
||||||
|
zip.putInt(container != NULL);
|
||||||
|
if (attacker) attacker->save(zip);
|
||||||
|
if (destructible) destructible->save(zip);
|
||||||
|
if (ai) ai->save(zip);
|
||||||
|
if (pickable) pickable->save(zip);
|
||||||
|
if (container) container->save(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::load(TCODZip& zip) {
|
||||||
|
x = zip.getInt();
|
||||||
|
y = zip.getInt();
|
||||||
|
ch = std::string(zip.getString());
|
||||||
|
col = TCOD_ColorRGB(zip.getInt(), zip.getInt(), zip.getInt());
|
||||||
|
name = zip.getString();
|
||||||
|
blocks = zip.getInt();
|
||||||
|
bool hasAttacker = zip.getInt();
|
||||||
|
bool hasDestructible = zip.getInt();
|
||||||
|
bool hasAi = zip.getInt();
|
||||||
|
bool hasPickable = zip.getInt();
|
||||||
|
bool hasContainer = zip.getInt();
|
||||||
|
if (hasAttacker) {
|
||||||
|
attacker = new Attacker(0.0f);
|
||||||
|
attacker->load(zip);
|
||||||
|
}
|
||||||
|
if (hasDestructible) {
|
||||||
|
destructible = Destructible::create(zip);
|
||||||
|
}
|
||||||
|
if (hasAi) {
|
||||||
|
ai = Ai::create(zip);
|
||||||
|
}
|
||||||
|
if (hasPickable) {
|
||||||
|
pickable = Pickable::create(zip);
|
||||||
|
}
|
||||||
|
if (hasContainer) {
|
||||||
|
container = new Container(0);
|
||||||
|
container->load(zip);
|
||||||
|
}
|
||||||
}
|
}
|
9
Actor.h
9
Actor.h
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "libtcod.hpp"
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Attacker;
|
class Attacker;
|
||||||
class Destructible;
|
class Destructible;
|
||||||
@ -8,14 +9,15 @@ class Ai;
|
|||||||
class Pickable;
|
class Pickable;
|
||||||
class Container;
|
class Container;
|
||||||
|
|
||||||
class Actor {
|
class Actor : public Persistent {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
int x, y; // position on map
|
int x, y; // position on map
|
||||||
std::string_view ch; // ascii code
|
std::string ch; // ascii code
|
||||||
TCOD_ColorRGB col; // color
|
TCOD_ColorRGB col; // color
|
||||||
std::string name;
|
std::string name;
|
||||||
bool blocks;
|
bool blocks;
|
||||||
|
bool fovOnly; // only display when in fov
|
||||||
Attacker* attacker;
|
Attacker* attacker;
|
||||||
Destructible* destructible;
|
Destructible* destructible;
|
||||||
Ai* ai;
|
Ai* ai;
|
||||||
@ -26,4 +28,7 @@ public:
|
|||||||
~Actor();
|
~Actor();
|
||||||
void render(TCOD_Console& cons) const;
|
void render(TCOD_Console& cons) const;
|
||||||
void update();
|
void update();
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
|
||||||
};
|
};
|
155
Ai.cpp
155
Ai.cpp
@ -1,4 +1,5 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include "libtcod.hpp"
|
||||||
#include "Ai.h"
|
#include "Ai.h"
|
||||||
#include "Actor.h"
|
#include "Actor.h"
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
@ -9,16 +10,54 @@
|
|||||||
#include "Pickable.h"
|
#include "Pickable.h"
|
||||||
#include "Container.h"
|
#include "Container.h"
|
||||||
|
|
||||||
|
PlayerAi::PlayerAi() : xpLevel(1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const int LEVEL_UP_BASE = 200;
|
||||||
|
const int LEVEL_UP_FACTOR = 150;
|
||||||
|
|
||||||
|
int PlayerAi::getNextLevelXp() {
|
||||||
|
return LEVEL_UP_BASE + xpLevel * LEVEL_UP_FACTOR;
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerAi::update(Actor* owner) {
|
void PlayerAi::update(Actor* owner) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
int dx = 0, dy = 0;
|
int dx = 0, dy = 0;
|
||||||
|
int levelUpXp = getNextLevelXp();
|
||||||
|
if (owner->destructible->xp >= levelUpXp) {
|
||||||
|
xpLevel++;
|
||||||
|
owner->destructible->xp -= levelUpXp;
|
||||||
|
engine->gui->message(engine->gui->lightYellow, "Your battle skills grow stronger! You reached level %d", xpLevel);
|
||||||
|
engine->gui->menu.clear();
|
||||||
|
engine->gui->menu.addItem(Menu::CONSTITUTION, "Constitution (+20HP)");
|
||||||
|
engine->gui->menu.addItem(Menu::STRENGTH, "Strength (+1 attack)");
|
||||||
|
engine->gui->menu.addItem(Menu::AGILITY, "Agility (+1 defense)");
|
||||||
|
Menu::MenuItemCode menuItem = engine->gui->menu.pick(engine->context, engine->console, Menu::PAUSE);
|
||||||
|
switch (menuItem) {
|
||||||
|
case Menu::CONSTITUTION:
|
||||||
|
owner->destructible->maxHp += 20;
|
||||||
|
owner->destructible->hp += 20;
|
||||||
|
break;
|
||||||
|
case Menu::STRENGTH:
|
||||||
|
owner->attacker->power += 1;
|
||||||
|
break;
|
||||||
|
case Menu::AGILITY:
|
||||||
|
owner->destructible->defense += 1;
|
||||||
|
break;
|
||||||
|
default:break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (owner->destructible && owner->destructible->isDead()) {
|
if (owner->destructible && owner->destructible->isDead()) {
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
engine->gameStatus = Engine::QUIT;
|
engine->gameStatus = Engine::QUIT;
|
||||||
break;
|
break;
|
||||||
|
case SDL_EVENT_KEY_DOWN:
|
||||||
|
if (event.key.key == SDLK_ESCAPE) {
|
||||||
|
engine->gameStatus = Engine::PAUSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -29,8 +68,8 @@ void PlayerAi::update(Actor* owner) {
|
|||||||
engine->context->convert_event_coordinates(event);
|
engine->context->convert_event_coordinates(event);
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_MOUSE_MOTION:
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
engine->mouse.cx = event.motion.x;
|
engine->mouse.cx = (int)event.motion.x;
|
||||||
engine->mouse.cy = event.motion.y;
|
engine->mouse.cy = (int)event.motion.y;
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
switch (event.key.key) {
|
switch (event.key.key) {
|
||||||
@ -46,10 +85,11 @@ void PlayerAi::update(Actor* owner) {
|
|||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
dx = 1;
|
dx = 1;
|
||||||
break;
|
break;
|
||||||
|
case SDLK_ESCAPE:
|
||||||
|
engine->gameStatus = Engine::PAUSE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (event.key.key >= SDLK_A && event.key.key <= SDLK_Z) {
|
handleActionKey(owner, event.key.key, event.key.mod);
|
||||||
handleActionKey(owner, event.key.key);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -68,7 +108,8 @@ void PlayerAi::update(Actor* owner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void PlayerAi::handleActionKey(Actor* owner, int sdlkey) {
|
|
||||||
|
void PlayerAi::handleActionKey(Actor* owner, int sdlkey, int mod) {
|
||||||
switch (sdlkey) {
|
switch (sdlkey) {
|
||||||
case SDLK_G: // pickup item
|
case SDLK_G: // pickup item
|
||||||
{
|
{
|
||||||
@ -79,28 +120,52 @@ void PlayerAi::handleActionKey(Actor* owner, int sdlkey) {
|
|||||||
if (actor->pickable && actor->x == owner->x && actor->y == owner->y) {
|
if (actor->pickable && actor->x == owner->x && actor->y == owner->y) {
|
||||||
if (actor->pickable->pick(actor, owner)) {
|
if (actor->pickable->pick(actor, owner)) {
|
||||||
found = true;
|
found = true;
|
||||||
engine->gui->message(TCOD_ColorRGB(200,200,200), "You pick up the %s.",
|
engine->gui->message(engine->gui->lightGrey, "You pick up the %s.",
|
||||||
actor->name.c_str());
|
actor->name.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (!found) {
|
else if (!found) {
|
||||||
found = true;
|
found = true;
|
||||||
engine->gui->message(TCOD_ColorRGB(255,0,0), "Your inventory is full.");
|
engine->gui->message(engine->gui->red, "Your inventory is full.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
engine->gui->message(TCOD_ColorRGB(200,200,200), "There's nothing here that you can pick up.");
|
engine->gui->message(engine->gui->lightGrey, "There's nothing here that you can pick up.");
|
||||||
}
|
}
|
||||||
|
if (engine->gameStatus != Engine::QUIT)
|
||||||
engine->gameStatus = Engine::NEW_TURN;
|
engine->gameStatus = Engine::NEW_TURN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_I:
|
case SDLK_I:
|
||||||
|
{
|
||||||
Actor* actor = choseFromInventory(owner);
|
Actor* actor = choseFromInventory(owner);
|
||||||
if (actor) {
|
if (actor) {
|
||||||
actor->pickable->use(actor, owner);
|
actor->pickable->use(actor, owner);
|
||||||
|
if (engine->gameStatus != Engine::QUIT)
|
||||||
engine->gameStatus = Engine::NEW_TURN;
|
engine->gameStatus = Engine::NEW_TURN;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDLK_D: // drop item
|
||||||
|
{
|
||||||
|
Actor* actor = choseFromInventory(owner);
|
||||||
|
if (actor) {
|
||||||
|
actor->pickable->drop(actor, owner);
|
||||||
|
if (engine->gameStatus != Engine::QUIT)
|
||||||
|
engine->gameStatus = Engine::NEW_TURN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDLK_PERIOD:
|
||||||
|
if (mod & SDL_KMOD_SHIFT) {
|
||||||
|
if (engine->stairs->x == owner->x && engine->stairs->y == owner->y) {
|
||||||
|
engine->nextLevel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
engine->gui->message(engine->gui->lightGrey, "There are no stairs here.");
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +189,7 @@ bool PlayerAi::moveOrAttack(Actor* owner, int targetx, int targety) {
|
|||||||
|| actor->pickable;
|
|| actor->pickable;
|
||||||
if (corpseOrItem
|
if (corpseOrItem
|
||||||
&& actor->x == targetx && actor->y == targety) {
|
&& actor->x == targetx && actor->y == targety) {
|
||||||
engine->gui->message(TCOD_ColorRGB(200,200,200), "There's a %s here.", actor->name.c_str());
|
engine->gui->message(engine->gui->lightGrey, "There's a %s here.", actor->name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
owner->x = targetx;
|
owner->x = targetx;
|
||||||
@ -156,7 +221,6 @@ Actor* PlayerAi::choseFromInventory(Actor* owner) {
|
|||||||
engine->context->present(*engine->console);
|
engine->context->present(*engine->console);
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (1) {
|
while (1) {
|
||||||
printf("HERE\n");
|
|
||||||
SDL_WaitEvent(&event);
|
SDL_WaitEvent(&event);
|
||||||
if (event.type == SDL_EVENT_KEY_UP) {
|
if (event.type == SDL_EVENT_KEY_UP) {
|
||||||
if (event.key.key >= SDLK_A && event.key.key < SDLK_A + owner->container->inventory.size()) {
|
if (event.key.key >= SDLK_A && event.key.key < SDLK_A + owner->container->inventory.size()) {
|
||||||
@ -214,3 +278,70 @@ void MonsterAi::moveOrAttack(Actor* owner, int targetx, int targety) {
|
|||||||
owner->attacker->attack(owner, engine->player);
|
owner->attacker->attack(owner, engine->player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfusedMonsterAi::ConfusedMonsterAi(int nbTurns, Ai* oldAi)
|
||||||
|
: nbTurns(nbTurns), oldAi(oldAi) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfusedMonsterAi::update(Actor* owner) {
|
||||||
|
TCODRandom* rng = TCODRandom::getInstance();
|
||||||
|
int dx = rng->getInt(-1, 1);
|
||||||
|
int dy = rng->getInt(-1, 1);
|
||||||
|
if (dx != 0 || dy != 0) {
|
||||||
|
int destx = owner->x + dx;
|
||||||
|
int desty = owner->y + dy;
|
||||||
|
if (engine->map->canWalk(destx, desty)) {
|
||||||
|
owner->x = destx;
|
||||||
|
owner->y = desty;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Actor* actor = engine->getActor(destx, desty);
|
||||||
|
if (actor) {
|
||||||
|
owner->attacker->attack(owner, actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nbTurns--;
|
||||||
|
if (nbTurns == 0) {
|
||||||
|
owner->ai = oldAi;
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ai* Ai::create(TCODZip& zip) {
|
||||||
|
AiType type = (AiType)zip.getInt();
|
||||||
|
Ai* ai = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case PLAYER: ai = new PlayerAi(); break;
|
||||||
|
case MONSTER: ai = new MonsterAi(); break;
|
||||||
|
case CONFUSED_MONSTER: ai = new ConfusedMonsterAi(0, NULL); break;
|
||||||
|
}
|
||||||
|
ai->load(zip);
|
||||||
|
return ai;
|
||||||
|
}
|
||||||
|
void MonsterAi::load(TCODZip& zip) {
|
||||||
|
moveCount = zip.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonsterAi::save(TCODZip& zip) {
|
||||||
|
zip.putInt(MONSTER);
|
||||||
|
zip.putInt(moveCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfusedMonsterAi::load(TCODZip& zip) {
|
||||||
|
nbTurns = zip.getInt();
|
||||||
|
oldAi = Ai::create(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfusedMonsterAi::save(TCODZip& zip) {
|
||||||
|
zip.putInt(CONFUSED_MONSTER);
|
||||||
|
zip.putInt(nbTurns);
|
||||||
|
oldAi->save(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerAi::load(TCODZip& zip) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerAi::save(TCODZip& zip) {
|
||||||
|
zip.putInt(PLAYER);
|
||||||
|
}
|
30
Ai.h
30
Ai.h
@ -1,28 +1,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Actor;
|
class Actor;
|
||||||
|
|
||||||
class Ai {
|
class Ai : public Persistent {
|
||||||
public:
|
public:
|
||||||
virtual void update(Actor* owner) = 0;
|
virtual void update(Actor* owner) = 0;
|
||||||
virtual ~Ai() {};
|
virtual ~Ai() {};
|
||||||
|
static Ai* create(TCODZip& zip);
|
||||||
|
protected:
|
||||||
|
enum AiType {
|
||||||
|
MONSTER, CONFUSED_MONSTER, PLAYER
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerAi : public Ai {
|
class PlayerAi : public Ai {
|
||||||
public:
|
public:
|
||||||
void update(Actor* owner);
|
void update(Actor* owner);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
int xpLevel;
|
||||||
|
PlayerAi();
|
||||||
|
int getNextLevelXp();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool moveOrAttack(Actor* owner, int targetx, int targety);
|
bool moveOrAttack(Actor* owner, int targetx, int targety);
|
||||||
void handleActionKey(Actor* owner, int sdlkey);
|
void handleActionKey(Actor* owner, int sdlkey, int mod);
|
||||||
Actor* choseFromInventory(Actor* owner);
|
Actor* choseFromInventory(Actor* owner);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MonsterAi : public Ai {
|
class MonsterAi : public Ai {
|
||||||
public:
|
public:
|
||||||
void update(Actor* owner);
|
void update(Actor* owner);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
protected:
|
protected:
|
||||||
int moveCount;
|
int moveCount;
|
||||||
void moveOrAttack(Actor* owner, int targetx, int targety);
|
void moveOrAttack(Actor* owner, int targetx, int targety);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ConfusedMonsterAi : public Ai {
|
||||||
|
public:
|
||||||
|
ConfusedMonsterAi(int nbTurns, Ai* oldAi);
|
||||||
|
void update(Actor* owner);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
protected:
|
||||||
|
int nbTurns;
|
||||||
|
Ai* oldAi;
|
||||||
|
};
|
14
Attacker.cpp
14
Attacker.cpp
@ -10,15 +10,23 @@ Attacker::Attacker(float power) : power(power) {
|
|||||||
void Attacker::attack(Actor* owner, Actor* target) {
|
void Attacker::attack(Actor* owner, Actor* target) {
|
||||||
if (target->destructible && !target->destructible->isDead()) {
|
if (target->destructible && !target->destructible->isDead()) {
|
||||||
if (power - target->destructible->defense > 0) {
|
if (power - target->destructible->defense > 0) {
|
||||||
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s attacks %s for %g hit points.\n", owner->name.c_str(), target->name.c_str(),
|
engine->gui->message(engine->gui->lightGrey, "%s attacks %s for %g hit points.\n", owner->name.c_str(), target->name.c_str(),
|
||||||
power - target->destructible->defense);
|
power - target->destructible->defense);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s attacks %s but it has no effect!\n", owner->name.c_str(), target->name.c_str());
|
engine->gui->message(engine->gui->lightGrey, "%s attacks %s but it has no effect!\n", owner->name.c_str(), target->name.c_str());
|
||||||
}
|
}
|
||||||
target->destructible->takeDamage(target, power);
|
target->destructible->takeDamage(target, power);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s attacks %s in vain.\n", owner->name.c_str(), target->name.c_str());
|
engine->gui->message(engine->gui->lightGrey, "%s attacks %s in vain.\n", owner->name.c_str(), target->name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Attacker::load(TCODZip& zip) {
|
||||||
|
power = zip.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Attacker::save(TCODZip& zip) {
|
||||||
|
zip.putFloat(power);
|
||||||
|
}
|
@ -1,11 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Actor;
|
class Actor;
|
||||||
|
|
||||||
class Attacker {
|
class Attacker : public Persistent {
|
||||||
public:
|
public:
|
||||||
float power; // hit points given
|
float power; // hit points given
|
||||||
|
|
||||||
Attacker(float power);
|
Attacker(float power);
|
||||||
void attack(Actor* owner, Actor* target);
|
void attack(Actor* owner, Actor* target);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
};
|
};
|
@ -14,7 +14,7 @@ project ("ADG5")
|
|||||||
find_package(libtcod CONFIG REQUIRED)
|
find_package(libtcod CONFIG REQUIRED)
|
||||||
|
|
||||||
# Add source to this project's executable.
|
# Add source to this project's executable.
|
||||||
add_executable (ADG5 "ADG5.cpp" "ADG5.h" "Actor.cpp" "Actor.h" "Map.h" "Map.cpp" "Engine.h" "Engine.cpp" "Destructible.h" "Destructible.cpp" "Attacker.h" "Attacker.cpp" "Ai.h" "Ai.cpp" "Gui.h" "Gui.cpp" "Container.h" "Container.cpp" "Pickable.h" "Pickable.cpp" "Healer.h" "Healer.cpp" "Lightningbolt.h" "Lightningbolt.cpp" "Fireball.h" "Fireball.cpp")
|
add_executable (ADG5 "ADG5.cpp" "ADG5.h" "Actor.cpp" "Actor.h" "Map.h" "Map.cpp" "Engine.h" "Engine.cpp" "Destructible.h" "Destructible.cpp" "Attacker.h" "Attacker.cpp" "Ai.h" "Ai.cpp" "Gui.h" "Gui.cpp" "Container.h" "Container.cpp" "Pickable.h" "Pickable.cpp" "Healer.h" "Healer.cpp" "Lightningbolt.h" "Lightningbolt.cpp" "Fireball.h" "Fireball.cpp" "Confuser.h" "Confuser.cpp" "Persistance.h")
|
||||||
|
|
||||||
target_link_libraries(ADG5
|
target_link_libraries(ADG5
|
||||||
PRIVATE
|
PRIVATE
|
||||||
@ -25,4 +25,9 @@ if (CMAKE_VERSION VERSION_GREATER 3.12)
|
|||||||
set_property(TARGET ADG5 PROPERTY CXX_STANDARD 20)
|
set_property(TARGET ADG5 PROPERTY CXX_STANDARD 20)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imgs
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/imgs)
|
||||||
# TODO: Add tests and install targets if needed.
|
# TODO: Add tests and install targets if needed.
|
||||||
|
39
Confuser.cpp
Normal file
39
Confuser.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Confuser.h"
|
||||||
|
#include "Engine.h"
|
||||||
|
#include "Gui.h"
|
||||||
|
#include "Ai.h"
|
||||||
|
#include "Actor.h"
|
||||||
|
|
||||||
|
|
||||||
|
Confuser::Confuser(int nbTurns, float range)
|
||||||
|
: nbTurns(nbTurns), range(range) {
|
||||||
|
}
|
||||||
|
bool Confuser::use(Actor* owner, Actor* wearer) {
|
||||||
|
engine->gui->message(engine->gui->lightBlue, "Left-click an enemy to confuse it,\nor right-click to cancel.");
|
||||||
|
int x, y;
|
||||||
|
if (!engine->pickATile(&x, &y, range)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Actor* actor = engine->getActor(x, y);
|
||||||
|
if (!actor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// confuse the monster for <nbTurns> turns
|
||||||
|
Ai* confusedAi = new ConfusedMonsterAi(nbTurns, actor->ai);
|
||||||
|
actor->ai = confusedAi;
|
||||||
|
engine->gui->message(engine->gui->lightGreen, "The eyes of the %s look vacant,\nas he starts to stumble around!",
|
||||||
|
actor->name.c_str());
|
||||||
|
return Pickable::use(owner, wearer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Confuser::load(TCODZip& zip) {
|
||||||
|
nbTurns = zip.getInt();
|
||||||
|
range = zip.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Confuser::save(TCODZip& zip) {
|
||||||
|
zip.putInt(CONFUSER);
|
||||||
|
zip.putInt(nbTurns);
|
||||||
|
zip.putFloat(range);
|
||||||
|
}
|
15
Confuser.h
Normal file
15
Confuser.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Pickable.h"
|
||||||
|
|
||||||
|
class Actor;
|
||||||
|
|
||||||
|
class Confuser : public Pickable {
|
||||||
|
public:
|
||||||
|
int nbTurns;
|
||||||
|
float range;
|
||||||
|
Confuser(int nbTurns, float range);
|
||||||
|
bool use(Actor* owner, Actor* wearer);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
};
|
@ -1,4 +1,5 @@
|
|||||||
#include "Container.h"
|
#include "Container.h"
|
||||||
|
#include "Actor.h"
|
||||||
|
|
||||||
Container::Container(int size) : size(size) {
|
Container::Container(int size) : size(size) {
|
||||||
}
|
}
|
||||||
@ -19,3 +20,22 @@ bool Container::add(Actor* actor) {
|
|||||||
void Container::remove(Actor* actor) {
|
void Container::remove(Actor* actor) {
|
||||||
inventory.remove(actor);
|
inventory.remove(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Container::load(TCODZip& zip) {
|
||||||
|
size = zip.getInt();
|
||||||
|
int nbActors = zip.getInt();
|
||||||
|
while (nbActors > 0) {
|
||||||
|
Actor* actor = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
|
||||||
|
actor->load(zip);
|
||||||
|
inventory.push(actor);
|
||||||
|
nbActors--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::save(TCODZip& zip) {
|
||||||
|
zip.putInt(size);
|
||||||
|
zip.putInt(inventory.size());
|
||||||
|
for (Actor** it = inventory.begin(); it != inventory.end(); it++) {
|
||||||
|
(*it)->save(zip);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "libtcod.hpp"
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
class Actor;
|
class Actor;
|
||||||
|
|
||||||
class Container {
|
class Container : public Persistent {
|
||||||
public:
|
public:
|
||||||
int size; // maximum number of actors. 0=unlimited
|
int size; // maximum number of actors. 0=unlimited
|
||||||
TCODList<Actor*> inventory;
|
TCODList<Actor*> inventory;
|
||||||
@ -12,4 +12,6 @@ public:
|
|||||||
~Container();
|
~Container();
|
||||||
bool add(Actor* actor);
|
bool add(Actor* actor);
|
||||||
void remove(Actor* actor);
|
void remove(Actor* actor);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
};
|
};
|
@ -3,8 +3,8 @@
|
|||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "Gui.h"
|
#include "Gui.h"
|
||||||
|
|
||||||
Destructible::Destructible(float maxHp, float defense, std::string corpseName) :
|
Destructible::Destructible(float maxHp, float defense, std::string corpseName, int xp) :
|
||||||
maxHp(maxHp), hp(maxHp), defense(defense), corpseName(corpseName) {
|
maxHp(maxHp), hp(maxHp), defense(defense), corpseName(corpseName), xp(xp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float Destructible::takeDamage(Actor* owner, float damage) {
|
float Destructible::takeDamage(Actor* owner, float damage) {
|
||||||
@ -39,23 +39,63 @@ void Destructible::die(Actor* owner) {
|
|||||||
engine->sendToBack(owner);
|
engine->sendToBack(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonsterDestructible::MonsterDestructible(float maxHp, float defense, std::string corpseName) :
|
MonsterDestructible::MonsterDestructible(float maxHp, float defense, std::string corpseName, int xp) :
|
||||||
Destructible(maxHp, defense, corpseName) {
|
Destructible(maxHp, defense, corpseName, xp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerDestructible::PlayerDestructible(float maxHp, float defense, std::string corpseName) :
|
PlayerDestructible::PlayerDestructible(float maxHp, float defense, std::string corpseName) :
|
||||||
Destructible(maxHp, defense, corpseName) {
|
Destructible(maxHp, defense, corpseName, 1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerDestructible::save(TCODZip& zip) {
|
||||||
|
zip.putInt(PLAYER);
|
||||||
|
Destructible::save(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonsterDestructible::save(TCODZip& zip) {
|
||||||
|
zip.putInt(MONSTER);
|
||||||
|
Destructible::save(zip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MonsterDestructible::die(Actor* owner) {
|
void MonsterDestructible::die(Actor* owner) {
|
||||||
// transform it into a nasty corpse! it doesn't block, can't be
|
// transform it into a nasty corpse! it doesn't block, can't be
|
||||||
// attacked and doesn't move
|
// attacked and doesn't move
|
||||||
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s is dead\n", owner->name.c_str());
|
engine->gui->message(engine->gui->lightGrey, "%s is dead. You gain %d xp",
|
||||||
|
owner->name.c_str(), xp);
|
||||||
|
engine->player->destructible->xp += xp;
|
||||||
|
|
||||||
Destructible::die(owner);
|
Destructible::die(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerDestructible::die(Actor* owner) {
|
void PlayerDestructible::die(Actor* owner) {
|
||||||
engine->gui->message(TCOD_ColorRGB(150, 0, 0), "You died!\n");
|
engine->gui->message(engine->gui->red, "You died!\n");
|
||||||
Destructible::die(owner);
|
Destructible::die(owner);
|
||||||
engine->gameStatus = Engine::DEFEAT;
|
engine->gameStatus = Engine::DEFEAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Destructible::load(TCODZip& zip) {
|
||||||
|
maxHp = zip.getFloat();
|
||||||
|
hp = zip.getFloat();
|
||||||
|
defense = zip.getFloat();
|
||||||
|
corpseName = zip.getString();
|
||||||
|
xp = zip.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destructible::save(TCODZip& zip) {
|
||||||
|
zip.putFloat(maxHp);
|
||||||
|
zip.putFloat(hp);
|
||||||
|
zip.putFloat(defense);
|
||||||
|
zip.putString(corpseName.c_str());
|
||||||
|
zip.putInt(xp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Destructible* Destructible::create(TCODZip& zip) {
|
||||||
|
DestructibleType type = (DestructibleType)zip.getInt();
|
||||||
|
Destructible* destructible = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case MONSTER: destructible = new MonsterDestructible(0, 0, "", 0); break;
|
||||||
|
case PLAYER: destructible = new PlayerDestructible(0, 0, ""); break;
|
||||||
|
}
|
||||||
|
destructible->load(zip);
|
||||||
|
return destructible;
|
||||||
|
}
|
@ -1,32 +1,45 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Actor;
|
class Actor;
|
||||||
|
|
||||||
class Destructible {
|
class Destructible : public Persistent {
|
||||||
public:
|
public:
|
||||||
float maxHp; // maximum health points
|
float maxHp; // maximum health points
|
||||||
float hp; // current health points
|
float hp; // current health points
|
||||||
float defense; // hit points deflected
|
float defense; // hit points deflected
|
||||||
std::string corpseName; // the actor's name once dead/destroyed
|
std::string corpseName; // the actor's name once dead/destroyed
|
||||||
|
|
||||||
Destructible(float maxHp, float defense, std::string corpseName);
|
Destructible(float maxHp, float defense, std::string corpseName, int xp);
|
||||||
virtual ~Destructible() {};
|
virtual ~Destructible() {};
|
||||||
inline bool isDead() { return hp <= 0; }
|
inline bool isDead() { return hp <= 0; }
|
||||||
float takeDamage(Actor* owner, float damage);
|
float takeDamage(Actor* owner, float damage);
|
||||||
virtual void die(Actor* owner);
|
virtual void die(Actor* owner);
|
||||||
|
|
||||||
float heal(float amount);
|
float heal(float amount);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
static Destructible* create(TCODZip& zip);
|
||||||
|
int xp; // XP gained when killing this monster (or player xp)
|
||||||
|
protected:
|
||||||
|
enum DestructibleType {
|
||||||
|
MONSTER, PLAYER
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class MonsterDestructible : public Destructible {
|
class MonsterDestructible : public Destructible {
|
||||||
public:
|
public:
|
||||||
MonsterDestructible(float maxHp, float defense, std::string corpseName);
|
MonsterDestructible(float maxHp, float defense, std::string corpseName, int xp);
|
||||||
|
void save(TCODZip& zip);
|
||||||
void die(Actor* owner);
|
void die(Actor* owner);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerDestructible : public Destructible {
|
class PlayerDestructible : public Destructible {
|
||||||
public:
|
public:
|
||||||
PlayerDestructible(float maxHp, float defense, std::string corpseName);
|
PlayerDestructible(float maxHp, float defense, std::string corpseName);
|
||||||
|
void save(TCODZip& zip);
|
||||||
void die(Actor* owner);
|
void die(Actor* owner);
|
||||||
};
|
};
|
190
Engine.cpp
190
Engine.cpp
@ -14,34 +14,65 @@ Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::
|
|||||||
this->console = console;
|
this->console = console;
|
||||||
this->screenWidth = screenWidth;
|
this->screenWidth = screenWidth;
|
||||||
this->screenHeight = screenHeight;
|
this->screenHeight = screenHeight;
|
||||||
|
this->level = 1;
|
||||||
gui = nullptr;
|
gui = nullptr;
|
||||||
map = nullptr;
|
map = nullptr;
|
||||||
player = nullptr;
|
player = nullptr;
|
||||||
fovRadius = 10;
|
fovRadius = 10;
|
||||||
computeFov = true;
|
computeFov = true;
|
||||||
gameStatus = STARTUP;
|
|
||||||
mouse.cx = 0;
|
mouse.cx = 0;
|
||||||
mouse.cy = 0;
|
mouse.cy = 0;
|
||||||
mouse.rbutton_pressed = false;
|
mouse.rbutton_pressed = false;
|
||||||
mouse.lbutton_pressed = false;
|
mouse.lbutton_pressed = false;
|
||||||
|
gameStatus = STARTUP;
|
||||||
|
stairs = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::init() {
|
void Engine::init() {
|
||||||
gui = new Gui(context, console);
|
player = new Actor(40, 25, "@", "player", engine->gui->white);
|
||||||
player = new Actor(40, 25, "@", "player", TCOD_ColorRGB(255, 255, 255));
|
|
||||||
player->destructible = new PlayerDestructible(30, 2, "player corpse");
|
player->destructible = new PlayerDestructible(30, 2, "player corpse");
|
||||||
player->attacker = new Attacker(5);
|
player->attacker = new Attacker(5);
|
||||||
player->ai = new PlayerAi();
|
player->ai = new PlayerAi();
|
||||||
player->container = new Container(26);
|
player->container = new Container(26);
|
||||||
actors.push(player);
|
actors.push(player);
|
||||||
map = new Map(80, 45);
|
stairs = new Actor(0, 0, ">", "stairs", TCOD_ColorRGB(255,255,255));
|
||||||
|
stairs->blocks = false;
|
||||||
|
stairs->fovOnly = false;
|
||||||
|
actors.push(stairs);
|
||||||
|
map = new Map(200, 100);
|
||||||
|
map->init(true);
|
||||||
gui->message(TCOD_ColorRGB(150,0,0),
|
gui->message(TCOD_ColorRGB(150,0,0),
|
||||||
"Welcome stranger!\nPrepare to perish in the Tombs of Andrew's Dunegon.");
|
"Welcome stranger!\nPrepare to perish in the Tombs of Andrew's Dunegon.");
|
||||||
|
gameStatus = STARTUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::nextLevel() {
|
||||||
|
level++;
|
||||||
|
gui->message(engine->gui->lightPurple, "You take a moment to rest, and recover your strength.");
|
||||||
|
player->destructible->heal(player->destructible->maxHp / 2);
|
||||||
|
gui->message(engine->gui->lightRed, "After a rare moment of peace, you descend\ndeeper into the heart of the dungeon...");
|
||||||
|
delete map;
|
||||||
|
// delete all actors but player and stairs
|
||||||
|
for (Actor** it = actors.begin(); it != actors.end(); it++) {
|
||||||
|
if (*it != player && *it != stairs) {
|
||||||
|
delete* it;
|
||||||
|
it = actors.remove(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create a new map
|
||||||
|
map = new Map(200, 100);
|
||||||
|
map->init(true);
|
||||||
|
gameStatus = STARTUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::term() {
|
||||||
|
actors.clearAndDelete();
|
||||||
|
if (map) delete map;
|
||||||
|
gui->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine::~Engine() {
|
Engine::~Engine() {
|
||||||
actors.clearAndDelete();
|
term();
|
||||||
delete map;
|
|
||||||
delete gui;
|
delete gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +80,7 @@ bool Engine::update() {
|
|||||||
if (gameStatus == STARTUP) map->computeFov();
|
if (gameStatus == STARTUP) map->computeFov();
|
||||||
gameStatus = IDLE;
|
gameStatus = IDLE;
|
||||||
player->update();
|
player->update();
|
||||||
|
|
||||||
if (gameStatus == NEW_TURN) {
|
if (gameStatus == NEW_TURN) {
|
||||||
for (Actor** iterator = actors.begin(); iterator != actors.end();
|
for (Actor** iterator = actors.begin(); iterator != actors.end();
|
||||||
iterator++) {
|
iterator++) {
|
||||||
@ -57,10 +89,15 @@ bool Engine::update() {
|
|||||||
actor->update();
|
actor->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (gameStatus == QUIT) {
|
||||||
if (gameStatus == QUIT) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (gameStatus == PAUSE) {
|
||||||
|
save();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +110,9 @@ void Engine::render(bool present) {
|
|||||||
for (Actor** iterator = actors.begin();
|
for (Actor** iterator = actors.begin();
|
||||||
iterator != actors.end(); iterator++) {
|
iterator != actors.end(); iterator++) {
|
||||||
Actor* actor = *iterator;
|
Actor* actor = *iterator;
|
||||||
if (actor != player && map->isInFov(actor->x, actor->y)) {
|
if (actor != player && ((!actor->fovOnly && map->isExplored(actor->x, actor->y))
|
||||||
|
|| map->isInFov(actor->x, actor->y))) {
|
||||||
|
|
||||||
actor->render(*console);
|
actor->render(*console);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,52 +146,155 @@ Actor* Engine::getClosestMonster(int x, int y, float range) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Engine::pickATile(int* x, int* y, float maxRange) {
|
bool Engine::pickATile(int* x, int* y, float maxRange) {
|
||||||
while (context->get_sdl_window() != nullptr) {
|
while (true) {
|
||||||
render(false);
|
render(false);
|
||||||
|
|
||||||
|
int offset_x = engine->player->x - Engine::VIEW_WIDTH / 2;
|
||||||
|
int offset_y = engine->player->y - Engine::VIEW_HEIGHT / 2;
|
||||||
|
|
||||||
// highlight the possible range
|
// highlight the possible range
|
||||||
for (int cx = 0; cx < map->width; cx++) {
|
for (int cx = offset_x; cx < offset_x + engine->VIEW_WIDTH; cx++) {
|
||||||
for (int cy = 0; cy < map->height; cy++) {
|
for (int cy = offset_y; cy < offset_y + engine->VIEW_HEIGHT; cy++) {
|
||||||
if (map->isInFov(cx, cy)
|
if (map->isInFov(cx, cy)
|
||||||
&& (maxRange == 0 || player->getDistance(cx, cy) <= maxRange)) {
|
&& (maxRange == 0 || player->getDistance(cx, cy) <= maxRange)) {
|
||||||
TCOD_ColorRGBA col = console->at(cx, cy).bg;
|
TCOD_ColorRGBA col = console->at(cx - offset_x, cy - offset_y).bg;
|
||||||
|
|
||||||
col.r = col.r * 1.2f;
|
col.r = (int)((float)col.r * 1.2f);
|
||||||
col.g = col.g * 1.2f;
|
col.g = (int)((float)col.g * 1.2f);
|
||||||
col.b = col.b * 1.2f;
|
col.b = (int)((float)col.b * 1.2f);
|
||||||
|
|
||||||
console->at(cx, cy).bg = col;
|
console->at(cx - offset_x, cy - offset_y).bg = col;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
|
||||||
|
mouse.lbutton_pressed = false;
|
||||||
|
mouse.rbutton_pressed = false;
|
||||||
|
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
engine->context->convert_event_coordinates(event);
|
engine->context->convert_event_coordinates(event);
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_MOUSE_MOTION:
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
engine->mouse.cx = event.motion.x;
|
engine->mouse.cx = (int)event.motion.x;
|
||||||
engine->mouse.cy = event.motion.y;
|
engine->mouse.cy = (int)event.motion.y;
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
engine->mouse.lbutton_pressed = event.button.button == SDL_BUTTON_LEFT;
|
engine->mouse.lbutton_pressed = event.button.button == SDL_BUTTON_LEFT;
|
||||||
engine->mouse.rbutton_pressed = event.button.button == SDL_BUTTON_RIGHT;
|
engine->mouse.rbutton_pressed = event.button.button == SDL_BUTTON_RIGHT;
|
||||||
break;
|
break;
|
||||||
|
case SDL_EVENT_QUIT:
|
||||||
|
engine->gameStatus = Engine::QUIT;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map->isInFov(mouse.cx, mouse.cy)
|
if (map->isInFov(mouse.cx + offset_x, mouse.cy + offset_y)
|
||||||
&& (maxRange == 0 || player->getDistance(mouse.cx, mouse.cy) <= maxRange)) {
|
&& (maxRange == 0 || player->getDistance(mouse.cx + offset_x, mouse.cy + offset_y) <= maxRange)) {
|
||||||
console->at(mouse.cx, mouse.cy).bg = TCOD_ColorRGBA(100, 100, 100, 255);
|
console->at(mouse.cx, mouse.cy).bg = TCOD_ColorRGBA(100, 100, 100, 255);
|
||||||
if (mouse.lbutton_pressed) {
|
if (mouse.lbutton_pressed) {
|
||||||
*x = mouse.cx;
|
*x = mouse.cx + offset_x;
|
||||||
*y = mouse.cy;
|
*y = mouse.cy + offset_y;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mouse.rbutton_pressed) {
|
if (mouse.rbutton_pressed) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
context->present(*console);
|
context->present(*console);
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
Actor* Engine::getActor(int x, int y) const {
|
||||||
|
for (Actor** iterator = actors.begin();
|
||||||
|
iterator != actors.end(); iterator++) {
|
||||||
|
Actor* actor = *iterator;
|
||||||
|
if (actor->x == x && actor->y == y && actor->destructible
|
||||||
|
&& !actor->destructible->isDead()) {
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::save() {
|
||||||
|
if (player->destructible->isDead()) {
|
||||||
|
TCODSystem::deleteFile("game.sav");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TCODZip zip;
|
||||||
|
// save the map first
|
||||||
|
zip.putInt(level);
|
||||||
|
zip.putInt(map->width);
|
||||||
|
zip.putInt(map->height);
|
||||||
|
map->save(zip);
|
||||||
|
// then the player
|
||||||
|
player->save(zip);
|
||||||
|
// then the stairs
|
||||||
|
stairs->save(zip);
|
||||||
|
// then all the other actors
|
||||||
|
zip.putInt(actors.size() - 2);
|
||||||
|
for (Actor** it = actors.begin(); it != actors.end(); it++) {
|
||||||
|
if (*it != player && *it != stairs) {
|
||||||
|
(*it)->save(zip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// finally the message log
|
||||||
|
gui->save(zip);
|
||||||
|
zip.saveToFile("game.sav");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::load() {
|
||||||
|
gui = new Gui(context, console);
|
||||||
|
gui->menu.clear();
|
||||||
|
gui->menu.addItem(Menu::NEW_GAME, "New game");
|
||||||
|
if (TCODSystem::fileExists("game.sav")) {
|
||||||
|
engine->gui->menu.addItem(Menu::CONTINUE, "Continue");
|
||||||
|
}
|
||||||
|
engine->gui->menu.addItem(Menu::EXIT, "Exit");
|
||||||
|
Menu::MenuItemCode menuItem = engine->gui->menu.pick(context, console);
|
||||||
|
|
||||||
|
if (menuItem == Menu::EXIT || menuItem == Menu::NONE) {
|
||||||
|
// Exit or window closed
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else if (menuItem == Menu::NEW_GAME) {
|
||||||
|
// New game
|
||||||
|
term();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
else if (menuItem == Menu::CONTINUE) {
|
||||||
|
// Continue
|
||||||
|
TCODZip zip;
|
||||||
|
term();
|
||||||
|
zip.loadFromFile("game.sav");
|
||||||
|
// load the map
|
||||||
|
level = zip.getInt();
|
||||||
|
int width = zip.getInt();
|
||||||
|
int height = zip.getInt();
|
||||||
|
map = new Map(width, height);
|
||||||
|
map->load(zip);
|
||||||
|
// then the player
|
||||||
|
player = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
|
||||||
|
player->load(zip);
|
||||||
|
actors.push(player);
|
||||||
|
// the stairs
|
||||||
|
stairs = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
|
||||||
|
stairs->load(zip);
|
||||||
|
actors.push(stairs);
|
||||||
|
// then all other actors
|
||||||
|
int nbActors = zip.getInt();
|
||||||
|
while (nbActors > 0) {
|
||||||
|
Actor* actor = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
|
||||||
|
actor->load(zip);
|
||||||
|
actors.push(actor);
|
||||||
|
nbActors--;
|
||||||
|
}
|
||||||
|
// finally the message log
|
||||||
|
|
||||||
|
gui->load(zip);
|
||||||
|
gameStatus = STARTUP;
|
||||||
|
}
|
||||||
}
|
}
|
10
Engine.h
10
Engine.h
@ -7,12 +7,15 @@ class Gui;
|
|||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
public:
|
public:
|
||||||
|
const static int VIEW_WIDTH = 80;
|
||||||
|
const static int VIEW_HEIGHT = 43;
|
||||||
enum GameStatus {
|
enum GameStatus {
|
||||||
STARTUP,
|
STARTUP,
|
||||||
IDLE,
|
IDLE,
|
||||||
NEW_TURN,
|
NEW_TURN,
|
||||||
VICTORY,
|
VICTORY,
|
||||||
DEFEAT,
|
DEFEAT,
|
||||||
|
PAUSE,
|
||||||
QUIT
|
QUIT
|
||||||
} gameStatus;
|
} gameStatus;
|
||||||
|
|
||||||
@ -21,6 +24,9 @@ public:
|
|||||||
TCOD_mouse_t mouse;
|
TCOD_mouse_t mouse;
|
||||||
TCODList<Actor*> actors;
|
TCODList<Actor*> actors;
|
||||||
Actor* player;
|
Actor* player;
|
||||||
|
Actor* stairs;
|
||||||
|
int level;
|
||||||
|
void nextLevel();
|
||||||
Map* map;
|
Map* map;
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
int fovRadius;
|
int fovRadius;
|
||||||
@ -28,12 +34,16 @@ public:
|
|||||||
tcod::Console* console;
|
tcod::Console* console;
|
||||||
Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::Console *console);
|
Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::Console *console);
|
||||||
void init();
|
void init();
|
||||||
|
void term();
|
||||||
~Engine();
|
~Engine();
|
||||||
bool update();
|
bool update();
|
||||||
void render(bool present = true);
|
void render(bool present = true);
|
||||||
void sendToBack(Actor* actor);
|
void sendToBack(Actor* actor);
|
||||||
Actor* getClosestMonster(int x, int y, float range) const;
|
Actor* getClosestMonster(int x, int y, float range) const;
|
||||||
bool pickATile(int* x, int* y, float maxRange = 0.0f);
|
bool pickATile(int* x, int* y, float maxRange = 0.0f);
|
||||||
|
Actor* getActor(int x, int y) const;
|
||||||
|
void load();
|
||||||
|
void save();
|
||||||
private:
|
private:
|
||||||
bool computeFov;
|
bool computeFov;
|
||||||
};
|
};
|
||||||
|
12
Fireball.cpp
12
Fireball.cpp
@ -10,21 +10,27 @@ Fireball::Fireball(float range, float damage)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Fireball::use(Actor* owner, Actor* wearer) {
|
bool Fireball::use(Actor* owner, Actor* wearer) {
|
||||||
engine->gui->message(TCOD_ColorRGB(0, 255, 255), "Left-click a target tile for the fireball,\nor right-click to cancel.");
|
engine->gui->message(engine->gui->lightBlue, "Left-click a target tile for the fireball,\nor right-click to cancel.");
|
||||||
int x, y;
|
int x, y;
|
||||||
if (!engine->pickATile(&x, &y)) {
|
if (!engine->pickATile(&x, &y)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
engine->gui->message(TCOD_ColorRGB(255, 255, 100), "The fireball explodes, burning everything within %g tiles!", range);
|
engine->gui->message(engine->gui->lightYellow, "The fireball explodes, burning everything within %g tiles!", range);
|
||||||
for (Actor** iterator = engine->actors.begin();
|
for (Actor** iterator = engine->actors.begin();
|
||||||
iterator != engine->actors.end(); iterator++) {
|
iterator != engine->actors.end(); iterator++) {
|
||||||
Actor* actor = *iterator;
|
Actor* actor = *iterator;
|
||||||
if (actor->destructible && !actor->destructible->isDead()
|
if (actor->destructible && !actor->destructible->isDead()
|
||||||
&& actor->getDistance(x, y) <= range) {
|
&& actor->getDistance(x, y) <= range) {
|
||||||
engine->gui->message(TCOD_ColorRGB(255, 255, 100), "The %s gets burned for %g hit points.",
|
engine->gui->message(engine->gui->lightYellow, "The %s gets burned for %g hit points.",
|
||||||
actor->name.c_str(), damage);
|
actor->name.c_str(), damage);
|
||||||
actor->destructible->takeDamage(actor, damage);
|
actor->destructible->takeDamage(actor, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pickable::use(owner, wearer);
|
return Pickable::use(owner, wearer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Fireball::save(TCODZip& zip) {
|
||||||
|
zip.putInt(FIREBALL);
|
||||||
|
zip.putFloat(range);
|
||||||
|
zip.putFloat(damage);
|
||||||
|
}
|
@ -7,4 +7,5 @@ class Fireball : public LightningBolt {
|
|||||||
public:
|
public:
|
||||||
Fireball(float range, float damage);
|
Fireball(float range, float damage);
|
||||||
bool use(Actor* owner, Actor* wearer);
|
bool use(Actor* owner, Actor* wearer);
|
||||||
|
void save(TCODZip& zip);
|
||||||
};
|
};
|
153
Gui.cpp
153
Gui.cpp
@ -1,8 +1,11 @@
|
|||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include "libtcod.hpp"
|
||||||
#include "Gui.h"
|
#include "Gui.h"
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "Actor.h"
|
#include "Actor.h"
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
#include "Destructible.h"
|
#include "Destructible.h"
|
||||||
|
#include "Ai.h"
|
||||||
|
|
||||||
static const int PANEL_HEIGHT = 7;
|
static const int PANEL_HEIGHT = 7;
|
||||||
static const int BAR_WIDTH = 20;
|
static const int BAR_WIDTH = 20;
|
||||||
@ -12,10 +15,29 @@ static const int MSG_HEIGHT = PANEL_HEIGHT - 1;
|
|||||||
Gui::Gui(tcod::Context *ctx, tcod::Console *root) {
|
Gui::Gui(tcod::Context *ctx, tcod::Console *root) {
|
||||||
con = ctx->new_console(engine->screenWidth, PANEL_HEIGHT);
|
con = ctx->new_console(engine->screenWidth, PANEL_HEIGHT);
|
||||||
this->root = root;
|
this->root = root;
|
||||||
|
darkGrey = TCOD_ColorRGB(100,100,100);
|
||||||
|
lightGrey = TCOD_ColorRGB(200,200,200);
|
||||||
|
white = TCOD_ColorRGB(255,255,255);
|
||||||
|
red = TCOD_ColorRGB(100, 50, 50);
|
||||||
|
green = TCOD_ColorRGB(50, 100, 50);
|
||||||
|
blue = TCOD_ColorRGB(50, 50, 100);
|
||||||
|
yellow = TCOD_ColorRGB(100, 100, 50);
|
||||||
|
black = TCOD_ColorRGB(0, 0, 0);
|
||||||
|
lightBlue = TCOD_ColorRGB(100, 100, 255);
|
||||||
|
lightGreen = TCOD_ColorRGB(100, 255, 100);
|
||||||
|
lightRed = TCOD_ColorRGB(255, 100, 100);
|
||||||
|
lightYellow = TCOD_ColorRGB(255, 255, 100);
|
||||||
|
lightPurple = TCOD_ColorRGB(255, 100, 255);
|
||||||
|
purple = TCOD_ColorRGB(100, 50, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gui::clear() {
|
||||||
|
log.clearAndDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
Gui::~Gui() {
|
Gui::~Gui() {
|
||||||
log.clearAndDelete();
|
delete con.release();
|
||||||
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui::render() {
|
void Gui::render() {
|
||||||
@ -24,13 +46,20 @@ void Gui::render() {
|
|||||||
renderBar(1, 1, BAR_WIDTH, "HP", engine->player->destructible->hp,
|
renderBar(1, 1, BAR_WIDTH, "HP", engine->player->destructible->hp,
|
||||||
engine->player->destructible->maxHp,
|
engine->player->destructible->maxHp,
|
||||||
TCOD_ColorRGB(255,100,100), TCOD_ColorRGB(100, 0, 0));
|
TCOD_ColorRGB(255,100,100), TCOD_ColorRGB(100, 0, 0));
|
||||||
|
// draw the XP bar
|
||||||
|
PlayerAi* ai = (PlayerAi*)engine->player->ai;
|
||||||
|
renderBar(1, 5, BAR_WIDTH, "XP(" + std::to_string(ai->xpLevel) + ")", (float)engine->player->destructible->xp,
|
||||||
|
(float)ai->getNextLevelXp(),
|
||||||
|
TCOD_ColorRGB(255, 100, 255), TCOD_ColorRGB(100,0,100));
|
||||||
|
|
||||||
|
tcod::print(con, { 3, 3 }, "Dungeon level " + std::to_string(engine->level), TCOD_ColorRGB(255, 255, 255), std::nullopt);
|
||||||
|
|
||||||
// draw the message log
|
// draw the message log
|
||||||
int y = 1;
|
int y = 1;
|
||||||
float colorCoef = 0.4f;
|
float colorCoef = 0.4f;
|
||||||
for (Message** it = log.begin(); it != log.end(); it++) {
|
for (Message** it = log.begin(); it != log.end(); it++) {
|
||||||
Message* message = *it;
|
Message* message = *it;
|
||||||
tcod::print(con, { MSG_X, y }, message->text, TCOD_ColorRGB(message->col.r * colorCoef, message->col.g * colorCoef, message->col.b * colorCoef), std::nullopt);
|
tcod::print(con, { MSG_X, y }, message->text, TCOD_ColorRGB((int)((float)message->col.r * colorCoef), (int)((float)message->col.g * colorCoef), (int)((float)message->col.b * colorCoef)), std::nullopt);
|
||||||
y++;
|
y++;
|
||||||
if (colorCoef < 1.0f) {
|
if (colorCoef < 1.0f) {
|
||||||
colorCoef += 0.3f;
|
colorCoef += 0.3f;
|
||||||
@ -68,7 +97,7 @@ void Gui::message(const TCOD_ColorRGB& col, const char* text, ...) {
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
va_start(ap, text);
|
va_start(ap, text);
|
||||||
vsprintf(buf, text, ap);
|
vsnprintf(buf, 128, text, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
char* lineBegin = buf;
|
char* lineBegin = buf;
|
||||||
@ -95,24 +124,132 @@ void Gui::message(const TCOD_ColorRGB& col, const char* text, ...) {
|
|||||||
} while (lineEnd);
|
} while (lineEnd);
|
||||||
}
|
}
|
||||||
void Gui::renderMouseLook() {
|
void Gui::renderMouseLook() {
|
||||||
if (!engine->map->isInFov(engine->mouse.cx, engine->mouse.cy)) {
|
int offset_x = engine->player->x - Engine::VIEW_WIDTH / 2;
|
||||||
|
int offset_y = engine->player->y - Engine::VIEW_HEIGHT / 2;
|
||||||
|
if (!engine->map->isInFov(engine->mouse.cx + offset_x, engine->mouse.cy + offset_y)) {
|
||||||
// if mouse is out of fov, nothing to render
|
// if mouse is out of fov, nothing to render
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char buf[128] = "";
|
char buf[128] = "";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
std::stringstream ss;
|
||||||
for (Actor** it = engine->actors.begin(); it != engine->actors.end(); it++) {
|
for (Actor** it = engine->actors.begin(); it != engine->actors.end(); it++) {
|
||||||
Actor* actor = *it;
|
Actor* actor = *it;
|
||||||
// find actors under the mouse cursor
|
// find actors under the mouse cursor
|
||||||
if (actor->x == engine->mouse.cx && actor->y == engine->mouse.cy) {
|
if (actor->x == engine->mouse.cx + offset_x && actor->y == engine->mouse.cy + offset_y) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
strcat(buf, ", ");
|
ss << ", ";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
strcat(buf, actor->name.c_str());
|
ss << actor->name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tcod::print(con, { 1, 0 }, buf, TCOD_ColorRGB(200, 200, 200), std::nullopt);
|
tcod::print(con, { 1, 0 }, ss.str(), TCOD_ColorRGB(200, 200, 200), std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gui::save(TCODZip& zip) {
|
||||||
|
zip.putInt(log.size());
|
||||||
|
for (Message** it = log.begin(); it != log.end(); it++) {
|
||||||
|
zip.putString((*it)->text.c_str());
|
||||||
|
zip.putInt((*it)->col.r);
|
||||||
|
zip.putInt((*it)->col.g);
|
||||||
|
zip.putInt((*it)->col.b);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gui::load(TCODZip& zip) {
|
||||||
|
int nbMessages = zip.getInt();
|
||||||
|
while (nbMessages > 0) {
|
||||||
|
const char* text = zip.getString();
|
||||||
|
TCOD_ColorRGB col(zip.getInt(), zip.getInt(), zip.getInt());
|
||||||
|
message(col, text);
|
||||||
|
nbMessages--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu::~Menu() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::clear() {
|
||||||
|
items.clearAndDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::addItem(MenuItemCode code, std::string label) {
|
||||||
|
MenuItem* item = new MenuItem();
|
||||||
|
item->code = code;
|
||||||
|
item->label = label;
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
const int PAUSE_MENU_WIDTH = 30;
|
||||||
|
const int PAUSE_MENU_HEIGHT = 15;
|
||||||
|
Menu::MenuItemCode Menu::pick(tcod::Context *ctx, tcod::Console *con, DisplayMode mode) {
|
||||||
|
|
||||||
|
int selectedItem = 0;
|
||||||
|
int menux, menuy;
|
||||||
|
while (true) {
|
||||||
|
if (mode == PAUSE) {
|
||||||
|
menux = engine->screenWidth / 2 - PAUSE_MENU_WIDTH / 2;
|
||||||
|
menuy = engine->screenHeight / 2 - PAUSE_MENU_HEIGHT / 2;
|
||||||
|
TCOD_ColorRGB fg(200, 180, 50);
|
||||||
|
TCOD_ColorRGB bg(0, 0, 0);
|
||||||
|
tcod::print_frame(*con, { menux, menuy, PAUSE_MENU_WIDTH, PAUSE_MENU_HEIGHT }, "Inventory", &fg, &bg, TCOD_BKGND_OVERLAY);
|
||||||
|
menux += 2;
|
||||||
|
menuy += 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
static TCODImage img("imgs/menu_background1.png");
|
||||||
|
tcod::draw_quartergraphics(*con, img);
|
||||||
|
TCOD_ColorRGB fg(200, 180, 50);
|
||||||
|
TCOD_ColorRGB bg(0, 0, 0);
|
||||||
|
menux = 7;
|
||||||
|
menuy = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentItem = 0;
|
||||||
|
for (MenuItem** it = items.begin(); it != items.end(); it++) {
|
||||||
|
if (currentItem == selectedItem) {
|
||||||
|
tcod::print(*con, { menux, menuy + currentItem * 3 }, (*it)->label,TCOD_ColorRGB(255, 100, 255), std::nullopt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tcod::print(*con, { menux, menuy + currentItem * 3 }, (*it)->label, TCOD_ColorRGB(200, 200, 200), std::nullopt);
|
||||||
|
}
|
||||||
|
currentItem++;
|
||||||
|
}
|
||||||
|
ctx->present(*con);
|
||||||
|
// check key presses
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
ctx->convert_event_coordinates(event);
|
||||||
|
switch (event.type) {
|
||||||
|
case SDL_EVENT_KEY_DOWN:
|
||||||
|
{
|
||||||
|
if (event.key.key == SDLK_UP) {
|
||||||
|
selectedItem--;
|
||||||
|
if (selectedItem < 0) {
|
||||||
|
selectedItem = items.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.key.key == SDLK_DOWN) {
|
||||||
|
selectedItem = (selectedItem + 1) % items.size();
|
||||||
|
}
|
||||||
|
else if (event.key.key == SDLK_RETURN) {
|
||||||
|
return items.get(selectedItem)->code;
|
||||||
|
}
|
||||||
|
else if (event.key.key == SDLK_ESCAPE) {
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_EVENT_QUIT:
|
||||||
|
engine->gameStatus = Engine::QUIT;
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
51
Gui.h
51
Gui.h
@ -1,14 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "libtcod.hpp"
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Gui {
|
class Menu {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum MenuItemCode {
|
||||||
|
NONE,
|
||||||
|
NEW_GAME,
|
||||||
|
CONTINUE,
|
||||||
|
EXIT,
|
||||||
|
CONSTITUTION,
|
||||||
|
STRENGTH,
|
||||||
|
AGILITY
|
||||||
|
};
|
||||||
|
enum DisplayMode {
|
||||||
|
MAIN,
|
||||||
|
PAUSE
|
||||||
|
};
|
||||||
|
~Menu();
|
||||||
|
void clear();
|
||||||
|
void addItem(MenuItemCode code, std::string label);
|
||||||
|
MenuItemCode pick(tcod::Context* ctx, tcod::Console* con, DisplayMode mode = MAIN);
|
||||||
|
protected:
|
||||||
|
struct MenuItem {
|
||||||
|
MenuItemCode code;
|
||||||
|
std::string label;
|
||||||
|
};
|
||||||
|
TCODList<MenuItem*> items;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Gui : public Persistent {
|
||||||
|
public:
|
||||||
|
TCOD_ColorRGB darkGrey;
|
||||||
|
TCOD_ColorRGB lightGrey;
|
||||||
|
TCOD_ColorRGB white;
|
||||||
|
TCOD_ColorRGB red;
|
||||||
|
TCOD_ColorRGB green;
|
||||||
|
TCOD_ColorRGB blue;
|
||||||
|
TCOD_ColorRGB yellow;
|
||||||
|
TCOD_ColorRGB black;
|
||||||
|
TCOD_ColorRGB lightBlue;
|
||||||
|
TCOD_ColorRGB lightGreen;
|
||||||
|
TCOD_ColorRGB lightRed;
|
||||||
|
TCOD_ColorRGB lightYellow;
|
||||||
|
TCOD_ColorRGB lightPurple;
|
||||||
|
TCOD_ColorRGB purple;
|
||||||
Gui(tcod::Context *ctx, tcod::Console *root);
|
Gui(tcod::Context *ctx, tcod::Console *root);
|
||||||
~Gui();
|
~Gui();
|
||||||
void render();
|
void render();
|
||||||
void message(const TCOD_ColorRGB& col, const char* text, ...);
|
void message(const TCOD_ColorRGB& col, const char* text, ...);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
void clear();
|
||||||
|
Menu menu;
|
||||||
protected:
|
protected:
|
||||||
tcod::Console con;
|
tcod::Console con;
|
||||||
tcod::Console* root;
|
tcod::Console* root;
|
||||||
@ -25,3 +71,4 @@ protected:
|
|||||||
|
|
||||||
TCODList<Message*> log;
|
TCODList<Message*> log;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,3 +14,12 @@ bool Healer::use(Actor* owner, Actor* wearer) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Healer::load(TCODZip& zip) {
|
||||||
|
amount = zip.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Healer::save(TCODZip& zip) {
|
||||||
|
zip.putInt(HEALER);
|
||||||
|
zip.putFloat(amount);
|
||||||
|
}
|
2
Healer.h
2
Healer.h
@ -9,4 +9,6 @@ public:
|
|||||||
Healer(float amount);
|
Healer(float amount);
|
||||||
bool use(Actor* owner, Actor* wearer);
|
bool use(Actor* owner, Actor* wearer);
|
||||||
~Healer() {};
|
~Healer() {};
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
};
|
};
|
||||||
|
@ -11,14 +11,25 @@ LightningBolt::LightningBolt(float range, float damage)
|
|||||||
bool LightningBolt::use(Actor* owner, Actor* wearer) {
|
bool LightningBolt::use(Actor* owner, Actor* wearer) {
|
||||||
Actor* closestMonster = engine->getClosestMonster(wearer->x, wearer->y, range);
|
Actor* closestMonster = engine->getClosestMonster(wearer->x, wearer->y, range);
|
||||||
if (!closestMonster) {
|
if (!closestMonster) {
|
||||||
engine->gui->message(TCOD_ColorRGB(200,200,200), "No enemy is close enough to strike.");
|
engine->gui->message(engine->gui->white, "No enemy is close enough to strike.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// hit closest monster for <damage> hit points
|
// hit closest monster for <damage> hit points
|
||||||
engine->gui->message(TCOD_ColorRGB(100, 255, 100),
|
engine->gui->message(engine->gui->lightBlue,
|
||||||
"A lighting bolt strikes the %s with a loud crack of thunder!\n"
|
"A lighting bolt strikes the %s with a crack of thunder!\n"
|
||||||
"The damage is %g hit points.",
|
"The damage is %g hit points.",
|
||||||
closestMonster->name.c_str(), damage);
|
closestMonster->name.c_str(), damage);
|
||||||
closestMonster->destructible->takeDamage(closestMonster, damage);
|
closestMonster->destructible->takeDamage(closestMonster, damage);
|
||||||
return Pickable::use(owner, wearer);
|
return Pickable::use(owner, wearer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LightningBolt::load(TCODZip& zip) {
|
||||||
|
range = zip.getFloat();
|
||||||
|
damage = zip.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightningBolt::save(TCODZip& zip) {
|
||||||
|
zip.putInt(LIGHTNING_BOLT);
|
||||||
|
zip.putFloat(range);
|
||||||
|
zip.putFloat(damage);
|
||||||
|
}
|
@ -9,4 +9,6 @@ public:
|
|||||||
float range, damage;
|
float range, damage;
|
||||||
LightningBolt(float range, float damage);
|
LightningBolt(float range, float damage);
|
||||||
bool use(Actor* owner, Actor* wearer);
|
bool use(Actor* owner, Actor* wearer);
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
};
|
};
|
108
Map.cpp
108
Map.cpp
@ -8,6 +8,8 @@
|
|||||||
#include "Healer.h"
|
#include "Healer.h"
|
||||||
#include "Lightningbolt.h"
|
#include "Lightningbolt.h"
|
||||||
#include "Fireball.h"
|
#include "Fireball.h"
|
||||||
|
#include "Confuser.h"
|
||||||
|
#include "Gui.h"
|
||||||
|
|
||||||
static const int ROOM_MAX_SIZE = 12;
|
static const int ROOM_MAX_SIZE = 12;
|
||||||
static const int ROOM_MIN_SIZE = 6;
|
static const int ROOM_MIN_SIZE = 6;
|
||||||
@ -23,15 +25,16 @@ private:
|
|||||||
public:
|
public:
|
||||||
BspListener(Map& map) : map(map), roomNum(0) {}
|
BspListener(Map& map) : map(map), roomNum(0) {}
|
||||||
bool visitNode(TCODBsp* node, void* userData) {
|
bool visitNode(TCODBsp* node, void* userData) {
|
||||||
|
|
||||||
if (node->isLeaf()) {
|
if (node->isLeaf()) {
|
||||||
int x, y, w, h;
|
int x, y, w, h;
|
||||||
|
bool withActors = (bool)userData;
|
||||||
// dig a room
|
// dig a room
|
||||||
TCODRandom* rng = TCODRandom::getInstance();
|
w = map.rng->getInt(ROOM_MIN_SIZE, node->w - 2);
|
||||||
w = rng->getInt(ROOM_MIN_SIZE, node->w - 2);
|
h = map.rng->getInt(ROOM_MIN_SIZE, node->h - 2);
|
||||||
h = rng->getInt(ROOM_MIN_SIZE, node->h - 2);
|
x = map.rng->getInt(node->x + 1, node->x + node->w - w - 1);
|
||||||
x = rng->getInt(node->x + 1, node->x + node->w - w - 1);
|
y = map.rng->getInt(node->y + 1, node->y + node->h - h - 1);
|
||||||
y = rng->getInt(node->y + 1, node->y + node->h - h - 1);
|
map.createRoom(roomNum == 0, x, y, x + w - 1, y + h - 1, withActors);
|
||||||
map.createRoom(roomNum == 0, x, y, x + w - 1, y + h - 1);
|
|
||||||
if (roomNum != 0) {
|
if (roomNum != 0) {
|
||||||
// dig a corridor from last room
|
// dig a corridor from last room
|
||||||
map.dig(lastx, lasty, x + w / 2, lasty);
|
map.dig(lastx, lasty, x + w / 2, lasty);
|
||||||
@ -46,14 +49,17 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Map::Map(int width, int height) : width(width), height(height) {
|
Map::Map(int width, int height) : width(width), height(height) {
|
||||||
|
seed = TCODRandom::getInstance()->getInt(0, 0x7FFFFFFF);
|
||||||
|
}
|
||||||
|
void Map::init(bool withActors) {
|
||||||
|
rng = new TCODRandom(seed, TCOD_RNG_CMWC);
|
||||||
tiles = new Tile[width * height];
|
tiles = new Tile[width * height];
|
||||||
map = new TCODMap(width, height);
|
map = new TCODMap(width, height);
|
||||||
TCODBsp bsp(0, 0, width, height);
|
TCODBsp bsp(0, 0, width, height);
|
||||||
bsp.splitRecursive(NULL, 8, ROOM_MAX_SIZE, ROOM_MAX_SIZE, 1.5f, 1.5f);
|
bsp.splitRecursive(rng, 8, ROOM_MAX_SIZE, ROOM_MAX_SIZE, 1.5f, 1.5f);
|
||||||
BspListener listener(*this);
|
BspListener listener(*this);
|
||||||
bsp.traverseInvertedLevelOrder(&listener, NULL);
|
bsp.traverseInvertedLevelOrder(&listener, (void*)withActors);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map::~Map() {
|
Map::~Map() {
|
||||||
delete[] tiles;
|
delete[] tiles;
|
||||||
delete map;
|
delete map;
|
||||||
@ -109,13 +115,16 @@ void Map::render(TCOD_Console& cons) const {
|
|||||||
static const TCOD_ColorRGB lightGround(200, 180, 50);
|
static const TCOD_ColorRGB lightGround(200, 180, 50);
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
|
int offset_x = engine->player->x - Engine::VIEW_WIDTH / 2;
|
||||||
|
int offset_y = engine->player->y - Engine::VIEW_HEIGHT / 2;
|
||||||
|
if (offset_x > x || Engine::VIEW_WIDTH + offset_x < x || offset_y > y || Engine::VIEW_HEIGHT + offset_y < y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (isInFov(x, y)) {
|
if (isInFov(x, y)) {
|
||||||
tcod::print(cons, { x, y }, " ", std::nullopt, isWall(x, y) ? lightWall : lightGround);
|
tcod::print(cons, { x - offset_x, y - offset_y }, " ", std::nullopt, isWall(x, y) ? lightWall : lightGround);
|
||||||
}
|
}
|
||||||
else if (isExplored(x, y)) {
|
else if (isExplored(x, y)) {
|
||||||
tcod::print(cons, { x, y }, " ", std::nullopt, isWall(x, y) ? darkWall : darkGround);
|
tcod::print(cons, { x- offset_x, y - offset_y }, " ", std::nullopt, isWall(x, y) ? darkWall : darkGround);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,8 +148,11 @@ void Map::dig(int x1, int y1, int x2, int y2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::createRoom(bool first, int x1, int y1, int x2, int y2) {
|
void Map::createRoom(bool first, int x1, int y1, int x2, int y2, bool withActors) {
|
||||||
dig(x1, y1, x2, y2);
|
dig(x1, y1, x2, y2);
|
||||||
|
if (!withActors) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (first) {
|
if (first) {
|
||||||
// put the player in the first room
|
// put the player in the first room
|
||||||
engine->player->x = (x1 + x2) / 2;
|
engine->player->x = (x1 + x2) / 2;
|
||||||
@ -167,28 +179,55 @@ void Map::createRoom(bool first, int x1, int y1, int x2, int y2) {
|
|||||||
nbItems--;
|
nbItems--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// set stairs position
|
||||||
|
engine->stairs->x = (x1 + x2) / 2;
|
||||||
|
engine->stairs->y = (y1 + y2) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::addMonster(int x, int y) {
|
void Map::addMonster(int x, int y) {
|
||||||
TCODRandom* rng = TCODRandom::getInstance();
|
TCODRandom* rng = TCODRandom::getInstance();
|
||||||
if (rng->getInt(0, 100) < 80) {
|
if (rng->getInt(0, 100) < 80) {
|
||||||
|
// create weak monster
|
||||||
|
if (engine->level < 5) {
|
||||||
|
// create an rat
|
||||||
|
Actor* orc = new Actor(x, y, "r", "rat",
|
||||||
|
engine->gui->purple);
|
||||||
|
orc->destructible = new MonsterDestructible(10, 0, "dead rat", 5);
|
||||||
|
orc->attacker = new Attacker(3);
|
||||||
|
orc->ai = new MonsterAi();
|
||||||
|
engine->actors.push(orc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
// create an orc
|
// create an orc
|
||||||
Actor* orc = new Actor(x, y, "o", "orc",
|
Actor* orc = new Actor(x, y, "o", "orc",
|
||||||
TCOD_ColorRGB(0, 100, 0));
|
engine->gui->green);
|
||||||
orc->destructible = new MonsterDestructible(10, 0, "dead orc");
|
orc->destructible = new MonsterDestructible(15, 0, "dead orc", 7);
|
||||||
orc->attacker = new Attacker(3);
|
orc->attacker = new Attacker(4.5f);
|
||||||
|
orc->ai = new MonsterAi();
|
||||||
|
engine->actors.push(orc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// create strong monster
|
||||||
|
if (engine->level < 5) {
|
||||||
|
// create an snake
|
||||||
|
Actor* orc = new Actor(x, y, "S", "snake",
|
||||||
|
engine->gui->green);
|
||||||
|
orc->destructible = new MonsterDestructible(16, 0, "dead snake", 10);
|
||||||
|
orc->attacker = new Attacker(4);
|
||||||
orc->ai = new MonsterAi();
|
orc->ai = new MonsterAi();
|
||||||
engine->actors.push(orc);
|
engine->actors.push(orc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// create a troll
|
// create a troll
|
||||||
Actor* troll = new Actor(x, y, "T", "troll",
|
Actor* troll = new Actor(x, y, "T", "troll",
|
||||||
TCOD_ColorRGB(0, 255, 0));
|
engine->gui->lightGreen);
|
||||||
troll->destructible = new MonsterDestructible(16, 1, "troll carcass");
|
troll->destructible = new MonsterDestructible(20, 1, "troll carcass", 15);
|
||||||
troll->attacker = new Attacker(4);
|
troll->attacker = new Attacker(7);
|
||||||
troll->ai = new MonsterAi();
|
troll->ai = new MonsterAi();
|
||||||
engine->actors.push(troll);
|
engine->actors.push(troll);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::addItem(int x, int y) {
|
void Map::addItem(int x, int y) {
|
||||||
@ -197,7 +236,7 @@ void Map::addItem(int x, int y) {
|
|||||||
if (dice < 70) {
|
if (dice < 70) {
|
||||||
// create a health potion
|
// create a health potion
|
||||||
Actor* healthPotion = new Actor(x, y, "!", "health potion",
|
Actor* healthPotion = new Actor(x, y, "!", "health potion",
|
||||||
TCOD_ColorRGB(128, 0, 128));
|
engine->gui->purple);
|
||||||
healthPotion->blocks = false;
|
healthPotion->blocks = false;
|
||||||
healthPotion->pickable = new Healer(4);
|
healthPotion->pickable = new Healer(4);
|
||||||
engine->actors.push(healthPotion);
|
engine->actors.push(healthPotion);
|
||||||
@ -205,7 +244,7 @@ void Map::addItem(int x, int y) {
|
|||||||
else if (dice < 70 + 10) {
|
else if (dice < 70 + 10) {
|
||||||
// create a scroll of lightning bolt
|
// create a scroll of lightning bolt
|
||||||
Actor* scrollOfLightningBolt = new Actor(x, y, "#", "scroll of lightning bolt",
|
Actor* scrollOfLightningBolt = new Actor(x, y, "#", "scroll of lightning bolt",
|
||||||
TCOD_ColorRGB(255, 100, 100));
|
engine->gui->lightRed);
|
||||||
scrollOfLightningBolt->blocks = false;
|
scrollOfLightningBolt->blocks = false;
|
||||||
scrollOfLightningBolt->pickable = new LightningBolt(5, 20);
|
scrollOfLightningBolt->pickable = new LightningBolt(5, 20);
|
||||||
engine->actors.push(scrollOfLightningBolt);
|
engine->actors.push(scrollOfLightningBolt);
|
||||||
@ -214,9 +253,32 @@ void Map::addItem(int x, int y) {
|
|||||||
else if (dice < 70 + 10 + 10) {
|
else if (dice < 70 + 10 + 10) {
|
||||||
// create a scroll of fireball
|
// create a scroll of fireball
|
||||||
Actor* scrollOfFireball = new Actor(x, y, "#", "scroll of fireball",
|
Actor* scrollOfFireball = new Actor(x, y, "#", "scroll of fireball",
|
||||||
TCOD_ColorRGB(255, 100, 100));
|
engine->gui->lightRed);
|
||||||
scrollOfFireball->blocks = false;
|
scrollOfFireball->blocks = false;
|
||||||
scrollOfFireball->pickable = new Fireball(3, 12);
|
scrollOfFireball->pickable = new Fireball(3, 12);
|
||||||
engine->actors.push(scrollOfFireball);
|
engine->actors.push(scrollOfFireball);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// create a scroll of confusion
|
||||||
|
Actor* scrollOfConfusion = new Actor(x, y, "#", "scroll of confusion",
|
||||||
|
engine->gui->lightRed);
|
||||||
|
scrollOfConfusion->blocks = false;
|
||||||
|
scrollOfConfusion->pickable = new Confuser(10, 8);
|
||||||
|
engine->actors.push(scrollOfConfusion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Map::save(TCODZip& zip) {
|
||||||
|
zip.putInt(seed);
|
||||||
|
for (int i = 0; i < width * height; i++) {
|
||||||
|
zip.putInt(tiles[i].explored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Map::load(TCODZip& zip) {
|
||||||
|
seed = zip.getInt();
|
||||||
|
init(false);
|
||||||
|
for (int i = 0; i < width * height; i++) {
|
||||||
|
tiles[i].explored = zip.getInt();
|
||||||
|
}
|
||||||
}
|
}
|
12
Map.h
12
Map.h
@ -1,16 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
struct Tile {
|
struct Tile {
|
||||||
bool explored; // has the player already seen this tile ?
|
bool explored; // has the player already seen this tile ?
|
||||||
Tile() : explored(false) {}
|
Tile() : explored(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Map {
|
class Map : public Persistent {
|
||||||
public:
|
public:
|
||||||
int width, height;
|
int width, height;
|
||||||
|
|
||||||
Map(int width, int height);
|
Map(int width, int height);
|
||||||
~Map();
|
~Map();
|
||||||
|
void init(bool withActors);
|
||||||
bool canWalk(int x, int y) const;
|
bool canWalk(int x, int y) const;
|
||||||
bool isWall(int x, int y) const;
|
bool isWall(int x, int y) const;
|
||||||
bool isInFov(int x, int y) const;
|
bool isInFov(int x, int y) const;
|
||||||
@ -18,12 +21,17 @@ public:
|
|||||||
void computeFov();
|
void computeFov();
|
||||||
void render(TCOD_Console& cons) const;
|
void render(TCOD_Console& cons) const;
|
||||||
void addItem(int x, int y);
|
void addItem(int x, int y);
|
||||||
|
long seed;
|
||||||
|
TCODRandom* rng;
|
||||||
|
void load(TCODZip& zip);
|
||||||
|
void save(TCODZip& zip);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Tile* tiles;
|
Tile* tiles;
|
||||||
friend class BspListener;
|
friend class BspListener;
|
||||||
TCODMap* map;
|
TCODMap* map;
|
||||||
void dig(int x1, int y1, int x2, int y2);
|
void dig(int x1, int y1, int x2, int y2);
|
||||||
void createRoom(bool first, int x1, int y1, int x2, int y2);
|
void createRoom(bool first, int x1, int y1, int x2, int y2, bool withActors);
|
||||||
void addMonster(int x, int y);
|
void addMonster(int x, int y);
|
||||||
void setWall(int x, int y);
|
void setWall(int x, int y);
|
||||||
};
|
};
|
7
Persistance.h
Normal file
7
Persistance.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Persistent {
|
||||||
|
public:
|
||||||
|
virtual void load(TCODZip& zip) = 0;
|
||||||
|
virtual void save(TCODZip& zip) = 0;
|
||||||
|
};
|
30
Pickable.cpp
30
Pickable.cpp
@ -2,6 +2,12 @@
|
|||||||
#include "Actor.h"
|
#include "Actor.h"
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "Container.h"
|
#include "Container.h"
|
||||||
|
#include "Gui.h"
|
||||||
|
#include "Healer.h"
|
||||||
|
#include "LightningBolt.h"
|
||||||
|
#include "Confuser.h"
|
||||||
|
#include "Fireball.h"
|
||||||
|
|
||||||
|
|
||||||
bool Pickable::pick(Actor* owner, Actor* wearer) {
|
bool Pickable::pick(Actor* owner, Actor* wearer) {
|
||||||
if (wearer->container && wearer->container->add(owner)) {
|
if (wearer->container && wearer->container->add(owner)) {
|
||||||
@ -19,3 +25,27 @@ bool Pickable::use(Actor* owner, Actor* wearer) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pickable::drop(Actor* owner, Actor* wearer) {
|
||||||
|
if (wearer->container) {
|
||||||
|
wearer->container->remove(owner);
|
||||||
|
engine->actors.push(owner);
|
||||||
|
owner->x = wearer->x;
|
||||||
|
owner->y = wearer->y;
|
||||||
|
engine->gui->message(engine->gui->lightGrey, "%s drops a %s.",
|
||||||
|
wearer->name.c_str(), owner->name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pickable* Pickable::create(TCODZip& zip) {
|
||||||
|
PickableType type = (PickableType)zip.getInt();
|
||||||
|
Pickable* pickable = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case HEALER: pickable = new Healer(0); break;
|
||||||
|
case LIGHTNING_BOLT: pickable = new LightningBolt(0, 0); break;
|
||||||
|
case CONFUSER: pickable = new Confuser(0, 0); break;
|
||||||
|
case FIREBALL: pickable = new Fireball(0, 0); break;
|
||||||
|
}
|
||||||
|
pickable->load(zip);
|
||||||
|
return pickable;
|
||||||
|
}
|
10
Pickable.h
10
Pickable.h
@ -1,10 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "libtcod.hpp"
|
||||||
|
#include "Persistance.h"
|
||||||
|
|
||||||
class Actor;
|
class Actor;
|
||||||
|
|
||||||
class Pickable {
|
class Pickable : public Persistent {
|
||||||
public:
|
public:
|
||||||
bool pick(Actor* owner, Actor* wearer);
|
bool pick(Actor* owner, Actor* wearer);
|
||||||
virtual bool use(Actor* owner, Actor* wearer);
|
virtual bool use(Actor* owner, Actor* wearer);
|
||||||
|
void drop(Actor* owner, Actor* wearer);
|
||||||
virtual ~Pickable() {};
|
virtual ~Pickable() {};
|
||||||
|
static Pickable* create(TCODZip& zip);
|
||||||
|
protected:
|
||||||
|
enum PickableType {
|
||||||
|
HEALER, LIGHTNING_BOLT, CONFUSER, FIREBALL
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
BIN
imgs/menu_background1.png
Normal file
BIN
imgs/menu_background1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
imgs/terminal10x16_gs_ro.png
Normal file
BIN
imgs/terminal10x16_gs_ro.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
Loading…
x
Reference in New Issue
Block a user