254 lines
7.5 KiB
C++
254 lines
7.5 KiB
C++
#include <SDL3/SDL.h>
|
|
#include "libtcod.hpp"
|
|
#include "Gui.h"
|
|
#include "Engine.h"
|
|
#include "Actor.h"
|
|
#include "Map.h"
|
|
#include "Destructible.h"
|
|
#include "Ai.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;
|
|
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() {
|
|
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 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
|
|
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((int)((float)message->col.r * colorCoef), (int)((float)message->col.g * colorCoef), (int)((float)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);
|
|
vsnprintf(buf, 128, 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;
|
|
std::stringstream ss;
|
|
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) {
|
|
ss << ", ";
|
|
}
|
|
else {
|
|
first = false;
|
|
}
|
|
ss << actor->name;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|