Files
dota_factory/src/balancing/ArenaSimulation.h
Malte Langkabel df7f60c898 migrate every factory query off BuildingSystem onto the free functions
The eleven forwarding members added last commit are gone; callers now read the
data directly through FactoryQueries.h. Simulation and ArenaSimulation expose
getFactoryState() so the UI, the balancing view and the tests can reach it.

isQueuedForDeconstruction joined the free functions along the way — it only
reaches findBuilding, so it was state-pure too.

No facade was introduced. The chained form was the reason one looked attractive,
but rewriting sim.getBuildings().findBuilding(id) to findBuilding(sim.getFactoryState(), id)
turned out to be mechanical, and the result says which data is read rather than
which system happens to own it.

BuildingSystem.cpp is down to 1735 lines and no longer answers questions about
the factory — it only changes it. What remains on it are the mutators, the tick
phases, and the queries that also need GameConfig.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
2026-08-04 21:52:49 +02:00

143 lines
4.1 KiB
C++

#pragma once
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <random>
#include <string>
#include <vector>
#include "BalancingConfig.h"
#include "BeltSystem.h"
#include "FactoryState.h"
#include "EntityAdmin.h"
#include "BuildingId.h"
#include "entt/entity/entity.hpp"
#include "BeamFiredEvent.h"
#include "GameConfig.h"
#include "Tick.h"
class AiSystem;
class BuildingSystem;
class CombatSystem;
class DynamicBodySystem;
class MovementIntentSystem;
class RepairSystem;
class SalvagerSystem;
class ShipSystem;
class DebrisSystem;
struct ArenaStatus
{
struct Entry
{
std::string displayName;
// Level suffix shown in the widget/inspect display. Set for the HQ and
// defence stations; empty for ships, which no longer have a level.
std::optional<int> level;
int total;
int surviving;
};
struct TeamStatus
{
std::string name;
double threatLevel = 0.0; // accumulated threat of the team's configured ships
// Remaining durability of the team's ships and defence stations (HQ
// excluded). currentEhp is summed live; maxEhp is the fixed full-HP
// baseline. See getEhpPercentText() for the displayed value.
double currentEhp = 0.0;
double maxEhp = 0.0;
std::vector<Entry> entries; // HQ first, then ships, then stations
// Remaining EHP as a whole-number percentage ("NN%"), or "n/a" when the
// team has no ships or stations (maxEhp == 0).
std::string getEhpPercentText() const;
};
TeamStatus teams[2];
bool finished = false;
std::optional<int> winnerTeam; // 0 or 1 when finished; nullopt while running
// Game time the fight has lasted (simulated ticks * fixed tick duration).
// Meaningful once finished; the battle duration shown for completed runs.
double durationSeconds = 0.0;
};
class ArenaSimulation
{
public:
ArenaSimulation(const GameConfig& gameConfig,
ArenaConfig arenaConfig,
unsigned int seed = 0);
~ArenaSimulation();
void run();
void requestStop();
void tickOnce();
std::vector<BeamFiredEvent> drainBeamFiredEvents();
ArenaStatus getStatus() const;
bool isFinished() const;
std::optional<int> getWinnerTeam() const;
Tick getCurrentTick() const;
const ArenaConfig& getArenaConfig() const;
const BuildingSystem& getBuildings() const;
const FactoryState& getFactoryState() const;
const ShipSystem& getShips() const;
const DebrisSystem& getDebrisSystem() const;
EntityAdmin& getAdmin();
const EntityAdmin& getAdmin() const;
private:
BuildingId allocateBuildingId();
void placeStructures();
void spawnShips();
void computeTeamMaxEhp();
void tick();
void tickDeaths();
void updateStatus();
const GameConfig& m_gameConfig;
ArenaConfig m_arenaConfig;
std::mt19937 m_rng;
Tick m_currentTick;
BuildingId m_nextBuildingId;
EntityAdmin m_admin;
FactoryState m_factoryState;
BeltSystem m_beltSystem;
std::unique_ptr<BuildingSystem> m_buildingSystem;
std::unique_ptr<ShipSystem> m_shipSystem;
std::unique_ptr<AiSystem> m_aiSystem;
std::unique_ptr<MovementIntentSystem> m_movementIntentSystem;
std::unique_ptr<DynamicBodySystem> m_dynamicBodySystem;
std::unique_ptr<CombatSystem> m_combatSystem;
std::unique_ptr<DebrisSystem> m_debrisSystem;
std::unique_ptr<SalvagerSystem> m_salvagerSystem;
std::unique_ptr<RepairSystem> m_repairSystem;
entt::entity m_team1HqEntity;
entt::entity m_team2HqEntity;
bool m_finished;
std::optional<int> m_winnerTeam;
std::atomic<bool> m_stopRequested;
// Static accumulated threat per team, computed once from the configured roster.
double m_teamThreat[2] = {0.0, 0.0};
// Full-HP baseline per team (ships + defence stations, HQ excluded), computed
// once after spawning; the EHP-percentage denominator.
double m_teamMaxEhp[2] = {0.0, 0.0};
std::vector<BeamFiredEvent> m_beamFiredEvents;
mutable std::mutex m_statusMutex;
ArenaStatus m_status;
};