From d633f9751dd1309758b81de8e762028d072faa04 Mon Sep 17 00:00:00 2001 From: dm1sh Date: Sat, 28 May 2022 16:32:08 +0300 Subject: [PATCH] Improved rendering system: fixed bugs, added min size limits, moved all drawing-related methods to Drawer class --- Controller.cpp | 156 ++++++++++++++++------------------- Controller.h | 22 +++-- Drawer.cpp | 218 ++++++++++++++++++++++++++++++++++--------------- Drawer.h | 35 ++++---- GamePanel.cpp | 103 +++++++++++++++-------- GamePanel.h | 14 +++- MainFrame.cpp | 116 ++++++++++++++++---------- utils.cpp | 9 +- utils.h | 16 ++-- 9 files changed, 418 insertions(+), 271 deletions(-) diff --git a/Controller.cpp b/Controller.cpp index 91e9336..9593175 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -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>(drawer.gridSize.x, vector(drawer.gridSize.y, EMPTY))); + table = TLVec( + gridSize.z, + vector>(gridSize.x, vector(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 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 positions, int past_pos, std::list::iterator past_ptr) { +int Controller::emplace_rand(int id, std::list positions, + int past_pos, + std::list::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 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; +} \ No newline at end of file diff --git a/Controller.h b/Controller.h index 966b71a..aba9834 100644 --- a/Controller.h +++ b/Controller.h @@ -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 positions, int past_pos, + std::list::iterator past_ptr); - int emplace_rand(int id, std::list positions, int past_pos, std::list::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::arraycardsCounter; + std::array cardsCounter; std::stack> steps; }; diff --git a/Drawer.cpp b/Drawer.cpp index a3e9def..d66f072 100644 --- a/Drawer.cpp +++ b/Drawer.cpp @@ -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; +} \ No newline at end of file diff --git a/Drawer.h b/Drawer.h index c1ac069..b780260 100644 --- a/Drawer.h +++ b/Drawer.h @@ -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 diff --git a/GamePanel.cpp b/GamePanel.cpp index 7656cf0..2ef17e8 100644 --- a/GamePanel.cpp +++ b/GamePanel.cpp @@ -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 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(); + } } diff --git a/GamePanel.h b/GamePanel.h index fe7d89d..dc807c1 100644 --- a/GamePanel.h +++ b/GamePanel.h @@ -5,14 +5,15 @@ #include -#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 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 diff --git a/MainFrame.cpp b/MainFrame.cpp index 11ed4bb..b32bb39 100644 --- a/MainFrame.cpp +++ b/MainFrame.cpp @@ -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; } diff --git a/utils.cpp b/utils.cpp index b8a1e45..b241429 100644 --- a/utils.cpp +++ b/utils.cpp @@ -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; +} \ No newline at end of file diff --git a/utils.h b/utils.h index eda1116..f8c8444 100644 --- a/utils.h +++ b/utils.h @@ -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>>; -enum Values { - MATCHED = -3, - EMPTY, - FREE -}; +enum Values { MATCHED = -3, EMPTY, FREE }; + +bool isPositive(const wxSize& size); #endif