diff options
Diffstat (limited to 'source/game/StarTilesetDatabase.cpp')
-rw-r--r-- | source/game/StarTilesetDatabase.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/source/game/StarTilesetDatabase.cpp b/source/game/StarTilesetDatabase.cpp new file mode 100644 index 0000000..5a7a32e --- /dev/null +++ b/source/game/StarTilesetDatabase.cpp @@ -0,0 +1,361 @@ +#include "StarAssets.hpp" +#include "StarCasting.hpp" +#include "StarRoot.hpp" +#include "StarTilesetDatabase.hpp" + +namespace Star { + +namespace Tiled { + using namespace Dungeon; + + EnumMap<TileLayer> const LayerNames{{TileLayer::Foreground, "front"}, {TileLayer::Background, "back"}}; + + Properties::Properties() : m_properties(JsonObject{}) {} + + Properties::Properties(Json const& json) : m_properties(json) {} + + Json Properties::toJson() const { + return m_properties; + } + + Properties Properties::inherit(Json const& properties) const { + return jsonMerge(properties, m_properties); + } + + Properties Properties::inherit(Properties const& properties) const { + return jsonMerge(properties.m_properties, m_properties); + } + + bool Properties::contains(String const& name) const { + return m_properties.contains(name); + } + + Maybe<BrushConstPtr> getClearBrush(bool value, Tiled::Properties&) { + if (value) + return Maybe<BrushConstPtr>(as<Brush>(make_shared<const ClearBrush>())); + return {}; + } + + BrushConstPtr getFrontBrush(String const& materialName, Tiled::Properties& properties) { + Maybe<float> hueshift = properties.opt<float>("hueshift"); + Maybe<MaterialColorVariant> colorVariant = properties.opt<size_t>("colorVariant"); + Maybe<String> mod = properties.opt<String>("mod"); + Maybe<float> modhueshift = properties.opt<float>("modhueshift"); + + return make_shared<const FrontBrush>(materialName, mod, hueshift, modhueshift, colorVariant); + } + + BrushConstPtr getBackBrush(String const& materialName, Tiled::Properties& properties) { + Maybe<float> hueshift = properties.opt<float>("hueshift"); + Maybe<MaterialColorVariant> colorVariant = properties.opt<size_t>("colorVariant"); + Maybe<String> mod = properties.opt<String>("mod"); + Maybe<float> modhueshift = properties.opt<float>("modhueshift"); + + return make_shared<const BackBrush>(materialName, mod, hueshift, modhueshift, colorVariant); + } + + BrushConstPtr getMaterialBrush(String const& materialName, Tiled::Properties& properties) { + TileLayer layer = LayerNames.getLeft(properties.get<String>("layer")); + + if (layer == TileLayer::Background) { + return getBackBrush(materialName, properties); + } else { + starAssert(layer == TileLayer::Foreground); + return getFrontBrush(materialName, properties); + } + } + + BrushConstPtr getPlayerStartBrush(String const&, Tiled::Properties&) { + return make_shared<PlayerStartBrush>(); + } + + BrushConstPtr getObjectBrush(String const& objectName, Tiled::Properties& properties) { + Star::Direction direction = Star::Direction::Right; + Json parameters; + + if (properties.contains("tilesetDirection")) + direction = DirectionNames.getLeft(properties.get<String>("tilesetDirection")); + if (properties.contains("flipX")) + direction = -direction; + + if (properties.contains("parameters")) { + parameters = properties.get<Json>("parameters"); + } + + parameters = parameters.opt().value(JsonObject{}); + + return make_shared<const ObjectBrush>(objectName, direction, parameters); + } + + BrushConstPtr getVehicleBrush(String const& vehicleName, Tiled::Properties& properties) { + Json parameters = JsonObject{}; + if (properties.contains("parameters")) { + parameters = properties.get<Json>("parameters"); + } + return make_shared<const VehicleBrush>(vehicleName, parameters); + } + + BrushConstPtr getWireBrush(String const& group, Tiled::Properties& properties) { + bool local = properties.opt<bool>("local").value(true); + + return make_shared<const WireBrush>(group, local); + } + + Json getSeed(Tiled::Properties& properties) { + String seed = properties.get<String>("seed"); + if (seed == "stable") + return seed; + return lexicalCast<uint64_t>(seed); + } + + BrushConstPtr getNpcBrush(String const& species, Tiled::Properties& properties) { + JsonObject brush; + brush["kind"] = "npc"; + brush["species"] = species; // this may be a single species or a comma + // separated list to be parsed later + if (properties.contains("seed")) { + brush["seed"] = getSeed(properties); + } + if (properties.contains("typeName")) + brush["typeName"] = properties.get<String>("typeName"); + brush["parameters"] = properties.opt<Json>("parameters").value(JsonObject{}); + return make_shared<const NpcBrush>(brush); + } + + BrushConstPtr getMonsterBrush(String const& typeName, Tiled::Properties& properties) { + JsonObject brush; + brush["kind"] = "monster"; + brush["typeName"] = typeName; + if (properties.contains("seed")) { + brush["seed"] = getSeed(properties); + } + brush["parameters"] = properties.opt<Json>("parameters").value(JsonObject{}); + return make_shared<const NpcBrush>(brush); + } + + BrushConstPtr getStagehandBrush(String const& typeName, Tiled::Properties& properties) { + JsonObject brush; + brush["type"] = typeName; + brush["parameters"] = properties.opt<Json>("parameters").value(JsonObject{}); + if (properties.contains("broadcastArea")) + brush["parameters"] = brush["parameters"].set("broadcastArea", properties.get<Json>("broadcastArea")); + if (typeName == "radiomessage" && properties.contains("radioMessage")) + brush["parameters"] = brush["parameters"].set("radioMessage", properties.get<Json>("radioMessage")); + return make_shared<const StagehandBrush>(brush); + } + + BrushConstPtr getDungeonIdBrush(String const& dungeonId, Tiled::Properties&) { + return make_shared<const DungeonIdBrush>(maybeLexicalCast<DungeonId>(dungeonId).value(NoDungeonId)); + } + + BrushConstPtr getBiomeItemsBrush(String const&, Tiled::Properties&) { + return make_shared<const BiomeItemsBrush>(); + } + + BrushConstPtr getBiomeTreeBrush(String const&, Tiled::Properties&) { + return make_shared<const BiomeTreeBrush>(); + } + + BrushConstPtr getItemBrush(String const& itemName, Tiled::Properties& properties) { + size_t count = properties.opt<size_t>("count").value(1); + Json parameters = properties.opt<Json>("parameters").value(JsonObject{}); + ItemDescriptor item(itemName, count, parameters); + return make_shared<const ItemBrush>(item); + } + + BrushConstPtr getSurfaceBrush(String const& variantStr, Tiled::Properties& properties) { + TileLayer layer = LayerNames.getLeft(properties.get<String>("layer")); + Maybe<int> variant = maybeLexicalCast<int>(variantStr); + Maybe<String> mod = properties.opt<String>("mod"); + + if (layer == TileLayer::Background) + return make_shared<const SurfaceBackgroundBrush>(variant, mod); + return make_shared<const SurfaceBrush>(variant, mod); + } + + BrushConstPtr getLiquidBrush(String const& liquidName, Tiled::Properties& properties) { + float quantity = properties.opt<float>("quantity").value(1.0f); + bool source = properties.opt<bool>("source").value(false); + return make_shared<const LiquidBrush>(liquidName, quantity, source); + } + + Maybe<BrushConstPtr> getInvalidBrush(bool invalidValue, Tiled::Properties& properties) { + if (!invalidValue) + return {}; + + return as<const Brush>(make_shared<const InvalidBrush>(properties.opt<String>("//name"))); + } + + RuleConstPtr getAirRule(String const&, Tiled::Properties& properties) { + TileLayer layer = LayerNames.getLeft(properties.get<String>("layer")); + return make_shared<const WorldGenMustContainAirRule>(layer); + } + + RuleConstPtr getSolidRule(String const&, Tiled::Properties& properties) { + TileLayer layer = LayerNames.getLeft(properties.get<String>("layer")); + return make_shared<const WorldGenMustContainSolidRule>(layer); + } + + RuleConstPtr getLiquidRule(String const&, Tiled::Properties&) { + return make_shared<const WorldGenMustContainLiquidRule>(); + } + + RuleConstPtr getNotLiquidRule(String const&, Tiled::Properties&) { + return make_shared<const WorldGenMustNotContainLiquidRule>(); + } + + RuleConstPtr getAllowOverdrawingRule(String const&, Tiled::Properties&) { + return make_shared<const AllowOverdrawingRule>(); + } + + template <typename T> + class PropertyReader { + public: + template <typename PropertyType, + typename GetterReturn = T, + typename Getter = function<GetterReturn(PropertyType, Tiled::Properties&)>> + void optRead(List<T>& list, String const& propertyName, Getter getter, Tiled::Properties& properties) { + auto appendFn = bind(&List<T>::append, &list, _1); + read<PropertyType, Getter>(propertyName, getter, properties).exec(appendFn); + } + + private: + template <typename PropertyType, typename Getter> + Maybe<T> read(String const& propertyName, Getter getter, Tiled::Properties& properties) { + Maybe<PropertyType> propertyValue = properties.opt<PropertyType>(propertyName); + if (propertyValue.isValid()) + return getter(*propertyValue, properties); + return {}; + } + }; + + Tile::Tile(Properties const& tileProperties, TileLayer layer, bool flipX) + : Dungeon::Tile(), properties(tileProperties) { + JsonObject computedProperties; + if (!properties.contains("layer")) { + computedProperties["layer"] = LayerNames.getRight(layer); + } else { + layer = LayerNames.getLeft(properties.get<String>("layer")); + } + + if (flipX) + computedProperties["flipX"] = "true"; + + if (layer == TileLayer::Background && !properties.contains("clear")) + // The magic pink tile/brush has the clear property set to "false". All + // other tiles default to clear="true". + computedProperties["clear"] = "true"; + + properties = properties.inherit(computedProperties); + + PropertyReader<BrushConstPtr> br; + br.optRead<bool, Maybe<BrushConstPtr>>(brushes, "clear", getClearBrush, properties); + br.optRead<String>(brushes, "material", getMaterialBrush, properties); + br.optRead<String>(brushes, "front", getFrontBrush, properties); + br.optRead<String>(brushes, "back", getBackBrush, properties); + br.optRead<String>(brushes, "playerstart", getPlayerStartBrush, properties); + br.optRead<String>(brushes, "object", getObjectBrush, properties); + br.optRead<String>(brushes, "vehicle", getVehicleBrush, properties); + br.optRead<String>(brushes, "wire", getWireBrush, properties); + br.optRead<String>(brushes, "npc", getNpcBrush, properties); + br.optRead<String>(brushes, "monster", getMonsterBrush, properties); + br.optRead<String>(brushes, "stagehand", getStagehandBrush, properties); + br.optRead<String>(brushes, "dungeonid", getDungeonIdBrush, properties); + br.optRead<String>(brushes, "biomeitems", getBiomeItemsBrush, properties); + br.optRead<String>(brushes, "biometree", getBiomeTreeBrush, properties); + br.optRead<String>(brushes, "item", getItemBrush, properties); + br.optRead<String>(brushes, "surface", getSurfaceBrush, properties); + br.optRead<String>(brushes, "liquid", getLiquidBrush, properties); + br.optRead<bool, Maybe<BrushConstPtr>>(brushes, "invalid", getInvalidBrush, properties); + + PropertyReader<RuleConstPtr> rr; + rr.optRead<String>(rules, "worldGenMustContainAir", getAirRule, properties); + rr.optRead<String>(rules, "worldGenMustContainSolid", getSolidRule, properties); + rr.optRead<String>(rules, "worldGenMustContainLiquid", getLiquidRule, properties); + rr.optRead<String>(rules, "worldGenMustNotContainLiquid", getNotLiquidRule, properties); + rr.optRead<String>(rules, "allowOverdrawing", getAllowOverdrawingRule, properties); + + if (auto connectorName = properties.opt<String>("connector")) { + auto newConnector = TileConnector(); + + newConnector.value = *connectorName; + + auto connectForwardOnly = properties.opt<bool>("connectForwardOnly"); + newConnector.forwardOnly = connectForwardOnly.value(false); + + if (auto connectDirection = properties.opt<String>("connectDirection")) + newConnector.direction = DungeonDirectionNames.getLeft(*connectDirection); + + connector = newConnector; + } + } + + Tileset::Tileset(Json const& json) { + Properties tilesetProperties(json.opt("properties").value(JsonObject{})); + Json tileProperties = json.opt("tileproperties").value(JsonObject{}); + + m_tilesBack.resize(json.getInt("tilecount")); + m_tilesFront.resize(json.getInt("tilecount")); + + for (auto const& entry : tileProperties.iterateObject()) { + size_t index = lexicalCast<size_t>(entry.first); + Properties properties = Properties(entry.second).inherit(tilesetProperties); + + m_tilesBack[index] = make_shared<Tile>(properties, TileLayer::Background); + m_tilesFront[index] = make_shared<Tile>(properties, TileLayer::Foreground); + } + } + + TileConstPtr const& Tileset::getTile(size_t id, TileLayer layer) const { + List<TileConstPtr> const& tileset = tiles(layer); + return tileset[id]; + } + + size_t Tileset::size() const { + starAssert(m_tilesBack.size() == m_tilesFront.size()); + return m_tilesBack.size(); + } + + List<TileConstPtr> const& Tileset::tiles(TileLayer layer) const { + if (layer == TileLayer::Background) + return m_tilesBack; + starAssert(layer == TileLayer::Foreground); + return m_tilesFront; + } +} + +TilesetDatabase::TilesetDatabase() : m_cacheMutex(), m_tilesetCache() {} + +Tiled::TilesetConstPtr TilesetDatabase::get(String const& path) const { + MutexLocker locker(m_cacheMutex); + return m_tilesetCache.get(path, TilesetDatabase::readTileset); +} + +Tiled::TilesetConstPtr TilesetDatabase::readTileset(String const& path) { + auto assets = Root::singleton().assets(); + return make_shared<Tiled::Tileset>(assets->json(path)); +} + +namespace Tiled { + Json PropertyConverter<Json>::to(String const& propertyValue) { + try { + return Json::parseJson(propertyValue); + } catch (JsonParsingException const& e) { + throw StarException::format("Error parsing Tiled property as Json: %s", outputException(e, false)); + } + } + + String PropertyConverter<Json>::from(Json const& propertyValue) { + return propertyValue.repr(); + } + + String PropertyConverter<String>::to(String const& propertyValue) { + return propertyValue; + } + + String PropertyConverter<String>::from(String const& propertyValue) { + return propertyValue; + } +} + +} |