change repair_tool application and add beams for salvager and repair_tool

This commit is contained in:
2026-06-18 22:14:09 +02:00
parent 7924e037aa
commit 9573b9789a
37 changed files with 498 additions and 199 deletions

View File

@@ -19,52 +19,91 @@ RepairSystem::RepairSystem(EntityAdmin& admin)
{
}
void RepairSystem::tick()
void RepairSystem::tick(Tick currentTick, std::vector<BeamFiredEvent>& outBeamFiredEvents)
{
TRACE();
// Apply heals whose mid-beam delay has elapsed (cycles started on prior ticks).
applyPendingHeals(currentTick);
const std::vector<RepairableInfo> repairables = buildRepairables(m_admin);
m_admin.forEach<RepairToolComponent, ModuleOwnerComponent>(
[&](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<PositionComponent>(owner.owner)) { return; }
const QVector2D ownerPos = m_admin.get<PositionComponent>(owner.owner).value;
// Honour the executor-set target if it is still valid and in range.
// 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<entt::entity> target;
if (tool.currentTarget)
{
const entt::entity t = *tool.currentTarget;
if (m_admin.isValid(t) && m_admin.hasAll<HealthComponent, PositionComponent>(t))
{
HealthComponent& th = m_admin.get<HealthComponent>(t);
const HealthComponent& th = m_admin.get<HealthComponent>(t);
const float dist =
(m_admin.get<PositionComponent>(t).value - ownerPos).length();
if (th.hp > 0.0f && th.hp < th.maxHp && dist <= tool.range_tiles)
{
th.hp = std::min(th.hp + tool.ratePerTick, th.maxHp);
return;
target = t;
}
}
}
// Fallback: heal the nearest damaged friendly within tool range.
tool.currentTarget = std::nullopt;
float bestDist = tool.range_tiles;
for (const RepairableInfo& r : repairables)
if (!target)
{
if (r.isEnemy) { continue; }
if (r.hp <= 0.0f || r.hp >= r.maxHp) { continue; }
const float dist = (r.position - ownerPos).length();
if (dist < bestDist)
tool.currentTarget = std::nullopt;
float bestDist = tool.range_tiles;
for (const RepairableInfo& r : repairables)
{
bestDist = dist;
tool.currentTarget = r.entity;
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 (!tool.currentTarget) { return; }
if (!target) { return; }
HealthComponent& targetHealth = m_admin.get<HealthComponent>(*tool.currentTarget);
targetHealth.hp = std::min(targetHealth.hp + tool.ratePerTick, targetHealth.maxHp);
// 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<PendingHeal>::iterator it = m_pendingHeals.begin();
while (it != m_pendingHeals.end())
{
if (it->appliesAt <= currentTick)
{
if (m_admin.isValid(it->target) && m_admin.hasAll<HealthComponent>(it->target))
{
HealthComponent& h = m_admin.get<HealthComponent>(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;
}
}
}