Add black hole and stuff
parent
fb506f6f05
commit
bd622d1873
|
|
@ -8,6 +8,7 @@
|
||||||
#include "components/LifetimeComponent.hpp"
|
#include "components/LifetimeComponent.hpp"
|
||||||
#include "components/NullZoneComponent.hpp"
|
#include "components/NullZoneComponent.hpp"
|
||||||
#include "components/PhysicsComponent.hpp"
|
#include "components/PhysicsComponent.hpp"
|
||||||
|
#include "components/PlayerBlackHoleComponent.hpp"
|
||||||
#include "components/ProbeStateComponent.hpp"
|
#include "components/ProbeStateComponent.hpp"
|
||||||
#include "components/ProjectionComponent.hpp"
|
#include "components/ProjectionComponent.hpp"
|
||||||
#include "components/RenderComponent.hpp"
|
#include "components/RenderComponent.hpp"
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,21 @@ inline void DrawMainMenu() {
|
||||||
"GRAVITY SURFING");
|
"GRAVITY SURFING");
|
||||||
|
|
||||||
GuiLabel((Rectangle){WINDOW_WIDTH / 2 - 200, WINDOW_HEIGHT / 2, 400, 60},
|
GuiLabel((Rectangle){WINDOW_WIDTH / 2 - 200, WINDOW_HEIGHT / 2, 400, 60},
|
||||||
"LEFT CLICK to turn on gravity well and attract probe\n"
|
"LEFT CLICK to place a black hole that lasts 1 second\n"
|
||||||
"Avoid hazards and collect stars to keep your meter from draining\n"
|
"Each black hole costs 10 meter and does not drain over time\n"
|
||||||
"If your meter runs out, gravity well turns off and you lose control\n"
|
"Avoid hazards and collect stars to refill meter\n"
|
||||||
"until you collect another star\n"
|
"Place as many black holes as your meter allows\n"
|
||||||
"Press SPACE or LEFT CLICK to start");
|
"Press SPACE or LEFT CLICK to start");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void DrawDeathStats(int stars) {
|
||||||
|
GuiLabel((Rectangle){WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 - 40, 300, 32}, "YOU DIED");
|
||||||
|
GuiLabel((Rectangle){WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 - 4, 300, 24},
|
||||||
|
TextFormat("Stars Collected: %d", stars));
|
||||||
|
GuiLabel((Rectangle){WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 + 28, 300, 20},
|
||||||
|
"Press ENTER or LEFT CLICK to retry");
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Controls Functions Definitions (local)
|
// Controls Functions Definitions (local)
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "Components.hpp"
|
#include "Components.hpp"
|
||||||
|
|
||||||
#include "EnergyBarRaygui.hpp"
|
#include "EnergyBarRaygui.hpp"
|
||||||
|
#include "raylib.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
std::shared_ptr<Entity> CreateProbe() {
|
std::shared_ptr<Entity> CreateProbe() {
|
||||||
|
|
@ -54,19 +55,8 @@ std::shared_ptr<Entity> CreateGravityWell() {
|
||||||
transform.y = 230.0f;
|
transform.y = 230.0f;
|
||||||
|
|
||||||
auto &well = e->AddComponent<GravityWellComponent>();
|
auto &well = e->AddComponent<GravityWellComponent>();
|
||||||
well.mass = static_cast<float>(1 << 22);
|
well.mass = 0.0f;
|
||||||
well.minDist = 28.0f;
|
well.minDist = 28.0f;
|
||||||
well.followLerp = 12.0f;
|
|
||||||
|
|
||||||
auto &render = e->AddComponent<RenderComponent>();
|
|
||||||
render.draw = [e]() {
|
|
||||||
auto transform = e->GetComponent<TransformComponent>();
|
|
||||||
if (!transform) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DrawCircleLines(static_cast<int>(transform->get().x), static_cast<int>(transform->get().y),
|
|
||||||
18.0f, Color{86, 197, 255, 255});
|
|
||||||
};
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,6 +75,8 @@ std::shared_ptr<Entity> CreateBlackHole(float x, float y) {
|
||||||
well.controlledByMouse = false;
|
well.controlledByMouse = false;
|
||||||
well.alwaysActive = true;
|
well.alwaysActive = true;
|
||||||
|
|
||||||
|
// shader removed: previously used for black hole lensing
|
||||||
|
|
||||||
auto &collider = e->AddComponent<ColliderComponent>();
|
auto &collider = e->AddComponent<ColliderComponent>();
|
||||||
collider.radius = 18.0f;
|
collider.radius = 18.0f;
|
||||||
|
|
||||||
|
|
@ -107,6 +99,52 @@ std::shared_ptr<Entity> CreateBlackHole(float x, float y) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Entity> CreatePlayerBlackHole(float worldX, float y, float scrollX, float ttl) {
|
||||||
|
auto e = std::make_shared<Entity>();
|
||||||
|
auto &transform = e->AddComponent<TransformComponent>();
|
||||||
|
transform.x = worldX - scrollX;
|
||||||
|
transform.y = y;
|
||||||
|
|
||||||
|
auto &scrollable = e->AddComponent<ScrollableComponent>();
|
||||||
|
scrollable.worldX = worldX;
|
||||||
|
|
||||||
|
auto &well = e->AddComponent<GravityWellComponent>();
|
||||||
|
well.mass = static_cast<float>(1 << 22);
|
||||||
|
well.minDist = 28.0f;
|
||||||
|
well.controlledByMouse = false;
|
||||||
|
well.alwaysActive = true;
|
||||||
|
|
||||||
|
// shader removed: previously used for black hole lensing
|
||||||
|
|
||||||
|
auto &lifetime = e->AddComponent<LifetimeComponent>();
|
||||||
|
lifetime.remaining = ttl;
|
||||||
|
|
||||||
|
// Mark this as a player-created black hole so it can be removed via right-click
|
||||||
|
e->AddComponent<PlayerBlackHoleComponent>();
|
||||||
|
|
||||||
|
auto &render = e->AddComponent<RenderComponent>();
|
||||||
|
render.draw = [e]() {
|
||||||
|
auto transform = e->GetComponent<TransformComponent>();
|
||||||
|
if (!transform) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lifetime = e->GetComponent<LifetimeComponent>();
|
||||||
|
if (!lifetime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int cx = static_cast<int>(transform->get().x);
|
||||||
|
const int cy = static_cast<int>(transform->get().y);
|
||||||
|
float t = 360 * lifetime->get().remaining / 1;
|
||||||
|
DrawCircleSector({(float)cx, (float)cy}, 28.0f, 0.0f, t, 32, Color{70, 95, 170, 25});
|
||||||
|
DrawCircleLines(cx, cy, 18.0f, Color{70, 95, 170, 240});
|
||||||
|
DrawCircleLines(cx, cy, 24.0f, Color{95, 120, 200, 120});
|
||||||
|
};
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Entity> CreateStar(float x, float y) {
|
std::shared_ptr<Entity> CreateStar(float x, float y) {
|
||||||
auto e = std::make_shared<Entity>();
|
auto e = std::make_shared<Entity>();
|
||||||
auto &t = e->AddComponent<TransformComponent>();
|
auto &t = e->AddComponent<TransformComponent>();
|
||||||
|
|
@ -115,7 +153,7 @@ std::shared_ptr<Entity> CreateStar(float x, float y) {
|
||||||
auto &scrollable = e->AddComponent<ScrollableComponent>();
|
auto &scrollable = e->AddComponent<ScrollableComponent>();
|
||||||
scrollable.worldX = x;
|
scrollable.worldX = x;
|
||||||
auto &collider = e->AddComponent<ColliderComponent>();
|
auto &collider = e->AddComponent<ColliderComponent>();
|
||||||
collider.radius = 12.0f;
|
collider.radius = 20.0f;
|
||||||
|
|
||||||
e->AddComponent<CollectibleComponent>();
|
e->AddComponent<CollectibleComponent>();
|
||||||
auto &render = e->AddComponent<RenderComponent>();
|
auto &render = e->AddComponent<RenderComponent>();
|
||||||
|
|
@ -126,9 +164,10 @@ std::shared_ptr<Entity> CreateStar(float x, float y) {
|
||||||
}
|
}
|
||||||
Vector2 center = {transform->get().x, transform->get().y};
|
Vector2 center = {transform->get().x, transform->get().y};
|
||||||
Color STAR_COLOR = Color{255, 223, 86, 255};
|
Color STAR_COLOR = Color{255, 223, 86, 255};
|
||||||
Color GLOW_COLOR = Color{255, 223, 86, 120};
|
Color GLOW_COLOR = Color{255, 223, 86, 80};
|
||||||
DrawCircleV(center, 12.0f, GLOW_COLOR);
|
DrawCircleV(center, 20.0f, GLOW_COLOR);
|
||||||
DrawPoly(center, 3, 4.0f, transform->get().x, STAR_COLOR);
|
DrawPoly(center, 3, 10.0f, transform->get().x, STAR_COLOR);
|
||||||
|
DrawPoly(center, 3, 10.0f, transform->get().x + 180, STAR_COLOR);
|
||||||
};
|
};
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
std::shared_ptr<Entity> CreateProbe();
|
std::shared_ptr<Entity> CreateProbe();
|
||||||
std::shared_ptr<Entity> CreateGravityWell();
|
std::shared_ptr<Entity> CreateGravityWell();
|
||||||
std::shared_ptr<Entity> CreateBlackHole(float x, float y);
|
std::shared_ptr<Entity> CreateBlackHole(float x, float y);
|
||||||
|
std::shared_ptr<Entity> CreatePlayerBlackHole(float worldX, float y, float scrollX,
|
||||||
|
float ttl = 1.0f);
|
||||||
std::shared_ptr<Entity> CreateStar(float x, float y);
|
std::shared_ptr<Entity> CreateStar(float x, float y);
|
||||||
std::shared_ptr<Entity> CreateAsteroid(float x, float y);
|
std::shared_ptr<Entity> CreateAsteroid(float x, float y);
|
||||||
std::shared_ptr<Entity> CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f);
|
std::shared_ptr<Entity> CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "components/GravityWellComponent.hpp"
|
#include "components/GravityWellComponent.hpp"
|
||||||
#include "components/NullZoneComponent.hpp"
|
#include "components/NullZoneComponent.hpp"
|
||||||
#include "components/PhysicsComponent.hpp"
|
#include "components/PhysicsComponent.hpp"
|
||||||
#include "components/StatsComponent.hpp"
|
|
||||||
#include "components/TransformComponent.hpp"
|
#include "components/TransformComponent.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -17,33 +16,9 @@ void GravityReceiverComponent::Update(float dt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!well && context->wellEntity) {
|
|
||||||
well = context->wellEntity;
|
|
||||||
}
|
|
||||||
if (!well) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto myTransform = entity->GetComponent<TransformComponent>();
|
auto myTransform = entity->GetComponent<TransformComponent>();
|
||||||
auto physics = entity->GetComponent<PhysicsComponent>();
|
auto physics = entity->GetComponent<PhysicsComponent>();
|
||||||
auto wellTransform = well->GetComponent<TransformComponent>();
|
if (!myTransform || !physics) {
|
||||||
auto wellGravity = well->GetComponent<GravityWellComponent>();
|
|
||||||
if (!myTransform || !physics || !wellTransform || !wellGravity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StatsComponent *statsPtr = nullptr;
|
|
||||||
if (context->statsEntity) {
|
|
||||||
auto stats = context->statsEntity->GetComponent<StatsComponent>();
|
|
||||||
if (stats) {
|
|
||||||
statsPtr = &stats->get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wellGravity->get().active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (statsPtr && statsPtr->value <= 0.0f) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,23 +47,35 @@ void GravityReceiverComponent::Update(float dt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float dx = wellTransform->get().x - myTransform->get().x;
|
if (!context->entities) {
|
||||||
const float dy = wellTransform->get().y - myTransform->get().y;
|
|
||||||
const float dist = std::sqrt(dx * dx + dy * dy);
|
|
||||||
if (dist <= 0.0001f) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float clampedDist = std::max(dist, wellGravity->get().minDist);
|
for (auto &other : *context->entities) {
|
||||||
const float force = wellGravity->get().mass / (clampedDist * clampedDist);
|
if (!other || other.get() == entity) {
|
||||||
const float nx = dx / dist;
|
continue;
|
||||||
const float ny = dy / dist;
|
}
|
||||||
|
|
||||||
physics->get().vx += nx * force * dt;
|
auto wellTransform = other->GetComponent<TransformComponent>();
|
||||||
physics->get().vy += ny * force * dt;
|
auto wellGravity = other->GetComponent<GravityWellComponent>();
|
||||||
|
if (!wellTransform || !wellGravity || !wellGravity->get().active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (statsPtr) {
|
const float dx = wellTransform->get().x - myTransform->get().x;
|
||||||
statsPtr->Drain(statsPtr->drainRate * dt);
|
const float dy = wellTransform->get().y - myTransform->get().y;
|
||||||
|
const float dist = std::sqrt(dx * dx + dy * dy);
|
||||||
|
if (dist <= 0.0001f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float clampedDist = std::max(dist, wellGravity->get().minDist);
|
||||||
|
const float force = wellGravity->get().mass / (clampedDist * clampedDist);
|
||||||
|
const float nx = dx / dist;
|
||||||
|
const float ny = dy / dist;
|
||||||
|
|
||||||
|
physics->get().vx += nx * force * dt;
|
||||||
|
physics->get().vy += ny * force * dt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,9 @@
|
||||||
#include "Component.hpp"
|
#include "Component.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies gravity well force to the probe and drains meter while active.
|
* Applies gravity from active wells to the probe.
|
||||||
*/
|
*/
|
||||||
struct GravityReceiverComponent : public Component {
|
struct GravityReceiverComponent : public Component {
|
||||||
/**
|
|
||||||
* The gravity well entity that this receiver is affected by. If null, the receiver will not
|
|
||||||
* apply any forces.
|
|
||||||
*/
|
|
||||||
Entity *well = nullptr;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the probe is inside a void zone, which disables the well's gravity.
|
* Whether the probe is inside a void zone, which disables the well's gravity.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,58 @@
|
||||||
#include "components/GravityWellComponent.hpp"
|
#include "components/GravityWellComponent.hpp"
|
||||||
|
|
||||||
|
#include "Entities.hpp"
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
#include "components/TransformComponent.hpp"
|
#include "components/PlayerBlackHoleComponent.hpp"
|
||||||
|
#include "components/StatsComponent.hpp"
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
void GravityWellComponent::Setup() {}
|
void GravityWellComponent::Setup() {}
|
||||||
|
|
||||||
void GravityWellComponent::Update(float dt) {
|
void GravityWellComponent::Update(float) {
|
||||||
auto transform = entity->GetComponent<TransformComponent>();
|
|
||||||
if (!transform) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!controlledByMouse) {
|
if (!controlledByMouse) {
|
||||||
active = alwaysActive;
|
active = alwaysActive;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
active = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
|
active = false;
|
||||||
|
|
||||||
|
if (!context || !context->entities) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right click removes all player-created black holes
|
||||||
|
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) {
|
||||||
|
for (auto &ent : *context->entities) {
|
||||||
|
if (!ent || ent->queuedForFree) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto marker = ent->GetComponent<PlayerBlackHoleComponent>();
|
||||||
|
if (marker) {
|
||||||
|
ent->QueueFree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do not early return; allow left-click placement to still happen in the same frame
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || !context->statsEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stats = context->statsEntity->GetComponent<StatsComponent>();
|
||||||
|
if (!stats || stats->get().value < placementCost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats->get().Drain(placementCost);
|
||||||
|
|
||||||
const Vector2 mouse = GetMousePosition();
|
const Vector2 mouse = GetMousePosition();
|
||||||
auto &t = transform->get();
|
const float worldX = context->scrollX + mouse.x;
|
||||||
const float blend = std::clamp(followLerp * dt, 0.0f, 1.0f);
|
|
||||||
t.x += (mouse.x - t.x) * blend;
|
auto blackHole = CreatePlayerBlackHole(worldX, mouse.y, context->scrollX, placementTtl);
|
||||||
t.y += (mouse.y - t.y) * blend;
|
blackHole->SetContext(context);
|
||||||
|
context->entities->push_back(blackHole);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GravityWellComponent::Cleanup() {}
|
void GravityWellComponent::Cleanup() {}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "Component.hpp"
|
#include "Component.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents player-controlled gravity source that follows mouse input.
|
* Handles gravity well behavior for both hazards and player-spawned black holes.
|
||||||
*/
|
*/
|
||||||
struct GravityWellComponent : public Component {
|
struct GravityWellComponent : public Component {
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,13 +18,12 @@ struct GravityWellComponent : public Component {
|
||||||
float minDist = 30.0f;
|
float minDist = 30.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the gravity well is currently active and should apply gravitational forces to
|
* Whether the gravity source is currently active and should apply gravitational force.
|
||||||
* receivers (e.g. tied to mouse button state).
|
|
||||||
*/
|
*/
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, active state is driven by left mouse button and this well follows the cursor.
|
* If true, this component is used as the player input controller.
|
||||||
*/
|
*/
|
||||||
bool controlledByMouse = true;
|
bool controlledByMouse = true;
|
||||||
|
|
||||||
|
|
@ -34,9 +33,14 @@ struct GravityWellComponent : public Component {
|
||||||
bool alwaysActive = false;
|
bool alwaysActive = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lerp factor for how quickly the gravity well follows the mouse position.
|
* Meter cost paid to place one player black hole.
|
||||||
*/
|
*/
|
||||||
float followLerp = 12.0f;
|
float placementCost = 10.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifetime in seconds for each player-placed black hole.
|
||||||
|
*/
|
||||||
|
float placementTtl = 1.0f;
|
||||||
|
|
||||||
void Setup() override;
|
void Setup() override;
|
||||||
void Update(float dt) override;
|
void Update(float dt) override;
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ bool PhysicsComponent::ComputeGravityDeltaVelocity(double px, double py, double
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (probeMeterDepleted && wellGravity->get().controlledByMouse) {
|
if (probeMeterDepleted && !wellGravity->get().alwaysActive) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Component.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker component to identify player-created black holes. It carries no
|
||||||
|
* behavior and is only used for runtime identification and removal.
|
||||||
|
*/
|
||||||
|
struct PlayerBlackHoleComponent : public Component {
|
||||||
|
void Setup() override {}
|
||||||
|
void Update(float) override {}
|
||||||
|
void Cleanup() override {}
|
||||||
|
};
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
#include "components/ProjectionComponent.hpp"
|
#include "components/ProjectionComponent.hpp"
|
||||||
|
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
#include "components/GravityReceiverComponent.hpp"
|
|
||||||
#include "components/GravityWellComponent.hpp"
|
#include "components/GravityWellComponent.hpp"
|
||||||
#include "components/PhysicsComponent.hpp"
|
#include "components/PhysicsComponent.hpp"
|
||||||
|
#include "components/StatsComponent.hpp"
|
||||||
#include "components/TransformComponent.hpp"
|
#include "components/TransformComponent.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
void ProjectionComponent::Setup() { points.clear(); }
|
void ProjectionComponent::Setup() { points.clear(); }
|
||||||
|
|
||||||
void ProjectionComponent::Update(float) {
|
void ProjectionComponent::Update(float) {
|
||||||
|
|
@ -18,25 +21,45 @@ void ProjectionComponent::Update(float) {
|
||||||
|
|
||||||
auto transform = entity->GetComponent<TransformComponent>();
|
auto transform = entity->GetComponent<TransformComponent>();
|
||||||
auto physics = entity->GetComponent<PhysicsComponent>();
|
auto physics = entity->GetComponent<PhysicsComponent>();
|
||||||
auto receiver = entity->GetComponent<GravityReceiverComponent>();
|
if (!transform || !physics || !context->entities) {
|
||||||
if (!transform || !physics || !receiver) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity *wellEntity = receiver->get().well ? receiver->get().well : context->wellEntity;
|
float placementCost = 10.0f;
|
||||||
if (!wellEntity) {
|
float previewMass = static_cast<float>(1 << 22);
|
||||||
return;
|
float previewMinDist = 28.0f;
|
||||||
|
if (context->wellEntity) {
|
||||||
|
auto controllerWell = context->wellEntity->GetComponent<GravityWellComponent>();
|
||||||
|
if (controllerWell) {
|
||||||
|
placementCost = controllerWell->get().placementCost;
|
||||||
|
if (controllerWell->get().mass > 0.0f) {
|
||||||
|
previewMass = controllerWell->get().mass;
|
||||||
|
}
|
||||||
|
previewMinDist = controllerWell->get().minDist;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wellTransform = wellEntity->GetComponent<TransformComponent>();
|
bool canPreviewPlacement = false;
|
||||||
auto wellGravity = wellEntity->GetComponent<GravityWellComponent>();
|
if (context->statsEntity) {
|
||||||
if (!wellTransform || !wellGravity) {
|
auto stats = context->statsEntity->GetComponent<StatsComponent>();
|
||||||
return;
|
canPreviewPlacement = stats && stats->get().value >= placementCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only highlight if the well is active, inactive will be a lighter color to show the potential
|
const Vector2 mouse = GetMousePosition();
|
||||||
// projection if the player were to activate it
|
const double previewX = static_cast<double>(mouse.x);
|
||||||
highlightActive = wellGravity->get().active;
|
const double previewY = static_cast<double>(mouse.y);
|
||||||
|
|
||||||
|
for (auto &other : *context->entities) {
|
||||||
|
if (!other || other.get() == entity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wellGravity = other->GetComponent<GravityWellComponent>();
|
||||||
|
if (wellGravity && wellGravity->get().active) {
|
||||||
|
highlightActive = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double px = static_cast<double>(transform->get().x);
|
double px = static_cast<double>(transform->get().x);
|
||||||
double py = static_cast<double>(transform->get().y);
|
double py = static_cast<double>(transform->get().y);
|
||||||
|
|
@ -46,6 +69,21 @@ void ProjectionComponent::Update(float) {
|
||||||
points.reserve(static_cast<size_t>(steps));
|
points.reserve(static_cast<size_t>(steps));
|
||||||
for (int i = 0; i < steps; ++i) {
|
for (int i = 0; i < steps; ++i) {
|
||||||
physics->get().SimulateStep(px, py, vx, vy, static_cast<double>(stepDt), true);
|
physics->get().SimulateStep(px, py, vx, vy, static_cast<double>(stepDt), true);
|
||||||
|
|
||||||
|
if (canPreviewPlacement && !physics->get().IsInsideNullZone(px)) {
|
||||||
|
const double dx = previewX - px;
|
||||||
|
const double dy = previewY - py;
|
||||||
|
const double dist = std::sqrt(dx * dx + dy * dy);
|
||||||
|
if (dist > 0.0001) {
|
||||||
|
const double clampedDist = std::max(dist, static_cast<double>(previewMinDist));
|
||||||
|
const double force = static_cast<double>(previewMass) / (clampedDist * clampedDist);
|
||||||
|
const double dt = static_cast<double>(stepDt);
|
||||||
|
vx += (dx / dist) * force * dt;
|
||||||
|
vy += (dy / dist) * force * dt;
|
||||||
|
physics->get().ClampVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
points.push_back({static_cast<float>(px), static_cast<float>(py)});
|
points.push_back({static_cast<float>(px), static_cast<float>(py)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ void SpawnComponent::Update(float) {
|
||||||
|
|
||||||
while (cursorWX < spawnLimit) {
|
while (cursorWX < spawnLimit) {
|
||||||
const float r = static_cast<float>(GetRandomValue(0, 99));
|
const float r = static_cast<float>(GetRandomValue(0, 99));
|
||||||
if (r < 50.0f) {
|
if (r < 70.0f) {
|
||||||
const float y = static_cast<float>(GetRandomValue(48, GetScreenHeight() - 48));
|
const float y = static_cast<float>(GetRandomValue(48, GetScreenHeight() - 48));
|
||||||
auto star = CreateStar(cursorWX, y);
|
auto star = CreateStar(cursorWX, y);
|
||||||
star->SetContext(context);
|
star->SetContext(context);
|
||||||
context->entities->push_back(star);
|
context->entities->push_back(star);
|
||||||
} else if (r < 78.0f) {
|
} else if (r < 90.0f) {
|
||||||
const float y = static_cast<float>(GetRandomValue(42, GetScreenHeight() - 42));
|
const float y = static_cast<float>(GetRandomValue(42, GetScreenHeight() - 42));
|
||||||
auto asteroid = CreateAsteroid(cursorWX, y);
|
auto asteroid = CreateAsteroid(cursorWX, y);
|
||||||
asteroid->SetContext(context);
|
asteroid->SetContext(context);
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
struct StatsComponent : public Component {
|
struct StatsComponent : public Component {
|
||||||
float value = 60.0f;
|
float value = 60.0f;
|
||||||
float maxValue = 100.0f;
|
float maxValue = 100.0f;
|
||||||
float drainRate = 14.0f;
|
float drainRate = 10.0f;
|
||||||
float gainPerStar = 28.0f;
|
float gainPerStar = 50.0f;
|
||||||
int stars = 0;
|
int stars = 0;
|
||||||
|
|
||||||
void Setup() override;
|
void Setup() override;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "scene/DeathScene.hpp"
|
#include "scene/DeathScene.hpp"
|
||||||
|
|
||||||
|
#include "EnergyBarRaygui.hpp"
|
||||||
#include "scene/GameplayScene.hpp"
|
#include "scene/GameplayScene.hpp"
|
||||||
#include "scene/SceneManager.hpp"
|
#include "scene/SceneManager.hpp"
|
||||||
#include "scene/StartMenuScene.hpp"
|
#include "scene/StartMenuScene.hpp"
|
||||||
|
|
@ -17,4 +18,7 @@ void DeathScene::Update(float) {
|
||||||
|
|
||||||
void DeathScene::Draw() {
|
void DeathScene::Draw() {
|
||||||
Scene::Draw();
|
Scene::Draw();
|
||||||
|
// Draw centered death stats using the HUD helper
|
||||||
|
// collectedStars is a private member; access via this-> to satisfy older compilers / linters
|
||||||
|
DrawDeathStats(this->collectedStars);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@ class StartMenuScene;
|
||||||
class DeathScene : public Scene {
|
class DeathScene : public Scene {
|
||||||
public:
|
public:
|
||||||
explicit DeathScene(SceneManager &owner) : Scene(owner) {}
|
explicit DeathScene(SceneManager &owner) : Scene(owner) {}
|
||||||
|
// Alternate constructor that accepts the star count to display.
|
||||||
|
explicit DeathScene(SceneManager &owner, int stars) : Scene(owner), collectedStars(stars) {}
|
||||||
void Update(float dt) override;
|
void Update(float dt) override;
|
||||||
void Draw() override;
|
void Draw() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int collectedStars = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,23 @@
|
||||||
#include "scene/DeathScene.hpp"
|
#include "scene/DeathScene.hpp"
|
||||||
#include "scene/SceneManager.hpp"
|
#include "scene/SceneManager.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
void GameplayScene::Enter() {
|
void GameplayScene::Enter() {
|
||||||
entities.clear();
|
entities.clear();
|
||||||
entities.reserve(20);
|
entities.reserve(20);
|
||||||
context = {};
|
context = {};
|
||||||
wantsDeathScene = false;
|
|
||||||
collectedCount = 0;
|
collectedCount = 0;
|
||||||
meterValue = 60.0f;
|
meterValue = 60.0f;
|
||||||
|
|
||||||
context.onPlayerDeath = [this]() { wantsDeathScene = true; };
|
context.onPlayerDeath = [this]() {
|
||||||
|
// Inject the collected star count into the queued DeathScene by passing it
|
||||||
|
// as a constructor argument. SceneManager::QueueSceneChange supports
|
||||||
|
// forwarding constructor args.
|
||||||
|
manager.QueueSceneChange<DeathScene>(collectedCount);
|
||||||
|
};
|
||||||
context.AddCollectiblePickedListener([this](Entity &collectible) {
|
context.AddCollectiblePickedListener([this](Entity &collectible) {
|
||||||
++collectedCount;
|
collectedCount++;
|
||||||
|
|
||||||
auto transform = collectible.GetComponent<TransformComponent>();
|
auto transform = collectible.GetComponent<TransformComponent>();
|
||||||
if (!transform || !context.entities) {
|
if (!transform || !context.entities) {
|
||||||
|
|
@ -56,23 +62,19 @@ void GameplayScene::Enter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameplayScene::Update(float dt) {
|
void GameplayScene::Exit() { entities.clear(); }
|
||||||
UpdateAllSystems(entities, dt);
|
|
||||||
|
|
||||||
if (wantsDeathScene) {
|
void GameplayScene::Update(float dt) { UpdateAllSystems(entities, dt); }
|
||||||
manager.QueueSceneChange<DeathScene>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameplayScene::Draw() {
|
void GameplayScene::Draw() {
|
||||||
std::optional<std::reference_wrapper<ScrollComponent>> worldScroll;
|
for (auto &entity : entities) {
|
||||||
if (context.worldEntity) {
|
if (!entity || entity->queuedForFree) {
|
||||||
worldScroll = context.worldEntity->GetComponent<ScrollComponent>();
|
continue;
|
||||||
}
|
}
|
||||||
|
auto render = entity->GetComponent<RenderComponent>();
|
||||||
std::optional<std::reference_wrapper<TransformComponent>> probeTransform;
|
if (render) {
|
||||||
if (context.probeEntity) {
|
render->get().Draw();
|
||||||
probeTransform = context.probeEntity->GetComponent<TransformComponent>();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene::Draw();
|
Scene::Draw();
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ class GameplayScene : public Scene {
|
||||||
public:
|
public:
|
||||||
explicit GameplayScene(SceneManager &owner) : Scene(owner) {}
|
explicit GameplayScene(SceneManager &owner) : Scene(owner) {}
|
||||||
void Enter() override;
|
void Enter() override;
|
||||||
|
void Exit() override;
|
||||||
void Update(float dt) override;
|
void Update(float dt) override;
|
||||||
void Draw() override;
|
void Draw() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GameContext context;
|
GameContext context;
|
||||||
bool wantsDeathScene = false;
|
|
||||||
int collectedCount = 0;
|
int collectedCount = 0;
|
||||||
float meterValue = 60.0f;
|
float meterValue = 60.0f;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue