#pragma once #include "StarImage.hpp" #include "StarItemDescriptor.hpp" #include "StarWorldGeometry.hpp" #include "StarGameTypes.hpp" #include "StarRandom.hpp" #include "StarSet.hpp" #include "StarThread.hpp" #include "StarLruCache.hpp" namespace Star { STAR_EXCEPTION(DungeonException, StarException); STAR_CLASS(DungeonGeneratorWorldFacade); STAR_CLASS(DungeonDefinition); STAR_CLASS(DungeonDefinitions); class DungeonGeneratorWorldFacade { public: virtual ~DungeonGeneratorWorldFacade() {} // Hint that the given rectangular region is dungeon generated, and thus // would not receive the normal entity generation steps. virtual void markRegion(RectI const& region) = 0; // Mark the region as needing terrain to properly integrate with the dungeon virtual void markTerrain(PolyF const& region) = 0; // Mark the region as needing space to properly integrate with the dungeon virtual void markSpace(PolyF const& region) = 0; virtual void setForegroundMaterial(Vec2I const& position, MaterialId material, MaterialHue hueshift, MaterialColorVariant colorVariant) = 0; virtual void setBackgroundMaterial(Vec2I const& position, MaterialId material, MaterialHue hueshift, MaterialColorVariant colorVariant) = 0; virtual void setForegroundMod(Vec2I const& position, ModId mod, MaterialHue hueshift) = 0; virtual void setBackgroundMod(Vec2I const& position, ModId mod, MaterialHue hueshift) = 0; virtual void placeObject(Vec2I const& pos, String const& objectName, Star::Direction direction, Json const& parameters) = 0; virtual void placeVehicle(Vec2F const& pos, String const& vehicleName, Json const& parameters) = 0; virtual void placeSurfaceBiomeItems(Vec2I const& pos) = 0; virtual void placeBiomeTree(Vec2I const& pos) = 0; virtual void addDrop(Vec2F const& position, ItemDescriptor const& item) = 0; virtual void spawnNpc(Vec2F const& position, Json const& parameters) = 0; virtual void spawnStagehand(Vec2F const& position, Json const& definition) = 0; virtual void setLiquid(Vec2I const& pos, LiquidStore const& liquid) = 0; virtual void connectWireGroup(List const& wireGroup) = 0; virtual void setTileProtection(DungeonId dungeonId, bool isProtected) = 0; virtual bool checkSolid(Vec2I const& position, TileLayer layer) = 0; virtual bool checkOpen(Vec2I const& position, TileLayer layer) = 0; virtual bool checkOceanLiquid(Vec2I const& position) = 0; virtual DungeonId getDungeonIdAt(Vec2I const& position) = 0; virtual void setDungeonIdAt(Vec2I const& position, DungeonId dungeonId) = 0; virtual void clearTileEntities(RectI const& bounds, Set const& positions, bool clearAnchoredObjects) = 0; virtual WorldGeometry getWorldGeometry() const = 0; virtual void setPlayerStart(Vec2F const& startPosition) = 0; }; namespace Dungeon { STAR_CLASS(DungeonGeneratorWriter); STAR_CLASS(PartReader); STAR_CLASS(Part); STAR_CLASS(Rule); STAR_CLASS(Brush); STAR_STRUCT(Tile); STAR_CLASS(Connector); class DungeonGeneratorWriter { public: DungeonGeneratorWriter(DungeonGeneratorWorldFacadePtr facade, Maybe terrainMarkingSurfaceLevel, Maybe terrainSurfaceSpaceExtends); Vec2I wrapPosition(Vec2I const& pos) const; void setMarkDungeonId(Maybe markDungeonId = {}); void requestLiquid(Vec2I const& pos, LiquidStore const& liquid); void setLiquid(Vec2I const& pos, LiquidStore const& liquid); void setForegroundMaterial(Vec2I const& position, MaterialId material, MaterialHue hueshift, MaterialColorVariant colorVariant); void setBackgroundMaterial(Vec2I const& position, MaterialId material, MaterialHue hueshift, MaterialColorVariant colorVariant); void setForegroundMod(Vec2I const& position, ModId mod, MaterialHue hueshift); void setBackgroundMod(Vec2I const& position, ModId mod, MaterialHue hueshift); bool needsForegroundBiomeMod(Vec2I const& position); bool needsBackgroundBiomeMod(Vec2I const& position); void placeObject(Vec2I const& position, String const& objectType, Direction direction, Json const& parameters); void placeVehicle(Vec2F const& pos, String const& vehicleName, Json const& parameters); void placeSurfaceBiomeItems(Vec2I const& pos); void placeBiomeTree(Vec2I const& pos); void addDrop(Vec2F const& position, ItemDescriptor const& item); void requestWire(Vec2I const& position, String const& wireGroup, bool partLocal); void spawnNpc(Vec2F const& pos, Json const& definition); void spawnStagehand(Vec2F const& pos, Json const& definition); void setPlayerStart(Vec2F const& startPosition); bool checkSolid(Vec2I position, TileLayer layer); bool checkOpen(Vec2I position, TileLayer layer); bool checkLiquid(Vec2I const& position); bool otherDungeonPresent(Vec2I position); void setDungeonId(Vec2I const& pos, DungeonId dungeonId); void markPosition(Vec2F const& pos); void markPosition(Vec2I const& pos); void finishPart(); void clearTileEntities(RectI const& bounds, Set const& positions, bool clearAnchoredObjects); void flushLiquid(); void flush(); List boundingBoxes() const; void reset(); private: struct Material { MaterialId material; MaterialHue hueshift; MaterialColorVariant colorVariant; }; struct Mod { ModId mod; MaterialHue hueshift; }; struct ObjectSettings { ObjectSettings() : direction() {} ObjectSettings(String const& objectName, Direction direction, Json const& parameters) : objectName(objectName), direction(direction), parameters(parameters) {} String objectName; Direction direction; Json parameters; }; DungeonGeneratorWorldFacadePtr m_facade; Maybe m_terrainMarkingSurfaceLevel; Maybe m_terrainSurfaceSpaceExtends; Map m_pendingLiquids; Map m_foregroundMaterial; Map m_backgroundMaterial; Map m_foregroundMod; Map m_backgroundMod; Map m_objects; Map> m_vehicles; Set m_biomeTrees; Set m_biomeItems; Map m_drops; Map m_npcs; Map m_stagehands; Map m_dungeonIds; Map m_liquids; StringMap> m_globalWires; List> m_localWires; StringMap> m_openLocalWires; Maybe m_markDungeonId; RectI m_currentBounds; List m_boundingBoxes; }; class Rule { public: static Maybe parse(Json const& rule); static List readRules(Json const& rules); virtual ~Rule() {} virtual bool checkTileCanPlace(Vec2I position, DungeonGeneratorWriter* writer) const; virtual bool overdrawable() const; virtual bool ignorePartMaximum() const; virtual bool allowSpawnCount(int currentCount) const; virtual bool doesNotConnectToPart(String const& name) const; virtual bool checkPartCombinationsAllowed(StringMap const& placementCounter) const; virtual bool requiresOpen() const; virtual bool requiresSolid() const; virtual bool requiresLiquid() const; protected: Rule() {} }; class WorldGenMustContainAirRule : public Rule { public: WorldGenMustContainAirRule(TileLayer layer) : layer(layer) {} virtual bool checkTileCanPlace(Vec2I position, DungeonGeneratorWriter* writer) const override; virtual bool requiresOpen() const override { return true; } TileLayer layer; }; class WorldGenMustContainSolidRule : public Rule { public: WorldGenMustContainSolidRule(TileLayer layer) : layer(layer) {} virtual bool checkTileCanPlace(Vec2I position, DungeonGeneratorWriter* writer) const override; virtual bool requiresSolid() const override { return true; } TileLayer layer; }; class WorldGenMustContainLiquidRule : public Rule { public: WorldGenMustContainLiquidRule() {} virtual bool checkTileCanPlace(Vec2I position, DungeonGeneratorWriter* writer) const override; virtual bool requiresLiquid() const override { return true; } }; class WorldGenMustNotContainLiquidRule : public Rule { public: WorldGenMustNotContainLiquidRule() {} virtual bool checkTileCanPlace(Vec2I position, DungeonGeneratorWriter* writer) const override; }; class AllowOverdrawingRule : public Rule { public: AllowOverdrawingRule() {} virtual bool overdrawable() const override { return true; } }; class IgnorePartMaximumRule : public Rule { public: IgnorePartMaximumRule() {} virtual bool ignorePartMaximum() const override { return true; } }; class MaxSpawnCountRule : public Rule { public: MaxSpawnCountRule(Json const& rule) { m_maxCount = rule.toArray()[1].toArray()[0].toInt(); } virtual bool allowSpawnCount(int currentCount) const override { return currentCount < m_maxCount; } private: int m_maxCount; }; class DoNotConnectToPartRule : public Rule { public: DoNotConnectToPartRule(Json const& rule) { for (auto entry : rule.toArray()[1].toArray()) m_partNames.add(entry.toString()); } virtual bool doesNotConnectToPart(String const& name) const override { return m_partNames.contains(name); } private: StringSet m_partNames; }; class DoNotCombineWithRule : public Rule { public: DoNotCombineWithRule(Json const& rule) { for (auto part : rule.toArray()[1].toArray()) m_parts.add(part.toString()); } virtual bool checkPartCombinationsAllowed(StringMap const& placementCounter) const override { for (auto part : m_parts) { if (placementCounter.contains(part) && (placementCounter.get(part) > 0)) return false; } return true; } private: StringSet m_parts; }; enum class Phase { ClearPhase, WallPhase, ModsPhase, ObjectPhase, BiomeTreesPhase, BiomeItemsPhase, WirePhase, ItemPhase, NpcPhase, DungeonIdPhase }; class Brush { public: static BrushConstPtr parse(Json const& brush); static List readBrushes(Json const& brushes); virtual ~Brush() {} virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const = 0; protected: Brush() {} }; class RandomBrush : public Brush { public: RandomBrush(Json const& brush); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: List m_brushes; int64_t m_seed; }; class ClearBrush : public Brush { public: ClearBrush() {} virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; }; class FrontBrush : public Brush { public: FrontBrush(String const& material, Maybe mod, Maybe hueshift, Maybe modhueshift, Maybe colorVariant); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_material; MaterialHue m_materialHue; MaterialColorVariant m_materialColorVariant; Maybe m_mod; MaterialHue m_modHue; }; class BackBrush : public Brush { public: BackBrush(String const& material, Maybe mod, Maybe hueshift, Maybe modhueshift, Maybe colorVariant); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_material; MaterialHue m_materialHue; MaterialColorVariant m_materialColorVariant; Maybe m_mod; MaterialHue m_modHue; }; class ObjectBrush : public Brush { public: ObjectBrush(String const& object, Star::Direction direction, Json const& parameters); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_object; Star::Direction m_direction; Json m_parameters; }; class VehicleBrush : public Brush { public: VehicleBrush(String const& vehicle, Json const& parameters); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_vehicle; Json m_parameters; }; class BiomeItemsBrush : public Brush { public: BiomeItemsBrush() {} virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; }; class BiomeTreeBrush : public Brush { public: BiomeTreeBrush() {} virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; }; class ItemBrush : public Brush { public: ItemBrush(ItemDescriptor const& item); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: ItemDescriptor m_item; }; class NpcBrush : public Brush { public: NpcBrush(Json const& brush); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: Json m_npc; }; class StagehandBrush : public Brush { public: StagehandBrush(Json const& definition); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: Json m_definition; }; class DungeonIdBrush : public Brush { public: DungeonIdBrush(DungeonId dungeonId); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: DungeonId m_dungeonId; }; class SurfaceBrush : public Brush { public: SurfaceBrush(Maybe variant, Maybe mod); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: int m_variant; Maybe m_mod; }; class SurfaceBackgroundBrush : public Brush { public: SurfaceBackgroundBrush(Maybe variant, Maybe mod); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: int m_variant; Maybe m_mod; }; class LiquidBrush : public Brush { public: LiquidBrush(String const& liquidName, float quantity, bool source); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_liquid; float m_quantity; bool m_source; }; class WireBrush : public Brush { public: WireBrush(String wireGroup, bool partLocal) : m_wireGroup(wireGroup), m_partLocal(partLocal) {} virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: String m_wireGroup; bool m_partLocal; }; class PlayerStartBrush : public Brush { public: virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; }; // InvalidBrush reports an error when it is painted. This brush is used on // tiles // that represent objects that have been removed from the game. class InvalidBrush : public Brush { public: InvalidBrush(Maybe nameHint); virtual void paint(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const override; private: Maybe m_nameHint; }; enum class Direction : uint8_t { Left = 0, Right = 1, Up = 2, Down = 3, Unknown = 4, Any = 5 }; extern EnumMap const DungeonDirectionNames; class Connector { public: Connector(Part* part, String value, bool forwardOnly, Direction direction, Vec2I offset); bool connectsTo(ConnectorConstPtr connector) const; String value() const; Vec2I positionAdjustment() const; Part* part() const; Vec2I offset() const; private: String m_value; bool m_forwardOnly; Direction m_direction; Vec2I m_offset; Part* m_part; }; typedef function TileCallback; class PartReader { public: virtual ~PartReader() {} virtual void readAsset(String const& asset) = 0; // Returns the dimensions of the part virtual Vec2U size() const = 0; // Iterate over every tile in every layer of the part. // The callback receives the position of the tile (within the part), and // the tile at that position. // The callback can return true to exit from the loop early. virtual void forEachTile(TileCallback const& callback) const = 0; // Calls the callback with only the tiles at the given position. virtual void forEachTileAt(Vec2I pos, TileCallback const& callback) const = 0; protected: PartReader() {} }; class Part { public: Part(DungeonDefinition* dungeon, Json const& part, PartReaderPtr reader); String const& name() const; Vec2U size() const; Vec2I anchorPoint() const; float chance() const; bool markDungeonId() const; Maybe minimumThreatLevel() const; Maybe maximumThreatLevel() const; bool clearAnchoredObjects() const; int placementLevelConstraint() const; bool ignoresPartMaximum() const; bool allowsPlacement(int currentPlacementCount) const; List const& connections() const; bool doesNotConnectTo(Part* part) const; bool checkPartCombinationsAllowed(StringMap const& placementCounts) const; bool collidesWithPlaces(Vec2I pos, Set& places) const; bool canPlace(Vec2I pos, DungeonGeneratorWriter* writer) const; void place(Vec2I pos, Set const& places, DungeonGeneratorWriter* writer) const; void forEachTile(TileCallback const& callback) const; private: void placePhase(Vec2I pos, Phase phase, Set const& places, DungeonGeneratorWriter* writer) const; bool tileUsesPlaces(Vec2I pos) const; Direction pickByEdge(Vec2I position, Vec2U size) const; Direction pickByNeighbours(Vec2I pos) const; void scanConnectors(); void scanAnchor(); PartReaderConstPtr m_reader; String m_name; List m_rules; DungeonDefinition* m_dungeon; List m_connections; Vec2I m_anchorPoint; bool m_overrideAllowAlways; Maybe m_minimumThreatLevel; Maybe m_maximumThreatLevel; bool m_clearAnchoredObjects; Vec2U m_size; float m_chance; bool m_markDungeonId; }; struct TileConnector { String value; bool forwardOnly; Direction direction = Direction::Unknown; }; struct Tile { bool canPlace(Vec2I position, DungeonGeneratorWriter* writer) const; void place(Vec2I position, Phase phase, DungeonGeneratorWriter* writer) const; bool usesPlaces() const; bool modifiesPlaces() const; bool collidesWithPlaces() const; bool requiresOpen() const; bool requiresSolid() const; bool requiresLiquid() const; List brushes; List rules; Maybe connector; }; } class DungeonDefinition { public: DungeonDefinition(JsonObject const& definition, String const& directory); JsonObject metadata() const; String directory() const; String name() const; String displayName() const; bool isProtected() const; Maybe gravity() const; Maybe breathable() const; StringMap const& parts() const; List const& anchors() const; Maybe const& optTileset() const; int maxParts() const; int maxRadius() const; int extendSurfaceFreeSpace() const; JsonObject metaData() const; private: JsonObject m_metadata; String m_directory; String m_name; String m_displayName; String m_species; bool m_isProtected; List m_rules; StringMap m_parts; List m_anchors; Maybe m_tileset; int m_maxRadius; int m_maxParts; int m_extendSurfaceFreeSpace; Maybe m_gravity; Maybe m_breathable; }; class DungeonDefinitions { public: DungeonDefinitions(); DungeonDefinitionConstPtr get(String const& name) const; JsonObject getMetadata(String const& name) const; private: static DungeonDefinitionPtr readDefinition(String const& path); StringMap m_paths; mutable Mutex m_cacheMutex; mutable HashLruCache m_definitionCache; }; class DungeonGenerator { public: DungeonGenerator(String const& dungeonName, uint64_t seed, float threatLevel, Maybe dungeonId); Maybe, Set>> generate(DungeonGeneratorWorldFacadePtr facade, Vec2I position, bool markSurfaceAndTerrain, bool forcePlacement); pair, Set> buildDungeon(Dungeon::PartConstPtr anchor, Vec2I pos, Dungeon::DungeonGeneratorWriter* writer, bool forcePlacement); Dungeon::PartConstPtr pickAnchor(); List findConnectablePart(Dungeon::ConnectorConstPtr connector) const; DungeonDefinitionConstPtr definition() const; private: DungeonDefinitionConstPtr m_def; RandomSource m_rand; float m_threatLevel; Maybe m_dungeonId; }; }