#include "CombatSystem.h" #include "EcsComponents.h" #include "EntityAdmin.h" static constexpr Tick kWeaponImpactDelayTicks = 5; CombatSystem::CombatSystem(const GameConfig& config) : m_config(config) { } void CombatSystem::tick(Tick currentTick, EntityAdmin& admin, BuildingSystem& /*buildings*/, std::vector& outFireEvents) { // Ship weapons. admin.forEach( [&](entt::entity e, Weapon& weapon, ThreatResponse& threat, Position& pos, Faction& faction) { weapon.currentTarget = threat.currentTarget; resolveWeapon(e, weapon, pos, faction, currentTick, admin, outFireEvents); }); // Station weapons. admin.forEach( [&](entt::entity e, Weapon& weapon, Position& pos, Faction& faction) { resolveWeapon(e, weapon, pos, faction, currentTick, admin, outFireEvents); }); } void CombatSystem::resolveWeapon( entt::entity shipEntity, Weapon& weapon, const Position& ownPos, const Faction& ownFaction, Tick currentTick, EntityAdmin& admin, std::vector& out) { if (weapon.cooldownTicks > 0.0f) { weapon.cooldownTicks -= 1.0f; } if (weapon.cooldownTicks > 0.0f) { return; } // Validate or clear existing target. if (weapon.currentTarget) { const entt::entity t = *weapon.currentTarget; if (!admin.isValid(t) || !admin.hasAll(t)) { weapon.currentTarget = std::nullopt; } else { const float distanceSquared = (ownPos.value - admin.get(t).value).lengthSquared(); if (distanceSquared > weapon.range * weapon.range) { weapon.currentTarget = std::nullopt; } } } // Acquire a new target if needed (nearest opposing-faction ship). if (!weapon.currentTarget) { float bestDistanceSquared = weapon.range * weapon.range; admin.forEach( [&](entt::entity candidate, const ShipIdentity& /*si*/, const Position& candidatePos, const Faction& candidateFaction) { const bool isValidTarget = ownFaction.isEnemy ? !candidateFaction.isEnemy : candidateFaction.isEnemy; if (!isValidTarget) { return; } const float distanceSquared = (candidatePos.value - ownPos.value).lengthSquared(); if (distanceSquared < bestDistanceSquared) { bestDistanceSquared = distanceSquared; weapon.currentTarget = candidate; } }); } if (!weapon.currentTarget) { return; } const entt::entity targetEntity = *weapon.currentTarget; m_pendingDamage.push_back({targetEntity, weapon.damage, currentTick + kWeaponImpactDelayTicks}); FireEvent evt; evt.shooter = shipEntity; evt.target = targetEntity; evt.emittedAt = currentTick; out.push_back(evt); weapon.cooldownTicks = static_cast(kTickRateHz) / weapon.fireRateHz; } void CombatSystem::applyPendingDamage(Tick currentTick, EntityAdmin& admin) { std::vector::iterator it = m_pendingDamage.begin(); while (it != m_pendingDamage.end()) { if (it->appliesAt <= currentTick) { if (admin.isValid(it->target) && admin.hasAll(it->target)) { admin.get(it->target).hp -= it->amount; } it = m_pendingDamage.erase(it); } else { ++it; } } }