Subsystem: HexMap - Adam-Poppenheimer/Civ-Clone GitHub Wiki
The HexMap subsystem handles the hexagonal grid of cells that comprises the area of play. It keeps a HexGrid with various query operations, handles pathfinding, and keeps track of gameplay elements core to the map itself, like rivers and terrain types. HexMap is the only subsystem whose code predates the beginning of the project: It was brought in from an older hex-based project I'd worked on, which was based on an implementation from Red Blob Games.
HexMap's two most important classes are IHexCell/HexCell and IHexGrid/HexGrid, whose names are fairly self-explanatory. HexGrid can be configured with X and Z cell counts, can create and clear the grid, and reveals collections of both the IHexCells in the game and the IMapChunks, which are collections of nearby cells grouped together for rendering purposes. HexGrid also keeps track of an IHexMesh for the river surface, river banks, river "ducking", and farms, all of which are explained in detail in the MapRendering section. HexGrid's rounded out with a Build() and Clear() method and a set of query operations, both to perform hex math (distance, getting neighbors, etc) and also perform certain terrain intersection operations.
HexGrid as a class most likely violates the principle of Single Responsibility. The HexMap subsystem in general is in several places tightly tied to MapRendering. Over development HexGrid has gone through many different iterations as the needs of the project and its architecture have changed. Those needs have largely informed the sub-optimal organization decisions within HexGrid, including the choice to make it a MonoBehaviour. Despite its difficulty, I have no doubt that HexGrid could be designed in such a way as to decouple it from the process of map rendering; such a change was simply not a high priority, given that HexGrid was functional and comprehensible during development.
HexCell is largely a POD, though some of its properties fire events when they're changed. Most of IHexCell's declared properties pertain to the game's mechanical structure, though there are some properties like AbsolutePosition that exist for the benefit of MapRendering, and OverlayAnchorPoint which exists for the sake of the UI. A refactor to more carefully separate out the non-simulation aspects of IHexCell/HexCell seems valuable, though it wasn't considered a priority during development.
HexMap contains a sizable number of events in the HexCellSignals class, including a set of pointer events that are fired by the CellEvenEmitter class currently (somewhat mysteriously) in the MapRendering namespace.
HexMap handles the yield of cells through things like CellInherentYieldLogic and CellYieldLogic (the former checks only the cell itself, the latter includes nodes, improvements, buildings, technologies, and golden ages). It checks for fresh water through the IFreshWaterLogic interface and its standard implementation. IHexPathfinder handles pathfinding more generally, a task pulled off of HexGrid because 1) it wasn't fundamental to the existence of the grid and 2) HexGrid was already very large and creating HexPathfinder made things easier to manage.
A lot of effort is made to make sure that cell terrains, shapes, vegetations, and features; as well as the presence of rivers; follow game-design rules not strictly necessary for technical correctness. RiverCornerValidityLogic and RiverCanon make sure rivers flow in the same direction. CellModificationLogic involves a lot of if/then logic to make sure certain nonsensical combinations cannot form (like a hilly flood plain or a forested ocean).