Browse Source

add broken tree branches to gather sticks

Kolja Strohm 1 month ago
parent
commit
588736ad6b

+ 3 - 1
.gitignore

@@ -370,4 +370,6 @@ FodyWeavers.xsd
 /Windows Version/fcInit.ini
 /Windows Version/data/models/
 /Windows Version/data/textures/
-/Windows Version/data/images/
+/Windows Version/data/images/
+*.m3
+*.ltdb

+ 8 - 8
FactoryCraft/BlockFilter.cpp

@@ -12,7 +12,7 @@ BlockFilterAnd::BlockFilterAnd()
     : BlockFilter()
 {}
 
-bool BlockFilterAnd::test(const Block* zBlock)
+bool BlockFilterAnd::test(const Block* zBlock) const
 {
     for (BlockFilter* filter : filters)
     {
@@ -82,7 +82,7 @@ BlockFilterOr::BlockFilterOr()
     : BlockFilter()
 {}
 
-bool BlockFilterOr::test(const Block* zBlock)
+bool BlockFilterOr::test(const Block* zBlock) const
 {
     for (BlockFilter* filter : filters)
     {
@@ -158,7 +158,7 @@ BlockFilterNot::~BlockFilterNot()
     if (filter) filter->release();
 }
 
-bool BlockFilterNot::test(const Block* zBlock)
+bool BlockFilterNot::test(const Block* zBlock) const
 {
     return !filter->test(zBlock);
 }
@@ -212,7 +212,7 @@ BlockFilterBlockType::BlockFilterBlockType()
     : BlockFilter()
 {}
 
-bool BlockFilterBlockType::test(const Block* zBlock)
+bool BlockFilterBlockType::test(const Block* zBlock) const
 {
     for (int blockTypeId : blockTypeIds)
     {
@@ -284,7 +284,7 @@ BlockFilterTypeGroup::BlockFilterTypeGroup()
     : BlockFilter()
 {}
 
-bool BlockFilterTypeGroup::test(const Block* zBlock)
+bool BlockFilterTypeGroup::test(const Block* zBlock) const
 {
     for (Framework::Text* groupName : groupNames)
     {
@@ -358,7 +358,7 @@ BlockFilterMaxHardness::BlockFilterMaxHardness()
     : BlockFilter()
 {}
 
-bool BlockFilterMaxHardness::test(const Block* zBlock)
+bool BlockFilterMaxHardness::test(const Block* zBlock) const
 {
     return zBlock->zBlockType()->getHardness() <= maxHardness;
 }
@@ -413,7 +413,7 @@ BlockFilterMinHardness::BlockFilterMinHardness()
       minHardness(0.f)
 {}
 
-bool BlockFilterMinHardness::test(const Block* zBlock)
+bool BlockFilterMinHardness::test(const Block* zBlock) const
 {
     return zBlock->zBlockType()->getHardness() >= minHardness;
 }
@@ -468,7 +468,7 @@ BlockFilterMaxHeat::BlockFilterMaxHeat()
       maxHeat(0.f)
 {}
 
-bool BlockFilterMaxHeat::test(const Block* zBlock)
+bool BlockFilterMaxHeat::test(const Block* zBlock) const
 {
     const FluidBlockType* type
         = dynamic_cast<const FluidBlockType*>(zBlock->zBlockType());

+ 9 - 9
FactoryCraft/BlockFilter.h

@@ -10,7 +10,7 @@ class BlockFilter : public Framework::ReferenceCounter
 {
 public:
     BlockFilter();
-    virtual bool test(const Block* zBlock) = 0;
+    virtual bool test(const Block* zBlock) const = 0;
 };
 
 class BlockFilterAnd : public BlockFilter
@@ -20,7 +20,7 @@ private:
 
 public:
     BlockFilterAnd();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void addFilter(BlockFilter* filter);
     Framework::RCArray<BlockFilter> getFilters() const;
 };
@@ -44,7 +44,7 @@ private:
 
 public:
     BlockFilterOr();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void addFilter(BlockFilter* filter);
     Framework::RCArray<BlockFilter> getFilters() const;
 };
@@ -69,7 +69,7 @@ private:
 public:
     BlockFilterNot();
     ~BlockFilterNot();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void setFilter(BlockFilter* filter);
     BlockFilter* zFilter() const;
 };
@@ -93,7 +93,7 @@ private:
 
 public:
     BlockFilterBlockType();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void addBlockTypeId(int blockTypeId);
     const Framework::Array<int>& getBlockTypeIds() const;
 };
@@ -119,7 +119,7 @@ private:
 
 public:
     BlockFilterTypeGroup();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void addGroupName(const Framework::Text groupName);
     const Framework::RCArray<Framework::Text>& getGroupNames() const;
 };
@@ -145,7 +145,7 @@ private:
 
 public:
     BlockFilterMaxHardness();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void setMaxHardness(float maxHardness);
     float getMaxHardness() const;
 };
@@ -171,7 +171,7 @@ private:
 
 public:
     BlockFilterMinHardness();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void setMinHardness(float minHardness);
     float getMinHardness() const;
 };
@@ -197,7 +197,7 @@ private:
 
 public:
     BlockFilterMaxHeat();
-    bool test(const Block* zBlock) override;
+    bool test(const Block* zBlock) const override;
     void setMaxHeat(float maxHeat);
     float getMaxHeat() const;
 };

+ 8 - 0
FactoryCraft/Chunk.cpp

@@ -753,6 +753,14 @@ const Block* Chunk::zBlockConst(Framework::Vec3<int> location) const
     return Game::INSTANCE->zBlockType(b.getB())->zDefault();
 }
 
+const Block* Chunk::zBlockConstWC(int x, int y, int z) const
+{
+    auto pos = Dimension::chunkCoordinates({x, y, z});
+    auto b = zBlockAt(pos);
+    if (b.isA()) return b;
+    return Game::INSTANCE->zBlockType(b.getB())->zDefault();
+}
+
 const Block* Chunk::zBlockConst(int z, int index) const
 {
     if (blocks[z] && blocks[z][index])

+ 1 - 0
FactoryCraft/Chunk.h

@@ -70,6 +70,7 @@ public:
     Framework::Either<Block*, int> zBlockAt(
         Framework::Vec3<int> cLocation) const;
     const Block* zBlockConst(Framework::Vec3<int> location) const;
+    const Block* zBlockConstWC(int x, int y, int z) const;
     void instantiateBlock(Framework::Vec3<int> location);
     void generateBlock(Framework::Vec3<int> location);
     void putBlockAt(Framework::Vec3<int> location, Block* block);

+ 198 - 45
FactoryCraft/JsonExpression.cpp

@@ -1012,29 +1012,30 @@ const char* JFloatOperatorBoolExpressionFactory::getTypeToken() const
     return "comparsion";
 }
 
-JBlockTypeBoolExpression::JBlockTypeBoolExpression()
+JSpecificBlockBoolExpression::JSpecificBlockBoolExpression()
     : JBoolExpression(),
-      typeId(0),
+      filter(0),
       x(0),
       y(0),
       z(0)
 {}
 
-JBlockTypeBoolExpression ::~JBlockTypeBoolExpression()
+JSpecificBlockBoolExpression::~JSpecificBlockBoolExpression()
 {
+    if (filter) filter->release();
     if (x) x->release();
     if (y) y->release();
     if (z) z->release();
 }
 
-bool JBlockTypeBoolExpression::isValidPosition(
+bool JSpecificBlockBoolExpression::isValidPosition(
     int x, int y, Chunk* currentChunk)
 {
     return currentChunk
         && Game::getChunkCenter(x, y) == currentChunk->getCenter();
 }
 
-Framework::Assembly::AssemblyBlock& JBlockTypeBoolExpression::buildAssembly(
+Framework::Assembly::AssemblyBlock& JSpecificBlockBoolExpression::buildAssembly(
     JExpressionMemory* zMemory)
 {
     Framework::Assembly::AssemblyBlock& xBlock = x->buildAssembly(zMemory);
@@ -1099,12 +1100,11 @@ Framework::Assembly::AssemblyBlock& JBlockTypeBoolExpression::buildAssembly(
     codeBlock.addPush(Framework::Assembly::R9, Framework::Assembly::LOWER32);
     codeBlock.addLoadValue(
         (__int64*)zMemory->zzCurrentChunk(), Framework::Assembly::R9);
-    codeBlock
-        .addMemberCall<bool (JBlockTypeBoolExpression::*)(int, int, Chunk*)>(
-            &JBlockTypeBoolExpression::isValidPosition,
-            Framework::Assembly::INT_VALUE,
-            {Framework::Assembly::R9},
-            {});
+    codeBlock.addMemberCall<bool (JSpecificBlockBoolExpression::*)(
+        int, int, Chunk*)>(&JSpecificBlockBoolExpression::isValidPosition,
+        Framework::Assembly::INT_VALUE,
+        {Framework::Assembly::R9},
+        {});
     codeBlock.addPop(Framework::Assembly::R9, Framework::Assembly::LOWER32);
     codeBlock.addPop(Framework::Assembly::R8, Framework::Assembly::LOWER32);
     codeBlock.addPop(Framework::Assembly::RDX, Framework::Assembly::LOWER32);
@@ -1114,19 +1114,25 @@ Framework::Assembly::AssemblyBlock& JBlockTypeBoolExpression::buildAssembly(
     codeBlock.addJump(Framework::Assembly::JZ, "end");
     codeBlock.addLoadValue(
         (__int64*)zMemory->zzCurrentChunk(), Framework::Assembly::RCX);
-    codeBlock.addMemberCall<int (Chunk::*)(int, int, int) const>(
-        &Chunk::getBlockTypeAtWC,
+    codeBlock.addMemberCall<const Block* (Chunk::*)(int, int, int) const>(
+        &Chunk::zBlockConstWC,
         Framework::Assembly::INT_VALUE,
         {Framework::Assembly::RCX,
             Framework::Assembly::RDX,
             Framework::Assembly::R8,
             Framework::Assembly::R9},
         {});
-    codeBlock.addLoadValue(&typeId, Framework::Assembly::RCX);
-    codeBlock.addCompare(Framework::Assembly::RAX,
-        Framework::Assembly::RCX,
-        Framework::Assembly::LOWER32);
-    codeBlock.addJump(Framework::Assembly::JNE, "end_false");
+    codeBlock.addMoveValue(Framework::Assembly::RDX, Framework::Assembly::RAX);
+    codeBlock.addLoadValue((__int64*)&filter, Framework::Assembly::RCX);
+    codeBlock.addMemberCall<bool (BlockFilter::*)(const Block*) const>(
+        &BlockFilter::test,
+        Framework::Assembly::INT_VALUE,
+        {Framework::Assembly::RCX, Framework::Assembly::RDX},
+        {});
+    codeBlock.addTest(Framework::Assembly::RAX,
+        Framework::Assembly::RAX,
+        Framework::Assembly::LOWER8);
+    codeBlock.addJump(Framework::Assembly::JZ, "end");
     codeBlock.addMoveValue(Framework::Assembly::RAX, (char)1);
     codeBlock.addJump(Framework::Assembly::JMP, "end");
     codeBlock.defineJumpTarget("end_false");
@@ -1135,59 +1141,63 @@ Framework::Assembly::AssemblyBlock& JBlockTypeBoolExpression::buildAssembly(
     return codeBlock;
 }
 
-void JBlockTypeBoolExpression::setTypeId(int typeId)
+void JSpecificBlockBoolExpression::setFilter(BlockFilter* filter)
 {
-    this->typeId = typeId;
+    if (this->filter)
+    {
+        this->filter->release();
+    }
+    this->filter = filter;
 }
 
-int JBlockTypeBoolExpression::getTypeId() const
+BlockFilter* JSpecificBlockBoolExpression::zFilter() const
 {
-    return typeId;
+    return filter;
 }
 
-void JBlockTypeBoolExpression::setX(JFloatExpression* x)
+void JSpecificBlockBoolExpression::setX(JFloatExpression* x)
 {
     if (this->x) this->x->release();
     this->x = x;
 }
 
-JFloatExpression* JBlockTypeBoolExpression::zX() const
+JFloatExpression* JSpecificBlockBoolExpression::zX() const
 {
     return x;
 }
 
-void JBlockTypeBoolExpression::setY(JFloatExpression* y)
+void JSpecificBlockBoolExpression::setY(JFloatExpression* y)
 {
     if (this->y) this->y->release();
     this->y = y;
 }
 
-JFloatExpression* JBlockTypeBoolExpression::zY() const
+JFloatExpression* JSpecificBlockBoolExpression::zY() const
 {
     return y;
 }
 
-void JBlockTypeBoolExpression::setZ(JFloatExpression* z)
+void JSpecificBlockBoolExpression::setZ(JFloatExpression* z)
 {
     if (this->z) this->z->release();
     this->z = z;
 }
 
-JFloatExpression* JBlockTypeBoolExpression::zZ() const
+JFloatExpression* JSpecificBlockBoolExpression::zZ() const
 {
     return z;
 }
 
-JBlockTypeBoolExpressionFactory::JBlockTypeBoolExpressionFactory()
+JSpecificBlockBoolExpressionFactory::JSpecificBlockBoolExpressionFactory()
     : SubTypeFactory()
 {}
 
-JBlockTypeBoolExpression* JBlockTypeBoolExpressionFactory::fromJson(
+JSpecificBlockBoolExpression* JSpecificBlockBoolExpressionFactory::fromJson(
     Framework::JSON::JSONObject* zJson) const
 {
-    JBlockTypeBoolExpression* result = new JBlockTypeBoolExpression();
-    result->setTypeId(Game::INSTANCE->getBlockTypeId(
-        zJson->zValue("blockType")->asString()->getString()));
+    JSpecificBlockBoolExpression* result = new JSpecificBlockBoolExpression();
+    result->setFilter(Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
+        zJson->zValue("condition")));
     result->setX(Game::INSTANCE->zTypeRegistry()->fromJson<JFloatExpression>(
         zJson->zValue("x")));
     result->setY(Game::INSTANCE->zTypeRegistry()->fromJson<JFloatExpression>(
@@ -1197,13 +1207,12 @@ JBlockTypeBoolExpression* JBlockTypeBoolExpressionFactory::fromJson(
     return result;
 }
 
-Framework::JSON::JSONObject* JBlockTypeBoolExpressionFactory::toJsonObject(
-    JBlockTypeBoolExpression* zObject) const
+Framework::JSON::JSONObject* JSpecificBlockBoolExpressionFactory::toJsonObject(
+    JSpecificBlockBoolExpression* zObject) const
 {
     Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
-    result->addValue("blockType",
-        new Framework::JSON::JSONString(
-            Game::INSTANCE->zBlockType(zObject->getTypeId())->getName()));
+    result->addValue("condition",
+        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zFilter()));
     result->addValue(
         "x", Game::INSTANCE->zTypeRegistry()->toJson(zObject->zX()));
     result->addValue(
@@ -1213,13 +1222,13 @@ Framework::JSON::JSONObject* JBlockTypeBoolExpressionFactory::toJsonObject(
     return result;
 }
 
-JSONObjectValidationBuilder* JBlockTypeBoolExpressionFactory::addToValidator(
+JSONObjectValidationBuilder*
+JSpecificBlockBoolExpressionFactory::addToValidator(
     JSONObjectValidationBuilder* builder) const
 {
     return builder
-        ->withRequiredAttribute("blockType",
-            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
-                BlockTypeNameFactory::TYPE_ID))
+        ->withRequiredAttribute("condition",
+            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
         ->withRequiredAttribute("x",
             Game::INSTANCE->zTypeRegistry()->getValidator<JFloatExpression>())
         ->withRequiredAttribute("y",
@@ -1228,7 +1237,151 @@ JSONObjectValidationBuilder* JBlockTypeBoolExpressionFactory::addToValidator(
             Game::INSTANCE->zTypeRegistry()->getValidator<JFloatExpression>());
 }
 
-const char* JBlockTypeBoolExpressionFactory::getTypeToken() const
+const char* JSpecificBlockBoolExpressionFactory::getTypeToken() const
+{
+    return "specificBlockMatches";
+}
+
+JFirstBlockAboveBoolExpression::JFirstBlockAboveBoolExpression()
+    : JBoolExpression(),
+      filter(0)
+{}
+
+JFirstBlockAboveBoolExpression::~JFirstBlockAboveBoolExpression()
+{
+    if (filter) filter->release();
+}
+
+Framework::Assembly::AssemblyBlock&
+JFirstBlockAboveBoolExpression::buildAssembly(JExpressionMemory* zMemory)
+{
+    // load x into R12
+    codeBlock.addLoadValue(
+        zMemory->getFloatVariableP("x"), Framework::Assembly::MM0);
+    codeBlock.addConversion(Framework::Assembly::R12,
+        Framework::Assembly::MM0,
+        Framework::Assembly::SINGLE_FLOAT,
+        Framework::Assembly::LOWER32,
+        1);
+    // load y into R13
+    codeBlock.addLoadValue(
+        zMemory->getFloatVariableP("y"), Framework::Assembly::MM0);
+    codeBlock.addConversion(Framework::Assembly::R13,
+        Framework::Assembly::MM0,
+        Framework::Assembly::SINGLE_FLOAT,
+        Framework::Assembly::LOWER32,
+        1);
+    // load z into R14
+    codeBlock.addLoadValue(
+        zMemory->getFloatVariableP("z"), Framework::Assembly::MM0);
+    codeBlock.addConversion(Framework::Assembly::R14,
+        Framework::Assembly::MM0,
+        Framework::Assembly::SINGLE_FLOAT,
+        Framework::Assembly::LOWER32,
+        1);
+    // load current chunk into R15
+    codeBlock.addLoadValue(
+        (__int64*)zMemory->zzCurrentChunk(), Framework::Assembly::R15);
+    // begin loop to check above blocks
+    codeBlock.defineJumpTarget("loop_start");
+    // increment height
+    codeBlock.addAddition(Framework::Assembly::R14, (char)1);
+    // check if height is above world height
+    codeBlock.addCompare(Framework::Assembly::R14, WORLD_HEIGHT);
+    codeBlock.addJump(Framework::Assembly::JGE, "end_false");
+    // load block type at current position into RAX
+    codeBlock.addMoveValue(Framework::Assembly::RCX, Framework::Assembly::R15);
+    codeBlock.addMoveValue(Framework::Assembly::RDX, Framework::Assembly::R12);
+    codeBlock.addMoveValue(Framework::Assembly::R8, Framework::Assembly::R13);
+    codeBlock.addMoveValue(Framework::Assembly::R9, Framework::Assembly::R14);
+    codeBlock.addMemberCall<int (Chunk::*)(int, int, int) const>(
+        &Chunk::getBlockTypeAtWC,
+        Framework::Assembly::INT_VALUE,
+        {Framework::Assembly::RCX,
+            Framework::Assembly::RDX,
+            Framework::Assembly::R8,
+            Framework::Assembly::R9},
+        {});
+    // check if block type is 0 (NO_BLOCK) or 1 (AIR)
+    codeBlock.addCompare(Framework::Assembly::RAX, 1);
+    codeBlock.addJump(Framework::Assembly::JLE, "loop_start");
+    // end of loop
+    // load current block into RDX
+    codeBlock.addMoveValue(Framework::Assembly::RCX, Framework::Assembly::R15);
+    codeBlock.addMoveValue(Framework::Assembly::RDX, Framework::Assembly::R12);
+    codeBlock.addMoveValue(Framework::Assembly::R8, Framework::Assembly::R13);
+    codeBlock.addMoveValue(Framework::Assembly::R9, Framework::Assembly::R14);
+    codeBlock.addMemberCall<const Block* (Chunk::*)(int, int, int) const>(
+        &Chunk::zBlockConstWC,
+        Framework::Assembly::INT_VALUE,
+        {Framework::Assembly::RCX,
+            Framework::Assembly::RDX,
+            Framework::Assembly::R8,
+            Framework::Assembly::R9},
+        {});
+    codeBlock.addMoveValue(Framework::Assembly::RDX, Framework::Assembly::RAX);
+    // load filter into RCX
+    codeBlock.addLoadValue((__int64*)&filter, Framework::Assembly::RCX);
+    // check filter condition
+    codeBlock.addMemberCall<bool (BlockFilter::*)(const Block*) const>(
+        &BlockFilter::test,
+        Framework::Assembly::INT_VALUE,
+        {Framework::Assembly::RCX, Framework::Assembly::RDX},
+        {});
+    codeBlock.addJump(Framework::Assembly::JMP, "end");
+    codeBlock.defineJumpTarget("end_false");
+    codeBlock.addMoveValue(Framework::Assembly::RAX, (char)0);
+    codeBlock.defineJumpTarget("end");
+    return codeBlock;
+}
+
+void JFirstBlockAboveBoolExpression::setFilter(BlockFilter* filter)
+{
+    if (this->filter)
+    {
+        this->filter->release();
+    }
+    this->filter = filter;
+}
+
+BlockFilter* JFirstBlockAboveBoolExpression::zFilter() const
 {
-    return "blockType";
-}
+    return filter;
+}
+
+JFirstBlockAboveBoolExpressionFactory::JFirstBlockAboveBoolExpressionFactory()
+    : SubTypeFactory()
+{}
+
+JFirstBlockAboveBoolExpression* JFirstBlockAboveBoolExpressionFactory::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    JFirstBlockAboveBoolExpression* result
+        = new JFirstBlockAboveBoolExpression();
+    result->setFilter(Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
+        zJson->zValue("condition")));
+    return result;
+}
+
+Framework::JSON::JSONObject*
+JFirstBlockAboveBoolExpressionFactory::toJsonObject(
+    JFirstBlockAboveBoolExpression* zObject) const
+{
+    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
+    result->addValue("condition",
+        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zFilter()));
+    return result;
+}
+
+JSONObjectValidationBuilder*
+JFirstBlockAboveBoolExpressionFactory::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return builder->withRequiredAttribute("condition",
+        Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>());
+}
+
+const char* JFirstBlockAboveBoolExpressionFactory::getTypeToken() const
+{
+    return "firstBlockAboveMatches";
+}

+ 42 - 12
FactoryCraft/JsonExpression.h

@@ -7,6 +7,7 @@
 #include <Text.h>
 #include <Trie.h>
 
+#include "BlockFilter.h"
 #include "Noise.h"
 #include "TypeRegistry.h"
 
@@ -322,17 +323,17 @@ public:
     const char* getTypeToken() const override;
 };
 
-class JBlockTypeBoolExpression : public JBoolExpression
+class JSpecificBlockBoolExpression : public JBoolExpression
 {
 private:
-    int typeId;
+    BlockFilter* filter;
     JFloatExpression* x;
     JFloatExpression* y;
     JFloatExpression* z;
 
 public:
-    JBlockTypeBoolExpression();
-    ~JBlockTypeBoolExpression();
+    JSpecificBlockBoolExpression();
+    ~JSpecificBlockBoolExpression();
 
 private:
     bool isValidPosition(int x, int y, Chunk* currentChunk);
@@ -341,8 +342,8 @@ public:
     Framework::Assembly::AssemblyBlock& buildAssembly(
         JExpressionMemory* zMemory) override;
 
-    void setTypeId(int typeId);
-    int getTypeId() const;
+    void setFilter(BlockFilter* filter);
+    BlockFilter* zFilter() const;
     void setX(JFloatExpression* x);
     JFloatExpression* zX() const;
     void setY(JFloatExpression* y);
@@ -351,16 +352,45 @@ public:
     JFloatExpression* zZ() const;
 };
 
-class JBlockTypeBoolExpressionFactory
-    : public SubTypeFactory<JBoolExpression, JBlockTypeBoolExpression>
+class JSpecificBlockBoolExpressionFactory
+    : public SubTypeFactory<JBoolExpression, JSpecificBlockBoolExpression>
 {
 public:
-    JBlockTypeBoolExpressionFactory();
-    JBlockTypeBoolExpression* fromJson(
+    JSpecificBlockBoolExpressionFactory();
+    JSpecificBlockBoolExpression* fromJson(
         Framework::JSON::JSONObject* zJson) const override;
     Framework::JSON::JSONObject* toJsonObject(
-        JBlockTypeBoolExpression* zObject) const override;
+        JSpecificBlockBoolExpression* zObject) const override;
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override;
     const char* getTypeToken() const override;
-};
+};
+
+class JFirstBlockAboveBoolExpression : public JBoolExpression
+{
+private:
+    BlockFilter* filter;
+
+public:
+    JFirstBlockAboveBoolExpression();
+    ~JFirstBlockAboveBoolExpression();
+    Framework::Assembly::AssemblyBlock& buildAssembly(
+        JExpressionMemory* zMemory) override;
+
+    void setFilter(BlockFilter* filter);
+    BlockFilter* zFilter() const;
+};
+
+class JFirstBlockAboveBoolExpressionFactory
+    : public SubTypeFactory<JBoolExpression, JFirstBlockAboveBoolExpression>
+{
+public:
+    JFirstBlockAboveBoolExpressionFactory();
+    JFirstBlockAboveBoolExpression* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        JFirstBlockAboveBoolExpression* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};

+ 2 - 1
FactoryCraft/TypeRegistry.cpp

@@ -131,7 +131,8 @@ TypeRegistry::TypeRegistry()
     registerSubType(new JOperatorFloatExpressionFactory());
     registerSubType(new JBoolOperatorBoolExpressionFactory());
     registerSubType(new JFloatOperatorBoolExpressionFactory());
-    registerSubType(new JBlockTypeBoolExpressionFactory());
+    registerSubType(new JSpecificBlockBoolExpressionFactory());
+    registerSubType(new JFirstBlockAboveBoolExpressionFactory());
 
     // world generator
     registerType(new WorldHeightLayerFactory());

+ 4 - 4
Windows Version/data/blocks/blockTypes.json

@@ -194,7 +194,7 @@
         "type": "specificItem",
         "condition": {
           "type": "chance",
-          "chance": 0.2
+          "chance": 0.1
         },
         "amount": 1,
         "itemType": "Wooden Stick"
@@ -442,7 +442,7 @@
         "type": "specificItem",
         "condition": {
           "type": "chance",
-          "chance": 0.2
+          "chance": 0.1
         },
         "amount": 1,
         "itemType": "Wooden Stick"
@@ -588,7 +588,7 @@
         "type": "specificItem",
         "condition": {
           "type": "chance",
-          "chance": 0.2
+          "chance": 0.1
         },
         "amount": 1,
         "itemType": "Wooden Stick"
@@ -769,7 +769,7 @@
         "type": "specificItem",
         "condition": {
           "type": "chance",
-          "chance": 0.2
+          "chance": 0.1
         },
         "amount": 1,
         "itemType": "Wooden Stick"

+ 34 - 0
Windows Version/data/blocks/plants.json

@@ -27,6 +27,40 @@
             }
         ]
     },
+    {
+        "type": "basicBlock",
+        "name": "Broken Tree Branches",
+        "itemType": null,
+        "model": {
+            "modelPath": "data/models/blocks.m3/brokenTreeBranches",
+            "texturePaths": [
+                "data/textures/blocks.ltdb/brokentreebranc"
+            ]
+        },
+        "transparent": true,
+        "mapColor": "0xA0863F25",
+        "hardness": 0.1,
+        "damagableByHand": true,
+        "drops": [
+            {
+                "type": "specificItem",
+                "amount": 1,
+                "condition": {
+                    "type": "allways"
+                },
+                "itemType": "Wooden Stick"
+            },
+            {
+                "type": "specificItem",
+                "amount": 1,
+                "condition": {
+                    "type": "chance",
+                    "chance": 0.5
+                },
+                "itemType": "Wooden Stick"
+            }
+        ]
+    },
     {
         "type": "growingPlant",
         "name": "WheatSeeds",

+ 55 - 6
Windows Version/data/generator/overworld.json

@@ -248,8 +248,13 @@
                     {
                         "type": "Kow",
                         "condition": {
-                            "blockType": "Dirt",
-                            "type": "blockType",
+                            "type": "specificBlockMatches",
+                            "condition": {
+                                "type": "types",
+                                "typeNames": [
+                                    "Dirt"
+                                ]
+                            },
                             "x": {
                                 "type": "variable",
                                 "name": "x"
@@ -501,11 +506,50 @@
                     }
                 ],
                 "plants": [
+                    {
+                        "plantBlock": "Broken Tree Branches",
+                        "locations": [
+                            "SURFACE"
+                        ],
+                        "condition": {
+                            "type": "firstBlockAboveMatches",
+                            "condition": {
+                                "type": "groups",
+                                "groupNames": [
+                                    "Leaves"
+                                ]
+                            }
+                        },
+                        "plantHeight": 1,
+                        "threshold": 0.7,
+                        "noise": {
+                            "type": "random",
+                            "seed": {
+                                "type": "operator",
+                                "operator": "+",
+                                "values": [
+                                    {
+                                        "type": "variable",
+                                        "name": "dimensionSeed"
+                                    },
+                                    {
+                                        "type": "constant",
+                                        "value": 57
+                                    }
+                                ]
+                            }
+                        }
+                    },
                     {
                         "plantBlock": "Grass",
                         "condition": {
-                            "blockType": "Dirt",
-                            "type": "blockType",
+                            "type": "specificBlockMatches",
+                            "condition": {
+                                "type": "types",
+                                "typeNames": [
+                                    "Dirt"
+                                ]
+                            },
                             "x": {
                                 "type": "variable",
                                 "name": "x"
@@ -555,8 +599,13 @@
                     {
                         "plantBlock": "Cotton Plant",
                         "condition": {
-                            "blockType": "Dirt",
-                            "type": "blockType",
+                            "type": "specificBlockMatches",
+                            "condition": {
+                                "type": "types",
+                                "typeNames": [
+                                    "Dirt"
+                                ]
+                            },
                             "x": {
                                 "type": "variable",
                                 "name": "x"

BIN
Windows Version/data/models/blocks.m3


+ 2 - 4
Windows Version/data/quests/quests.json

@@ -71,7 +71,7 @@
             {
                 "questId": "wood_sticks",
                 "questName": "Wooden Sticks",
-                "description": "You can get some wooden sticks by harvesting tree leaves.",
+                "description": "You can get some wooden sticks by harvesting tree leaves or gather broken tree branches from the ground beneath trees.",
                 "imagePath": "itemType:Wooden Stick",
                 "requirements": [
                     {
@@ -201,9 +201,8 @@
             {
                 "questId": "resin",
                 "questName": "Resin",
-                "description": "Gather some resin by removing the tree bark from resin trees.",
+                "description": "Gather some resin by removing the bark from a pine trees.",
                 "imagePath": "itemType:Resin",
-                "mainQuest": true,
                 "requirements": [
                     {
                         "type": "item_in_inventory",
@@ -239,7 +238,6 @@
                         "campfire"
                     ]
                 ],
-                "mainQuest": true,
                 "requirements": [
                     {
                         "type": "item_in_inventory",