mirror of
https://gitlab.com/manzerbredes/ochess.git
synced 2025-04-07 02:26:29 +02:00
396 lines
12 KiB
C++
396 lines
12 KiB
C++
#include "BoardCanvas.hpp"
|
|
|
|
wxDEFINE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent);
|
|
|
|
BoardCanvas::BoardCanvas(wxFrame *parent)
|
|
: wxPanel(parent), black_side(false), is_black_turn(true), frozen(false),
|
|
lock_square_size(false), t(new Theme()), t_captures(new Theme()) {
|
|
board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR";
|
|
is_dragging = false;
|
|
valid_drag = false;
|
|
adata.reuseBuffer=false;
|
|
adata.buffer=nullptr;
|
|
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
|
|
SetClockTime(-1, -1, -1, false);
|
|
SetClockTime(-1, -1, -1, true);
|
|
ApplyPreferences();
|
|
// The following should be called when using an EVT_PAINT handler
|
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
adata.duration=200;
|
|
adata.duration_fast=80;
|
|
adata.fps=30;
|
|
|
|
Bind(wxEVT_KEY_DOWN, [p=this](wxKeyEvent &e){e.ResumePropagation(1);e.Skip();});
|
|
Bind(wxEVT_KEY_UP, [p=this](wxKeyEvent &e){e.ResumePropagation(1);e.Skip();});
|
|
}
|
|
|
|
BoardCanvas::~BoardCanvas() {
|
|
delete t;
|
|
delete t_captures;
|
|
}
|
|
|
|
BoardCanvas::BoardCanvas(wxFrame *parent, std::uint32_t square_width,
|
|
bool frozen)
|
|
: BoardCanvas(parent) {
|
|
t->ResizeSquaresAndPieces(square_width);
|
|
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
|
|
this->frozen = true;
|
|
lock_square_size = true;
|
|
}
|
|
|
|
void BoardCanvas::OnPaint(wxPaintEvent &event) {
|
|
wxBufferedPaintDC dc(this);
|
|
dc.SetBackground(*wxWHITE_BRUSH);
|
|
dc.Clear();
|
|
|
|
if(!adata.reuseBuffer){
|
|
// Setting up required attributes
|
|
REFRESH_MOUSE_LOCATION();
|
|
square_width = t->GetSquaresSizes();
|
|
canvas_size = this->GetSize();
|
|
boardX = (canvas_size.x - (8 * square_width)) / 2;
|
|
boardY = (canvas_size.y - (8 * square_width)) / 2;
|
|
if (boardX > canvas_size.x)
|
|
boardX = 0;
|
|
if (boardY > canvas_size.y)
|
|
boardY = 0;
|
|
|
|
// Setup buffer (later use for animations)
|
|
if(adata.buffer!=nullptr)
|
|
free(adata.buffer);
|
|
adata.buffer=new wxBitmap(canvas_size.x,canvas_size.y,32);
|
|
wxMemoryDC memDC(*adata.buffer);
|
|
DrawBoard(memDC);
|
|
dc.Blit(0,0,canvas_size.x,canvas_size.y,(wxDC*)&memDC,0,0);
|
|
}
|
|
else {
|
|
// Otherwise reuse buffer and animate
|
|
dc.DrawBitmap(*adata.buffer, 0, 0, true);
|
|
double percent=adata.frame/adata.frames;
|
|
// Draw moving piece
|
|
dc.DrawBitmap(*t->Get(adata.piece_moved),
|
|
adata.src.x + adata.frame*(adata.transVect.x/adata.frames),
|
|
adata.src.y + adata.frame*(adata.transVect.y/adata.frames), false);
|
|
// end drawing
|
|
adata.frame++;
|
|
if(adata.frame>=adata.frames){
|
|
adata.reuseBuffer=false;
|
|
SetupBoard(adata.final_board, adata.final_is_black_turn, adata.final_captures);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BoardCanvas::ApplyPreferences() {
|
|
if (t != NULL)
|
|
delete t;
|
|
if (t_captures != NULL)
|
|
delete t_captures;
|
|
t = new Theme();
|
|
t_captures = new Theme();
|
|
|
|
CONFIG_OPEN(config);
|
|
black_side = config->Read("board/black_by_default", false);
|
|
if (lock_square_size) {
|
|
t->ResizeSquaresAndPieces(square_width);
|
|
} else {
|
|
t->ResizeSquaresAndPieces(config->Read("board/square_size", 80));
|
|
}
|
|
t->SetSquareRadius(config->Read("board/corner_radius", 10));
|
|
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
|
|
CONFIG_CLOSE(config);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void BoardCanvas::SetupBoard(std::string board, bool is_black_turn,
|
|
std::map<char, std::uint8_t> captures) {
|
|
this->board = board;
|
|
this->is_black_turn = is_black_turn;
|
|
this->captures = captures;
|
|
Refresh();
|
|
}
|
|
|
|
void BoardCanvas::Animate(const std::string &board, bool is_black_turn, std::map<char, std::uint8_t> captures, std::string src, std::string dst,bool faster){
|
|
adata.final_board=board;
|
|
adata.final_is_black_turn=is_black_turn;
|
|
adata.final_captures=captures;
|
|
|
|
std::uint8_t pfile = src[0]-'a';
|
|
std::uint8_t prank = src[1]-'1';
|
|
adata.piece_moved = this->board[pfile + 8 * (7-prank)]; // Piece to move
|
|
|
|
// Now remove the piece that will be moved
|
|
this->board[pfile + 8 * (7-prank)]=' ';
|
|
wxMemoryDC memDC(*adata.buffer);
|
|
DrawBoard(memDC);
|
|
|
|
// Now compute piece start position and translation vector
|
|
std::uint32_t piece_width = t->GetPiecesSizes();
|
|
std::uint32_t centrer_offset = (square_width - piece_width) / 2;
|
|
if (!black_side) {
|
|
prank = 7 - prank;
|
|
pfile = 7 - pfile;
|
|
}
|
|
std::uint32_t x = boardX + (7 - pfile) * square_width;
|
|
std::uint32_t y = boardY + prank * square_width;
|
|
adata.src.x = x + centrer_offset;
|
|
adata.src.y = y + centrer_offset;
|
|
// Now dst:
|
|
pfile = dst[0]-'a';
|
|
prank = dst[1]-'1';
|
|
if (!black_side) {
|
|
prank = 7 - prank;
|
|
pfile = 7 - pfile;
|
|
}
|
|
x = boardX + (7 - pfile) * square_width;
|
|
y = boardY + prank * square_width;
|
|
adata.transVect.x=x-adata.src.x+centrer_offset;
|
|
adata.transVect.y=y-adata.src.y+centrer_offset;
|
|
|
|
// Start animation:
|
|
adata.reuseBuffer=true;
|
|
int duration_backup=adata.duration;
|
|
adata.duration=faster ? adata.duration_fast : adata.duration;
|
|
int frame_duration=(1000/adata.fps);
|
|
adata.frame=0;
|
|
adata.frames=adata.duration/frame_duration; // total number of frames
|
|
int time_per_frame=adata.duration/adata.frames;
|
|
wxStopWatch sw;
|
|
for(int i=adata.frames;i>0;i--){
|
|
Refresh();
|
|
Update();
|
|
int delay=sw.Time()-frame_duration;
|
|
if(delay>5){ // 5ms tolerance
|
|
wxMilliSleep(delay);
|
|
}
|
|
sw.Start(0);
|
|
}
|
|
adata.duration=faster ? duration_backup : duration_backup;
|
|
adata.reuseBuffer=false;
|
|
}
|
|
|
|
void BoardCanvas::DrawBoard(wxDC &dc) {
|
|
std::uint32_t piece_width = t->GetPiecesSizes();
|
|
std::uint32_t centrer_offset = (square_width - piece_width) / 2;
|
|
wxSize numbers_size=dc.GetTextExtent("0");
|
|
|
|
bool DrawDraggingPiece = false;
|
|
char dp = 'p';
|
|
std::uint32_t dpx = 0, dpy = 0;
|
|
for (std::int8_t file = 7; file >= 0; file--) {
|
|
for (std::uint8_t rank = 0; rank <= 7; rank++) {
|
|
std::uint32_t x = boardX + (7 - file) * square_width;
|
|
std::uint32_t y = boardY + rank * square_width;
|
|
if ((file + rank) % 2 == 0) {
|
|
if (file == 0 && rank == 0) {
|
|
dc.DrawBitmap(*t->Get('1'), x, y, true);
|
|
} else if (file == 7 && rank == 7) {
|
|
dc.DrawBitmap(*t->Get('2'), x, y, true);
|
|
} else {
|
|
dc.DrawBitmap(*t->Get('s'), x, y, true);
|
|
}
|
|
} else {
|
|
if (file == 7 && rank == 0) {
|
|
dc.DrawBitmap(*t->Get('0'), x, y, true);
|
|
} else if (file == 0 && rank == 7) {
|
|
dc.DrawBitmap(*t->Get('3'), x, y, true);
|
|
} else {
|
|
dc.DrawBitmap(*t->Get('S'), x, y, true);
|
|
}
|
|
}
|
|
|
|
// Draw numbers
|
|
dc.SetFont(wxFont(*wxNORMAL_FONT).MakeBold());
|
|
if(file==0){ // Right numbers
|
|
dc.DrawText(wxString((char)('1'+7-rank)),
|
|
x+square_width+numbers_size.x/2,y+square_width/2-numbers_size.y/2);
|
|
}
|
|
if(rank==7){ // Bottom numbers
|
|
dc.DrawText(wxString((char)('a'+7-file)),
|
|
x+square_width/2-numbers_size.x/2,y+square_width);
|
|
}
|
|
|
|
std::uint8_t prank = rank;
|
|
std::uint8_t pfile = file;
|
|
if (black_side) {
|
|
prank = 7 - rank;
|
|
pfile = 7 - file;
|
|
}
|
|
std::uint32_t px = x + centrer_offset;
|
|
std::uint32_t py = y + centrer_offset;
|
|
char piece = board[(7 - pfile) + 8 * prank];
|
|
if (is_dragging && (7 - pfile) == active_square.x &&
|
|
(7 - prank) == active_square.y) {
|
|
dp = piece;
|
|
dpx = px - (lastClickX - mouseX);
|
|
dpy = py - (lastClickY - mouseY);
|
|
DrawDraggingPiece = true;
|
|
continue;
|
|
}
|
|
if (piece != ' ') {
|
|
dc.DrawBitmap(*t->Get(piece), px, py, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw badge
|
|
dc.SetPen(wxPen(*wxBLACK, 3));
|
|
std::uint32_t badgeY = boardY;
|
|
std::uint32_t badgeWidth = square_width / 2;
|
|
if (is_black_turn) {
|
|
dc.SetBrush(*wxBLACK_BRUSH);
|
|
if (black_side) {
|
|
badgeY = boardY + (8 * square_width) - badgeWidth;
|
|
}
|
|
} else {
|
|
dc.SetBrush(*wxWHITE_BRUSH);
|
|
if (!black_side) {
|
|
badgeY = boardY + (8 * square_width) - badgeWidth;
|
|
}
|
|
}
|
|
wxRect badge(boardX + (8 * square_width) + badgeWidth, badgeY, badgeWidth,
|
|
badgeWidth);
|
|
dc.DrawRectangle(badge);
|
|
|
|
// Draw captures first for white then for black
|
|
std::uint32_t captures_size = t_captures->GetPiecesSizes();
|
|
std::uint8_t padding = numbers_size.y+10;
|
|
std::uint32_t offsetX = 0;
|
|
std::uint32_t offsetY = -(captures_size + padding);
|
|
if (black_side) {
|
|
offsetY = 8 * square_width + padding;
|
|
}
|
|
for (char p : {'P', 'N', 'B', 'R', 'Q'}) {
|
|
if (captures.find(p) != captures.end()) {
|
|
for (std::uint8_t i = 0; i < captures[p]; i++) {
|
|
dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY);
|
|
offsetX += captures_size / 2;
|
|
}
|
|
offsetX += captures_size / 2;
|
|
}
|
|
}
|
|
offsetX = 0;
|
|
if (black_side) {
|
|
offsetY = -(captures_size + padding);
|
|
} else {
|
|
offsetY = 8 * square_width + padding;
|
|
}
|
|
for (char p : {'p', 'n', 'b', 'r', 'q'}) {
|
|
if (captures.find(p) != captures.end()) {
|
|
for (std::uint8_t i = 0; i < captures[p]; i++) {
|
|
dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY);
|
|
offsetX += captures_size / 2;
|
|
}
|
|
offsetX += captures_size / 2;
|
|
}
|
|
}
|
|
|
|
// Draw dragging piece
|
|
if (DrawDraggingPiece) {
|
|
dc.DrawBitmap(*t->Get(dp), dpx, dpy, false);
|
|
}
|
|
|
|
// Draw numbers
|
|
for (char l = 'a'; l < 'a' + 8; l++) {
|
|
dc.DrawText(wxString(l), wxPoint(boardX + l - 'a' * square_width,
|
|
boardY + 8 * square_width + 10));
|
|
}
|
|
|
|
// Draw Clocks
|
|
if (std::get<0>(black_time) >= 0) {
|
|
wxFont font = dc.GetFont();
|
|
ClockTime clock = black_side ? white_time : black_time;
|
|
wxString time =
|
|
wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock));
|
|
if (std::get<0>(clock) > 0) {
|
|
time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock));
|
|
} else if (std::get<1>(clock) > 0) {
|
|
time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock));
|
|
}
|
|
dc.SetFont(font.Scale(1.5).MakeBold());
|
|
wxCoord width, height;
|
|
dc.GetTextExtent(time, &width, &height);
|
|
dc.DrawText(time,
|
|
wxPoint(boardX + square_width * 8 - width, boardY - height - numbers_size.y*2));
|
|
clock = black_side ? black_time : white_time;
|
|
time = wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock));
|
|
if (std::get<0>(clock) > 0) {
|
|
time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock));
|
|
} else if (std::get<1>(clock) > 0) {
|
|
time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock));
|
|
}
|
|
dc.GetTextExtent(time, &width, &height);
|
|
dc.DrawText(time, wxPoint(boardX + square_width * 8 - width,
|
|
boardY + square_width * 8 + numbers_size.y*2));
|
|
}
|
|
}
|
|
|
|
void BoardCanvas::MouseEvent(wxMouseEvent &event) {
|
|
if (!frozen) {
|
|
if (event.Dragging()) {
|
|
if (valid_drag) {
|
|
is_dragging = true;
|
|
Refresh();
|
|
}
|
|
} else {
|
|
if (is_dragging) {
|
|
is_dragging = false;
|
|
valid_drag = false;
|
|
// Handle drop
|
|
REFRESH_MOUSE_LOCATION();
|
|
INIT_CURRENT_SQUARE();
|
|
|
|
if (IsCurrentSquareValid) {
|
|
wxLogDebug("Drag end on square (%d,%d)", file, rank);
|
|
/// Play the move
|
|
wxCommandEvent event(PLAY_MOVE_EVENT, GetId());
|
|
event.SetEventObject(this);
|
|
std::string move = ((char)('a' + active_square.x)) +
|
|
std::to_string(+active_square.y + 1) +
|
|
((char)('a' + file)) + std::to_string(rank + 1);
|
|
event.SetString(move);
|
|
ProcessEvent(event);
|
|
}
|
|
}
|
|
if (event.LeftDown()) {
|
|
SetFocus();
|
|
REFRESH_MOUSE_LOCATION();
|
|
lastClickX = mouseX;
|
|
lastClickY = mouseY;
|
|
INIT_CURRENT_SQUARE();
|
|
if (IsCurrentSquareValid) {
|
|
active_square.x = file;
|
|
active_square.y = rank;
|
|
if (board[(7 - rank) * 8 + file] != ' ') {
|
|
wxLogDebug("Drag start on square (%d,%d)", file, rank);
|
|
valid_drag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BoardCanvas::Zoom(std::int32_t zoom) {
|
|
t->Zoom(zoom);
|
|
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
|
|
Refresh();
|
|
}
|
|
|
|
void BoardCanvas::Swap() {
|
|
black_side = !black_side;
|
|
Refresh();
|
|
}
|
|
|
|
void BoardCanvas::SetClockTime(short hours, short min, short sec,
|
|
bool IsBlack) {
|
|
if (IsBlack) {
|
|
black_time = std::make_tuple(hours, min, sec);
|
|
} else {
|
|
white_time = std::make_tuple(hours, min, sec);
|
|
}
|
|
}
|
|
|
|
wxBEGIN_EVENT_TABLE(BoardCanvas, wxPanel) EVT_PAINT(BoardCanvas::OnPaint)
|
|
EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent)
|
|
wxEND_EVENT_TABLE()
|