#include "components/PhysicsComponent.hpp" #include "Entity.hpp" #include "components/GravityWellComponent.hpp" #include "components/NullZoneComponent.hpp" #include "components/StatsComponent.hpp" #include "components/TransformComponent.hpp" #include #include void PhysicsComponent::Setup() {} void PhysicsComponent::ClampVelocity(float &vxRef, float &vyRef) const { const float speed = std::sqrt(vxRef * vxRef + vyRef * vyRef); if (speed > speedCap && speed > 0.0f) { const float scale = speedCap / speed; vxRef *= scale; vyRef *= scale; } } void PhysicsComponent::ClampVelocity(double &vxRef, double &vyRef) const { const double speed = std::sqrt(vxRef * vxRef + vyRef * vyRef); if (speed > static_cast(speedCap) && speed > 0.0) { const double scale = static_cast(speedCap) / speed; vxRef *= scale; vyRef *= scale; } } void PhysicsComponent::IntegratePosition(float &xRef, float &yRef, float vxValue, float vyValue, float dt) { xRef += vxValue * dt; yRef += vyValue * dt; } void PhysicsComponent::IntegratePosition(double &xRef, double &yRef, double vxValue, double vyValue, double dt) { xRef += vxValue * dt; yRef += vyValue * dt; } bool PhysicsComponent::IsInsideNullZone(double xPos) const { if (!context || !context->entities) { return false; } for (auto &other : *context->entities) { if (!other || other.get() == entity) { continue; } auto zone = other->GetComponent(); auto zoneTransform = other->GetComponent(); if (!zone || !zoneTransform) { continue; } const double left = static_cast(zoneTransform->get().x); const double right = left + static_cast(zone->get().width); if (xPos >= left && xPos <= right) { return true; } } return false; } // looks weird because of formatter bool PhysicsComponent::ComputeGravityDeltaVelocity(double px, double py, double dt, double &dvx, double &dvy, bool ignoreWellActive) const { dvx = 0.0; dvy = 0.0; if (!context || !context->entities) { return false; } bool probeMeterDepleted = false; if (context && entity == context->probeEntity && context->statsEntity) { auto stats = context->statsEntity->GetComponent(); if (stats && stats->get().value <= 0.0f) { // meter drained, block gravity from anything that isn't always active probeMeterDepleted = true; } } if (IsInsideNullZone(px)) { // null zone overrides gravity, so the probe/globally simulated object stops feeling wells return false; } bool applied = false; for (auto &other : *context->entities) { if (!other || other.get() == entity) { continue; } auto wellTransform = other->GetComponent(); auto wellGravity = other->GetComponent(); if (!wellTransform || !wellGravity) { continue; } if (probeMeterDepleted && !wellGravity->get().alwaysActive) { continue; } if (!ignoreWellActive && !wellGravity->get().active) { continue; } const double dx = static_cast(wellTransform->get().x) - px; const double dy = static_cast(wellTransform->get().y) - py; const double dist = std::sqrt(dx * dx + dy * dy); if (dist <= 0.0001) { continue; } // guard against infinite force by respecting the well's minimum distance const double clampedDist = std::max(dist, static_cast(wellGravity->get().minDist)); const double force = static_cast(wellGravity->get().mass) / (clampedDist * clampedDist); dvx += (dx / dist) * force * dt; dvy += (dy / dist) * force * dt; applied = true; } return applied; } void PhysicsComponent::SimulateStep(double &xRef, double &yRef, double &vxRef, double &vyRef, double dt, bool ignoreWellActive) const { double dvx = 0.0; double dvy = 0.0; ComputeGravityDeltaVelocity(xRef, yRef, dt, dvx, dvy, ignoreWellActive); vxRef += dvx; vyRef += dvy; ClampVelocity(vxRef, vyRef); IntegratePosition(xRef, yRef, vxRef, vyRef, dt); } void PhysicsComponent::Update(float dt) { auto transform = entity->GetComponent(); if (!transform) { return; } double x = static_cast(transform->get().x); double y = static_cast(transform->get().y); double vxLocal = static_cast(vx); double vyLocal = static_cast(vy); SimulateStep(x, y, vxLocal, vyLocal, static_cast(dt), false); transform->get().x = static_cast(x); transform->get().y = static_cast(y); vx = static_cast(vxLocal); vy = static_cast(vyLocal); } void PhysicsComponent::Cleanup() {}