Files
oboe/src/game/boe.ui.cpp

200 lines
6.0 KiB
C++

//
// boe.ui.cpp
// BoE
//
// Created by Celtic Minstrel on 20-01-27.
//
//
#include "boe.ui.hpp"
#include <numeric>
#include "tools/enum_map.hpp"
#include "boe.consts.hpp"
#include "boe.locutils.hpp"
#include "boe.graphics.hpp"
#include "gfx/render_shapes.hpp"
#include "gfx/render_image.hpp"
#include "fileio/resmgr/res_image.hpp"
#include "mathutil.hpp"
#include "sounds.hpp"
#include "winutil.hpp"
namespace UI {
cToolbar toolbar;
}
sf::RenderTexture& cToolbar::cache()
{
static sf::RenderTexture instance;
return instance;
}
// The location of each UI area
enum_map(eGuiArea, rectangle) win_to_rects = {
{7,19,358,298}, // terrain view
{385,19,423,285}, // action buttons
{7,305,123,576}, // pc stats area
{132,305,276,576}, // inventory area
{360,19,381,298}, // info bar
{285,305,423,561}, // text transcript
};
// Get the location of the button icon in buttons.png.
static location btn_pos(eToolbarButton icon) {
location pos;
pos.x = icon % 6;
pos.y = icon / 6;
return pos;
}
// The list of buttons for each mode
static const std::vector<eToolbarButton> out_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_CAMP, TOOLBAR_SCROLL, TOOLBAR_SAVE, TOOLBAR_LOAD};
static const std::vector<eToolbarButton> town_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_TALK, TOOLBAR_HAND, TOOLBAR_USE, TOOLBAR_MAP, TOOLBAR_SWORD};
static const std::vector<eToolbarButton> fight_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_SHIELD, TOOLBAR_BAG, TOOLBAR_WAIT, TOOLBAR_SHOOT, TOOLBAR_END, TOOLBAR_ACT};
// Source rects for each button type in buttons.png.
static const rectangle btn_src_rects[] = {{0,0,38,38}, {0,38,19,76}, {19,38,38,76}};
eToolbarButton cToolbar::button_hit(sf::RenderWindow& win, location click, cFramerateLimiter& fps_limiter) {
rectangle dest_rect = win_to_rects[WINRECT_ACTBTNS];
click.x -= dest_rect.left;
click.y -= dest_rect.top;
if(click.in(total_rect)) {
for(int i = 0; i < toolbar.size(); i++) {
if(click.in(toolbar[i].bounds)) {
sf::Event e;
bool done = false, clicked = false;
win.setActive();
active = i;
while(!done){
redraw_screen(REFRESH_NONE);
while(pollEvent(win, e)) {
if(e.type == sf::Event::MouseButtonReleased){
done = true;
location clickPos(e.mouseButton.x, e.mouseButton.y);
clickPos = win.mapPixelToCoords(clickPos);
clickPos.x -= dest_rect.left;
clickPos.y -= dest_rect.top;
clicked = toolbar[i].bounds.contains(clickPos);
active = -1;
} else if(e.type == sf::Event::MouseMoved){
location toPos(e.mouseMove.x, e.mouseMove.y);
toPos = win.mapPixelToCoords(toPos);
toPos.x -= dest_rect.left;
toPos.y -= dest_rect.top;
active = toolbar[i].bounds.contains(toPos) ? i : -1;
}
}
fps_limiter.frame_finished();
}
play_sound(37, time_in_ticks(5));
redraw_screen(REFRESH_NONE);
if(clicked) return toolbar[i].btn;
}
}
}
return TOOLBAR_NONE;
}
cToolbar::eMode cToolbar::get_mode() {
if(is_combat()) return COMBAT;
if(is_town()) return TOWN;
return OUTDOORS;
}
void cToolbar::init() {
eMode mode = get_mode();
if(mode == cur_mode) return;
cur_mode = mode;
switch(mode) {
case UNKNOWN: case OUTDOORS:
place_buttons(out_buttons);
break;
case TOWN:
place_buttons(town_buttons);
break;
case COMBAT:
place_buttons(fight_buttons);
break;
}
draw_buttons();
// Calculate total bounds
if(toolbar.empty()) return;
total_rect = std::accumulate(toolbar.begin() + 1, toolbar.end(), toolbar[0].bounds, [](const rectangle& accum, const cButton& next) {
return rectunion(accum, next.bounds);
});
}
void cToolbar::place_buttons(const std::vector<eToolbarButton>& src) {
// Calculate the positions of the toolbar buttons
toolbar.clear();
int offset = 0;
bool bottom_half = false;
for(eToolbarButton btn : src) {
location slot = btn_pos(btn);
cButton btn_info;
btn_info.btn = btn;
if(slot.y == 2) {
// Small button (3rd row)
btn_info.type = bottom_half ? BTN_SM_LO : BTN_SM_HI;
btn_info.bounds = {0,0,19,38};
if(bottom_half) {
btn_info.bounds.offset(0,19);
bottom_half = false;
} else bottom_half = true;
} else {
// Large button (1st & 2nd rows)
btn_info.type = BTN_LG;
btn_info.bounds = {0,0,38,38};
}
btn_info.bounds.offset(offset, 0);
if(btn_info.type != BTN_SM_HI) offset += 38;
toolbar.push_back(btn_info);
}
}
void cToolbar::draw_buttons() {
// Draw the buttons into the cache
static bool inited = false;
if(!inited) {
inited = true;
// TODO: Possibly this should be based on total_rect instead of hard-coded?
cache().create(266, 38);
cache().clear(sf::Color::Black);
}
sf::Texture& buttons_gworld = *ResMgr::graphics.get("buttons");
for(const auto& btn : toolbar) {
rectangle icon_rect = {0, 0, 32, 32}, to_rect = btn.bounds;
location slot = btn_pos(btn.btn);
// Small buttons are half the height, so the icon is also half the height
if(btn.type != BTN_LG) icon_rect.bottom /= 2;
// buttons.png consists of 1 row of 38x38 buttons, two rows of 32x32 icons, and one row of 32x16 icons.
icon_rect.offset(32 * slot.x, 38 + 32 * slot.y);
rect_draw_some_item(buttons_gworld, btn_src_rects[btn.type], cache(), to_rect);
to_rect.inset(3,3);
// Insetting the small rect overcompensates, so correct for that.
if(btn.type != BTN_LG) to_rect.bottom += 3;
rect_draw_some_item(buttons_gworld, icon_rect, cache(), to_rect, sf::BlendAlpha);
}
cache().display();
}
void cToolbar::draw(sf::RenderTarget& targ) {
init();
// Determine actual rect
rectangle to = win_to_rects[WINRECT_ACTBTNS];
to.width() = total_rect.width();
// Prepare background for additive blending
fill_rect(targ, to, sf::Color::Black);
if(active >= 0 && active < toolbar.size()) {
rectangle press_rect = toolbar[active].bounds;
press_rect.offset(win_to_rects[WINRECT_ACTBTNS].topLeft());
fill_rect(targ, press_rect, sf::Color::Blue);
}
// Add the cached toolbar over the background
rect_draw_some_item(cache().getTexture(), rectangle(cache()), targ, to, sf::BlendAdd);
}