adg5/Gui.cpp
2025-04-21 17:03:26 +10:00

210 lines
5.7 KiB
C++

#include <SDL3/SDL.h>
#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;
}
void Gui::clear() {
log.clearAndDelete();
}
Gui::~Gui() {
delete con.release();
clear();
}
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, TCOD_CENTER);
}
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);
}
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);
}
Menu::MenuItemCode Menu::pick(tcod::Context *ctx, tcod::Console *con) {
static TCODImage img("menu_background1.png");
int selectedItem = 0;
while (true) {
img.blit2x(*con, 0, 0);
int currentItem = 0;
for (MenuItem** it = items.begin(); it != items.end(); it++) {
if (currentItem == selectedItem) {
tcod::print(*con, { 10, 10 + currentItem * 3 }, (*it)->label,TCOD_ColorRGB(255, 100, 255), std::nullopt);
}
else {
tcod::print(*con, { 10, 10 + 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;
}
}
}
}