#include "RepairSystem.h" #include #include #include #include #include "BehaviorTargeting.h" #include "EntityAdmin.h" #include "HealthComponent.h" #include "ModuleOwnerComponent.h" #include "PositionComponent.h" #include "RepairToolComponent.h" #include "tracing.h" RepairSystem::RepairSystem(EntityAdmin& admin) : m_admin(admin) { } void RepairSystem::tick(Tick currentTick, std::vector& outBeamFiredEvents) { TRACE(); // Apply heals whose mid-beam delay has elapsed (cycles started on prior ticks). applyPendingHeals(currentTick); const std::vector repairables = buildRepairables(m_admin); m_admin.forEach( [&](entt::entity /*re*/, RepairToolComponent& tool, const ModuleOwnerComponent& owner) { if (tool.cooldownTicksRemaining > 0) { --tool.cooldownTicksRemaining; } if (tool.cooldownTicksRemaining > 0) { return; } if (tool.repairIntervalTicks <= 0) { return; } if (!m_admin.hasAll(owner.owner)) { return; } const QVector2D ownerPos = m_admin.get(owner.owner).value; // Choose a target: honour the executor-set target if it is still valid // and in range, else fall back to the nearest damaged friendly in range. std::optional target; if (tool.currentTarget) { const entt::entity t = *tool.currentTarget; if (m_admin.isValid(t) && m_admin.hasAll(t)) { const HealthComponent& th = m_admin.get(t); const float dist = (m_admin.get(t).value - ownerPos).length(); if (th.hp > 0.0f && th.hp < th.maxHp && dist <= tool.range_tiles) { target = t; } } } if (!target) { tool.currentTarget = std::nullopt; float bestDist = tool.range_tiles; for (const RepairableInfo& r : repairables) { if (r.isEnemy) { continue; } if (r.hp <= 0.0f || r.hp >= r.maxHp) { continue; } const float dist = (r.position - ownerPos).length(); if (dist < bestDist) { bestDist = dist; tool.currentTarget = r.entity; } } target = tool.currentTarget; } if (!target) { return; } // Start a repair cycle: emit the beam now, apply the heal mid-beam, and // begin the cooldown at cycle start (not at effect application). outBeamFiredEvents.push_back( BeamFiredEvent{BeamKind::Repair, owner.owner, *target, currentTick}); m_pendingHeals.push_back({*target, tool.repairAmountHp, currentTick + kBeamImpactDelayTicks}); tool.cooldownTicksRemaining = tool.repairIntervalTicks; }); } void RepairSystem::applyPendingHeals(Tick currentTick) { std::vector::iterator it = m_pendingHeals.begin(); while (it != m_pendingHeals.end()) { if (it->appliesAt <= currentTick) { if (m_admin.isValid(it->target) && m_admin.hasAll(it->target)) { HealthComponent& h = m_admin.get(it->target); if (h.hp > 0.0f && h.hp < h.maxHp) { h.hp = std::min(h.hp + it->amountHp, h.maxHp); } } it = m_pendingHeals.erase(it); } else { ++it; } } }