diff --git a/CMakeLists.txt b/CMakeLists.txt index a71ca58..2571ede 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,9 @@ add_executable(smb src/systems/EnemySystem.cpp src/AABB.cpp src/SoundManager.cpp - src/systems/SoundSystem.cpp) + src/systems/SoundSystem.cpp + src/systems/ScoreSystem.cpp + ) set_property(TARGET smb PROPERTY CXX_STANDARD 17) diff --git a/assets/tileset.png b/assets/tileset.png index 2fc601a..343c827 100644 Binary files a/assets/tileset.png and b/assets/tileset.png differ diff --git a/include/Components.h b/include/Components.h index 01794e3..eb9b0bd 100644 --- a/include/Components.h +++ b/include/Components.h @@ -258,7 +258,7 @@ struct FloatingPointsComponent : public Component { }; struct TextComponent : public Component { - explicit TextComponent(std::string& text) : text{std::move(text)} {} + explicit TextComponent(std::string&& text) : text{std::move(text)} {} std::string text; SDL_Texture* texture = nullptr; @@ -268,6 +268,12 @@ struct TextComponent : public Component { } }; +struct AddScoreComponent : public Component { + explicit AddScoreComponent(int score, bool addCoin = false) : score{score}, coin{addCoin} {} + int score = 0; + bool coin = false; +}; + struct TileMapComponent : public Component { TileMapComponent(uint16_t width, uint16_t height) : mapWidth{width}, mapHeight{height}, diff --git a/include/Game.h b/include/Game.h index 0e01a1b..f5f8927 100644 --- a/include/Game.h +++ b/include/Game.h @@ -15,6 +15,7 @@ #include "systems/AnimationSystem.h" #include "systems/PlayerSystem.h" #include "systems/SoundSystem.h" +#include "systems/ScoreSystem.h" #include "Constants.h" class Game { diff --git a/include/TextureManager.h b/include/TextureManager.h index 81a3cce..c99555e 100644 --- a/include/TextureManager.h +++ b/include/TextureManager.h @@ -76,6 +76,9 @@ enum TextureId { CASTLE_6, CASTLE_7, CASTLE_8, + COIN_SMALL_1, + COIN_SMALL_2, + COIN_SMALL_3, }; class TextureManager { diff --git a/include/systems/RenderSystem.h b/include/systems/RenderSystem.h index 561b1c0..cb66228 100644 --- a/include/systems/RenderSystem.h +++ b/include/systems/RenderSystem.h @@ -31,7 +31,7 @@ class RenderSystem : public System { TextureManager* textureManager{}; CameraComponent* camera{}; - void renderEntities(std::vector entities); + void renderEntities(std::vector entities, bool followCamera = true); void renderText(std::vector entities); diff --git a/include/systems/ScoreSystem.h b/include/systems/ScoreSystem.h new file mode 100644 index 0000000..5720bdc --- /dev/null +++ b/include/systems/ScoreSystem.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ecs/ecs.h" +#include "Components.h" +#include "Constants.h" + +class ScoreSystem : public System { + + void onAddedToWorld(World* world) override; + + void tick(World* world) override; + + void handleEvent(SDL_Event& event) override; + + void onRemovedFromWorld(World* world) override; + + ~ScoreSystem() override = default; + +private: + Entity* scoreEntity; + Entity* coinsEntity; + Entity* timeLeftEntity; + + int score = 0; + int coins = 0; + int time = 255 * 60; +}; \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 0f303df..46a00d6 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -24,6 +24,7 @@ void Game::init(const char* title, int width, int height, bool fullscreen) { world.registerSystem(new EnemySystem()); world.registerSystem(new CallbackSystem()); world.registerSystem(new AnimationSystem()); + world.registerSystem(new ScoreSystem()); world.registerSystem(new TileSystem()); world.registerSystem(new PhysicsSystem()); } diff --git a/src/TextureManager.cpp b/src/TextureManager.cpp index e4a7c10..2ad6c4e 100644 --- a/src/TextureManager.cpp +++ b/src/TextureManager.cpp @@ -90,6 +90,10 @@ TextureManager::TextureManager(SDL_Renderer* renderer) : renderer{renderer} { textures.insert_or_assign(CASTLE_6, new SDL_Rect{34, 85, TILE_SIZE, TILE_SIZE}); textures.insert_or_assign(CASTLE_7, new SDL_Rect{17, 102, TILE_SIZE, TILE_SIZE}); textures.insert_or_assign(CASTLE_8, new SDL_Rect{34, 102, TILE_SIZE, TILE_SIZE}); + + textures.insert_or_assign(COIN_SMALL_1, new SDL_Rect{119, 254, 5, 8}); + textures.insert_or_assign(COIN_SMALL_2, new SDL_Rect{129, 254, 5, 8}); + textures.insert_or_assign(COIN_SMALL_3, new SDL_Rect{124, 262, 5, 8}); } void TextureManager::renderTexture(TextureId textureId, SDL_Rect& dstRect, bool flipH, bool flipV) { diff --git a/src/systems/EnemySystem.cpp b/src/systems/EnemySystem.cpp index b5ad2c7..e0b9aee 100644 --- a/src/systems/EnemySystem.cpp +++ b/src/systems/EnemySystem.cpp @@ -57,6 +57,7 @@ void turtleShellInteractions(World* world, Entity* shell) { auto transform = other->get(); if (AABBCollision(shell->get(), other->get())) { world->create()->assign("100", transform->getCenterX(), transform->y); + world->create()->assign(100); flipEnemy(other); shell->remove(); shell->remove(); @@ -92,11 +93,13 @@ void EnemySystem::tick(World* world) { enemy->assign(TextureId::GOOMBA_CRUSHED); enemy->assign(*enemyTransform); world->create()->assign("100", enemyTransform->getCenterX(), enemyTransform->y); + world->create()->assign(100); } break; case Enemy::Type::TURTLE: stepOnTurtle(enemy); world->create()->assign("200", enemyTransform->getCenterX(), enemyTransform->y); + world->create()->assign(200); break; case Enemy::Type::TURTLE_SHELL: { if (enemy->get()->accX == 0) { @@ -133,6 +136,7 @@ void EnemySystem::tick(World* world) { if (bottomTile && bottomTile->has()) { if (bottomTile->get()->hit) { world->create()->assign("100", transform->getCenterX(), transform->y); + world->create()->assign(100); flipEnemy(entity); } } diff --git a/src/systems/MapSystem.cpp b/src/systems/MapSystem.cpp index daedcec..2cfeb55 100644 --- a/src/systems/MapSystem.cpp +++ b/src/systems/MapSystem.cpp @@ -27,12 +27,15 @@ void MapSystem::tick(World* world) { || transform->left() > camera->right() + CAMERA_WORLD_OFFSET || transform->top() > camera->bottom() ) { - tileMap->set( - (int) (transform->getCenterX() / TILE_SIZE), - (int) (transform->getCenterY() / TILE_SIZE), - nullptr - ); - world->destroy(entity); + if (entity->has()) { + tileMap->set( + (int) (transform->getCenterX() / TILE_SIZE), + (int) (transform->getCenterY() / TILE_SIZE), + nullptr + ); + } + + if (!entity->has()) world->destroy(entity); } } diff --git a/src/systems/PlayerSystem.cpp b/src/systems/PlayerSystem.cpp index 052845a..1358219 100644 --- a/src/systems/PlayerSystem.cpp +++ b/src/systems/PlayerSystem.cpp @@ -220,8 +220,7 @@ void PlayerSystem::tick(World* world) { if (sprint) kinetic->accX *= 1.5; if (jump) { player->get()->accY = -MARIO_JUMP_ACCELERATION; - auto jumpsound = world->create(); - jumpsound->assign(Sound::Id::JUMP); + world->create()->assign(Sound::Id::JUMP); } if ((bool) std::abs(kinetic->speedX) || (bool) std::abs(kinetic->accX)) { if ((kinetic->speedX > 0 && kinetic->accX < 0) || @@ -303,6 +302,7 @@ void PlayerSystem::eatMushroom(World* world) { player->get()->getCenterX(), player->get()->y ); + world->create()->assign(1000); if (player->has()) return; player->assign(); player->assign( @@ -386,5 +386,5 @@ void PlayerSystem::onAddedToWorld(World* world) { player->assign(); player->assign(); player->assign(); - player->assign(40, 40, TILE_SIZE - 4, SMALL_MARIO_COLLIDER_HEIGHT); + player->assign(40, 140, TILE_SIZE - 4, SMALL_MARIO_COLLIDER_HEIGHT); } diff --git a/src/systems/RenderSystem.cpp b/src/systems/RenderSystem.cpp index c6e922c..f833aae 100644 --- a/src/systems/RenderSystem.cpp +++ b/src/systems/RenderSystem.cpp @@ -9,6 +9,7 @@ void RenderSystem::tick(World* world) { renderEntities(world->find()); renderEntities(world->find()); renderEntities(world->find()); + renderEntities(world->find(), false); renderText(world->find()); //Editor @@ -68,12 +69,17 @@ RenderSystem::~RenderSystem() { SDL_DestroyRenderer(renderer); } -void RenderSystem::renderEntities(std::vector entities) { +void RenderSystem::renderEntities(std::vector entities, bool followCamera) { for (auto entity : entities) { auto transform = entity->get(); auto texture = entity->get(); - dstRect.x = transform->left() - camera->left() + texture->offSetX; - dstRect.y = transform->top() - camera->top() + texture->offSetY; + if (followCamera) { + dstRect.x = transform->left() - camera->left() + texture->offSetX; + dstRect.y = transform->top() - camera->top() + texture->offSetY; + } else { + dstRect.x = transform->left() + texture->offSetX; + dstRect.y = transform->top() + texture->offSetY; + } dstRect.w = texture->w > 0 ? texture->w : transform->w; dstRect.h = texture->h > 0 ? texture->h : transform->h; @@ -93,8 +99,8 @@ void RenderSystem::renderText(std::vector entities) { SDL_FreeSurface(surface); } - dstRect.x = transformComponent->left() - camera->left(); - dstRect.y = transformComponent->top() - camera->top(); + dstRect.x = transformComponent->left(); + dstRect.y = transformComponent->top(); dstRect.w = transformComponent->w; dstRect.h = transformComponent->h; diff --git a/src/systems/ScoreSystem.cpp b/src/systems/ScoreSystem.cpp new file mode 100644 index 0000000..b10ebd5 --- /dev/null +++ b/src/systems/ScoreSystem.cpp @@ -0,0 +1,123 @@ +#include "systems/ScoreSystem.h" + +void ScoreSystem::onAddedToWorld(World* world) { + System::onAddedToWorld(world); + + auto paddingH = 22; + auto paddingV = 12; + auto textHeight = 10; + auto spacingV = -3; + + auto availableWidth = SNES_RESOLUTION_WIDTH - paddingH * 2; + auto columns = 4; + auto columnWidth = availableWidth / columns; + + //FIRST COLUMN (left aligned) + auto marioEntity = world->create(); + marioEntity->assign("mario"); + marioEntity->assign(paddingH, paddingV, 40, textHeight); + + scoreEntity = world->create(); + scoreEntity->assign("000000"); + scoreEntity->assign(paddingH, paddingV + textHeight + spacingV, 48, textHeight); + + // SECOND COLUMN (center aligned) + coinsEntity = world->create(); + coinsEntity->assign("x00"); + auto w = 24; + coinsEntity->assign(paddingH + columnWidth + (columnWidth - w) / 2 + 5, + paddingV + textHeight + spacingV, w, textHeight); + auto coinIco = world->create(); + coinIco->assign(TextureId::COIN_SMALL_1); + coinIco->assign( + std::vector{ + TextureId::COIN_SMALL_1, + TextureId::COIN_SMALL_1, + TextureId::COIN_SMALL_1, + TextureId::COIN_SMALL_3, + TextureId::COIN_SMALL_2, + TextureId::COIN_SMALL_3 + }, 10); + + coinIco->assign(""); + coinIco->assign( + paddingH + columnWidth + (columnWidth - w) / 2 - 4, + paddingV + textHeight + spacingV + 1, 5, 8 + ); + + // THIRD COLUMN (center aligned) + auto worldEntity = world->create(); + worldEntity->assign("world"); + w = 40; + worldEntity->assign(paddingH + columnWidth * 2 + (columnWidth - w) / 2, + paddingV, w, textHeight); + + auto worldNumberEntity = world->create(); + worldNumberEntity->assign("1 1"); + w = 20; + worldNumberEntity->assign(paddingH + columnWidth * 2 + (columnWidth - w) / 2, + paddingV + textHeight + spacingV, w, textHeight); + + // FOURTH COLUMN (right aligned) + auto timeEntity = world->create(); + timeEntity->assign("time"); + timeEntity->assign(SNES_RESOLUTION_WIDTH - paddingH - 32, paddingV, 32, textHeight); + + timeLeftEntity = world->create(); + timeLeftEntity->assign("000"); + timeLeftEntity->assign(SNES_RESOLUTION_WIDTH - paddingH - 24, + paddingV + textHeight + spacingV, 24, textHeight); +} + +void ScoreSystem::tick(World* world) { + bool updateScore = false; + bool updateCoins = false; + + for (auto e : world->find()) { + auto s = e->get(); + score += s->score; + updateScore = true; + if (s->coin) { + coins++; + updateCoins = true; + } + world->destroy(e); + } + + if (updateScore) { + auto scoreString = std::to_string(score); + auto final = std::string{}; + auto zeros = 6 - scoreString.length(); + while (zeros > 0) { + zeros--; + final += '0'; + } + final += scoreString; + scoreEntity->assign(std::move(final)); + } + + if (updateCoins) { + auto final = std::string{}; + auto coinString = std::to_string(coins); + auto zeros = 2 - coinString.length(); + while (zeros > 0) { + zeros--; + final += '0'; + } + final += coinString; + coinsEntity->assign("x" + final); + } + + time--; + timeLeftEntity->assign(std::to_string(time / 60)); +} + +void ScoreSystem::handleEvent(SDL_Event& event) { + System::handleEvent(event); +} + +void ScoreSystem::onRemovedFromWorld(World* world) { + System::onRemovedFromWorld(world); + world->destroy(coinsEntity); + world->destroy(scoreEntity); +} diff --git a/src/systems/TileSystem.cpp b/src/systems/TileSystem.cpp index 01c797b..ba2e7e3 100644 --- a/src/systems/TileSystem.cpp +++ b/src/systems/TileSystem.cpp @@ -52,6 +52,7 @@ void createCoin(World* world, Entity* block) { coin->assign([=]() { auto transform = coin->get(); world->create()->assign("100", transform->getCenterX(), transform->y); + world->create()->assign(100, true); world->destroy(coin); }, 20); @@ -132,13 +133,15 @@ void TileSystem::tick(World* world) { entity->remove(); } + for (auto points: world->find()) { + auto camera = world->findFirst()->get(); auto pointsComponent = points->get(); auto textLength = pointsComponent->text.length(); auto pointEntity = world->create(); - pointEntity->assign(pointsComponent->text); + pointEntity->assign(std::move(pointsComponent->text)); auto w = textLength * 2; - pointEntity->assign(pointsComponent->x - w / 2, pointsComponent->y, w, 14); + pointEntity->assign((pointsComponent->x - w / 2) - camera->left(), pointsComponent->y, w, 14); pointEntity->assign(0, 0); pointEntity->get()->speedY = -2.0f; pointEntity->assign([=]() { world->destroy(pointEntity); }, 50);