Something went wrong. Try again.
A card game engine for Magic: The Gathering inspired by Card Forge
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230#include "app.h"#include "raylib.h"#include <map>#include <string>#include "assets.h"#include <vector>#include <uuid.h>#include <zone.h>#include <player.h>#include "ecs.h"#include <stdexcept>#include <card.h>using namespace std;
App::App(uuids::uuid_random_generator* gen) { if (gen == nullptr) { return; }
assets = getAssets(); game = Game(&ecs, vector<string>({ "Player 1", "Player 2", "Player 3", "Player 4" }), gen);}
void App::run() { InitWindow(1920, 1080, "ManaGrove"); SetTargetFPS(60); initGame();
while (!WindowShouldClose()) { updateGame();
BeginDrawing(); ClearBackground(BLACK); drawGame(); EndDrawing(); }
cleanGame();
CloseWindow();}
void App::initGame() { Image noCardLoadedImage = LoadImageFromMemory(".png", &assets["no_card.png"][0], (int)assets["no_card.png"].size()); cardWidth = (1080.0f / 6.0f) * ((float)(noCardLoadedImage.width) / (float)(noCardLoadedImage.height)); noCardImage = LoadTextureFromImage(noCardLoadedImage); UnloadImage(noCardLoadedImage); TraceLog(LOG_INFO, "Card width is %.2f", cardWidth);
font = LoadFontFromMemory(".ttf", &assets["Carlito-Regular.ttf"][0], (int)assets["Carlito-Regular.ttf"].size(), 24, NULL, 0);
tuple<flecs::entity, vector<CardDefId>> data = game.setupTestData(); mainPlayerEntity = get<flecs::entity>(data); vector<CardDefId> cards = get<vector<CardDefId>>(data); vector<string> cardName = vector<string>({ "forest.png", "mountain.png", "swamp.png", "island.png", "plains.png" });
for (unsigned int i = 0; i < 5; i++) { Image cardImage = LoadImageFromMemory(".png", &assets[cardName[i]][0], (int)assets[cardName[i]].size()); Texture cardTexture = LoadTextureFromImage(cardImage); UnloadImage(cardImage); textures.insert(pair{cards[i], cardTexture}); }
zones.insert(pair{"topRight", map<string, Rectangle>()}); zones.insert(pair{"topLeft", map<string, Rectangle>()}); zones.insert(pair{"bottomLeft", map<string, Rectangle>()});
map<string, Rectangle> bottomRight;
bottomRight.insert(pair{"battlefield", Rectangle{ (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f, (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f * (2.0f / 3.0f) }}); bottomRight.insert(pair{"command", Rectangle{ 1920.0f - 361.0f - cardWidth, 1080.0f * (5.0f / 6.0f), cardWidth, 1080.0f / 6.0f }});
bottomRight.insert(pair{"exile", Rectangle{ 1920.0f - 361.0f - (cardWidth * 2.0f), 1080.0f * (5.0f / 6.0f), cardWidth, 1080.0f / 6.0f }});
bottomRight.insert(pair{"graveyard", Rectangle{ 1920.0f - 361.0f - (cardWidth * 3.0f), 1080.0f * (5.0f / 6.0f), cardWidth, 1080.0f / 6.0 }});
bottomRight.insert(pair{"library", Rectangle{ 1920.0f - 361.0f - (cardWidth * 4.0f), 1080.0f * (5.0f / 6.0f), cardWidth, 1080.0f / 6.0 }});
bottomRight.insert(pair{"hand", Rectangle{ (1920.0f - 361.0f) / 2.0f, 1080.0f * (5.0f / 6.0f), ((1920.0f - 361.0f) / 2.0f) - (cardWidth * 4.0f), 1080.0f / 6.0f }}); zones.insert(pair{"bottomRight", bottomRight});}
void App::updateGame() { updateHandCards();}
void App::updateHandCards() { float cardAreaWidth = ((1920.0f - 361.0f) / 2.0f) - (cardWidth * 4.0f); Player& playerData = mainPlayerEntity.get_mut<Player>(); Zone& handZone = playerData.hand.get_mut<Zone>(); unsigned int cardNum = (unsigned int)handZone.cards.size(); float spacing;
if (cardNum == 1) { spacing = cardAreaWidth - cardWidth; } else { spacing = (cardAreaWidth - cardWidth) / ((float)cardNum - 1.0f); }
float startX = (1920.0f - 361.0f) / 2.0f; float yCoord = 1080.0f * (5.0f / 6.0f); // calculate each card's x/y coord for (unsigned int i = 0; i < cardNum; i++) { flecs::entity entity = handZone.cards[i];
if (!entity.has<CardInstance>()) { throw "Not a card instance"; }
if (!entity.has<CardRect>()) { throw "Entity does not have rectangle"; }
const CardInstance card = entity.get<CardInstance>(); CardRect& rec = entity.get_mut<CardRect>();
if (entity.has<Dragged>()) { continue; }
rec.x = startX + ((float)i * spacing); rec.y = yCoord; rec.h = 1080.0f / 6.0f; rec.w = cardWidth;
entity.add<RenderFront>(); }}
void App::drawGame() { renderDebugAreas(); flecs::query<Moused> mousedQuery = ecs.query<Moused>(); flecs::entity mousedEntity = mousedQuery.first();
if (mousedEntity.is_alive()) { const CardInstance& instance = mousedEntity.get<CardInstance>(); float calcHeight = 509.0f * (textures[instance.source].height / textures[instance.source].width); DrawTextureEx(textures[instance.source], Vector2{ 1920.0f - 361.0f, 1080.0f - calcHeight }, 0.0f, (361.0f / textures[instance.source].width), WHITE); } else { float calcHeight = 509.0f * (noCardImage.height / noCardImage.width); DrawTextureEx(noCardImage, Vector2{ 1920.0f - 361.0f, 1080.0f - calcHeight }, 0.0f, (361.0f / noCardImage.width), WHITE); }
flecs::query<const CardInstance, const CardRect, const RenderFront> cardQuery = ecs.query<const CardInstance, const CardRect, const RenderFront>();
cardQuery.each([this](flecs::entity entity, const CardInstance& instance, const CardRect& rect, const RenderFront& rf) { DrawTextureEx(textures[instance.source], Vector2{ rect.x, rect.y }, 0.0f, rect.h / textures[instance.source].height, WHITE); });}
void App::renderDebugAreas() { // Player 2 stuff DrawRectangleRec(Rectangle{ 0.0f, 0.0f, (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f}, Color{ 0x7f, 0x00, 0x00, 0xff }); DrawTextEx(font, "Player 2", Vector2{ (1920.0f - 361.0f) / 4.0f, 1080.0f / 4.0f }, 24.0f, 1.0f, WHITE); // Player 3 stuff DrawRectangleRec(Rectangle{ 0.0f, 1080.0f / 2.0f, (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f }, Color{ 0x55, 0x00, 0x00, 0xff }); DrawTextEx(font, "Player 3", Vector2{ (1920.0f - 361.0f) / 4.0f, 1080.0f * (3.0f / 4.0f) }, 24.0f, 1.0f, WHITE); // Player 4 stuff DrawRectangleRec(Rectangle{ (1920.0f - 361.0f) / 2.0f, 0.0f, (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f }, Color{ 0x00, 0x7f, 0x00, 0xff }); DrawTextEx(font, "Player 4", Vector2{ (1920.0f - 361.0f) * (3.0f / 4.0f), 1080.0f / 4.0f }, 24.0f, 1.0f, WHITE); // Player 1 stuff DrawRectangleRec(Rectangle{ (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f, (1920.0f - 361.0f) / 2.0f, 1080.0f / 2.0f }, Color{ 0x00, 0x55, 0x00, 0xff }); // // Command Zone DrawRectangleRec(zones["bottomRight"]["command"], Color{ 0x00, 0xff, 0x00, 0xff }); DrawTextEx(font, "Command", Vector2{ 1920.0f - 361.0f - cardWidth, 1080.0f * (11.0f / 12.0f) }, 24.0f, 1.0f, BLACK); // // Exile Zone DrawRectangleRec(zones["bottomRight"]["exile"], Color{ 0x3f, 0x3f, 0xff, 0xff }); DrawTextEx(font, "Exile", Vector2{ 1920.0f - 361.0f - (cardWidth * 2.0f), 1080.0f * (11.0f / 12.0f) }, 24.0f, 1.0f, Color{ 0xee, 0xee, 0xee, 0xff }); // // Graveyard Zone DrawRectangleRec(zones["bottomRight"]["graveyard"], Color{ 0xff, 0x3f, 0x3f, 0xff }); DrawTextEx(font, "Graveyard", Vector2{ 1920.0f - 361.0f - (cardWidth * 3.0f), 1080.0f * (11.0f / 12.0f) }, 24.0f, 1.0f, BLACK); // // Library Zone DrawRectangleRec(zones["bottomRight"]["library"], Color{ 0xff, 0xff, 0x00, 0xff }); DrawTextEx(font, "Library", Vector2{ 1920.0f - 361.0f - (cardWidth * 4.0f), 1080.0f * (11.0f / 12.0f) }, 24.0f, 1.0f, BLACK); // // Hand Zone DrawRectangleRec(zones["bottomRight"]["hand"], Color{ 0x00, 0xff, 0xff, 0xff }); DrawTextEx(font, "Hand", Vector2{ (1920.0f - 361.0f) / 2.0f, 1080.0f * (11.0f / 12.0f) }, 24.0f, 1.0f, BLACK);}
void App::cleanGame() { UnloadTexture(noCardImage); for (const auto& [key, value] : textures) { UnloadTexture(value); }}