extracted game logic into separate library

This commit is contained in:
Lauchmelder 2022-01-15 16:45:52 +01:00
parent c6a262a57f
commit 690b577e87
27 changed files with 388 additions and 175 deletions

View file

@ -9,4 +9,6 @@ project ("KoiKoi")
add_subdirectory ("vendor/spdlog")
add_subdirectory ("vendor/glfw")
add_subdirectory ("vendor/lol")
add_subdirectory ("engine")
add_subdirectory ("src")

21
engine/Board.cpp Normal file
View file

@ -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;
}

27
engine/Board.hpp Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include "Card.hpp"
typedef std::function<void(std::shared_ptr<Card>)> RevealCallbackFunc;
class Game;
class Board
{
public:
Board(Game* parent);
void RevealCard();
inline const std::vector<std::shared_ptr<Card>>& GetOpenCards() { return openCards; }
void SetRevealCallback(RevealCallbackFunc callbackFunc);
private:
std::vector<std::shared_ptr<Card>> openCards;
RevealCallbackFunc callback;
Game* parent;
};

10
engine/CMakeLists.txt Normal file
View file

@ -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}
)

7
engine/Card.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "Card.hpp"
Card::Card(Month month, int type) :
suit(month), type(type)
{
}

19
engine/Card.hpp Normal file
View file

@ -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;
};

32
engine/CardStack.cpp Normal file
View file

@ -0,0 +1,32 @@
#include "CardStack.hpp"
#include <algorithm>
#include <chrono>
#include <random>
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<Card>(static_cast<Month>(month), type));
}
}
// Shuffle stack
std::default_random_engine engine(time(0));
std::shuffle(stack.begin(), stack.end(), engine);
}
CardStack::~CardStack()
{
}
std::shared_ptr<Card> CardStack::DrawCard()
{
std::shared_ptr<Card> drawnCard = stack.back();
stack.pop_back();
return drawnCard;
}

19
engine/CardStack.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <memory>
#include <vector>
#include "Card.hpp"
class CardStack
{
public:
CardStack();
~CardStack();
std::shared_ptr<Card> DrawCard();
inline bool Empty() const { return stack.empty(); }
private:
std::vector<std::shared_ptr<Card>> stack;
};

14
engine/Game.cpp Normal file
View file

@ -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();
}

19
engine/Game.hpp Normal file
View file

@ -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;
};

View file

@ -10,8 +10,8 @@
#include <stbi_image.h>
#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<lol::Shader>(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);

View file

@ -4,6 +4,7 @@
#include <lol/lol.hpp>
#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<lol::Layer*> layerStack;

View file

@ -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> 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> card : openCards)
card->Draw(camera);
}

View file

@ -1,19 +0,0 @@
#pragma once
#include <lol/lol.hpp>
#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<std::shared_ptr<Card>> openCards;
};

View file

@ -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
)

View file

@ -1,27 +0,0 @@
#pragma once
#include <lol/lol.hpp>
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<lol::Texture2D> cards;
Month suit;
int type;
glm::vec2 textureOffset;
};

View file

@ -1,9 +1,10 @@
#include "Card.hpp"
#include "CardSprite.hpp"
#include <spdlog/spdlog.h>
#include "ObjectIDs.hpp"
Card::Card(lol::ObjectManager& manager, Month month, int type)
CardSprite::CardSprite(lol::ObjectManager& manager, std::shared_ptr<Card> card) :
card(card)
{
try
{
@ -36,22 +37,14 @@ Card::Card(lol::ObjectManager& manager, Month month, int type)
shader = manager.Get<lol::Shader>(SHADER_CARD);
cards = manager.Get<lol::Texture2D>(TEXTURE_CARD_ATLAS);
UpdateSuitAndType(month, type);
}
void Card::UpdateSuitAndType(Month month, int type)
{
this->suit = month;
this->type = type;
textureOffset = glm::vec2(
static_cast<int>(this->suit) / 12.0f,
this->type / 4.0f
static_cast<int>(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);

21
src/CardSprite.hpp Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <lol/lol.hpp>
#include "Card.hpp"
class CardSprite : public lol::Drawable, public lol::Transformable
{
public:
CardSprite(lol::ObjectManager& manager, std::shared_ptr<Card> 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<lol::Texture2D> cards;
glm::vec2 textureOffset;
std::shared_ptr<Card> card;
};

View file

@ -1,22 +0,0 @@
#pragma once
#include <lol/lol.hpp>
#include "Card.hpp"
class CardStack : public lol::Transformable, public lol::Drawable
{
public:
CardStack(lol::ObjectManager& manager);
~CardStack();
std::shared_ptr<Card> 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<lol::Texture2D> backside;
std::vector<std::shared_ptr<Card>> stack;
};

View file

@ -1,14 +1,10 @@
#include "CardStack.hpp"
#include <algorithm>
#include <chrono>
#include <random>
#include "CardStackSprite.hpp"
#include <spdlog/spdlog.h>
#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<lol::Shader>(SHADER_CARD);
backside = manager.Get<lol::Texture2D>(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<Card>(manager, static_cast<Month>(month), type));
}
}
// Shuffle stack
std::default_random_engine engine(time(0));
std::shuffle(stack.begin(), stack.end(), engine);
texture = manager.Get<lol::Texture2D>(TEXTURE_BACKSIDE);
}
CardStack::~CardStack()
CardStackSprite::~CardStackSprite()
{
}
std::shared_ptr<Card> CardStack::DrawCard()
void CardStackSprite::PreRender(const lol::CameraBase& camera)
{
std::shared_ptr<Card> 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);

18
src/CardStackSprite.hpp Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include <lol/lol.hpp>
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<lol::Texture2D> texture;
};

View file

@ -1,9 +1,10 @@
#include "Window.hpp"
#include <spdlog/spdlog.h>
#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);
}

View file

@ -5,19 +5,23 @@
#include <GLFW/glfw3.h>
#include <lol/lol.hpp>
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;

55
src/layers/BoardLayer.cpp Normal file
View file

@ -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> 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
));
}
}
}

22
src/layers/BoardLayer.hpp Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <lol/lol.hpp>
#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> card);
private:
const Board& board;
lol::ObjectManager& manager;
std::vector<CardSprite> sprites;
};

View file

@ -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);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <lol/lol.hpp>
#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;
};