diff --git a/as6/Components.hpp b/as6/Components.hpp index d501dc4..7d06bec 100644 --- a/as6/Components.hpp +++ b/as6/Components.hpp @@ -112,6 +112,8 @@ struct NullZoneComponent : public Component { struct ColliderComponent : public Component { float radius = 8.0f; + bool monitoring = false; + bool monitorable = true; std::vector> onCollision; void Setup() override {} @@ -125,7 +127,35 @@ struct ColliderComponent : public Component { onCollision.push_back(std::move(callback)); } - void Update(float) override {} + void Update(float) override { + if (!monitoring || !context || !context->entities || entity->queuedForFree) { + return; + } + + auto selfTransform = entity->GetComponent(); + if (!selfTransform) { + return; + } + + for (auto &other : *context->entities) { + if (!other || other.get() == entity || other->queuedForFree) { + continue; + } + + auto otherCollider = other->GetComponent(); + auto otherTransform = other->GetComponent(); + if (!otherCollider || !otherTransform || !otherCollider->get().monitorable) { + continue; + } + + const float dx = selfTransform->get().x - otherTransform->get().x; + const float dy = selfTransform->get().y - otherTransform->get().y; + const float r = radius + otherCollider->get().radius; + if ((dx * dx + dy * dy) <= (r * r)) { + EmitCollision(*other); + } + } + } void Cleanup() override {} }; @@ -166,22 +196,35 @@ struct ProjectionComponent : public Component { void Cleanup() override {} }; -struct CollectibleComponent : public Component { - bool collected = false; +struct LifetimeComponent : public Component { + float remaining = 0.0f; + void Setup() override {} + void Update(float dt) override { + remaining -= dt; + if (remaining <= 0.0f) { + entity->QueueFree(); + } + } + void Cleanup() override {} +}; +struct CollectibleComponent : public Component { void Setup() override { auto selfCollider = entity->GetComponent(); if (!selfCollider) { return; } + selfCollider->get().monitoring = true; + selfCollider->get().AddCollisionListener([this](Entity &other) { - if (collected || !context || !context->probeEntity || &other != context->probeEntity) { + if (entity->queuedForFree || !context || !context->probeEntity || + &other != context->probeEntity) { return; } - collected = true; context->EmitCollectiblePicked(*entity); + entity->QueueFree(); if (!context->hudEntity) { return; @@ -208,6 +251,8 @@ struct HazardComponent : public Component { return; } + selfCollider->get().monitoring = true; + selfCollider->get().AddCollisionListener([this](Entity &other) { if (!context || !context->probeEntity || &other != context->probeEntity) { return; diff --git a/as6/Entities.hpp b/as6/Entities.hpp index e597fb7..2916d38 100644 --- a/as6/Entities.hpp +++ b/as6/Entities.hpp @@ -19,6 +19,7 @@ std::shared_ptr CreateProbe() { auto &collider = e->AddComponent(); collider.radius = 7.0f; + collider.monitorable = true; auto &probeState = e->AddComponent(); probeState.spawnX = 96.0f; @@ -107,6 +108,32 @@ std::shared_ptr CreateAsteroid(float x, float y) { return e; } +std::shared_ptr CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f) { + auto e = std::make_shared(); + auto &t = e->AddComponent(); + t.x = x; + t.y = y; + + auto &physics = e->AddComponent(); + physics.vx = vx; + physics.vy = vy; + physics.speedCap = 260.0f; + + auto &lifetime = e->AddComponent(); + lifetime.remaining = ttl; + + auto &render = e->AddComponent(); + render.draw = [e]() { + auto transform = e->GetComponent(); + if (!transform) { + return; + } + DrawCircleV({transform->get().x, transform->get().y}, 3.0f, Color{245, 196, 104, 220}); + }; + + return e; +} + std::shared_ptr CreateNullZone(float x, float width) { auto e = std::make_shared(); auto &t = e->AddComponent(); diff --git a/as6/SceneManager.hpp b/as6/SceneManager.hpp index 62039c6..a683c1c 100644 --- a/as6/SceneManager.hpp +++ b/as6/SceneManager.hpp @@ -150,7 +150,19 @@ inline void GameplayScene::Enter() { meterValue = 60.0f; context.onPlayerDeath = [this]() { wantsDeathScene = true; }; - context.AddCollectiblePickedListener([this](Entity &) { ++collectedCount; }); + context.AddCollectiblePickedListener([this](Entity &collectible) { + ++collectedCount; + + auto transform = collectible.GetComponent(); + if (!transform || !context.entities) { + return; + } + + context.entities->push_back( + CreateDebris(transform->get().x - 4.0f, transform->get().y, -22.0f, -36.0f)); + context.entities->push_back( + CreateDebris(transform->get().x + 4.0f, transform->get().y, 24.0f, -28.0f)); + }); context.AddMeterChangedListener([this](float, float newValue) { meterValue = newValue; }); entities.push_back(CreateWorld()); @@ -164,10 +176,29 @@ inline void GameplayScene::Enter() { if (auto meter = entities.back()->GetComponent()) { meterValue = meter->get().value; } + + context.entities = &entities; + context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr; + context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr; + context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr; + context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr; + + for (auto &entity : entities) { + if (!entity) { + continue; + } + entity->SetContext(&context); + } } inline void GameplayScene::Update(float dt) { - UpdateAllSystems(entities, context, dt); + UpdateAllSystems(entities, dt); + + context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr; + context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr; + context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr; + context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr; + if (wantsDeathScene) { manager.QueueSceneChange(); } @@ -195,12 +226,7 @@ inline void GameplayScene::Draw() { DrawText(TextFormat("stars: %i", collectedCount), 14, 100, 16, Color{255, 223, 86, 255}); for (auto &entity : entities) { - if (!entity) { - continue; - } - - auto collectible = entity->GetComponent(); - if (collectible && collectible->get().collected) { + if (!entity || entity->queuedForFree) { continue; } diff --git a/as6/Systems.hpp b/as6/Systems.hpp index fc62952..93e2145 100644 --- a/as6/Systems.hpp +++ b/as6/Systems.hpp @@ -1,67 +1,20 @@ #pragma once -#include "Components.hpp" #include "Entity.hpp" -#include "GameContext.hpp" #include #include #include -void UpdateAllSystems(std::vector> &entities, GameContext &context, - float deltaTime) { - context.entities = &entities; - context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr; - context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr; - context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr; - context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr; - +void UpdateAllSystems(std::vector> &entities, float deltaTime) { for (auto &entity : entities) { if (!entity) { continue; } - if (entity->context != &context) { - entity->SetContext(&context); - } - entity->Update(deltaTime); } - for (size_t i = 0; i < entities.size(); ++i) { - auto &a = entities[i]; - if (!a) { - continue; - } - - auto aTransform = a->GetComponent(); - auto aCollider = a->GetComponent(); - if (!aTransform || !aCollider) { - continue; - } - - for (size_t j = i + 1; j < entities.size(); ++j) { - auto &b = entities[j]; - if (!b) { - continue; - } - - auto bTransform = b->GetComponent(); - auto bCollider = b->GetComponent(); - if (!bTransform || !bCollider) { - continue; - } - - const float dx = aTransform->get().x - bTransform->get().x; - const float dy = aTransform->get().y - bTransform->get().y; - const float r = aCollider->get().radius + bCollider->get().radius; - if ((dx * dx + dy * dy) <= (r * r)) { - aCollider->get().EmitCollision(*b); - bCollider->get().EmitCollision(*a); - } - } - } - auto remover = [](const std::shared_ptr &entity) { if (!entity) { return true;