Files
dota_factory/src/lib/sim/Simulation.h

194 lines
7.1 KiB
C++

#pragma once
#include <map>
#include <memory>
#include <random>
#include <set>
#include <string>
#include <vector>
#include <QPoint>
#include "BeltSystem.h"
#include "EntityAdmin.h"
#include "entt/entity/entity.hpp"
#include "SchematicChoiceOption.h"
#include "BuildingType.h"
#include "BuildingId.h"
#include "EventHandler.h"
#include "WeaponFiredEvent.h"
#include "GameConfig.h"
#include "Rotation.h"
#include "Tick.h"
#include "TracePrintRequestedEvent.h"
class AiSystem;
class BuildingSystem;
class CombatSystem;
class DynamicBodySystem;
class MovementIntentSystem;
class ShipSystem;
class ScrapSystem;
class WaveSystem;
class Simulation: public CombinedEventHandler<TracePrintRequestedEvent>
{
public:
explicit Simulation(GameConfig config, unsigned int seed = 0);
~Simulation();
const GameConfig& config() const;
// Reinitializes all simulation state as if constructed fresh.
void reset(unsigned int seed = 0);
// Reloads config then reinitializes all simulation state.
void reset(GameConfig newConfig, unsigned int seed = 0);
// Advances the simulation by one tick. Tick order per architecture.md §Tick Order.
void tick();
// Returns all fire events accumulated since the last drain, clearing the
// internal queue. Call once per rendered frame (REQ-SHP-FIRING-BEAM).
std::vector<WeaponFiredEvent> drainWeaponFiredEvents();
// Returns the pending schematic choices (empty if no drop is pending).
const std::vector<SchematicChoiceOption>& getPendingSchematicChoices() const;
// Returns true if there are pending schematic choices waiting for player input.
bool hasSchematicChoicesPending() const;
// Applies the player's chosen schematic from the pending choices.
// choiceIndex must be in [0, pendingChoices.size()).
// Clears the pending choices after application.
void applySchematicChoice(int choiceIndex);
Tick currentTick() const;
int buildingBlocksStock() const;
bool isGameOver() const;
double threatLevel() const;
double threatAccumulationRate() const;
double maxFactoryProductionThreatRate() const;
double currentFactoryProductionThreatRate() const;
int bossWaveCounter() const;
Tick bossCountdownTicks() const;
Tick normalGapRemainingTicks() const;
// Ship schematic state queries.
int schematicLevel(const std::string& shipId) const;
bool isSchematicUnlocked(const std::string& shipId) const;
// Module schematic state queries.
int moduleSchematicLevel(const std::string& moduleId) const;
bool isModuleSchematicUnlocked(const std::string& moduleId) const;
// Implicit recipe/item unlock queries (REQ-LOCK-IMPLICIT).
bool isRecipeUnlocked(const std::string& recipeId) const;
bool isItemUnlocked(const std::string& itemId) const;
// Checks affordability, deducts building blocks, and places the building.
// Returns the new entity id, or kInvalidBuildingId if blocks are insufficient.
BuildingId tryPlaceBuilding(BuildingType type, QPoint anchor, Rotation rotation);
// Demolishes the building with the given id and refunds building blocks.
void demolish(BuildingId id);
BuildingSystem& buildings();
const BuildingSystem& buildings() const;
BeltSystem& belts();
const BeltSystem& belts() const;
ShipSystem& ships();
const ShipSystem& ships() const;
ScrapSystem& scraps();
const ScrapSystem& scraps() const;
EntityAdmin& admin();
const EntityAdmin& admin() const;
private:
void handleEvent(std::shared_ptr<const TracePrintRequestedEvent> event) override;
BuildingId allocateBuildingId(); // Strictly increasing; never returns kInvalidBuildingId.
// Populate HQ, player defence stations, and the first enemy station set.
void placeInitialStructures();
// Place two enemy defence stations for the given generation level.
// Stores their IDs in m_currentEnemyStationIds.
void placeEnemyStationSet(int generation);
// Tick step 9: remove dead ships and buildings, drop scrap, handle push.
void tickDeathsAndLoot();
// Generate up to 3 schematic choices (REQ-DEF-SCHEMATIC-DROP) for the player.
void generateSchematicChoices(int destroyedStationLevel);
GameConfig m_config;
std::mt19937 m_rng;
Tick m_currentTick;
Tick m_nextDepartureTick;
BuildingId m_nextBuildingId;
int m_buildingBlocksStock;
bool m_gameOver = false;
// Pre-placed structure IDs.
BuildingId m_hqBuildingId; // Building id (for belt integration)
entt::entity m_hqProxyEntity; // ECS entity (HP, targeting)
entt::entity m_playerStation1Entity;
entt::entity m_playerStation2Entity;
entt::entity m_currentEnemyStationEntities[2];
// Schematic unlock state (REQ-DEF-SCHEMATIC-DROP).
struct SchematicState
{
bool unlocked;
int level;
};
std::map<std::string, SchematicState> m_schematicLevels;
std::map<std::string, SchematicState> m_moduleSchematicLevels;
// Explicitly unlocked assembler recipe schematics (REQ-LOCK-EXPLICIT).
std::set<std::string> m_unlockedRecipeSchematicIds;
// Implicit unlock sets derived from schematic state (REQ-LOCK-IMPLICIT).
std::set<std::string> m_unlockedRecipeIds;
std::set<std::string> m_unlockedItemIds;
// Recomputes m_unlockedRecipeIds and m_unlockedItemIds from current schematic state.
void recomputeUnlocked();
// Result of the REQ-LOCK-IMPLICIT traversal.
struct UnlockedSets
{
std::set<std::string> itemIds;
std::set<std::string> recipeIds;
};
// Pure REQ-LOCK-IMPLICIT traversal given hypothetical explicit-unlock sets.
UnlockedSets computeUnlockedSets(const std::set<std::string>& unlockedShipSchematicIds,
const std::set<std::string>& unlockedModuleSchematicIds,
const std::set<std::string>& unlockedRecipeSchematicIds) const;
// Current explicit-unlock id sets, derived from m_schematicLevels / m_moduleSchematicLevels.
std::set<std::string> getUnlockedShipSchematicIds() const;
std::set<std::string> getUnlockedModuleSchematicIds() const;
// Display names (deduplicated, alphabetical) of output items of recipes in
// hypothetical.recipeIds that are not yet in m_unlockedRecipeIds.
std::vector<std::string> computeNewlyUnlockedItemNames(const UnlockedSets& hypothetical) const;
EntityAdmin m_admin;
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<ScrapSystem> m_scrapSystem;
std::unique_ptr<WaveSystem> m_waveSystem;
std::unique_ptr<CombatSystem> m_combatSystem;
std::vector<WeaponFiredEvent> m_weaponFiredEvents;
std::vector<SchematicChoiceOption> m_pendingSchematicChoices;
};