Improvements

This commit is contained in:
Andrew Pamment 2025-04-22 11:38:20 +10:00
parent 843606bcbf
commit ef522be8ef
16 changed files with 81 additions and 38 deletions

View File

@ -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("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;

View File

@ -16,7 +16,12 @@ Actor::Actor(int x, int y, std::string_view ch, std::string name, const TCOD_Col
} }
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() {

12
Ai.cpp
View File

@ -27,7 +27,7 @@ void PlayerAi::update(Actor* owner) {
if (owner->destructible->xp >= levelUpXp) { if (owner->destructible->xp >= levelUpXp) {
xpLevel++; xpLevel++;
owner->destructible->xp -= levelUpXp; owner->destructible->xp -= levelUpXp;
engine->gui->message(TCOD_ColorRGB(255, 255, 100), "Your battle skills grow stronger! You reached level %d", xpLevel); engine->gui->message(engine->gui->lightYellow, "Your battle skills grow stronger! You reached level %d", xpLevel);
engine->gui->menu.clear(); engine->gui->menu.clear();
engine->gui->menu.addItem(Menu::CONSTITUTION, "Constitution (+20HP)"); engine->gui->menu.addItem(Menu::CONSTITUTION, "Constitution (+20HP)");
engine->gui->menu.addItem(Menu::STRENGTH, "Strength (+1 attack)"); engine->gui->menu.addItem(Menu::STRENGTH, "Strength (+1 attack)");
@ -120,18 +120,18 @@ void PlayerAi::handleActionKey(Actor* owner, int sdlkey, int mod) {
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) if (engine->gameStatus != Engine::QUIT)
engine->gameStatus = Engine::NEW_TURN; engine->gameStatus = Engine::NEW_TURN;
@ -163,7 +163,7 @@ void PlayerAi::handleActionKey(Actor* owner, int sdlkey, int mod) {
engine->nextLevel(); engine->nextLevel();
} }
else { else {
engine->gui->message(TCOD_ColorRGB(200, 200, 200), "There are no stairs here."); engine->gui->message(engine->gui->lightGrey, "There are no stairs here.");
} }
} }
break; break;
@ -189,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;

View File

@ -10,16 +10,16 @@ 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());
} }
} }

View File

@ -10,7 +10,7 @@ Confuser::Confuser(int nbTurns, float range)
: nbTurns(nbTurns), range(range) { : nbTurns(nbTurns), range(range) {
} }
bool Confuser::use(Actor* owner, Actor* wearer) { bool Confuser::use(Actor* owner, Actor* wearer) {
engine->gui->message(TCOD_ColorRGB(0, 255, 255), "Left-click an enemy to confuse it,\nor right-click to cancel."); engine->gui->message(engine->gui->lightBlue, "Left-click an enemy to confuse it,\nor right-click to cancel.");
int x, y; int x, y;
if (!engine->pickATile(&x, &y, range)) { if (!engine->pickATile(&x, &y, range)) {
return false; return false;
@ -22,7 +22,7 @@ bool Confuser::use(Actor* owner, Actor* wearer) {
// confuse the monster for <nbTurns> turns // confuse the monster for <nbTurns> turns
Ai* confusedAi = new ConfusedMonsterAi(nbTurns, actor->ai); Ai* confusedAi = new ConfusedMonsterAi(nbTurns, actor->ai);
actor->ai = confusedAi; actor->ai = confusedAi;
engine->gui->message(TCOD_ColorRGB(100, 255, 100), "The eyes of the %s look vacant,\nas he starts to stumble around!", engine->gui->message(engine->gui->lightGreen, "The eyes of the %s look vacant,\nas he starts to stumble around!",
actor->name.c_str()); actor->name.c_str());
return Pickable::use(owner, wearer); return Pickable::use(owner, wearer);
} }

View File

@ -60,7 +60,7 @@ void MonsterDestructible::save(TCODZip& 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. You gain %d xp", engine->gui->message(engine->gui->lightGrey, "%s is dead. You gain %d xp",
owner->name.c_str(), xp); owner->name.c_str(), xp);
engine->player->destructible->xp += xp; engine->player->destructible->xp += xp;
@ -68,7 +68,7 @@ void MonsterDestructible::die(Actor* 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;
} }

View File

@ -27,7 +27,7 @@ Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::
} }
void Engine::init() { void Engine::init() {
player = new Actor(40, 25, "@", "player", TCOD_ColorRGB(255, 255, 255)); player = new Actor(40, 25, "@", "player", engine->gui->white);
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();
@ -37,7 +37,7 @@ void Engine::init() {
stairs->blocks = false; stairs->blocks = false;
stairs->fovOnly = false; stairs->fovOnly = false;
actors.push(stairs); actors.push(stairs);
map = new Map(80, 43); map = new Map(200, 100);
map->init(true); 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.");
@ -46,9 +46,9 @@ void Engine::init() {
void Engine::nextLevel() { void Engine::nextLevel() {
level++; level++;
gui->message(TCOD_ColorRGB(255, 100, 255), "You take a moment to rest, and recover your strength."); gui->message(engine->gui->lightPurple, "You take a moment to rest, and recover your strength.");
player->destructible->heal(player->destructible->maxHp / 2); player->destructible->heal(player->destructible->maxHp / 2);
gui->message(TCOD_ColorRGB(255, 100, 100), "After a rare moment of peace, you descend\ndeeper into the heart of the dungeon..."); gui->message(engine->gui->lightRed, "After a rare moment of peace, you descend\ndeeper into the heart of the dungeon...");
delete map; delete map;
// delete all actors but player and stairs // delete all actors but player and stairs
for (Actor** it = actors.begin(); it != actors.end(); it++) { for (Actor** it = actors.begin(); it != actors.end(); it++) {
@ -58,7 +58,7 @@ void Engine::nextLevel() {
} }
} }
// create a new map // create a new map
map = new Map(80, 43); map = new Map(200, 100);
map->init(true); map->init(true);
gameStatus = STARTUP; gameStatus = STARTUP;
} }

View File

@ -7,6 +7,8 @@ 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,

View File

@ -10,18 +10,18 @@ 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);
} }

20
Gui.cpp
View File

@ -15,6 +15,20 @@ 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() { void Gui::clear() {
@ -186,8 +200,10 @@ Menu::MenuItemCode Menu::pick(tcod::Context *ctx, tcod::Console *con, DisplayMod
else { else {
static TCODImage img("menu_background1.png"); static TCODImage img("menu_background1.png");
img.blit2x(*con, 0, 0); img.blit2x(*con, 0, 0);
menux = 10; TCOD_ColorRGB fg(200, 180, 50);
menuy = con->get_height() / 3; TCOD_ColorRGB bg(0, 0, 0);
menux = 7;
menuy = 30;
} }
int currentItem = 0; int currentItem = 0;

15
Gui.h
View File

@ -5,6 +5,7 @@
class Menu { class Menu {
public: public:
enum MenuItemCode { enum MenuItemCode {
NONE, NONE,
NEW_GAME, NEW_GAME,
@ -32,6 +33,20 @@ protected:
class Gui : public Persistent { class Gui : public Persistent {
public: 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();

View File

@ -11,11 +11,11 @@ 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 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);

24
Map.cpp
View File

@ -9,6 +9,7 @@
#include "Lightningbolt.h" #include "Lightningbolt.h"
#include "Fireball.h" #include "Fireball.h"
#include "Confuser.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;
@ -114,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);
} }
} }
} }
@ -185,7 +189,7 @@ void Map::addMonster(int x, int y) {
if (rng->getInt(0, 100) < 80) { if (rng->getInt(0, 100) < 80) {
// 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", 5); orc->destructible = new MonsterDestructible(10, 0, "dead orc", 5);
orc->attacker = new Attacker(3); orc->attacker = new Attacker(3);
orc->ai = new MonsterAi(); orc->ai = new MonsterAi();
@ -194,7 +198,7 @@ void Map::addMonster(int x, int y) {
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", 10); troll->destructible = new MonsterDestructible(16, 1, "troll carcass", 10);
troll->attacker = new Attacker(4); troll->attacker = new Attacker(4);
troll->ai = new MonsterAi(); troll->ai = new MonsterAi();
@ -208,7 +212,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);
@ -216,7 +220,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);
@ -225,7 +229,7 @@ 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);
@ -233,7 +237,7 @@ void Map::addItem(int x, int y) {
else { else {
// create a scroll of confusion // create a scroll of confusion
Actor* scrollOfConfusion = new Actor(x, y, "#", "scroll of confusion", Actor* scrollOfConfusion = new Actor(x, y, "#", "scroll of confusion",
TCOD_ColorRGB(255, 100, 100)); engine->gui->lightRed);
scrollOfConfusion->blocks = false; scrollOfConfusion->blocks = false;
scrollOfConfusion->pickable = new Confuser(10, 8); scrollOfConfusion->pickable = new Confuser(10, 8);
engine->actors.push(scrollOfConfusion); engine->actors.push(scrollOfConfusion);

View File

@ -32,7 +32,7 @@ void Pickable::drop(Actor* owner, Actor* wearer) {
engine->actors.push(owner); engine->actors.push(owner);
owner->x = wearer->x; owner->x = wearer->x;
owner->y = wearer->y; owner->y = wearer->y;
engine->gui->message(TCOD_ColorRGB(200,200,200), "%s drops a %s.", engine->gui->message(engine->gui->lightGrey, "%s drops a %s.",
wearer->name.c_str(), owner->name.c_str()); wearer->name.c_str(), owner->name.c_str());
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
terminal10x16_gs_ro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB