add live ship stats panel

This commit is contained in:
2026-06-07 21:07:19 +02:00
parent 37a70ea321
commit f097e9a25f
20 changed files with 723 additions and 45 deletions

View File

@@ -3,6 +3,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/TracePrintRequestedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/TickAdvancedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingBlocksChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/EntitySelectedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/GameSpeedChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/BossWaveUpdatedEvent.h
PARENT_SCOPE

View File

@@ -0,0 +1,21 @@
#ifndef ENTITY_SELECTED_EVENT_H
#define ENTITY_SELECTED_EVENT_H
#include <optional>
#include "entt/entity/entity.hpp"
#include "Event.h"
class EntitySelectedEvent : public Event
{
public:
explicit EntitySelectedEvent(std::optional<entt::entity> entity)
: entity(entity)
{
}
const std::optional<entt::entity> entity;
};
#endif // ENTITY_SELECTED_EVENT_H

View File

@@ -5,6 +5,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/BeltSystem.h
${CMAKE_CURRENT_SOURCE_DIR}/Building.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.h
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayout.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprint.h
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsCalculator.h
@@ -18,6 +19,7 @@ SET(SRCS
${CMAKE_CURRENT_SOURCE_DIR}/TickDriver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/BeltSystem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/BuildingSystem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/EntityHitTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ShipStatsCalculator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/WaveSystem.cpp
PARENT_SCOPE

View File

@@ -0,0 +1,56 @@
#include "EntityHitTest.h"
#include <cmath>
#include "EntityAdmin.h"
#include "PositionComponent.h"
#include "StationBodyComponent.h"
#include "HealthComponent.h"
entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
{
const QPoint tile(static_cast<int>(std::floor(worldPos.x())),
static_cast<int>(std::floor(worldPos.y())));
entt::entity stationHit = entt::null;
admin.forEach<StationBodyComponent, HealthComponent>(
[&](entt::entity entity, const StationBodyComponent& sb, const HealthComponent& h)
{
if (stationHit != entt::null) { return; }
if (h.hp <= 0.0f) { return; }
for (const QPoint& cell : sb.bodyCells)
{
if (cell == tile)
{
stationHit = entity;
return;
}
}
});
if (stationHit != entt::null)
{
return stationHit;
}
constexpr float kShipHitRadiusSquared = 0.5f * 0.5f;
entt::entity bestShip = entt::null;
float bestDistSquared = kShipHitRadiusSquared;
admin.forEach<PositionComponent, HealthComponent>(
[&](entt::entity entity, const PositionComponent& pos, const HealthComponent& h)
{
if (h.hp <= 0.0f) { return; }
if (admin.hasAll<StationBodyComponent>(entity)) { return; }
const float dx = pos.value.x() - worldPos.x();
const float dy = pos.value.y() - worldPos.y();
const float distSquared = dx * dx + dy * dy;
if (distSquared < bestDistSquared)
{
bestDistSquared = distSquared;
bestShip = entity;
}
});
return bestShip;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <QVector2D>
#include "entt/entity/entity.hpp"
class EntityAdmin;
entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos);

View File

@@ -3,6 +3,16 @@
#include <map>
#include <utility>
#include "DynamicBodyComponent.h"
#include "EntityAdmin.h"
#include "HealthComponent.h"
#include "ModuleOwnerComponent.h"
#include "RepairToolComponent.h"
#include "SalvageCargoComponent.h"
#include "SensorRangeComponent.h"
#include "Tick.h"
#include "WeaponComponent.h"
ShipStats calculateShipStats(const GameConfig& config,
const std::string& shipId,
int level,
@@ -216,3 +226,68 @@ ShipStats calculateShipStats(const GameConfig& config,
return result;
}
ShipStats buildShipStatsFromEntity(const EntityAdmin& admin, entt::entity shipEntity)
{
ShipStats result{};
const HealthComponent& health = admin.get<HealthComponent>(shipEntity);
const DynamicBodyComponent& body = admin.get<DynamicBodyComponent>(shipEntity);
const SensorRangeComponent& sensor = admin.get<SensorRangeComponent>(shipEntity);
result.hp = health.maxHp;
result.maxSpeed_tps = body.maxSpeed_tpt * kTickRateHz;
result.sensorRange_tiles = sensor.value_tiles;
result.mainAcceleration_tpss = body.mainAcceleration_tptt * kTickRateHz * kTickRateHz;
result.maneuveringAcceleration_tpss = body.maneuveringAcceleration_tptt * kTickRateHz * kTickRateHz;
result.angularAcceleration_radpss = body.maxAngularAcceleration_rptt * kTickRateHz * kTickRateHz;
result.maxRotationSpeed_radps = body.maxRotationSpeed_rpt * kTickRateHz;
float weaponDps = 0.0f;
float weaponMaxRange = 0.0f;
bool hasWeapons = false;
float salvageRate = 0.0f;
float salvageMaxRange = 0.0f;
bool hasSalvage = false;
float repairRate = 0.0f;
float repairMaxRange = 0.0f;
bool hasRepair = false;
admin.forEach<ModuleOwnerComponent, WeaponComponent>(
[&](entt::entity /*child*/, const ModuleOwnerComponent& owner, const WeaponComponent& w)
{
if (owner.owner != shipEntity) { return; }
hasWeapons = true;
weaponDps += w.damage * w.fireRateHz;
if (w.range_tiles > weaponMaxRange) { weaponMaxRange = w.range_tiles; }
});
admin.forEach<ModuleOwnerComponent, SalvageCargoComponent>(
[&](entt::entity /*child*/, const ModuleOwnerComponent& owner, const SalvageCargoComponent& s)
{
if (owner.owner != shipEntity) { return; }
hasSalvage = true;
const float rate = (s.collectionIntervalTicks > 0)
? static_cast<float>(kTickRateHz) / static_cast<float>(s.collectionIntervalTicks)
: 0.0f;
salvageRate += rate;
if (s.collectionRange_tiles > salvageMaxRange) { salvageMaxRange = s.collectionRange_tiles; }
});
admin.forEach<ModuleOwnerComponent, RepairToolComponent>(
[&](entt::entity /*child*/, const ModuleOwnerComponent& owner, const RepairToolComponent& r)
{
if (owner.owner != shipEntity) { return; }
hasRepair = true;
repairRate += r.ratePerTick * kTickRateHz;
if (r.range_tiles > repairMaxRange) { repairMaxRange = r.range_tiles; }
});
if (hasWeapons) { result.weapons = ShipStats::WeaponStats{weaponDps, weaponMaxRange}; }
if (hasSalvage) { result.salvage = ShipStats::SalvageStats{salvageRate, salvageMaxRange}; }
if (hasRepair) { result.repair = ShipStats::RepairStats{repairRate, repairMaxRange}; }
return result;
}

View File

@@ -4,9 +4,13 @@
#include <string>
#include <vector>
#include "entt/entity/entity.hpp"
#include "GameConfig.h"
#include "ShipLayout.h"
class EntityAdmin;
// Effective stats for a ship with a given layout, after applying all passive
// module modifiers per REQ-MOD-STAT-CALC. Values are in display units:
// speeds in tiles/s, ranges in tiles, accelerations in tiles/s² or rad/s².
@@ -47,3 +51,6 @@ ShipStats calculateShipStats(const GameConfig& config,
const std::string& shipId,
int level,
const std::vector<PlacedModule>& modules);
ShipStats buildShipStatsFromEntity(const EntityAdmin& admin, entt::entity shipEntity);