Improved rendering system: fixed bugs, added min size limits, moved all drawing-related methods to Drawer class

This commit is contained in:
Dmitriy Shishkov 2022-05-28 16:32:08 +03:00
parent 43c90fc63b
commit d633f9751d
No known key found for this signature in database
GPG Key ID: 26720CB2A9608C97
9 changed files with 418 additions and 271 deletions

View File

@ -1,38 +1,15 @@
#include "Controller.h"
Controller::Controller(Drawer& drawer): drawer(drawer) {};
void Controller::resize(const wxSize& tableSize) {
wxSize& resolution = drawer.resolution;
Dimensions& gridSize = drawer.gridSize;
wxRect& tablePixelRect = drawer.tablePixelRect;
resolution = tableSize;
if (stopwatch >= 0) {
int gridPoint = mmin(resolution.x / (gridSize.x * TILE_WIDTH),
resolution.y / (gridSize.y * TILE_HEIGHT));
if (gridPoint > 2) {
tablePixelRect.SetSize({gridPoint * TILE_WIDTH * gridSize.x, gridPoint * TILE_HEIGHT * gridSize.y});
drawer.tilePixelSize.Set(gridPoint * TILE_WIDTH, gridPoint * TILE_HEIGHT);
}
tablePixelRect.SetPosition({(resolution.x - tablePixelRect.width) / 2,
(resolution.y - tablePixelRect.height) / 2});
}
drawer.setBG(tableSize);
drawer.initScreen(table);
}
Controller::Controller(Drawer& drawer) : drawer(drawer){};
void Controller::loadLayout(const wxString& path) {
layout.openFile(path);
drawer.gridSize = layout.getDimensions();
gridSize = layout.getDimensions();
table = TLVec(drawer.gridSize.z, vector<vector<CardT>>(drawer.gridSize.x, vector<CardT>(drawer.gridSize.y, EMPTY)));
table = TLVec(
gridSize.z,
vector<vector<CardT>>(gridSize.x, vector<CardT>(gridSize.y, EMPTY)));
layout.readLayout(table);
@ -60,8 +37,6 @@ void Controller::fill(bool solveable) {
void Controller::fillSolveableTable() {
time_t start_time = time(NULL);
auto& gridSize = drawer.gridSize;
std::list<ThreePoint> positions;
int overall = gridSize.z * gridSize.x * gridSize.y;
@ -87,16 +62,19 @@ void Controller::fillSolveableTable() {
past_pos = emplace_rand(id, positions, past_pos, past_ptr);
cardsCounter[id]--;
} else
} else
emplace_rand(id, positions, past_pos, past_ptr);
}
wxLogInfo(wxString::Format("Filling took %i seconds", start_time - time(NULL)));
wxLogInfo(
wxString::Format("Filling took %i seconds", start_time - time(NULL)));
}
int Controller::emplace_rand(int id, std::list<ThreePoint> positions, int past_pos, std::list<ThreePoint>::iterator past_ptr) {
int Controller::emplace_rand(int id, std::list<ThreePoint> positions,
int past_pos,
std::list<ThreePoint>::iterator past_ptr) {
int d = rand() % positions.size() - past_pos;
if (d > 0)
for (int i = 0; i < d; i++)
past_ptr++;
@ -119,9 +97,9 @@ int Controller::emplace_rand(int id, std::list<ThreePoint> positions, int past_p
}
void Controller::free_table() {
for (int z = 0; z < drawer.gridSize.z; z++)
for (int x = 0; x < drawer.gridSize.x; x++)
for (int y = 0; y < drawer.gridSize.y; y++) {
for (int z = 0; z < gridSize.z; z++)
for (int x = 0; x < gridSize.x; x++)
for (int y = 0; y < gridSize.y; y++) {
CardT id = table[z][x][y];
if (id >= 0) {
@ -136,14 +114,14 @@ void Controller::free_table() {
void Controller::fillRandom() {
srand(time(NULL));
wxLogDebug(wxString::Format("%i", remaining));
auto not_end = remaining;
for (int z = 0; z < drawer.gridSize.z && not_end; z++)
for (int x = 0; x < drawer.gridSize.x && not_end; x++)
for (int y = 0; y < drawer.gridSize.y && not_end; y++)
for (int z = 0; z < gridSize.z && not_end; z++)
for (int x = 0; x < gridSize.x && not_end; x++)
for (int y = 0; y < gridSize.y && not_end; y++)
if (table[z][x][y] == FREE) {
table[z][x][y] = genRandId();
not_end--;
@ -166,8 +144,8 @@ CardT Controller::genRandId() {
}
/**
* It also changes point to top right coordinate of card
*/
* It also changes point to top right coordinate of card
*/
CardT* Controller::getCardByPosition(ThreePoint& point) {
int8_t topIndex = -1;
CardT* res = nullptr;
@ -185,10 +163,10 @@ CardT* Controller::getCardByPosition(ThreePoint& point) {
if (point.x > 0)
for (int z = table.size() - 1; z >= 0; z--)
if (table[z][point.x-1][point.y] >= 0) {
if (table[z][point.x - 1][point.y] >= 0) {
if (z > topIndex) {
topIndex = z;
res = &table[z][point.x-1][point.y];
res = &table[z][point.x - 1][point.y];
realPos.x = point.x - 1;
realPos.y = point.y;
@ -198,10 +176,10 @@ CardT* Controller::getCardByPosition(ThreePoint& point) {
if (point.y > 0)
for (int z = table.size() - 1; z >= 0; z--)
if (table[z][point.x][point.y-1] >= 0) {
if (table[z][point.x][point.y - 1] >= 0) {
if (z > topIndex) {
topIndex = z;
res = &table[z][point.x][point.y-1];
res = &table[z][point.x][point.y - 1];
realPos.x = point.x;
realPos.y = point.y - 1;
@ -211,10 +189,10 @@ CardT* Controller::getCardByPosition(ThreePoint& point) {
if (point.x > 0 && point.y > 0)
for (int z = table.size() - 1; z >= 0; z--)
if (table[z][point.x-1][point.y-1] >= 0) {
if (table[z][point.x - 1][point.y - 1] >= 0) {
if (z > topIndex) {
topIndex = z;
res = &table[z][point.x-1][point.y-1];
res = &table[z][point.x - 1][point.y - 1];
realPos.x = point.x - 1;
realPos.y = point.y - 1;
@ -229,45 +207,50 @@ CardT* Controller::getCardByPosition(ThreePoint& point) {
return res;
}
bool Controller::available(const ThreePoint& point) {
bool Controller::available(const ThreePoint& point) const {
return upFree(point) && sideFree(point);
}
bool Controller::upFree(const ThreePoint& point) {
bool Controller::upFree(const ThreePoint& point) const {
if (point.z == table.size() - 1)
return true;
return !(
(table[point.z + 1][point.x][point.y] >= 0) ||
(point.x > 0 && table[point.z + 1][point.x - 1][point.y] >= 0) ||
(point.y > 0 && table[point.z + 1][point.x][point.y - 1] >= 0) ||
(point.x > 0 && point.y > 0 && table[point.z + 1][point.x - 1][point.y - 1] >= 0) ||
(point.x < table[point.z].size() - 1 && table[point.z + 1][point.x + 1][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 && table[point.z + 1][point.x][point.y + 1] >= 0) ||
(point.x < table[point.z].size() - 1 && point.y < table[point.z][point.x].size() - 1 && table[point.z + 1][point.x + 1][point.y + 1] >= 0) ||
(point.x > 0 && point.y < table[point.z][point.x].size() - 1 && table[point.z + 1][point.x - 1][point.y + 1] >= 0) ||
(point.x < table[point.z].size() - 1 && point.y > 0 && table[point.z + 1][point.x + 1][point.y - 1] >= 0)
);
return !((table[point.z + 1][point.x][point.y] >= 0) ||
(point.x > 0 && table[point.z + 1][point.x - 1][point.y] >= 0) ||
(point.y > 0 && table[point.z + 1][point.x][point.y - 1] >= 0) ||
(point.x > 0 && point.y > 0 &&
table[point.z + 1][point.x - 1][point.y - 1] >= 0) ||
(point.x < table[point.z].size() - 1 &&
table[point.z + 1][point.x + 1][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 &&
table[point.z + 1][point.x][point.y + 1] >= 0) ||
(point.x < table[point.z].size() - 1 &&
point.y < table[point.z][point.x].size() - 1 &&
table[point.z + 1][point.x + 1][point.y + 1] >= 0) ||
(point.x > 0 && point.y < table[point.z][point.x].size() - 1 &&
table[point.z + 1][point.x - 1][point.y + 1] >= 0) ||
(point.x < table[point.z].size() - 1 && point.y > 0 &&
table[point.z + 1][point.x + 1][point.y - 1] >= 0));
}
bool Controller::sideFree(const ThreePoint& point) {
bool Controller::sideFree(const ThreePoint& point) const {
bool lfree = true;
bool rfree = true;
if (point.x > 1)
lfree = !(
(point.y > 0 && table[point.z][point.x-2][point.y-1] >= 0) ||
(table[point.z][point.x-2][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 && table[point.z][point.x-2][point.y+1] >= 0)
);
if (point.x > 1)
lfree =
!((point.y > 0 && table[point.z][point.x - 2][point.y - 1] >= 0) ||
(table[point.z][point.x - 2][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 &&
table[point.z][point.x - 2][point.y + 1] >= 0));
if (point.x < table[point.z].size() - 2)
rfree = !(
(point.y > 0 && table[point.z][point.x+2][point.y-1] >= 0) ||
(table[point.z][point.x+2][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 && table[point.z][point.x+2][point.y+1] >= 0)
);
rfree =
!((point.y > 0 && table[point.z][point.x + 2][point.y - 1] >= 0) ||
(table[point.z][point.x + 2][point.y] >= 0) ||
(point.y < table[point.z][point.x].size() - 1 &&
table[point.z][point.x + 2][point.y + 1] >= 0));
return lfree || rfree;
}
@ -281,9 +264,11 @@ void Controller::handleClick(const wxPoint& point) {
auto card = getCardByPosition(pos);
if (pos.z >= 0 && available(pos)) {
if (selected != nullptr && sameValues(*card, *selected) && selected != card) {
steps.push({CardEntry{drawer.marked, *selected}, CardEntry{pos, *card}});
if (selected != nullptr && sameValues(*card, *selected) &&
selected != card) {
steps.push({CardEntry{drawer.marked, *selected},
CardEntry{pos, *card}});
*selected = MATCHED;
*card = MATCHED;
@ -296,19 +281,18 @@ void Controller::handleClick(const wxPoint& point) {
selected = card;
drawer.marked = pos;
}
drawer.initScreen(table);
}
}
}
bool Controller::sameValues(CardT a, CardT b) {
if (a == b) return true;
else if (a >= 38 && b >= 38)
bool Controller::sameValues(CardT a, CardT b) const {
if (a == b)
return true;
else if (a >= 38 && b >= 38)
return true;
else if (a >= 34 && a <= 37 && b >= 34 && b <= 37)
return true;
return false;
}
@ -323,3 +307,7 @@ void Controller::undo() {
steps.pop();
}
}
bool Controller::gameStarted() const {
return stopwatch > 0;
}

View File

@ -16,8 +16,6 @@ public:
int stopwatch = -1;
void resize(const wxSize& tableSize);
void loadLayout(const wxString& path);
void handleClick(const wxPoint& point);
@ -31,6 +29,11 @@ public:
uint8_t remaining;
void undo();
bool gameStarted() const;
Dimensions gridSize;
private:
Drawer& drawer;
XmlLayout layout;
@ -40,21 +43,22 @@ private:
CardT* selected = nullptr;
void fillSolveableTable();
void fillRandom();
int emplace_rand(int id, std::list<ThreePoint> positions, int past_pos,
std::list<ThreePoint>::iterator past_ptr);
int emplace_rand(int id, std::list<ThreePoint> positions, int past_pos, std::list<ThreePoint>::iterator past_ptr);
void fillRandom();
CardT genRandId();
CardT* getCardByPosition(ThreePoint& point);
bool available(const ThreePoint& point);
bool upFree(const ThreePoint& point);
bool sideFree(const ThreePoint& point);
bool available(const ThreePoint& point) const;
bool upFree(const ThreePoint& point) const;
bool sideFree(const ThreePoint& point) const;
bool sameValues(CardT a, CardT b);
bool sameValues(CardT a, CardT b) const;
std::array<uint8_t, TILE_IMAGES_N>cardsCounter;
std::array<uint8_t, TILE_IMAGES_N> cardsCounter;
std::stack<std::array<CardEntry, 2>> steps;
};

View File

@ -1,42 +1,166 @@
#include "Drawer.h"
static const char* tileImageNames[] = { "Pin1", "Pin2", "Pin3", "Pin4", "Pin5", "Pin6", "Pin7", "Pin8", "Pin9", "Sou1", "Sou2", "Sou3", "Sou4", "Sou5", "Sou6", "Sou7", "Sou8", "Sou9", "Man1", "Man2", "Man3", "Man4", "Man5", "Man6", "Man7", "Man8", "Man9", "Chun", "Haku", "Hatsu", "Nan", "Pei", "Shaa", "Ton", "Flower1", "Flower2", "Flower3", "Flower4", "Season1", "Season2", "Season3", "Season4" };
static const char* tileImageNames[] = {
// clang-format off
"Pin1", "Pin2", "Pin3", "Pin4", "Pin5", "Pin6", "Pin7", "Pin8", "Pin9",
"Sou1", "Sou2", "Sou3", "Sou4", "Sou5", "Sou6", "Sou7", "Sou8", "Sou9",
"Man1", "Man2", "Man3", "Man4", "Man5", "Man6", "Man7", "Man8", "Man9",
"Chun", "Haku", "Hatsu",
"Nan", "Pei", "Shaa", "Ton",
"Flower1", "Flower2", "Flower3", "Flower4",
"Season1", "Season2", "Season3", "Season4"
// clang-format on
};
Drawer::Drawer(): marked{-1, -1, -1} {
static const wxColor lGreen{0x07, 0x55, 0x2b};
static const wxColor dGreen{0x01, 0x2d, 0x16};
Drawer::Drawer() : marked{-1, -1, -1} {
for (int i = 0; i < TILE_IMAGES_N; i++) {
if (!tileImages[i].LoadFile(_("./resources/tiles/") + _(tileImageNames[i]) + _(".png"), wxBITMAP_TYPE_PNG))
wxLogDebug(_("./resources/tiles/") + _(tileImageNames[i]) + _(".png ") + wxString::Format(wxT("%i"), i));
bool succeed = tileImages[i].LoadFile(
_("./resources/tiles/") + _(tileImageNames[i]) + _(".png"),
wxBITMAP_TYPE_PNG);
if (!succeed)
wxLogDebug(_("failed to load tile ./resources/tiles/") +
_(tileImageNames[i]) + _(".png with index") +
wxString::Format("%i", i));
}
}
void Drawer::drawTable(wxDC& dc) {
if (isScreenReady)
dc.DrawBitmap(screenBitmap, 0, 0, false);
dc.DrawBitmap(bgBitmap, 0, 0, false);
if (boardBitmap.IsOk()) {
wxLogDebug("Drawing board");
dc.DrawBitmap(boardBitmap, tablePixelRect.GetPosition(), true);
}
}
wxBitmap copyBitmap(const wxBitmap& old) {
return old.GetSubBitmap(wxRect(0, 0, old.GetWidth(), old.GetHeight()));
}
void Drawer::composeBG() {
bgBitmap = wxBitmap(resolution);
void Drawer::setBG(const wxSize& tableSize) {
bgBitmap = wxBitmap(tableSize);
wxLogDebug(
wxString::Format("Rebuild bg %i %i", resolution.x, resolution.y));
wxMemoryDC dc;
dc.SelectObject(bgBitmap);
dc.GradientFillConcentric(wxRect(wxPoint(0, 0), tableSize), wxColor(7, 85, 45), wxColor(1, 45, 22));
isBgReady = true;
dc.GradientFillConcentric(wxRect(wxPoint(0, 0), resolution), lGreen,
dGreen);
}
wxPoint Drawer::toGrid(const wxPoint& point) {
void Drawer::composeBoard(const TLVec& layout, const Dimensions& gridSize) {
boardBitmap = wxBitmap(tablePixelRect.GetSize());
wxLogDebug(_("Rebuild board"));
wxMemoryDC dc;
dc.SelectObject(boardBitmap);
for (int z = 0; z < gridSize.z; z++)
for (int x = 0; x < gridSize.x; x++)
for (int y = 0; y < gridSize.y; y++) {
CardT c = layout[z][x][y];
if (c >= 0)
drawTile(dc, c, fromGrid(x, y), z);
}
wxMask* mask = new wxMask(boardBitmap, wxColor(0x00, 0x00, 0x00));
boardBitmap.SetMask(mask);
}
void Drawer::drawTile(wxDC& dc, int8_t index, const wxPoint& position,
uint8_t zIndex) const {
wxBrush _bgColor = dc.GetBrush();
wxBrush front = wxColor(0xff, 0xff, 0xff);
wxBrush back = wxColor(0xc8, 0xc8, 0xc8);
if (position == fromGrid({marked.x, marked.y}) && marked.z == zIndex) {
front = wxColor(0xc8, 0xff, 0xc8);
back = wxColor(0xbe, 0xdc, 0xbe);
}
dc.SetBrush(back);
dc.DrawRoundedRectangle(position.x + (tilePixelSize.GetWidth() / 10 + 3) -
(tilePixelSize.GetWidth() / 10 + 3) * zIndex,
position.y + (tilePixelSize.GetHeight() / 10 + 3) -
(tilePixelSize.GetHeight() / 10 + 3) * zIndex,
tilePixelSize.GetWidth() * 2,
tilePixelSize.GetHeight() * 2, 10);
dc.SetBrush(front);
dc.DrawRoundedRectangle(
position.x - (tilePixelSize.GetWidth() / 10 + 3) * zIndex,
position.y - (tilePixelSize.GetHeight() / 10 + 3) * zIndex,
tilePixelSize.GetWidth() * 2, tilePixelSize.GetHeight() * 2, 10);
dc.SetBrush(_bgColor);
if (tileImages[index].IsOk()) {
wxPoint pos;
pos.x = position.x + 10 - (tilePixelSize.GetWidth() / 10 + 3) * zIndex;
pos.y = position.y + 10 - (tilePixelSize.GetHeight() / 10 + 3) * zIndex;
if (tileImages[index].GetWidth() != tilePixelSize.x * 2)
dc.DrawBitmap(tileImages[index].Scale(tilePixelSize.x * 2 - 20,
tilePixelSize.y * 2 - 20),
pos);
else
dc.DrawBitmap(tileImages[index], pos);
}
}
void Drawer::resizeBg(const wxSize& resolution) {
if (this->resolution != resolution) {
this->resolution = resolution;
composeBG();
}
}
/**
* Resizes tile and whole board bitmap size to the resolution, set in this
* instance
*/
bool Drawer::resizeBoard(const TLVec& layout, const Dimensions& gridSize) {
bool res = false;
const int gridPoint = mmin(resolution.x / (gridSize.x * TILE_WIDTH),
resolution.y / (gridSize.y * TILE_HEIGHT));
wxLogDebug(wxString::Format("Resize board: %i", gridPoint));
if (gridPoint >= MIN_GRID_POINT) {
if (gridPoint != prevGridPoint) {
tablePixelRect.SetSize({gridPoint * TILE_WIDTH * gridSize.x,
gridPoint * TILE_HEIGHT * gridSize.y});
tilePixelSize.Set(gridPoint * TILE_WIDTH, gridPoint * TILE_HEIGHT);
}
tablePixelRect.SetPosition(
{(resolution.x - tablePixelRect.width) / 2,
(resolution.y - tablePixelRect.height) / 2});
if (gridPoint != prevGridPoint) {
composeBoard(layout, gridSize);
res = true;
}
prevGridPoint = gridPoint;
}
return res;
}
wxPoint Drawer::toGrid(const wxPoint& point) const {
wxPoint out(-1, -1);
if (point.x >= tablePixelRect.x &&
point.x <= tablePixelRect.x + tablePixelRect.width &&
point.y >= tablePixelRect.y &&
point.y <= tablePixelRect.y + tablePixelRect.height)
{
point.y <= tablePixelRect.y + tablePixelRect.height) {
out.x = (point.x - tablePixelRect.x) / tilePixelSize.x;
out.y = (point.y - tablePixelRect.y) / tilePixelSize.y;
}
@ -44,61 +168,21 @@ wxPoint Drawer::toGrid(const wxPoint& point) {
return out;
}
wxPoint Drawer::fromGrid(int x, int y) {
return { tablePixelRect.x + x * tilePixelSize.x,
tablePixelRect.y + y * tilePixelSize.y };
wxPoint Drawer::fromGrid(int x, int y) const {
return {x * tilePixelSize.x, y * tilePixelSize.y};
}
wxPoint Drawer::fromGrid(const wxPoint& point) {
wxPoint Drawer::fromGrid(const wxPoint& point) const {
return fromGrid(point.x, point.y);
}
void Drawer::initScreen(const TLVec& layout) {
if (isBgReady) {
screenBitmap = copyBitmap(bgBitmap);
wxSize Drawer::composeMinSize(const wxSize& gridSize) {
wxSize ms;
wxLogDebug(_("Reinit"));
ms.SetWidth(MIN_GRID_POINT * TILE_WIDTH * gridSize.x);
ms.SetHeight(MIN_GRID_POINT * TILE_HEIGHT * gridSize.y);
wxMemoryDC dc;
dc.SelectObject(screenBitmap);
wxLogDebug(wxString::Format("MinSize %i %i", ms.x, ms.y));
for (int z = 0; z < gridSize.z; z++)
for (int x = 0; x < gridSize.x; x++)
for (int y = 0; y < gridSize.y; y++) {
CardT c = layout[z][x][y];
if (c >= 0)
drawTile(dc, c, fromGrid(x, y), z);
}
isScreenReady = true;
}
}
void Drawer::drawTile(wxDC& dc, int8_t index, const wxPoint& position, uint8_t zIndex) {
wxBrush _bgColor = dc.GetBrush();
wxBrush front = wxColor(255, 255, 255);
wxBrush back = wxColor(200, 200, 200);
if (position == fromGrid({marked.x, marked.y}) && marked.z == zIndex) {
front = wxColor(200, 255, 200);
back = wxColor(190, 220, 190);
}
dc.SetBrush(back);
dc.DrawRoundedRectangle(position.x + (tilePixelSize.GetWidth()/10 + 3) - (tilePixelSize.GetWidth()/10 + 3)*zIndex, position.y + (tilePixelSize.GetHeight()/10 + 3) - (tilePixelSize.GetHeight()/10 + 3)*zIndex, tilePixelSize.GetWidth() * 2, tilePixelSize.GetHeight() * 2, 10);
dc.SetBrush(front);
dc.DrawRoundedRectangle(position.x - (tilePixelSize.GetWidth()/10 + 3)*zIndex, position.y - (tilePixelSize.GetHeight()/10 + 3)*zIndex, tilePixelSize.GetWidth() * 2, tilePixelSize.GetHeight() * 2, 10);
dc.SetBrush(_bgColor);
if (tileImages[index].IsOk()) {
if (tileImages[index].GetWidth() != tilePixelSize.x * 2)
dc.DrawBitmap(tileImages[index].Scale(tilePixelSize.x * 2 - 20, tilePixelSize.y * 2 - 20), {position.x + 10 - (tilePixelSize.GetWidth()/10 + 3)*zIndex, position.y + 10 - (tilePixelSize.GetHeight()/10 + 3)*zIndex});
else
dc.DrawBitmap(tileImages[index], {position.x + 10 - (tilePixelSize.GetWidth()/10 + 3)*zIndex, position.y + 10 - (tilePixelSize.GetHeight()/10 + 3)*zIndex});
}
}
return ms;
}

View File

@ -10,39 +10,44 @@
#define TILE_IMAGES_N 42
#define MIN_GRID_POINT 3
class Drawer {
public:
Drawer();
void drawTable(wxDC& dc);
void composeBG();
void composeBoard(const TLVec& layout, const Dimensions& gridSize);
void resizeBg(const wxSize& tableSize);
bool resizeBoard(const TLVec& layout, const Dimensions& gridSize);
wxPoint toGrid(const wxPoint& point) const;
wxPoint fromGrid(int x, int y) const;
wxPoint fromGrid(const wxPoint& point) const;
wxSize composeMinSize(const wxSize& gridSize);
wxSize tableSize;
wxSize tilePixelSize; // кратно 600x800
wxSize tilePixelSize; // кратно 3x4, по умолчанию 600x800
wxSize resolution;
Dimensions gridSize;
wxRect tablePixelRect;
void setBG(const wxSize& tableSize);
void initScreen(const TLVec& layout);
wxPoint toGrid(const wxPoint& point);
wxPoint fromGrid(int x, int y);
wxPoint fromGrid(const wxPoint& point);
ThreePoint marked;
private:
void drawScreen(wxDC& dc);
void drawTile(wxDC& dc, int8_t index, const wxPoint& position, uint8_t zIndex);
void drawTile(wxDC& dc, int8_t index, const wxPoint& position,
uint8_t zIndex) const;
wxImage tileImages[TILE_IMAGES_N];
wxBitmap bgBitmap;
wxBitmap screenBitmap;
wxBitmap boardBitmap;
bool isBgReady = false;
bool isScreenReady = false;
int prevGridPoint;
};
#endif

View File

@ -4,65 +4,96 @@
#include "utils.h"
GamePanel::GamePanel(wxFrame* parent) : wxPanel(parent), controller(drawer) {
// clang-format off
wxBEGIN_EVENT_TABLE(GamePanel, wxPanel)
EVT_PAINT(GamePanel::OnPaint)
EVT_SIZE(GamePanel::OnResize)
EVT_TIMER(TIMER_ID, GamePanel::OnTimer)
EVT_LEFT_DOWN(GamePanel::OnClick)
wxEND_EVENT_TABLE();
// clang-format on
GamePanel::GamePanel(wxFrame* parent)
: wxPanel(parent), controller(drawer),
sb(((wxFrame*)this->GetParent())->GetStatusBar()), timer(this, TIMER_ID) {
SetBackgroundStyle(wxBG_STYLE_PAINT);
Bind(wxEVT_PAINT, &GamePanel::OnPaint, this);
Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) -> void {
this->controller.resize(evt.GetSize());
});
timer = new wxTimer(this, 1);
Bind(wxEVT_TIMER, &GamePanel::OnTimer, this, timer->GetId());
Bind(wxEVT_LEFT_DOWN, &GamePanel::OnClick, this);
}
void GamePanel::Start(const wxString& path, bool solveable) {
void GamePanel::Start(const wxString& path, bool solveable,
std::function<void(const wxSize& size)> setMinSize) {
wxLogDebug(_("Started game"));
controller.stopwatch = 0;
controller.loadLayout(path);
controller.fill(solveable);
timer->Start(1000, wxTIMER_CONTINUOUS);
if (sb == nullptr)
sb = ((wxFrame*)this->GetParent())->GetStatusBar();
setMinSize(drawer.composeMinSize(controller.gridSize));
timer.Start(1000, wxTIMER_CONTINUOUS);
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
sb->SetStatusText(PRemaining(controller.remaining), 1);
drawer.initScreen(controller.getTable());
bool redrawn =
drawer.resizeBoard(controller.getTable(), controller.gridSize);
if (!redrawn)
drawer.composeBoard(controller.getTable(), controller.gridSize);
Refresh();
}
void GamePanel::undo() {
controller.undo();
drawer.composeBoard(controller.getTable(), controller.gridSize);
Refresh();
}
void GamePanel::reshuffle(bool solveable) {
controller.free_table();
controller.fill(solveable);
drawer.composeBoard(controller.getTable(), controller.gridSize);
Refresh();
}
void GamePanel::OnPaint(wxPaintEvent& _) {
wxAutoBufferedPaintDC dc(this);
wxLogDebug(_("OnPaint"));
drawer.drawTable(dc);
}
void GamePanel::OnResize(wxSizeEvent& _) {
const wxSize& resolution = GetClientSize();
wxLogDebug(wxString::Format("OnResize %i %i", resolution.x, resolution.y));
if (isPositive(resolution)) {
drawer.resizeBg(resolution);
if (controller.gameStarted())
drawer.resizeBoard(controller.getTable(), controller.gridSize);
}
Refresh();
}
void GamePanel::OnTimer(wxTimerEvent& _) {
controller.stopwatch += 1;
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
}
void GamePanel::OnClick(wxMouseEvent& _) {
controller.handleClick(ScreenToClient(wxGetMousePosition()));
sb->SetStatusText(PRemaining(controller.remaining), 1);
if (controller.gameStarted()) {
controller.handleClick(ScreenToClient(wxGetMousePosition()));
sb->SetStatusText(PRemaining(controller.remaining), 1);
Refresh();
}
void GamePanel::undo() {
controller.undo();
drawer.initScreen(controller.getTable());
Refresh();
}
void GamePanel::reshuffle(bool solveable) {
controller.free_table();
controller.fill(solveable);
drawer.initScreen(controller.getTable());
Refresh();
drawer.composeBoard(controller.getTable(), controller.gridSize);
Refresh();
}
}

View File

@ -5,14 +5,15 @@
#include <wx/stopwatch.h>
#include "Drawer.h"
#include "Controller.h"
#include "Drawer.h"
class GamePanel : public wxPanel {
public:
GamePanel(wxFrame* parent);
void Start(const wxString& path, bool solveable);
void Start(const wxString& path, bool solveable,
std::function<void(const wxSize& size)> setMinSize);
void undo();
void reshuffle(bool solveable);
@ -22,11 +23,16 @@ private:
Controller controller;
void OnPaint(wxPaintEvent& _);
void OnResize(wxSizeEvent& _);
void OnTimer(wxTimerEvent& _);
void OnClick(wxMouseEvent& _);
wxDECLARE_EVENT_TABLE();
wxStatusBar* sb = nullptr;
wxTimer* timer = nullptr;
wxTimer timer;
};
#define TIMER_ID 1
#endif

View File

@ -1,47 +1,53 @@
#include "MainFrame.h"
#include "AboutDlg.h"
#include "HelpDlg.h"
#include "RulesDlg.h"
#include "AboutDlg.h"
#include "resources/icon.xpm"
MainFrame::MainFrame()
: wxFrame(nullptr, wxID_ANY, _("Маджонг (пасьянс)"), wxDefaultPosition, wxSize(800, 600)),
dataDirPath(wxStandardPaths::Get().GetUserDataDir())
{
: wxFrame(nullptr, wxID_ANY, _("Маджонг (пасьянс)"), wxDefaultPosition,
wxSize(800, 600)),
dataDirPath(wxStandardPaths::Get().GetUserDataDir()) {
SetIcon(logo_icon);
initMenu();
bindMenu();
Bind(wxEVT_SHOW, [this](wxShowEvent& _) -> void {
if (openLayout())
panel->Start(layoutPath, solveable,
[this](const wxSize& size) -> void {
this->SetMinClientSize(size);
});
});
CreateStatusBar(2);
panel = new GamePanel(this);
panel->SetFocus();
if (openLayout())
panel->Start(layoutPath, solveable);
}
void MainFrame::initMenu() {
wxMenu *menuGame = new wxMenu;
wxMenu* menuGame = new wxMenu;
menuGame->Append(IDM_New_Game, _("Начать сначала"));
menuGame->Append(IDM_Open, _("Открыть карту"));
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
menuGame->Enable(IDM_Solveable, false); // TODO: finish solveable table generation
menuGame->Enable(IDM_Solveable,
false); // TODO: finish solveable table generation
menuGame->AppendSeparator();
menuGame->Append(IDM_Undo, _("Отменить ход"));
menuGame->Append(IDM_Reshuffle, _("Перемешать поле"));
menuGame->AppendSeparator();
menuGame->Append(IDM_Exit, _("Выход"));
wxMenu *menuHelp = new wxMenu;
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(IDM_Help, _("Инструкция"));
menuHelp->Append(IDM_Rules, _("Правила игры"));
menuHelp->Append(IDM_About, _("О программе"));
wxMenuBar *menuBar = new wxMenuBar;
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuGame, _("Игра"));
menuBar->Append(menuHelp, _("Помощь"));
@ -49,57 +55,77 @@ void MainFrame::initMenu() {
}
void MainFrame::bindMenu() {
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
Close();
}, IDM_Exit);
Bind(
wxEVT_MENU, [this](wxCommandEvent& _) -> void { Close(); }, IDM_Exit);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
if (openLayout())
panel->Start(layoutPath, solveable);
}, IDM_Open);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void {
if (openLayout())
panel->Start(layoutPath, solveable,
[this](const wxSize& size) -> void {
this->SetMinClientSize(size);
});
},
IDM_Open);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
(new HelpDlg(this, -1))->Show();
}, IDM_Help);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void { (new HelpDlg(this, -1))->Show(); },
IDM_Help);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
(new AboutDlg(this, -1))->Show();
}, IDM_About);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void { (new AboutDlg(this, -1))->Show(); },
IDM_About);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
(new RulesDlg(this, -1))->Show();
}, IDM_Rules);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void { (new RulesDlg(this, -1))->Show(); },
IDM_Rules);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
if (!layoutPath.IsEmpty() || openLayout()) {
panel->Start(layoutPath, solveable);
Refresh();
}
}, IDM_New_Game);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void {
if (!layoutPath.IsEmpty() || openLayout()) {
panel->Start(layoutPath, solveable,
[this](const wxSize& size) -> void {
this->SetMinClientSize(size);
});
}
},
IDM_New_Game);
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) -> void {
solveable = evt.IsChecked();
}, IDM_Solveable);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& evt) -> void { solveable = evt.IsChecked(); },
IDM_Solveable);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
panel->undo();
}, IDM_Undo);
Bind(
wxEVT_MENU, [this](wxCommandEvent& _) -> void { panel->undo(); },
IDM_Undo);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
panel->reshuffle(solveable);
}, IDM_Reshuffle);
Bind(
wxEVT_MENU,
[this](wxCommandEvent& _) -> void { panel->reshuffle(solveable); },
IDM_Reshuffle);
}
/**
* Shows a file opening dialog asking for .smlf file if succed, sets its path to layoutPath
* Shows a file opening dialog asking for .smlf file if succed, sets its path to
* layoutPath
* @return true if user have chosen a file, false if cancelled
*/
bool MainFrame::openLayout() {
wxFileDialog openFileDlg(this, _("Открыть карту"), dataDirPath + wxFileName::GetPathSeparator() + _("layouts"), _("Turtle.smlf"), _("Файлы Mahjong карт (*.smlf)|*.smlf"), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxFileDialog openFileDlg(
this, _("Открыть карту"),
dataDirPath + wxFileName::GetPathSeparator() + _("layouts"),
_("Turtle.smlf"), _("Файлы Mahjong карт (*.smlf)|*.smlf"),
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDlg.ShowModal() == wxID_CANCEL)
return false;
layoutPath = openFileDlg.GetPath();
layoutPath = openFileDlg.GetPath();
return true;
}

View File

@ -1,7 +1,8 @@
#include "utils.h"
wxString LTimeToStr(int time) {
return wxString::Format(_("%d:%02d:%02d"), time / 3600, (time / 60) % 60, time % 60);
return wxString::Format(_("%d:%02d:%02d"), time / 3600, (time / 60) % 60,
time % 60);
}
int upDiv(int a, int b) {
@ -13,5 +14,9 @@ wxString itowxS(int a) {
}
wxString PRemaining(uint8_t remaining) {
return wxString::Format("%i%%", remaining*100 / 144);
return wxString::Format("%i%%", remaining * 100 / 144);
}
bool isPositive(const wxSize& size) {
return size.x > 0 && size.y > 0;
}

16
utils.h
View File

@ -19,15 +19,15 @@ using CardT = int16_t;
class Dimensions : public wxSize {
public:
Dimensions(int _z, int _x, int _y): wxSize(_x, _y), z(_z) {};
Dimensions(): wxSize(), z(0) {};
Dimensions(int _z, int _x, int _y) : wxSize(_x, _y), z(_z){};
Dimensions() : wxSize(), z(0){};
int z;
};
class ThreePoint {
public:
ThreePoint(int _z, int _x, int _y): x(_x), y(_y), z(_z) {};
ThreePoint(): x(0), y(0), z(0) {};
ThreePoint(int _z, int _x, int _y) : x(_x), y(_y), z(_z){};
ThreePoint() : x(0), y(0), z(0){};
int x;
int y;
int z;
@ -41,10 +41,8 @@ public:
using TLVec = vector<vector<vector<CardT>>>;
enum Values {
MATCHED = -3,
EMPTY,
FREE
};
enum Values { MATCHED = -3, EMPTY, FREE };
bool isPositive(const wxSize& size);
#endif