chainreaction/board_widget.cpp

242 lines
7.2 KiB
C++

#include <SDL2/SDL_assert.h>
#include <SDL2/SDL_events.h>
#include <basic_widgets/core/draw.hpp>
#include <basic_widgets/core/renderer.hpp>
#include <basic_widgets/core/texture.hpp>
#include "board_widget.hpp"
#include "game.hpp"
using namespace bwidgets;
using namespace game;
using namespace game::data;
ui::BoardWidget::BoardWidget(Widget* p)
: Widget(p)
{
_click_area = &_widget_area;
click_handler =
[this]
(const SDL_MouseButtonEvent& p) -> void
{
if (board != nullptr)
{
auto coord = new SDL_Point(this->board_coord_from_input({p.x, p.y}));
SDL_Event play_ev;
SDL_zero(play_ev);
play_ev.type = this->event_type;
play_ev.user.code = (Sint32)BoardWidget::event_code::PLAY;
play_ev.user.data1 = this;
play_ev.user.data2 = coord;
SDL_PushEvent(&play_ev);
}
};
}
ui::BoardWidget::~BoardWidget()
{
delete _circle_blue;
delete _circle_red;
}
SDL_Point ui::BoardWidget::board_coord_from_input(const SDL_Point& p) const
{
if (board == nullptr)
return {-1, -1};
return {
(p.x - _viewport.x - _widget_area.x) / (_widget_area.w / board->width),
(p.y - _viewport.y - _widget_area.y) / (_widget_area.h / board->height)
};
}
void ui::BoardWidget::_handle_rendering()
{
if (!_circle_blue || !_circle_red)
_handle_texture_update();
_renderer->draw_color(color_bg)->fill_rect(_widget_area);
_render_empty_board();
_render_square_content();
}
void ui::BoardWidget::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
_widget_area = {0, 0, vp.w, vp.h};
auto smallest_size {
_widget_area.w < _widget_area.h
? _widget_area.w
: _widget_area.h
};
_border_area = {
_widget_area.x,
_widget_area.y,
smallest_size / _border_ratio,
smallest_size / _border_ratio
};
_widget_area = {
_border_area.x + _border_area.w,
_border_area.y + _border_area.w,
_widget_area.w - 2 * (_border_area.w),
_widget_area.h - 2 * (_border_area.w)
};
}
void ui::BoardWidget::_draw_atoms(const SDL_Rect& area, int atoms, core::Texture* texture)
{
const auto radius = area.w < area.h ? area.w / 8 : area.h / 8;
const auto diameter = radius * 2;
const SDL_Point center {area.w / 2 - radius, area.h / 2 - radius};
const SDL_Point diagonal_radius {
(int)std::sqrt(radius * radius / 2),
(int)std::sqrt(radius * radius / 2),
};
const int disabled = -1;
SDL_Point render_origins[4] {
{
atoms == 1 || atoms == 3
? center.x
: atoms == 2 || atoms == 4
? center.x + diagonal_radius.x
: disabled,
atoms == 1 || atoms == 3
? center.y
: atoms == 2 || atoms == 4
? center.y + diagonal_radius.y
: disabled
},
{
atoms >= 2 && atoms <= 4
? center.x - diagonal_radius.x
: disabled,
atoms >= 2 && atoms <= 4
? center.y - diagonal_radius.y
: disabled,
},
{
atoms == 3
? center.x + diagonal_radius.x
: atoms == 4
? center.x + diagonal_radius.x
: disabled,
atoms == 3
? center.y + diagonal_radius.y
: atoms == 4
? center.y - diagonal_radius.y
: disabled
},
{
atoms == 4
? center.x - diagonal_radius.x
: disabled,
atoms == 4
? center.y + diagonal_radius.y
: disabled
}
};
for (auto o : render_origins)
{
const int size {o.x == disabled ? 0 : diameter};
_renderer->copy(texture, NULL, {area.x + o.x, area.y + o.y, size, size});
}
}
void ui::BoardWidget::_handle_renderer_change(core::Renderer*)
{
core::OpaqueStruct<core::Texture>::discard(_circle_blue);
core::OpaqueStruct<core::Texture>::discard(_circle_red);
}
void ui::BoardWidget::_render_empty_board()
{
_renderer->draw_color(color_outline);
if (board)
{
for (int col = 0; col <= board->width; col++)
{
int x = col * _widget_area.w / board->width;
_renderer->draw_line({_widget_area.x + x, _widget_area.y},
{_widget_area.x + x, _widget_area.y + _widget_area.h});
}
for (int row = 0; row <= board->height; row++)
{
int y = row * _widget_area.h / board->height;
_renderer->draw_line({_widget_area.x, _widget_area.y + y},
{_widget_area.x + _widget_area.w, _widget_area.y + y});
}
}
for (int xy = 0; xy < _border_area.w; xy++)
{
const float color_factor = 1 - (float)xy / _border_area.w / 3;
_renderer->draw_color({
(uint8_t)(color_outline.r * color_factor),
(uint8_t)(color_outline.g * color_factor),
(uint8_t)(color_outline.b * color_factor),
color_outline.a})->draw_rect({
_border_area.x + xy,
_border_area.y + xy,
_widget_area.w + 2 * (_widget_area.x - _border_area.x - xy) + 1,
_widget_area.h + 2 * (_widget_area.y - _border_area.y - xy) + 1
});
}
}
void ui::BoardWidget::_render_square_content()
{
if (!board)
return;
for (int y = 0; y < board->height; y++)
{
for (int x = 0; x < board->width; x++)
{
SDL_Rect sqr_area {
_widget_area.x
+ x * _widget_area.w / board->width + 1,
_widget_area.y
+ y * _widget_area.h / board->height + 1,
_widget_area.w / board->width - 2,
_widget_area.h / board->height - 2
};
const auto& sqr = Board::square(*board, {x, y});
auto capacity = logic::square_capacity(*board, {x, y});
if (sqr.value > capacity)
{
_renderer->draw_color(color_hl1)->fill_rect(sqr_area);
}
else if (sqr.value == capacity)
{
_renderer->draw_color(color_hl2)->fill_rect(sqr_area);
}
_draw_atoms(sqr_area, sqr.value,
sqr.owner == game::data::Player::P1
? _circle_blue
: _circle_red);
}
}
}
void ui::BoardWidget::_handle_texture_update()
{
SDL_assert_release(!_circle_blue && !_circle_red);
if (!board)
return;
auto circle_size = _widget_area.w > _widget_area.h
? _widget_area.h / board->height / 4
: _widget_area.w / board->width / 4;
auto aa = circle_size > 16 ? 3 : 0;
_circle_blue = core::filled_circle({0, 0, 255, 255}, circle_size, _renderer, aa);
_circle_red = core::filled_circle({255, 0, 0, 255}, circle_size, _renderer, aa);
}