#include "components/ProjectionComponent.hpp" #include "Entity.hpp" #include "components/GravityWellComponent.hpp" #include "components/PhysicsComponent.hpp" #include "components/StatsComponent.hpp" #include "components/TransformComponent.hpp" #include #include void ProjectionComponent::Setup() { points.clear(); } void ProjectionComponent::Update(float) { points.clear(); highlightActive = false; if (!context || !context->probeEntity || entity != context->probeEntity) { return; } auto transform = entity->GetComponent(); auto physics = entity->GetComponent(); if (!transform || !physics || !context->entities) { return; } float placementCost = 10.0f; float previewMass = static_cast(1 << 22); float previewMinDist = 28.0f; if (context->wellEntity) { auto controllerWell = context->wellEntity->GetComponent(); if (controllerWell) { placementCost = controllerWell->get().placementCost; if (controllerWell->get().mass > 0.0f) { previewMass = controllerWell->get().mass; } previewMinDist = controllerWell->get().minDist; } } bool canPreviewPlacement = false; if (context->statsEntity) { auto stats = context->statsEntity->GetComponent(); canPreviewPlacement = stats && stats->get().value >= placementCost; } const Vector2 mouse = GetMousePosition(); const double previewX = static_cast(mouse.x); const double previewY = static_cast(mouse.y); for (auto &other : *context->entities) { if (!other || other.get() == entity) { continue; } auto wellGravity = other->GetComponent(); if (wellGravity && wellGravity->get().active) { highlightActive = true; break; } } double px = static_cast(transform->get().x); double py = static_cast(transform->get().y); double vx = static_cast(physics->get().vx); double vy = static_cast(physics->get().vy); points.reserve(static_cast(steps)); for (int i = 0; i < steps; ++i) { physics->get().SimulateStep(px, py, vx, vy, static_cast(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(previewMinDist)); const double force = static_cast(previewMass) / (clampedDist * clampedDist); const double dt = static_cast(stepDt); vx += (dx / dist) * force * dt; vy += (dy / dist) * force * dt; physics->get().ClampVelocity(vx, vy); } } points.push_back({static_cast(px), static_cast(py)}); } Draw(); } void ProjectionComponent::Cleanup() { points.clear(); } void ProjectionComponent::Draw() const { if (points.empty()) { return; } const Color base = highlightActive ? activeColor : inactiveColor; for (size_t i = 0; i < points.size(); ++i) { // t goes from 0 to 1 across the points, used for fading effect const float t = (points.size() <= 1) ? 0.0f : (float)i / (points.size() - 1); Color c = base; float alphaScale = 1.0f - t; // multiply alpha to make it fade towards the end c.a = (unsigned char)((float)(base.a) * alphaScale); DrawCircleV(points[i], pointRadius, c); } }