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

summaryrefslogtreecommitdiff
path: root/source/game/objects/StarFarmableObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/objects/StarFarmableObject.cpp')
-rw-r--r--source/game/objects/StarFarmableObject.cpp187
1 files changed, 187 insertions, 0 deletions
diff --git a/source/game/objects/StarFarmableObject.cpp b/source/game/objects/StarFarmableObject.cpp
new file mode 100644
index 0000000..d8855bf
--- /dev/null
+++ b/source/game/objects/StarFarmableObject.cpp
@@ -0,0 +1,187 @@
+#include "StarFarmableObject.hpp"
+#include "StarLexicalCast.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarRoot.hpp"
+#include "StarAssets.hpp"
+#include "StarRandom.hpp"
+#include "StarPlantDatabase.hpp"
+#include "StarPlant.hpp"
+#include "StarWorldServer.hpp"
+#include "StarTreasure.hpp"
+#include "StarItemDrop.hpp"
+#include "StarLogging.hpp"
+#include "StarObjectDatabase.hpp"
+#include "StarMaterialDatabase.hpp"
+
+namespace Star {
+
+FarmableObject::FarmableObject(ObjectConfigConstPtr config, Json const& parameters) : Object(config, parameters) {
+ m_stages = configValue("stages", JsonArray({JsonObject()})).toArray();
+ m_stage = configValue("startingStage", 0).toInt();
+
+ m_stageAlt = -1;
+ m_stageEnterTime = 0.0;
+ m_nextStageTime = 0.0;
+ m_finalStage = false;
+
+ auto assets = Root::singleton().assets();
+ m_minImmersion = configValue("minImmersion", 0).toFloat();
+ m_maxImmersion = configValue("maxImmersion", 2).toFloat();
+ m_immersion = SlidingWindow(assets->json("/farming.config:immersionWindow").toFloat(),
+ assets->json("/farming.config:immersionResolution").toUInt(), (m_minImmersion + m_maxImmersion) / 2);
+
+ m_consumeSoilMoisture = configValue("consumeSoilMoisture", true).toBool();
+}
+
+void FarmableObject::update(uint64_t currentStep) {
+ Object::update(currentStep);
+
+ if (isMaster()) {
+ if (m_nextStageTime == 0) {
+ m_nextStageTime = world()->epochTime();
+ enterStage(m_stage);
+ }
+
+ while (!m_finalStage && world()->epochTime() >= m_nextStageTime)
+ enterStage(m_stage + 1);
+
+ // update immersion and check whether farmable should break
+ m_immersion.update(bind(&Object::liquidFillLevel, this));
+ if (m_immersion.average() > m_maxImmersion || m_immersion.average() < m_minImmersion)
+ breakObject(false);
+ }
+}
+
+bool FarmableObject::damageTiles(List<Vec2I> const& position, Vec2F const& sourcePosition, TileDamage const& tileDamage) {
+ if ((tileDamage.type != TileDamageType::Beamish && tileDamage.type != TileDamageType::Blockish && tileDamage.type != TileDamageType::Plantish) || !harvest())
+ return Object::damageTiles(position, sourcePosition, tileDamage);
+
+ return false;
+}
+
+InteractAction FarmableObject::interact(InteractRequest const&) {
+ harvest();
+ return {};
+}
+
+bool FarmableObject::harvest() {
+ if (isMaster() && m_stages.get(m_stage).contains("harvestPool")) {
+ for (auto const& treasureItem : Root::singleton().treasureDatabase()->createTreasure(m_stages.get(m_stage).getString("harvestPool"), world()->threatLevel()))
+ world()->addEntity(ItemDrop::createRandomizedDrop(treasureItem, position()));
+
+ if (m_stages.get(m_stage).contains("resetToStage")) {
+ m_nextStageTime = world()->epochTime();
+ enterStage(m_stages.get(m_stage).getInt("resetToStage"));
+ } else
+ breakObject(true);
+
+ return true;
+ }
+ return false;
+}
+
+int FarmableObject::stage() const {
+ return m_stage;
+}
+
+void FarmableObject::enterStage(int newStage) {
+ newStage = clamp<int>(newStage, 0, m_stages.size() - 1);
+
+ // attempt to consume water from the soil if needed
+ if (m_consumeSoilMoisture && newStage > m_stage) {
+ if (auto orientation = currentOrientation()) {
+ auto assets = Root::singleton().assets();
+ auto materialDatabase = Root::singleton().materialDatabase();
+ auto wetToDryMods = assets->json("/farming.config:wetToDryMods");
+
+ // try to transform all anchor spaces, back out and reset stage time if
+ // they're not wet
+ for (auto anchor : orientation->anchors) {
+ auto pos = tilePosition() + anchor.position;
+ if (auto newMod = wetToDryMods.optString(materialDatabase->modName(world()->mod(pos, anchor.layer)))) {
+ world()->modifyTile(pos, PlaceMod{anchor.layer, materialDatabase->modId(*newMod), MaterialHue()}, true);
+ } else {
+ Vec2F durationRange = jsonToVec2F(m_stages.get(m_stage).get("duration", JsonArray({0, 0})));
+ m_nextStageTime = world()->epochTime() + Random::randf(durationRange[0], durationRange[1]);
+
+ return;
+ }
+ }
+ }
+ }
+
+ // TODO: remove this hacky tree stuff and make plants handle it
+ if (m_stages.get(newStage).getBool("tree", false)) {
+ String stemName = configValue("stemName").toString();
+ float stemHueShift = configValue("stemHueShift", 0).toFloat();
+ String foliageName = configValue("foliageName", "").toString();
+ float foliageHueShift = configValue("foliageHueShift", 0).toFloat();
+ Vec2I position = tilePosition();
+
+ TreeVariant tv;
+ auto plantDatabase = Root::singleton().plantDatabase();
+ if (!foliageName.empty())
+ tv = plantDatabase->buildTreeVariant(stemName, stemHueShift, foliageName, foliageHueShift);
+ else
+ tv = plantDatabase->buildTreeVariant(stemName, stemHueShift);
+
+ auto plant = plantDatabase->createPlant(tv, Random::randi64());
+ plant->setTilePosition(position);
+
+ if (anySpacesOccupied(plant->spaces()) || !allSpacesOccupied(plant->roots())) {
+ newStage = 0;
+ } else {
+ world()->timer(2, [plant](World* world) {
+ world->addEntity(plant);
+ });
+
+ m_finalStage = true;
+ breakObject(true);
+ return;
+ }
+ }
+
+ if (newStage == (int)m_stages.size() - 1) {
+ m_finalStage = true;
+ } else {
+ m_finalStage = false;
+ m_stageEnterTime = m_nextStageTime;
+ Vec2F durationRange = jsonToVec2F(m_stages.get(newStage).get("duration", JsonArray({0, 0})));
+ m_nextStageTime += Random::randf(durationRange[0], durationRange[1]);
+ }
+
+ m_interactive.set(m_stages.get(newStage).contains("harvestPool"));
+
+ // keep the same variant if stages have same number of alts
+ if (m_stageAlt == -1 || m_stages.get(newStage).getInt("alts", 1) != m_stages.get(m_stage).getInt("alts", 1))
+ m_stageAlt = Random::randInt(m_stages.get(newStage).getInt("alts", 1) - 1);
+
+ m_stage = newStage;
+
+ setImageKey("stage", toString(m_stage));
+ setImageKey("alt", toString(m_stageAlt));
+}
+
+void FarmableObject::readStoredData(Json const& diskStore) {
+ Object::readStoredData(diskStore);
+
+ m_stage = diskStore.getInt("stage");
+ m_stageAlt = diskStore.getInt("stageAlt");
+ m_stageEnterTime = diskStore.getDouble("stageEnterTime");
+ m_nextStageTime = diskStore.getDouble("nextStageTime");
+
+ m_finalStage = (m_stage == (int)m_stages.size() - 1);
+ setImageKey("stage", toString(m_stage));
+ setImageKey("alt", toString(m_stageAlt));
+}
+
+Json FarmableObject::writeStoredData() const {
+ return Object::writeStoredData().setAll({
+ {"stage", m_stage},
+ {"stageAlt", m_stageAlt},
+ {"stageEnterTime", m_stageEnterTime},
+ {"nextStageTime", m_nextStageTime}
+ });
+}
+
+}