/*
This file is part of Minitrem.
Minitrem is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Minitrem is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Minitrem. If not, see .
*/
#include "game.hpp"
#include
namespace interface {
using namespace game; // FIXME
state_t::state_t(sf::RenderWindow *window_, game::state_t *game_)
{
window = window_;
game = game_;
}
static sf::Vector2f compute_pan(sf::RenderWindow *window, sf::Vector2f pan_ref)
{
sf::Vector2i mouse = sf::Mouse::getPosition(*window);
sf::Vector2f vmouse = window->mapPixelToCoords(mouse);
return -(vmouse - pan_ref);
}
void state_t::start_following(void)
{
camera.following = true;
camera.zero_point_s = camera.center;
camera.center = v2f_t(0, 0);
print(text::get(text::FOLLOWING_ON));
}
void state_t::stop_following(void)
{
camera.following = false;
camera.center += camera.zero_point_s;
camera.zero_point_s = v2f_t(0, 0);
print(text::get(text::FOLLOWING_OFF));
}
void pie_menu_t::open(v2f_t wmouse, v2f_t mouse)
{
size_t layer = 0;
float base_radius = 50.0f; // FIXME
for (size_t i = 0; i < items.size(); ) {
size_t left, layer_cap, on_layer;
left = items.size() - i;
layer_cap = (layer + 1) * (layer + 1);
on_layer = std::min(left, layer_cap);
for (size_t j = 0; j < on_layer; j++) {
pie_item_t *item = &items[i + j];
float dt = 2 * M_PI / on_layer;
item->r0 = layer * base_radius;
item->r1 = (layer + 1) * base_radius;
item->t0 = j * dt;
item->t1 = (j + 1) * dt;
}
i += on_layer;
layer++;
}
is_open = true;
x_world = wmouse;
x = mouse;
}
void pie_menu_t::close(game::state_t *game)
{
if (!is_open)
return;
if (selected)
game->command(x_world, selected->action);
is_open = false;
items.clear();
}
void pie_menu_t::update(v2f_t mouse)
{
v2f_t delta;
float r, t;
delta = mouse - x;
r = delta.len();
t = atan2(delta[1], delta[0]);
if (t < 0)
t += 2 * M_PI;
selected = nullptr;
for (pie_item_t &i : items) {
if (r >= i.r0 && r <= i.r1 && t >= i.t0 && t <= i.t1) {
selected = &i;
break;
}
}
}
static int get_selection_type(void)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LControl))
return game::SELECT_OR;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift))
return game::SELECT_XOR;
return game::SELECT_NEW;
}
void state_t::tick(double dt)
{
vec_t window_size;
sf::Event event;
v2f_t view_size;
v2f_t follow_center(0, 0), view_center, pan_delta = v2f_t(0, 0);
float view_scale;
sf::Vector2i mouse;
v2f_t wmouse; // Mouse position in world space;
window_size = window->getSize();
expfade(&camera.zoom_s, camera.zoom, 15, dt);
view_scale = 4.5 * exp(camera.zoom_s * 0.12);
if (window_size[0] < window_size[1]) {
view_size[0] = view_scale * window_size[0] / window_size[1];
view_size[1] = view_scale;
} else {
view_size[0] = view_scale;
view_size[1] = view_scale * window_size[1] / window_size[0];
}
if (!game->selected_units.size() && camera.following)
stop_following();
if (camera.following) {
float limit;
for (entity_t *ent : game->selected_units)
follow_center += ent->render_bounds.center();
follow_center /= game->selected_units.size();
expfade(&camera.zero_point_s, follow_center, 15, dt);
limit = view_scale * 0.2f;
if (camera.center.len() > limit * 2.0f)
stop_following();
else if (camera.center.len() > limit)
expfade(&camera.center, camera.center.norm() * limit, 3, dt);
}
view_center = camera.zero_point_s + camera.center;
window->setView(sf::View(view_center, view_size));
if (camera.panning) {
pan_delta = compute_pan(window, camera.pan_ref);
view_center += compute_pan(window, camera.pan_ref);
window->setView(sf::View(view_center, view_size));
}
camera_3d[0] = view_center[0];
camera_3d[1] = view_center[1];
camera_3d[2] = view_scale;
mouse = sf::Mouse::getPosition(*window);
wmouse = window->mapPixelToCoords(sf::Mouse::getPosition(*window));
while (window->pollEvent(event)) {
// FIXME: refactor this nested switch clusterfuck
switch (event.type) {
case sf::Event::Closed:
window->close();
return;
case sf::Event::MouseButtonPressed:
switch (event.mouseButton.button) {
case sf::Mouse::Button::Left:
select.selecting = true;
select.rect[0] = wmouse;
select.rect[1] = wmouse;
break;
case sf::Mouse::Button::Right:
if (game->populate_pie_menu(pie_menu.items))
pie_menu.open(wmouse, mouse);
break;
case sf::Mouse::Button::Middle:
camera.panning = true;
camera.pan_ref = wmouse;
break;
default:;
}
break;
case sf::Event::MouseButtonReleased:
switch (event.mouseButton.button) {
case sf::Mouse::Button::Left:
if (select.selecting)
game->select(select.rect, select.type);
select.selecting = false;
break;
case sf::Mouse::Button::Right:
pie_menu.close(game);
break;
case sf::Mouse::Button::Middle:
if (camera.panning)
camera.center += pan_delta;
camera.panning = false;
break;
default:;
}
break;
case sf::Event::MouseWheelScrolled:
camera.zoom -= event.mouseWheelScroll.delta;
if (camera.zoom < 0)
camera.zoom = 0;
if (camera.zoom > 17)
camera.zoom = 17;
break;
case sf::Event::KeyPressed:
switch (event.key.code) {
case sf::Keyboard::Key::Space:
if (!game->paused) {
game->pause();
print(text::get(text::PAUSED));
} else {
game->resume();
print(text::get(text::UNPAUSED));
}
break;
case sf::Keyboard::Key::F:
if (camera.following)
stop_following();
else
start_following();
break;
case sf::Keyboard::Key::F1:
debug_draw_cmodels ^= 1;
print("debug_draw_cmodels = " + std::to_string(debug_draw_cmodels));
break;
case sf::Keyboard::Key::F2:
debug_draw_paths ^= 1;
print("debug_draw_paths = " + std::to_string(debug_draw_paths));
break;
case sf::Keyboard::Key::F3:
debug_draw_tile_coords ^= 1;
print("debug_draw_tile_coords = " + std::to_string(debug_draw_tile_coords));
break;
case sf::Keyboard::Key::F4:
debug_AI ^= 1;
print("debug_AI = " + std::to_string(debug_AI));
break;
default:;
}
default:;
}
}
if (select.selecting) {
select.type = get_selection_type();
select.rect[1] = wmouse;
}
pie_menu.update(mouse);
}
void state_t::print(std::string str)
{
log.push_back((log_entry_t){game->now, str});
std::cout << str << std::endl;
}
void pie_menu_t::render_to(render::state_t *render)
{
if (!is_open)
return;
for (pie_item_t &i : items) {
sf::Color color;
v2f_t rad, center;
if (&i == selected)
color = sf::Color(255, 255, 255, 120);
else
color = sf::Color(255, 255, 255, 40);
render->render_ring_sect(x, i.r0, i.r1, i.t0, i.t1, color);
color.a = 255;
if (i.r0 == 0.0f) {
center = x;
} else {
center = x + (i.r1 + i.r0) / 2 *
v2f_t::rad((i.t1 + i.t0) / 2);
}
render->render_text(center, 15, i.label, render::text_align_t::ALIGN_CENTER_BOTTOM, color);
}
}
void state_t::render_to(render::state_t *render)
{
size_t w = window->getSize().x, h = window->getSize().y;
v2f_t x;
std::stringstream ss;
if (select.selecting) {
sf::Color color;
switch (select.type) {
case SELECT_NEW:
color = sf::Color(60, 60, 150);
break;
case SELECT_OR:
color = sf::Color(60, 150, 60);
break;
case SELECT_XOR:
color = sf::Color(150, 150, 60);
break;
}
render->render_hlrect(select.rect, color);
}
window->setView(sf::View(sf::FloatRect(0, 0, w, h)));
em = std::max(w, h) * 0.017;
pie_menu.render_to(render);
for (auto i = log.begin(); i != log.end(); ) {
if (i->time + 3 < game->now)
i = log.erase(i);
else
i++;
}
x = v2f_t(0.0f, 0.0f);
for (log_entry_t &entry : log) {
render->render_text(x, em, entry.text, render::ALIGN_LEFT_TOP, sf::Color::White);
x[1] += em;
}
x = v2f_t(0.0f, h - em * 1.5);
ss << std::fixed << std::setprecision(3);
ss << "Game t/F/B: " << game->time * 1.0e-9 << "/" << game->frames << "/" << game->frames_behind;
render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White);
x[1] -= em;
ss.str(std::string());
ss << std::fixed << std::setprecision(1);
ss << "FPS: " << fc_render.freq_ma() << ", " << fc_game.freq_ma() << " Hz";
render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White);
x[1] -= em;
ss.str(std::string());
ss << "View S/T/E: ";
ss << render->stats.sectors << "/";
ss << render->stats.tiles << "/";
ss << render->stats.entities;
render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White);
x[1] -= em;
ss.str(std::string());
ss << "Awake: " << game->awake_entities.size() << "/" << game->world.stats.entities;
render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White);
x[1] -= em;
ss.str(std::string());
ss << "World S/T:";
ss << game->world.stats.sectors << "/";
ss << game->world.stats.tiles;
#ifdef CONFIG_SHOW_RSS
ss << ", " << sys_get_rss() / 1000000.0f;
ss << "MB RSS";
#endif
render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White);
}
} // namespace interface