From 690b577e87ce91f7a0e503e2ac13f3790ce4f7a7 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Sat, 15 Jan 2022 16:45:52 +0100 Subject: [PATCH] extracted game logic into separate library --- CMakeLists.txt | 2 + engine/Board.cpp | 21 +++++++++ engine/Board.hpp | 27 +++++++++++ engine/CMakeLists.txt | 10 ++++ engine/Card.cpp | 7 +++ engine/Card.hpp | 19 ++++++++ engine/CardStack.cpp | 32 +++++++++++++ engine/CardStack.hpp | 19 ++++++++ engine/Game.cpp | 14 ++++++ engine/Game.hpp | 19 ++++++++ src/Application.cpp | 23 ++++++--- src/Application.hpp | 4 ++ src/Board.cpp | 50 -------------------- src/Board.hpp | 19 -------- src/CMakeLists.txt | 9 ++-- src/Card.hpp | 27 ----------- src/{Card.cpp => CardSprite.cpp} | 21 +++------ src/CardSprite.hpp | 21 +++++++++ src/CardStack.hpp | 22 --------- src/{CardStack.cpp => CardStackSprite.cpp} | 36 +++----------- src/CardStackSprite.hpp | 18 +++++++ src/Window.cpp | 15 +++++- src/Window.hpp | 7 ++- src/layers/BoardLayer.cpp | 55 ++++++++++++++++++++++ src/layers/BoardLayer.hpp | 22 +++++++++ src/layers/CardStackLayer.cpp | 25 ++++++++++ src/layers/CardStackLayer.hpp | 19 ++++++++ 27 files changed, 388 insertions(+), 175 deletions(-) create mode 100644 engine/Board.cpp create mode 100644 engine/Board.hpp create mode 100644 engine/CMakeLists.txt create mode 100644 engine/Card.cpp create mode 100644 engine/Card.hpp create mode 100644 engine/CardStack.cpp create mode 100644 engine/CardStack.hpp create mode 100644 engine/Game.cpp create mode 100644 engine/Game.hpp delete mode 100644 src/Board.cpp delete mode 100644 src/Board.hpp delete mode 100644 src/Card.hpp rename src/{Card.cpp => CardSprite.cpp} (79%) create mode 100644 src/CardSprite.hpp delete mode 100644 src/CardStack.hpp rename src/{CardStack.cpp => CardStackSprite.cpp} (60%) create mode 100644 src/CardStackSprite.hpp create mode 100644 src/layers/BoardLayer.cpp create mode 100644 src/layers/BoardLayer.hpp create mode 100644 src/layers/CardStackLayer.cpp create mode 100644 src/layers/CardStackLayer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 63c3e6a..df175db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,4 +9,6 @@ project ("KoiKoi") add_subdirectory ("vendor/spdlog") add_subdirectory ("vendor/glfw") add_subdirectory ("vendor/lol") + +add_subdirectory ("engine") add_subdirectory ("src") diff --git a/engine/Board.cpp b/engine/Board.cpp new file mode 100644 index 0000000..9e041ec --- /dev/null +++ b/engine/Board.cpp @@ -0,0 +1,21 @@ +#include "Board.hpp" + +#include "Game.hpp" + +Board::Board(Game* parent) : + parent(parent) +{ + +} + +void Board::RevealCard() +{ + openCards.push_back(parent->GetStack().DrawCard()); + if (callback) + callback(openCards.back()); +} + +void Board::SetRevealCallback(RevealCallbackFunc callbackFunc) +{ + callback = callbackFunc; +} diff --git a/engine/Board.hpp b/engine/Board.hpp new file mode 100644 index 0000000..fb12eaa --- /dev/null +++ b/engine/Board.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include "Card.hpp" + +typedef std::function)> RevealCallbackFunc; + +class Game; + +class Board +{ +public: + Board(Game* parent); + + void RevealCard(); + inline const std::vector>& GetOpenCards() { return openCards; } + + void SetRevealCallback(RevealCallbackFunc callbackFunc); + +private: + std::vector> openCards; + + RevealCallbackFunc callback; + Game* parent; +}; \ No newline at end of file diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..897ff1b --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(koikoiengine STATIC + "Game.cpp" + "Card.cpp" + "CardStack.cpp" + "Board.cpp" +) + +target_include_directories(koikoiengine PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) \ No newline at end of file diff --git a/engine/Card.cpp b/engine/Card.cpp new file mode 100644 index 0000000..bf68d13 --- /dev/null +++ b/engine/Card.cpp @@ -0,0 +1,7 @@ +#include "Card.hpp" + +Card::Card(Month month, int type) : + suit(month), type(type) +{ + +} \ No newline at end of file diff --git a/engine/Card.hpp b/engine/Card.hpp new file mode 100644 index 0000000..62dd083 --- /dev/null +++ b/engine/Card.hpp @@ -0,0 +1,19 @@ +#pragma once + +enum class Month +{ + January, February, March, April, May, June, July, August, September, October, November, December +}; + +class Card +{ +public: + Card(Month month, int type); + + inline Month GetSuit() { return suit; } + inline int GetType() { return type; } + +private: + Month suit; + int type; +}; \ No newline at end of file diff --git a/engine/CardStack.cpp b/engine/CardStack.cpp new file mode 100644 index 0000000..4875412 --- /dev/null +++ b/engine/CardStack.cpp @@ -0,0 +1,32 @@ +#include "CardStack.hpp" + +#include +#include +#include + +CardStack::CardStack() +{ + // Create a full stack of cards + for (int month = 0; month < 11; month++) + { + for (int type = 0; type < 4; type++) + { + stack.push_back(std::make_shared(static_cast(month), type)); + } + } + + // Shuffle stack + std::default_random_engine engine(time(0)); + std::shuffle(stack.begin(), stack.end(), engine); +} + +CardStack::~CardStack() +{ +} + +std::shared_ptr CardStack::DrawCard() +{ + std::shared_ptr drawnCard = stack.back(); + stack.pop_back(); + return drawnCard; +} diff --git a/engine/CardStack.hpp b/engine/CardStack.hpp new file mode 100644 index 0000000..2d3b56d --- /dev/null +++ b/engine/CardStack.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "Card.hpp" + +class CardStack +{ +public: + CardStack(); + ~CardStack(); + + std::shared_ptr DrawCard(); + inline bool Empty() const { return stack.empty(); } + +private: + std::vector> stack; +}; \ No newline at end of file diff --git a/engine/Game.cpp b/engine/Game.cpp new file mode 100644 index 0000000..4a63e98 --- /dev/null +++ b/engine/Game.cpp @@ -0,0 +1,14 @@ +#include "Game.hpp" + +Game::Game() : + board(this) +{ + +} + +void Game::Setup() +{ + // Put 8 cards on the table + for (int i = 0; i < 8; i++) + board.RevealCard(); +} \ No newline at end of file diff --git a/engine/Game.hpp b/engine/Game.hpp new file mode 100644 index 0000000..5b875d4 --- /dev/null +++ b/engine/Game.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "CardStack.hpp" +#include "Board.hpp" + +class Game +{ +public: + Game(); + ~Game() {} + + void Setup(); + inline CardStack& GetStack() { return stack; } + inline Board& GetBoard() { return board; } + +private: + CardStack stack; + Board board; +}; \ No newline at end of file diff --git a/src/Application.cpp b/src/Application.cpp index 71bf0a9..bb0b83f 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -10,8 +10,8 @@ #include #include "Window.hpp" -#include "CardStack.hpp" -#include "Board.hpp" +#include "layers/CardStackLayer.hpp" +#include "layers/BoardLayer.hpp" #include "resources.h" #include "ObjectIDs.hpp" @@ -41,6 +41,12 @@ void Application::Run() } } +void Application::OnKeyPressed(unsigned int character) +{ + if (character == 'd' || character == 'D') + game.GetBoard().RevealCard(); +} + Application::Application() : valid(true), window(nullptr) { @@ -56,7 +62,7 @@ Application::Application() : } spdlog::debug("GLFW initialized"); - window = new Window(1280, 720, "Koi Koi"); + window = new Window(this, 1280, 720, "Koi Koi"); valid = window->IsValid(); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) @@ -78,11 +84,14 @@ Application::Application() : manager.Create(SHADER_CARD, std::string((char*)card_vs, card_vs_size), std::string((char*)card_fs, card_fs_size)); spdlog::debug("Done!"); - spdlog::debug("Creating card stack"); - + spdlog::debug("Setting up layers"); + layerStack.push_back(new CardStackLayer(manager, game.GetStack())); + BoardLayer* boardLayer = new BoardLayer(manager, game.GetBoard()); + game.GetBoard().SetRevealCallback(std::bind(&BoardLayer::OnRevealCard, boardLayer, std::placeholders::_1)); + layerStack.push_back(boardLayer); - spdlog::debug("Setting up board"); - layerStack.push_back(new Board(manager)); + spdlog::debug("Setting up game"); + game.Setup(); glViewport(0, 0, 1280, 720); diff --git a/src/Application.hpp b/src/Application.hpp index dd7f607..1bee718 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -4,6 +4,7 @@ #include #include "CardStack.hpp" +#include "Game.hpp" class Window; @@ -19,11 +20,14 @@ public: public: void Run(); + void OnKeyPressed(unsigned int character); // TODO: Remove later on private: bool valid; Window* window; + Game game; + lol::ObjectManager manager; std::vector layerStack; diff --git a/src/Board.cpp b/src/Board.cpp deleted file mode 100644 index fe8f33a..0000000 --- a/src/Board.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "Board.hpp" - -#include "CardStack.hpp" -#include "ObjectIDs.hpp" - -Board::Board(lol::ObjectManager& manager) : - Layer("Board"), stack(manager) -{ - stack.SetScale(glm::vec3(0.2f)); - stack.SetPosition(glm::vec3( - 0.25f - (stack.GetSize().x + 0.05f), - 0.5f - (stack.GetSize().y * 0.5f), - 0.0f - )); - - // Draw 8 cards from stack - for (int y = 0; y < 2; y++) - { - for (int x = 0; x < 4; x++) - { - std::shared_ptr card = stack.DrawCard(); - card->SetScale(glm::vec3(0.2f)); - - glm::vec3 cardSize = card->GetSize(); - card->SetPosition(glm::vec3( - 0.25f + (cardSize.x + 0.05f) * x, - 0.5f - cardSize.y - 0.025f + (cardSize.y + 0.05f) * y, - 0.0f - )); - - openCards.push_back(card); - } - } -} - -Board::~Board() -{ -} - -void Board::OnUpdate() -{ -} - -void Board::OnRender(lol::CameraBase& camera) -{ - stack.Draw(camera); - - for (std::shared_ptr card : openCards) - card->Draw(camera); -} diff --git a/src/Board.hpp b/src/Board.hpp deleted file mode 100644 index d7ce5bf..0000000 --- a/src/Board.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "Card.hpp" -#include "CardStack.hpp" - -class Board : public lol::Layer -{ -public: - Board(lol::ObjectManager& manager); - ~Board(); - - void OnUpdate() override; - void OnRender(lol::CameraBase& camera) override; - -private: - CardStack stack; - std::vector> openCards; -}; \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f93a01c..3ac261a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,9 +2,10 @@ add_executable(koikoi "main.cpp" "Application.cpp" "Window.cpp" - "Card.cpp" - "CardStack.cpp" - "Board.cpp" + "CardSprite.cpp" + "CardStackSprite.cpp" + "layers/CardStackLayer.cpp" + "layers/BoardLayer.cpp" ) set( RC_DEPENDS "" ) @@ -41,10 +42,12 @@ target_include_directories(koikoi PUBLIC spdlog glfw lol + koikoiengine ) target_link_libraries(koikoi spdlog glfw lol + koikoiengine ) \ No newline at end of file diff --git a/src/Card.hpp b/src/Card.hpp deleted file mode 100644 index d054382..0000000 --- a/src/Card.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -enum class Month -{ - January, February, March, April, May, June, July, August, September, October, November, December -}; - -class Card : public lol::Drawable, public lol::Transformable -{ -public: - Card(lol::ObjectManager& manager, Month month, int type); - - void UpdateSuitAndType(Month month, int type); - glm::vec3 GetSize() { return glm::vec3(0.61035f, 1.0f, 0.0f) * GetScale(); } - -private: - void PreRender(const lol::CameraBase& camera) override; - -private: - std::shared_ptr cards; - - Month suit; - int type; - glm::vec2 textureOffset; -}; \ No newline at end of file diff --git a/src/Card.cpp b/src/CardSprite.cpp similarity index 79% rename from src/Card.cpp rename to src/CardSprite.cpp index cdae13a..f5c8fd4 100644 --- a/src/Card.cpp +++ b/src/CardSprite.cpp @@ -1,9 +1,10 @@ -#include "Card.hpp" +#include "CardSprite.hpp" #include #include "ObjectIDs.hpp" -Card::Card(lol::ObjectManager& manager, Month month, int type) +CardSprite::CardSprite(lol::ObjectManager& manager, std::shared_ptr card) : + card(card) { try { @@ -36,22 +37,14 @@ Card::Card(lol::ObjectManager& manager, Month month, int type) shader = manager.Get(SHADER_CARD); cards = manager.Get(TEXTURE_CARD_ATLAS); - - UpdateSuitAndType(month, type); -} - -void Card::UpdateSuitAndType(Month month, int type) -{ - this->suit = month; - this->type = type; - + textureOffset = glm::vec2( - static_cast(this->suit) / 12.0f, - this->type / 4.0f + static_cast(card->GetSuit()) / 12.0f, + card->GetType() / 4.0f ); } -void Card::PreRender(const lol::CameraBase& camera) +void CardSprite::PreRender(const lol::CameraBase& camera) { cards->Bind(); shader->SetUniform("offset", textureOffset); diff --git a/src/CardSprite.hpp b/src/CardSprite.hpp new file mode 100644 index 0000000..d48327b --- /dev/null +++ b/src/CardSprite.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "Card.hpp" + +class CardSprite : public lol::Drawable, public lol::Transformable +{ +public: + CardSprite(lol::ObjectManager& manager, std::shared_ptr card); + + glm::vec3 GetSize() { return glm::vec3(0.61035f, 1.0f, 0.0f) * GetScale(); } + +private: + void PreRender(const lol::CameraBase& camera) override; + +private: + std::shared_ptr cards; + glm::vec2 textureOffset; + + std::shared_ptr card; +}; \ No newline at end of file diff --git a/src/CardStack.hpp b/src/CardStack.hpp deleted file mode 100644 index f0a4058..0000000 --- a/src/CardStack.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include "Card.hpp" - -class CardStack : public lol::Transformable, public lol::Drawable -{ -public: - CardStack(lol::ObjectManager& manager); - ~CardStack(); - - std::shared_ptr DrawCard(); - inline bool Empty() { return stack.empty(); } - glm::vec3 GetSize() { return glm::vec3(0.61035f, 1.0f, 0.0f) * GetScale(); } - -private: - void PreRender(const lol::CameraBase& camera) override; - -private: - std::shared_ptr backside; - std::vector> stack; -}; \ No newline at end of file diff --git a/src/CardStack.cpp b/src/CardStackSprite.cpp similarity index 60% rename from src/CardStack.cpp rename to src/CardStackSprite.cpp index 372398b..564df45 100644 --- a/src/CardStack.cpp +++ b/src/CardStackSprite.cpp @@ -1,14 +1,10 @@ -#include "CardStack.hpp" - -#include -#include -#include +#include "CardStackSprite.hpp" #include #include "ObjectIDs.hpp" -CardStack::CardStack(lol::ObjectManager& manager) +CardStackSprite::CardStackSprite(lol::ObjectManager& manager) { try { @@ -40,36 +36,16 @@ CardStack::CardStack(lol::ObjectManager& manager) } shader = manager.Get(SHADER_CARD); - backside = manager.Get(TEXTURE_BACKSIDE); - - // Create a full stack of cards - for (int month = 0; month < 11; month++) - { - for (int type = 0; type < 4; type++) - { - stack.push_back(std::make_shared(manager, static_cast(month), type)); - } - } - - // Shuffle stack - std::default_random_engine engine(time(0)); - std::shuffle(stack.begin(), stack.end(), engine); + texture = manager.Get(TEXTURE_BACKSIDE); } -CardStack::~CardStack() +CardStackSprite::~CardStackSprite() { } -std::shared_ptr CardStack::DrawCard() +void CardStackSprite::PreRender(const lol::CameraBase& camera) { - std::shared_ptr drawnCard = stack.back(); - stack.pop_back(); - return drawnCard; -} - -void CardStack::PreRender(const lol::CameraBase& camera) -{ - backside->Bind(); + texture->Bind(); shader->SetUniform("offset", glm::vec2(0.0f)); shader->SetUniform("model", transformation); diff --git a/src/CardStackSprite.hpp b/src/CardStackSprite.hpp new file mode 100644 index 0000000..cab830a --- /dev/null +++ b/src/CardStackSprite.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +class CardStackSprite : public lol::Transformable, public lol::Drawable +{ +public: + CardStackSprite(lol::ObjectManager& manager); + ~CardStackSprite(); + + glm::vec3 GetSize() { return glm::vec3(0.61035f, 1.0f, 0.0f) * GetScale(); } + +private: + void PreRender(const lol::CameraBase& camera) override; + +private: + std::shared_ptr texture; +}; \ No newline at end of file diff --git a/src/Window.cpp b/src/Window.cpp index cc2c260..3d64b27 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -1,9 +1,10 @@ #include "Window.hpp" #include +#include "Application.hpp" -Window::Window(int width, int height, const std::string& title) : - window(NULL), valid(true) +Window::Window(Application* app, int width, int height, const std::string& title) : + window(NULL), valid(true), app(app) { window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL); if (window == NULL) @@ -28,10 +29,20 @@ Window::Window(int width, int height, const std::string& title) : } ); + glfwSetCharCallback(window, + [](GLFWwindow* window, unsigned int character) + { + UserData* data = (UserData*)glfwGetWindowUserPointer(window); + data->app->OnKeyPressed(character); + } + ); + camera = lol::OrthogonalCamera(0.0f, ((float)width / (float)height) * 1.0f, 0.0f, 1.0f); data.camera = &camera; spdlog::debug("Created orthogonal camera"); + data.app = app; + glfwSetWindowUserPointer(window, (void*)&data); } diff --git a/src/Window.hpp b/src/Window.hpp index 65be36a..e1ddd58 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -5,19 +5,23 @@ #include #include +class Application; + struct UserData { lol::OrthogonalCamera* camera; + Application* app; }; class Window { public: - Window(int width, int height, const std::string& title); + Window(Application* app, int width, int height, const std::string& title); ~Window(); inline bool IsValid() { return valid; } inline bool ShouldClose() { return glfwWindowShouldClose(window); } + inline GLFWwindow* GetNativeWindow() { return window; } void Clear(); void Draw(lol::Layer& layer); @@ -26,6 +30,7 @@ public: private: bool valid; GLFWwindow* window; + Application* app; UserData data; lol::OrthogonalCamera camera; diff --git a/src/layers/BoardLayer.cpp b/src/layers/BoardLayer.cpp new file mode 100644 index 0000000..b27e18c --- /dev/null +++ b/src/layers/BoardLayer.cpp @@ -0,0 +1,55 @@ +#include "BoardLayer.hpp" + +BoardLayer::BoardLayer(lol::ObjectManager& manager, const Board& board) : + lol::Layer("Board"), manager(manager), board(board) +{ +} + +void BoardLayer::OnUpdate() +{ +} + +void BoardLayer::OnRender(lol::CameraBase& camera) +{ + for (CardSprite& sprite : sprites) + camera.Draw(sprite); +} + +void BoardLayer::OnRevealCard(std::shared_ptr card) +{ + sprites.push_back(CardSprite(manager, card)); + CardSprite& newCard = sprites.back(); + newCard.SetScale(glm::vec3(0.2f)); + + // If this is the first card to be put on the board, place it in the top left slot + if (sprites.size() == 1) + { + newCard.SetPosition(glm::vec3( + 0.25f + 0.05f, + 0.5f + 0.025f, + 0.0f + )); + } + else + { + // If an even number of cards has been drawn, placce the last one below the previous one + if (sprites.size() % 2 == 0) + { + CardSprite& spriteBeforeLast = sprites[sprites.size() - 2]; + newCard.SetPosition(glm::vec3( + spriteBeforeLast.GetPosition().x, + spriteBeforeLast.GetPosition().y - newCard.GetSize().y - 0.05f, + 0.0f + )); + } + else // Else, place it in the next row + { + CardSprite& secondSpriteBeforeLast = sprites[sprites.size() - 3]; + newCard.SetPosition(glm::vec3( + secondSpriteBeforeLast.GetPosition().x + newCard.GetSize().x + 0.05f, + secondSpriteBeforeLast.GetPosition().y, + 0.0f + )); + } + } +} diff --git a/src/layers/BoardLayer.hpp b/src/layers/BoardLayer.hpp new file mode 100644 index 0000000..1e53404 --- /dev/null +++ b/src/layers/BoardLayer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "Board.hpp" +#include "../CardSprite.hpp" + +class BoardLayer : public lol::Layer +{ +public: + BoardLayer(lol::ObjectManager& manager, const Board& board); + + void OnUpdate() override; + void OnRender(lol::CameraBase& camera) override; + + void OnRevealCard(std::shared_ptr card); + +private: + const Board& board; + + lol::ObjectManager& manager; + std::vector sprites; +}; \ No newline at end of file diff --git a/src/layers/CardStackLayer.cpp b/src/layers/CardStackLayer.cpp new file mode 100644 index 0000000..de11f42 --- /dev/null +++ b/src/layers/CardStackLayer.cpp @@ -0,0 +1,25 @@ +#include "CardStackLayer.hpp" + +CardStackLayer::CardStackLayer(lol::ObjectManager& manager, const CardStack& stack) : + lol::Layer("Stack"), stack(stack), sprite(manager) +{ + sprite.SetScale(glm::vec3(0.2f)); + sprite.SetPosition(glm::vec3( + 0.25f - sprite.GetSize().x - 0.05f, + 0.5f - 0.5f* sprite.GetSize().y, + 0.0f + )); +} + +void CardStackLayer::OnUpdate() +{ + if (stack.Empty()) + sprite.SetScale(glm::vec3(0.0f)); + else + sprite.SetScale(glm::vec3(0.2f)); +} + +void CardStackLayer::OnRender(lol::CameraBase& camera) +{ + camera.Draw(sprite); +} diff --git a/src/layers/CardStackLayer.hpp b/src/layers/CardStackLayer.hpp new file mode 100644 index 0000000..94ffc60 --- /dev/null +++ b/src/layers/CardStackLayer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "CardStack.hpp" + +#include "../CardStackSprite.hpp" + +class CardStackLayer : public lol::Layer +{ +public: + CardStackLayer(lol::ObjectManager& manager, const CardStack& stack); + + void OnUpdate() override; + void OnRender(lol::CameraBase& camera) override; + +private: + const CardStack& stack; + CardStackSprite sprite; +}; \ No newline at end of file