242 lines
7.2 KiB
C++
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);
|
|
}
|