Files
dota_factory/src/balancing/ArenaSimulation.h
Malte Langkabel e7bfd91054 number the buildings from the factory that holds them
The building-id counter was the last piece of factory data living on Simulation
behind a callback: every construction site, every building, and every tile a
station entity claims took its id from a std::function BuildingSystem held, which
the arena and three test fixtures each had to supply.

It moves into FactoryState as nextBuildingId, handed out by
allocateBuildingId(state) in FactoryQueries beside the other operations over the
state. BuildingSystem's callback is gone; so are Simulation::allocateBuildingId
and ArenaSimulation::allocateBuildingId, whose remaining callers now allocate
from the state directly.

Checksum order is untouched: Simulation folds the counter where it always did.

What is left on BuildingSystem is the config, the belts, the RNG, and two
callbacks that reach genuinely outside the factory -- spawning a finished ship
into the entity model, and testing an output group against the unlock state.
Neither is factory data, so this is where the migration stops.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
2026-08-19 17:31:48 +02:00

140 lines
4.0 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 FactoryState& getFactoryState() const;
const ShipSystem& getShips() const;
const DebrisSystem& getDebrisSystem() const;
EntityAdmin& getAdmin();
const EntityAdmin& getAdmin() const;
private:
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;
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;
};