#pragma once #include #include #include #include "BeltDragPath.h" #include "BuildingId.h" #include "BuildingType.h" #include "FactoryState.h" #include "GameConfig.h" #include "Rotation.h" // Where a building may be placed, and what is already sitting on those tiles. // Free functions over the factory state and the config — they read no other // system state, so they do not belong to BuildingSystem. // True if every body cell lies inside the world: 0 <= y < world.height_tiles and // x >= the current asteroid left edge (REQ-BLD-PLACE-VALID). Terrain type is not // checked — see isPlacementValid for the full rule. bool bodyCellsWithinWorldBounds(const FactoryState& state, const GameConfig& config, const std::vector& bodyCells, QPoint anchor); // True if the placement satisfies REQ-BLD-PLACE-VALID terrain and world-bounds // rules: every ship-dock (S) cell sits in space (x >= 0), every other body (A) // cell sits on the asteroid (x < 0 and x >= the left edge), and every cell has // 0 <= y < world.height_tiles. There is no right-side bound — space extends // rightward. Tile occupancy is NOT checked here. bool isPlacementValid(const FactoryState& state, const GameConfig& config, BuildingType type, QPoint anchor, Rotation rotation); // The building or site that a ghost of the given type/anchor/rotation would // rotate in place rather than replace: same type, same body cells, one owner // (REQ-BLD-ROTATE-IN-PLACE). Tunnels never qualify. std::optional findRotateInPlaceTarget(const FactoryState& state, const GameConfig& config, BuildingType type, QPoint anchor, Rotation rot); // True if placing here would actually do something: the terrain and bounds rules of // isPlacementValid hold, and the body cells are either all free or occupied only by // a building this placement would rotate in place. This is the question the ghost // asks to colour itself and the click path asks before enqueuing a command // (REQ-BLD-GHOST, REQ-BLD-PLACE-VALID, REQ-BLD-ROTATE-IN-PLACE). bool canPlaceBuilding(const FactoryState& state, const GameConfig& config, BuildingType type, QPoint anchor, Rotation rotation); // What a belt drag would do to one tile of its path (REQ-BLD-BELT-DRAG). enum class BeltTileAction { PlaceNew, // empty, valid cell: a new belt, subject to affordability RotateInPlace, // already a belt (or belt site): re-oriented, free Invalid // occupied by something else, or invalid terrain }; struct BeltDragResolved { BeltTileAction action; bool affordable; // meaningful only for PlaceNew std::optional rotateId; // set only for RotateInPlace }; // Classifies every tile of a belt drag path against the current factory state, // spending `buildingBlocksStock` cumulatively across the PlaceNew tiles so a path // longer than the player can afford is only partly buildable (REQ-BLD-BELT-DRAG). // Shared so the previewed ghosts and the placement on release cannot disagree. std::vector resolveBeltDragPath(const std::vector& path, const FactoryState& state, const GameConfig& config, int buildingBlocksStock);