Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarTilesetDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarTilesetDatabase.cpp')
-rw-r--r--source/game/StarTilesetDatabase.cpp361
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;
+ }
+}
+
+}