This commit is contained in:
Andrew Pamment 2025-04-20 17:57:56 +10:00
parent 7d6094a049
commit cefeacdae9
9 changed files with 173 additions and 15 deletions

7
Ai.cpp
View File

@ -5,6 +5,7 @@
#include "Map.h"
#include "Destructible.h"
#include "Attacker.h"
#include "Gui.h"
void PlayerAi::update(Actor* owner) {
SDL_Event event;
@ -25,6 +26,10 @@ void PlayerAi::update(Actor* owner) {
while (SDL_PollEvent(&event)) {
engine->context->convert_event_coordinates(event);
switch (event.type) {
case SDL_EVENT_MOUSE_MOTION:
engine->mouse.cx = event.motion.x;
engine->mouse.cy = event.motion.y;
break;
case SDL_EVENT_KEY_DOWN:
switch (event.key.key) {
case SDLK_UP:
@ -76,7 +81,7 @@ bool PlayerAi::moveOrAttack(Actor* owner, int targetx, int targety) {
Actor* actor = *iterator;
if (actor->destructible && actor->destructible->isDead()
&& actor->x == targetx && actor->y == targety) {
printf("There's a %s here\n", actor->name.c_str());
engine->gui->message(TCOD_ColorRGB(150,150,150), "There's a %s here\n", actor->name.c_str());
}
}
owner->x = targetx;

View File

@ -1,6 +1,8 @@
#include "Attacker.h"
#include "Actor.h"
#include "Destructible.h"
#include "Engine.h"
#include "Gui.h"
Attacker::Attacker(float power) : power(power) {
}
@ -8,15 +10,15 @@ Attacker::Attacker(float power) : power(power) {
void Attacker::attack(Actor* owner, Actor* target) {
if (target->destructible && !target->destructible->isDead()) {
if (power - target->destructible->defense > 0) {
printf("%s attacks %s for %g hit points.\n", owner->name.c_str(), target->name.c_str(),
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s attacks %s for %g hit points.\n", owner->name.c_str(), target->name.c_str(),
power - target->destructible->defense);
}
else {
printf("%s attacks %s but it has no effect!\n", owner->name.c_str(), target->name.c_str());
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());
}
target->destructible->takeDamage(target, power);
}
else {
printf("%s attacks %s in vain.\n", owner->name.c_str(), target->name.c_str());
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s attacks %s in vain.\n", owner->name.c_str(), target->name.c_str());
}
}

View File

@ -14,7 +14,7 @@ project ("ADG5")
find_package(libtcod CONFIG REQUIRED)
# 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")
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")
target_link_libraries(ADG5
PRIVATE

View File

@ -1,6 +1,7 @@
#include "Destructible.h"
#include "Actor.h"
#include "Engine.h"
#include "Gui.h"
Destructible::Destructible(float maxHp, float defense, std::string corpseName) :
maxHp(maxHp), hp(maxHp), defense(defense), corpseName(corpseName) {
@ -41,12 +42,12 @@ PlayerDestructible::PlayerDestructible(float maxHp, float defense, std::string c
void MonsterDestructible::die(Actor* owner) {
// transform it into a nasty corpse! it doesn't block, can't be
// attacked and doesn't move
printf("%s is dead\n", owner->name.c_str());
engine->gui->message(TCOD_ColorRGB(150, 150, 150), "%s is dead\n", owner->name.c_str());
Destructible::die(owner);
}
void PlayerDestructible::die(Actor* owner) {
printf("You died!\n");
engine->gui->message(TCOD_ColorRGB(150, 0, 0), "You died!\n");
Destructible::die(owner);
engine->gameStatus = Engine::DEFEAT;
}

View File

@ -6,13 +6,14 @@
#include "Destructible.h"
#include "Attacker.h"
#include "Ai.h"
#include "Gui.h"
Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::Console *console) {
this->context = context;
this->console = console;
this->screenWidth = screenWidth;
this->screenHeight = screenHeight;
gui = nullptr;
map = nullptr;
player = nullptr;
fovRadius = 10;
@ -21,24 +22,26 @@ Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::
}
void Engine::init() {
gui = new Gui(context, console);
player = new Actor(40, 25, "@", "player", TCOD_ColorRGB(255, 255, 255));
player->destructible = new PlayerDestructible(30, 2, "player corpse");
player->attacker = new Attacker(5);
player->ai = new PlayerAi();
actors.push(player);
map = new Map(80, 45);
gui->message(TCOD_ColorRGB(150,0,0),
"Welcome stranger!\nPrepare to perish in the Tombs Andrew's Dunegon Game.");
}
Engine::~Engine() {
actors.clearAndDelete();
delete map;
delete gui;
}
bool Engine::update() {
if (gameStatus == STARTUP) map->computeFov();
gameStatus = IDLE;
player->update();
if (gameStatus == NEW_TURN) {
for (Actor** iterator = actors.begin(); iterator != actors.end();
@ -69,10 +72,7 @@ void Engine::render() {
}
}
player->render(*console);
std::string hp = "HP : " + std::to_string(player->destructible->hp) + "/" + std::to_string(player->destructible->maxHp);
tcod::print(*console, { 1, 1 }, hp, TCOD_ColorRGB(255, 255, 255), std::nullopt);
gui->render();
context->present(*console);
}

View File

@ -1,6 +1,7 @@
#pragma once
class Actor;
class Map;
class Gui;
#include "libtcod.hpp"
@ -17,10 +18,11 @@ public:
int screenWidth;
int screenHeight;
TCOD_mouse_t mouse;
TCODList<Actor*> actors;
Actor* player;
Map* map;
Gui* gui;
int fovRadius;
tcod::Context *context;
tcod::Console* console;

118
Gui.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "Gui.h"
#include "Engine.h"
#include "Actor.h"
#include "Map.h"
#include "Destructible.h"
static const int PANEL_HEIGHT = 7;
static const int BAR_WIDTH = 20;
static const int MSG_X = BAR_WIDTH + 2;
static const int MSG_HEIGHT = PANEL_HEIGHT - 1;
Gui::Gui(tcod::Context *ctx, tcod::Console *root) {
con = ctx->new_console(engine->screenWidth, PANEL_HEIGHT);
this->root = root;
}
Gui::~Gui() {
log.clearAndDelete();
}
void Gui::render() {
// clear the GUI console
con.clear();
renderBar(1, 1, BAR_WIDTH, "HP", engine->player->destructible->hp,
engine->player->destructible->maxHp,
TCOD_ColorRGB(255,100,100), TCOD_ColorRGB(100, 0, 0));
// draw the message log
int y = 1;
float colorCoef = 0.4f;
for (Message** it = log.begin(); it != log.end(); 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);
y++;
if (colorCoef < 1.0f) {
colorCoef += 0.3f;
}
}
renderMouseLook();
tcod::blit(*root, con, { 0, engine->screenHeight - PANEL_HEIGHT }, { 0, 0, engine->screenWidth, PANEL_HEIGHT });
}
void Gui::renderBar(int x, int y, int width, std::string name,
float value, float maxValue, const TCOD_ColorRGB& barColor,
const TCOD_ColorRGB& backColor) {
tcod::draw_rect(con, { x, y, width, 1 }, ' ', std::nullopt, backColor);
int barWidth = (int)(value / maxValue * width);
if (barWidth > 0) {
// draw the bar
tcod::draw_rect(con, { x, y, barWidth, 1 }, ' ', std::nullopt, barColor);
}
tcod::print(con, { x + width / 2, y }, name + " : " + std::to_string((int)value) + "/" + std::to_string((int)maxValue), TCOD_ColorRGB(255, 255, 255), std::nullopt);
}
Gui::Message::Message(std::string text, const TCOD_ColorRGB& col) :
text(text), col(col) {
}
Gui::Message::~Message() {
}
void Gui::message(const TCOD_ColorRGB& col, const char* text, ...) {
// build the text
va_list ap;
char buf[128];
va_start(ap, text);
vsprintf(buf, text, ap);
va_end(ap);
char* lineBegin = buf;
char* lineEnd;
if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0';
do {
// make room for the new message
if (log.size() == MSG_HEIGHT) {
Message* toRemove = log.get(0);
log.remove(toRemove);
delete toRemove;
}
lineEnd = strchr(lineBegin, '\n');
if (lineEnd) {
*lineEnd = '\0';
}
Message* msg = new Message(lineBegin, col);
log.push(msg);
// go to next line
lineBegin = lineEnd + 1;
} while (lineEnd);
}
void Gui::renderMouseLook() {
if (!engine->map->isInFov(engine->mouse.cx, engine->mouse.cy)) {
// if mouse is out of fov, nothing to render
return;
}
char buf[128] = "";
bool first = true;
for (Actor** it = engine->actors.begin(); it != engine->actors.end(); it++) {
Actor* actor = *it;
// find actors under the mouse cursor
if (actor->x == engine->mouse.cx && actor->y == engine->mouse.cy) {
if (!first) {
strcat(buf, ", ");
}
else {
first = false;
}
strcat(buf, actor->name.c_str());
}
}
tcod::print(con, { 1, 0 }, buf, TCOD_ColorRGB(200, 200, 200), std::nullopt);
}

27
Gui.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "libtcod.hpp"
class Gui {
public:
Gui(tcod::Context *ctx, tcod::Console *root);
~Gui();
void render();
void message(const TCOD_ColorRGB& col, const char* text, ...);
protected:
tcod::Console con;
tcod::Console* root;
void renderBar(int x, int y, int width, std::string name,
float value, float maxValue, const TCOD_ColorRGB& barColor,
const TCOD_ColorRGB& backColor);
void renderMouseLook();
struct Message {
std::string text;
TCOD_ColorRGB col;
Message(std::string text, const TCOD_ColorRGB& col);
~Message();
};
TCODList<Message*> log;
};

View File

@ -72,6 +72,9 @@ bool Map::canWalk(int x, int y) const {
}
bool Map::isInFov(int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return false;
}
if (map->isInFov(x, y)) {
tiles[x + y * width].explored = true;
return true;