Browse Source

add frontDirection to blocks

Kolja Strohm 3 tuần trước cách đây
mục cha
commit
6e04fa0148

+ 15 - 6
FactoryCraft/Area.cpp

@@ -48,12 +48,21 @@ Framework::Vec3<int> getDirection(Directions dir)
 
 int getDirectionIndex(Direction dir)
 {
-    if (dir == NORTH) return 0;
-    if (dir == EAST) return 1;
-    if (dir == SOUTH) return 2;
-    if (dir == WEST) return 3;
-    if (dir == TOP) return 4;
-    if (dir == BOTTOM) return 5;
+    switch (dir)
+    {
+    case NORTH:
+        return 0;
+    case EAST:
+        return 1;
+    case SOUTH:
+        return 2;
+    case WEST:
+        return 3;
+    case TOP:
+        return 4;
+    case BOTTOM:
+        return 5;
+    }
     assert(false);
     return -1;
 }

+ 10 - 10
FactoryCraft/Area.h

@@ -11,19 +11,19 @@ struct Area
     int dimensionId;
 };
 
-enum Direction
+enum Direction : char
 {
-    NO_DIRECTION = 0,
-    NORTH = 1,
-    EAST = 2,
-    SOUTH = 4,
-    WEST = 8,
-    TOP = 16,
-    BOTTOM = 32,
-    INSIDE = 64
+    NO_DIRECTION = (char)0,
+    NORTH = (char)1,
+    EAST = (char)2,
+    SOUTH = (char)4,
+    WEST = (char)8,
+    TOP = (char)16,
+    BOTTOM = (char)32,
+    INSIDE = (char)64
 };
 
-typedef int Directions;
+typedef char Directions;
 
 #define ANY_DIRECTION NORTH | EAST | SOUTH | WEST | TOP | BOTTOM
 

+ 30 - 4
FactoryCraft/Block.cpp

@@ -13,9 +13,21 @@
 
 Block::Block(
     int typeId, Framework::Vec3<int> pos, int dimensionId, bool hasInventory)
-    : Inventory(pos, dimensionId, hasInventory)
+    : Block(typeId,
+          pos,
+          dimensionId,
+          hasInventory,
+          Game::INSTANCE->zBlockType(typeId)->getDefaultFrontDirection())
+{}
+
+Block::Block(int typeId,
+    Framework::Vec3<int> pos,
+    int dimensionId,
+    bool hasInventory,
+    Direction frontDirection)
+    : Inventory(pos, dimensionId, hasInventory),
+      frontDirection(frontDirection)
 {
-    transparent = false;
     passable = false;
     hp = 1;
     maxHP = 1;
@@ -31,6 +43,8 @@ Block::Block(
     interactable = 0;
     deadAndRemoved = 0;
     memset(lightEmisionColor, 0, 3);
+    transparent = 0;
+    transmissionRequested = 0;
     mapColor = 0;
 }
 
@@ -407,6 +421,16 @@ int Block::getMapColor() const
     return mapColor;
 }
 
+void Block::setFrontDirection(Direction frontDirection)
+{
+    this->frontDirection = frontDirection;
+}
+
+Direction Block::getFrontDirection() const
+{
+    return frontDirection;
+}
+
 BasicBlockItem::BasicBlockItem(int itemTypeId,
     int blockTypeId,
     Framework::Text name,
@@ -456,7 +480,8 @@ BasicBlockItemType::BasicBlockItemType()
       hardness(1.f),
       speedModifier(1.f),
       blockTypeName(""),
-      placeableProof(0)
+      placeableProof(0),
+      blockTypeId(-1)
 {}
 
 BasicBlockItemType::BasicBlockItemType(Framework::Text name,
@@ -475,7 +500,8 @@ BasicBlockItemType::BasicBlockItemType(Framework::Text name,
       hardness(hardness),
       speedModifier(speedModifier),
       blockTypeName(blockTypeName),
-      placeableProof(placeableProof)
+      placeableProof(placeableProof),
+      blockTypeId(-1)
 {
     setName(name);
     setModel(model);

+ 8 - 0
FactoryCraft/Block.h

@@ -31,6 +31,7 @@ private:
     bool onTickCalled;
 
 protected:
+    Direction frontDirection;
     bool transparent;
     bool passable;
     float hp;
@@ -81,6 +82,11 @@ public:
         Framework::Vec3<int> pos,
         int dimensionId,
         bool hasInventory);
+    Block(int typeId,
+        Framework::Vec3<int> pos,
+        int dimensionId,
+        bool hasInventory,
+        Direction frontDirection);
     virtual ~Block();
 
     void tick(TickQueue* zQueue) override;
@@ -112,6 +118,8 @@ public:
     virtual void filterPassingLight(unsigned char rgb[3]) const;
     void updateModel(ModelInfo* zInfo) const;
     int getMapColor() const;
+    void setFrontDirection(Direction frontDirection);
+    Direction getFrontDirection() const;
 
     friend BlockType;
 };

+ 16 - 1
FactoryCraft/BlockType.cpp

@@ -22,7 +22,8 @@ BlockType::BlockType()
       groupNames(),
       hardness(1.f),
       damagableByHand(0),
-      itemType(0)
+      itemType(0),
+      defaultFrontDirection(Direction::NORTH)
 {}
 
 BlockType::~BlockType()
@@ -43,6 +44,7 @@ void BlockType::loadSuperBlock(
     zReader->read((char*)&zBlock->speedModifier, 4);
     zReader->read((char*)&zBlock->mapColor, 4);
     zReader->read((char*)&zBlock->interactable, 1);
+    zReader->read((char*)&zBlock->frontDirection, 1);
     int strCount;
     zReader->read((char*)&strCount, 4);
     for (int i = 0; i < strCount; i++)
@@ -71,6 +73,7 @@ void BlockType::saveSuperBlock(
     zWriter->write((char*)&zBlock->speedModifier, 4);
     zWriter->write((char*)&zBlock->mapColor, 4);
     zWriter->write((char*)&zBlock->interactable, 1);
+    zWriter->write((char*)&zBlock->frontDirection, 1);
     int strCount = zBlock->structures.getEntryCount();
     zWriter->write((char*)&strCount, 4);
     for (MultiblockStructure* structure : zBlock->structures)
@@ -95,6 +98,7 @@ void BlockType::createSuperBlock(Block* zBlock, Item* zItem) const
         }
     }
     zBlock->mapColor = initialMapColor;
+    zBlock->frontDirection = defaultFrontDirection;
 }
 
 void BlockType::createSuperItem(Block* zBlock, Item* zItem) const
@@ -170,6 +174,7 @@ void BlockType::writeTypeInfo(StreamWriter* zWriter) const
         char flowDist = getFlowDistance();
         zWriter->write(&flowDist, 1);
     }
+    zWriter->write((char*)&defaultFrontDirection, 1);
     int maxHp = getInitialMaxHP();
     zWriter->write((char*)&maxHp, 4);
     zModel()->writeTo(zWriter);
@@ -345,6 +350,16 @@ BlockType::getInteractionConfigs() const
     return interactionConfigs;
 }
 
+void BlockType::setDefaultFrontDirection(Direction defaultFrontDirection)
+{
+    this->defaultFrontDirection = defaultFrontDirection;
+}
+
+Direction BlockType::getDefaultFrontDirection() const
+{
+    return defaultFrontDirection;
+}
+
 int BlockType::getTypeId(const char* name)
 {
     Text n = name;

+ 64 - 1
FactoryCraft/BlockType.h

@@ -6,6 +6,7 @@
 #include <Writer.h>
 #include <XML.h>
 
+#include "Area.h"
 #include "DropConfig.h"
 #include "InteractionConfig.h"
 #include "ModelInfo.h"
@@ -39,6 +40,7 @@ private:
     bool damagableByHand;
     Framework::RCArray<InteractionConfig> interactionConfigs;
     const ItemType* itemType;
+    Direction defaultFrontDirection;
 
 protected:
     Block* defaultBlock;
@@ -105,6 +107,8 @@ public:
     bool isDamagableByHand() const;
     void addInteractionConfig(InteractionConfig* config);
     const Framework::RCArray<InteractionConfig>& getInteractionConfigs() const;
+    void setDefaultFrontDirection(Direction defaultFrontDirection);
+    Direction getDefaultFrontDirection() const;
 
     static int getTypeId(const char* name);
     static Framework::Text getTypeName(int id);
@@ -161,6 +165,33 @@ public:
                 Game::INSTANCE->zTypeRegistry()->fromJson<InteractionConfig>(
                     value));
         }
+        Framework::Text frontDirection
+            = zJson->zValue("defaultFrontDirection")->asString()->getString();
+        if (frontDirection.isEqual("NORTH"))
+        {
+            zType->setDefaultFrontDirection(Direction::NORTH);
+        }
+        else if (frontDirection.isEqual("EAST"))
+        {
+            zType->setDefaultFrontDirection(Direction::EAST);
+        }
+
+        else if (frontDirection.isEqual("SOUTH"))
+        {
+            zType->setDefaultFrontDirection(Direction::SOUTH);
+        }
+        else if (frontDirection.isEqual("WEST"))
+        {
+            zType->setDefaultFrontDirection(Direction::WEST);
+        }
+        else if (frontDirection.isEqual("TOP"))
+        {
+            zType->setDefaultFrontDirection(Direction::TOP);
+        }
+        else if (frontDirection.isEqual("BOTTOM"))
+        {
+            zType->setDefaultFrontDirection(Direction::BOTTOM);
+        }
         return result;
     }
 
@@ -211,6 +242,33 @@ public:
                     interaction));
         }
         result->addValue("interactions", interactions);
+        switch (zType->getDefaultFrontDirection())
+        {
+        case Direction::NORTH:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("NORTH"));
+            break;
+        case Direction::EAST:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("EAST"));
+            break;
+        case Direction::SOUTH:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("SOUTH"));
+            break;
+        case Direction::WEST:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("WEST"));
+            break;
+        case Direction::TOP:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("TOP"));
+            break;
+        case Direction::BOTTOM:
+            result->addValue("defaultFrontDirection",
+                new Framework::JSON::JSONString("BOTTOM"));
+            break;
+        }
         return result;
     }
 
@@ -269,7 +327,12 @@ public:
             ->withDefault(new Framework::JSON::JSONArray())
             ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
                     ->getValidator<InteractionConfig>())
-            ->finishArray();
+
+            ->finishArray()
+            ->withRequiredString("defaultFrontDirection")
+            ->whichIsOneOf({"NORTH", "EAST", "SOUTH", "WEST", "TOP", "BOTTOM"})
+            ->withDefault("NORTH")
+            ->finishString();
     }
 
 protected:

+ 45 - 19
FactoryCraft/Chunk.cpp

@@ -151,8 +151,14 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
                     {
                         state |= 2;
                     }
+                    if (blocks[z] && blocks[z][index]
+                        && blocks[z][index]->getFrontDirection()
+                               != type->getDefaultFrontDirection())
+                    {
+                        state |= 4;
+                    }
                     zWriter->write((char*)&state, 1);
-                    if ((state | 1) == state)
+                    if (state & 1)
                     {
                         FluidBlock* fluidBlock
                             = blocks[z]
@@ -165,7 +171,7 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
                                           : 0;
                         zWriter->write(&data, 1);
                     }
-                    if ((state | 2) == state)
+                    if (state & 2)
                     {
                         float speedModifier
                             = blocks[z] && blocks[z][index]
@@ -173,6 +179,12 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
                                 : type->zDefault()->getSpeedModifier();
                         zWriter->write((char*)&speedModifier, 4);
                     }
+                    if (state & 4)
+                    {
+                        Direction frontDirection
+                            = blocks[z][index]->getFrontDirection();
+                        zWriter->write((char*)&frontDirection, 1);
+                    }
                 }
             }
         }
@@ -325,9 +337,8 @@ void Chunk::sendLightToClient(
                             zWriter->write((char*)&x, 4);
                             zWriter->write((char*)&y, 4);
                             zWriter->write((char*)&z, 4);
-                            zWriter->write(
-                                (char*)(zNeighbours[dir]->lightData
-                                        + index * 6),
+                            zWriter->write((char*)(zNeighbours[dir]->lightData
+                                                   + index * 6),
                                 6);
                         }
                     }
@@ -362,8 +373,7 @@ bool Chunk::isVisible(int z, int index) const
             {
                 Framework::Either<Block*, int> n = BlockTypeEnum::NO_BLOCK;
                 Framework::Vec3<int> pos
-                    = getDirection((Directions)getDirectionFromIndex(d))
-                    + indexPos;
+                    = getDirection(getDirectionFromIndex(d)) + indexPos;
                 if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0
                     && pos.y < CHUNK_SIZE && pos.z >= 0 && pos.z < WORLD_HEIGHT)
                 {
@@ -961,40 +971,56 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
 void Chunk::sendBlockInfo(Framework::Vec3<int> location)
 {
     int index = Chunk::index(location.x, location.y);
-    char* msg = new char[14];
-    msg[0] = 0; // set block
     int typeId = blockIds[location.z] ? blockIds[location.z][index] : 0;
-    *(unsigned short*)(msg + 1) = typeId;
-    *(int*)(msg + 3) = index * WORLD_HEIGHT + location.z;
     char state = 0;
+    int size = 8;
     const BlockType* type = Game::INSTANCE->zBlockType(typeId);
     if (type->isFluid())
     {
         state |= 1;
+        size += 2;
     }
     if ((blocks[location.z] && blocks[location.z][index]
             && blocks[location.z][index]->isPassable())
         || (type->zDefault()->isPassable()))
     {
         state |= 2;
+        size += 4;
     }
+    if (blocks[location.z] && blocks[location.z][index]
+        && blocks[location.z][index]->getFrontDirection()
+               != type->getDefaultFrontDirection())
+    {
+        state |= 4;
+        size += 1;
+    }
+    char* msg = new char[size];
+    msg[0] = 0; // set block
+    *(unsigned short*)(msg + 1) = typeId;
+    *(int*)(msg + 3) = index * WORLD_HEIGHT + location.z;
     msg[7] = state;
-    if ((state | 1) == state)
+    int i = 8;
+    if (state & 1)
     {
         FluidBlock* fluidBlock = dynamic_cast<FluidBlock*>(
             blocks[location.z] ? blocks[location.z][index] : 0);
-        msg[8] = fluidBlock ? fluidBlock->getFlowOptions() : 0;
-        msg[9] = fluidBlock ? fluidBlock->getDistanceToSource() : 0;
+        msg[i++] = fluidBlock ? fluidBlock->getFlowOptions() : 0;
+        msg[i++] = fluidBlock ? fluidBlock->getDistanceToSource() : 0;
     }
-    if ((state | 2) == state)
+    if (state & 2)
     {
-        *(float*)(msg + 10) = blocks[location.z] && blocks[location.z][index]
-                                ? blocks[location.z][index]->getSpeedModifier()
-                                : type->zDefault()->getSpeedModifier();
+        *(float*)(msg + i) = blocks[location.z] && blocks[location.z][index]
+                               ? blocks[location.z][index]->getSpeedModifier()
+                               : type->zDefault()->getSpeedModifier();
+        index += 4;
+    }
+    if (state & 4)
+    {
+        *(msg + i++) = blocks[location.z][index]->getFrontDirection();
     }
     NetworkMessage* message = new NetworkMessage();
     message->addressChunck(this);
-    message->setMessage(msg, 14);
+    message->setMessage(msg, size);
     notifyObservers(message);
     if (blocks[location.z] && blocks[location.z][index])
     {

+ 4 - 5
FactoryCraft/DimensionGenerator.cpp

@@ -12,7 +12,8 @@ WorldHeightLayer::WorldHeightLayer()
     : ReferenceCounter(),
       noiseConfig(0),
       noise(0),
-      value(0)
+      value(0),
+      valueP(0)
 {}
 
 WorldHeightLayer::~WorldHeightLayer()
@@ -413,8 +414,7 @@ Chunk* BiomedCavedDimensionGenerator::generateChunk(int centerX, int centerY)
                         for (int i = 0; i < 6; i++)
                         {
                             Framework::Vec3<int> pos
-                                = getDirection(
-                                      (Directions)getDirectionFromIndex(i))
+                                = getDirection(getDirectionFromIndex(i))
                                 + Framework::Vec3<int>(
                                     x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, z);
                             const Block* neighborBlock = 0;
@@ -621,8 +621,7 @@ void BiomedCavedDimensionGenerator::postprocessChunk(Chunk* zChunk)
                             for (int i = 0; i < 6; i++)
                             {
                                 Framework::Vec3<int> pos
-                                    = getDirection(
-                                          (Directions)getDirectionFromIndex(i))
+                                    = getDirection(getDirectionFromIndex(i))
                                     + Framework::Vec3<int>(x, y, z);
                                 const Block* neighborBlock = 0;
                                 if (pos.x >= 0 && pos.x < CHUNK_SIZE

+ 7 - 7
FactoryCraft/Entity.cpp

@@ -172,17 +172,17 @@ void ActionTarget::toMessage(
             }
             Framework::Text targetUIMLText
                 = targetUIML ? targetUIML->toString() : Framework::Text();
-            char* message = new char[18 + targetUIMLText.getLength() + 2];
+            char* message = new char[15 + targetUIMLText.getLength() + 2];
             message[0] = 3;
             message[1] = 2;
             *(int*)(message + 2) = zTarget->blockPos.x;
             *(int*)(message + 6) = zTarget->blockPos.y;
             *(int*)(message + 10) = zTarget->blockPos.z;
-            *(int*)(message + 14) = zTarget->targetBlockSide;
+            *(message + 14) = zTarget->targetBlockSide;
             short len = (short)targetUIMLText.getLength();
-            *(short*)(message + 18) = len;
-            memcpy(message + 20, targetUIMLText.getText(), len);
-            zMsg->setMessage(message, 18 + len + 2);
+            *(short*)(message + 15) = len;
+            memcpy(message + 17, targetUIMLText.getText(), len);
+            zMsg->setMessage(message, 15 + len + 2);
         }
     }
     else
@@ -211,7 +211,7 @@ void ActionTarget::save(ActionTarget* zTarget, Framework::StreamWriter* zWriter)
             zWriter->write((char*)&zTarget->blockPos.x, 4);
             zWriter->write((char*)&zTarget->blockPos.y, 4);
             zWriter->write((char*)&zTarget->blockPos.z, 4);
-            zWriter->write((char*)&zTarget->targetBlockSide, 4);
+            zWriter->write((char*)&zTarget->targetBlockSide, 1);
         }
     }
     else
@@ -238,7 +238,7 @@ ActionTarget* ActionTarget::load(Framework::StreamReader* zReader)
         zReader->read((char*)&pos.x, 4);
         zReader->read((char*)&pos.y, 4);
         zReader->read((char*)&pos.z, 4);
-        zReader->read((char*)&side, 4);
+        zReader->read((char*)&side, 1);
         return new ActionTarget(pos, side);
     }
     return 0;

+ 2 - 0
FactoryCraft/FactoryCraft.vcxproj

@@ -179,6 +179,7 @@
     <ClInclude Include="NoBlock.h" />
     <ClInclude Include="NoiseInterpolator.h" />
     <ClInclude Include="OpenDialogInteractionConfig.h" />
+    <ClInclude Include="Orientation.h" />
     <ClInclude Include="PlaceableProof.h" />
     <ClInclude Include="PlantConfig.h" />
     <ClInclude Include="Player.h" />
@@ -307,6 +308,7 @@
     <ClCompile Include="Noise.cpp" />
     <ClCompile Include="NoiseInterpolator.cpp" />
     <ClCompile Include="OpenDialogInteractionConfig.cpp" />
+    <ClCompile Include="Orientation.cpp" />
     <ClCompile Include="PlaceableProof.cpp" />
     <ClCompile Include="PlantConfig.cpp" />
     <ClCompile Include="Player.cpp" />

+ 6 - 3
FactoryCraft/JsonExpression.cpp

@@ -64,7 +64,8 @@ Chunk** JExpressionMemory::zzCurrentChunk()
 
 JFloatExpression::JFloatExpression()
     : ReferenceCounter(),
-      compiled(0)
+      compiled(0),
+      memory(0)
 {}
 
 float JFloatExpression::getValue()
@@ -88,7 +89,8 @@ FloatFunc JFloatExpression::compile(JExpressionMemory* zMemory)
 
 JBoolExpression::JBoolExpression()
     : ReferenceCounter(),
-      compiled(0)
+      compiled(0),
+      memory(0)
 {}
 
 bool JBoolExpression::getValue()
@@ -274,7 +276,8 @@ const char* JConstantFloatExpressionFactory::getTypeToken() const
 }
 
 JConstantBoolExpression::JConstantBoolExpression()
-    : JBoolExpression()
+    : JBoolExpression(),
+      value(0)
 {}
 
 Framework::Assembly::AssemblyBlock& JConstantBoolExpression::buildAssembly(

+ 2 - 2
FactoryCraft/NoBlock.cpp

@@ -53,7 +53,7 @@ Block* NoBlockBlockType::createBlockAt(
 }
 
 NoBlock::NoBlock()
-    : Block(BlockTypeEnum::NO_BLOCK, {0, 0, 0}, 0, false)
+    : Block(BlockTypeEnum::NO_BLOCK, {0, 0, 0}, 0, false, NORTH)
 {
     transparent = 0;
     passable = 0;
@@ -72,7 +72,7 @@ void NoBlock::onPostTick() {}
 NoBlock NoBlock::INSTANCE;
 
 AirBlock::AirBlock()
-    : Block(BlockTypeEnum::AIR, {0, 0, 0}, 0, false)
+    : Block(BlockTypeEnum::AIR, {0, 0, 0}, 0, false, NORTH)
 {
     transparent = 1;
     passable = 1;

+ 187 - 0
FactoryCraft/Orientation.cpp

@@ -0,0 +1,187 @@
+#include "Orientation.h"
+
+#include "BlockFilter.h"
+#include "Constants.h"
+#include "Game.h"
+#include "Chunk.h"
+
+OrientationConfig::OrientationConfig()
+    : ReferenceCounter()
+{}
+
+AttachToNeighborOrientationConfig::AttachToNeighborOrientationConfig()
+    : OrientationConfig(),
+      validDirections(NO_DIRECTION),
+      filter(0)
+{}
+
+AttachToNeighborOrientationConfig::~AttachToNeighborOrientationConfig()
+{
+    if (filter)
+    {
+        filter->release();
+    }
+}
+
+Direction AttachToNeighborOrientationConfig::calculateOrientation(
+    Framework::Vec3<int> pos, int dimensionId, Chunk* zChunk) const
+{
+    for (int i = 0; i < 6; i++)
+    {
+        Direction direction = getDirectionFromIndex(i);
+        if ((validDirections & direction) != 0)
+        {
+            Framework::Vec3<int> neighborPos = pos + getDirection(direction);
+            if (neighborPos.z >= 0 && neighborPos.z < WORLD_HEIGHT
+                && Game::getChunkCenter(neighborPos.x, neighborPos.y) == zChunk->getCenter())
+            {
+                const Block* zNeighborBlock = zChunk->zBlockConstWC(
+                    neighborPos.x, neighborPos.y, neighborPos.z);
+                if (zNeighborBlock && filter->test(zNeighborBlock))
+                {
+                    return getOppositeDirection(direction);
+                }
+            }
+        }
+    }
+    return NORTH;
+}
+
+void AttachToNeighborOrientationConfig::setValidDirections(
+    Directions validDirections)
+{
+    this->validDirections = validDirections;
+}
+
+Directions AttachToNeighborOrientationConfig::getValidDirections() const
+{
+    return validDirections;
+}
+
+void AttachToNeighborOrientationConfig::setFilter(BlockFilter* filter)
+{
+    if (this->filter)
+    {
+        this->filter->release();
+    }
+    this->filter = filter;
+}
+
+BlockFilter* AttachToNeighborOrientationConfig::zFilter() const
+{
+    return filter;
+}
+
+AttachToNeighborOrientationConfigFactory::
+    AttachToNeighborOrientationConfigFactory()
+    : SubTypeFactory()
+{}
+
+AttachToNeighborOrientationConfig*
+AttachToNeighborOrientationConfigFactory::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    AttachToNeighborOrientationConfig* result
+        = new AttachToNeighborOrientationConfig();
+    result->setFilter(Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
+        zJson->zValue("condition")));
+    for (Framework::JSON::JSONValue* direction :
+        *zJson->zValue("directions")->asArray())
+    {
+        Framework::Text dirStr = direction->asString()->getString();
+        if (dirStr.isEqual("TOP"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::TOP);
+        }
+        else if (dirStr.isEqual("BOTTOM"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::BOTTOM);
+        }
+        else if (dirStr.isEqual("EAST"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::EAST);
+        }
+        else if (dirStr.isEqual("NORTH"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::NORTH);
+        }
+        else if (dirStr.isEqual("WEST"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::WEST);
+        }
+        else if (dirStr.isEqual("SOUTH"))
+        {
+            result->setValidDirections(
+                result->getValidDirections() | Direction::SOUTH);
+        }
+    }
+    return result;
+}
+
+Framework::JSON::JSONObject*
+AttachToNeighborOrientationConfigFactory::toJsonObject(
+    AttachToNeighborOrientationConfig* zObject) const
+{
+    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
+    result->addValue("condition",
+        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zFilter()));
+    Framework::JSON::JSONArray* directionsArray
+        = new Framework::JSON::JSONArray();
+    if (zObject->getValidDirections() & Direction::TOP)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("TOP"));
+    }
+    if (zObject->getValidDirections() & Direction::BOTTOM)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("BOTTOM"));
+    }
+    if (zObject->getValidDirections() & Direction::EAST)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("EAST"));
+    }
+    if (zObject->getValidDirections() & Direction::NORTH)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("NORTH"));
+    }
+    if (zObject->getValidDirections() & Direction::WEST)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("WEST"));
+    }
+    if (zObject->getValidDirections() & Direction::SOUTH)
+    {
+        directionsArray->addValue(new Framework::JSON::JSONString("SOUTH"));
+    }
+    result->addValue("directions", directionsArray);
+    return result;
+}
+
+JSONObjectValidationBuilder*
+AttachToNeighborOrientationConfigFactory::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    Framework::JSON::JSONArray* defaultDirectionsArray
+        = new Framework::JSON::JSONArray();
+    defaultDirectionsArray->addValue(new Framework::JSON::JSONString("EAST"));
+    defaultDirectionsArray->addValue(new Framework::JSON::JSONString("NORTH"));
+    defaultDirectionsArray->addValue(new Framework::JSON::JSONString("WEST"));
+    defaultDirectionsArray->addValue(new Framework::JSON::JSONString("SOUTH"));
+    return builder
+        ->withRequiredAttribute("condition",
+            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
+        ->withRequiredArray("directions")
+        ->addAcceptedStringInArray()
+        ->whichIsOneOf({"TOP", "BOTTOM", "EAST", "NORTH", "WEST", "SOUTH"})
+        ->finishString()
+        ->withDefault(defaultDirectionsArray)
+        ->finishArray();
+}
+
+const char* AttachToNeighborOrientationConfigFactory::getTypeToken() const
+{
+    return "attachToNeighbor";
+}

+ 53 - 0
FactoryCraft/Orientation.h

@@ -0,0 +1,53 @@
+#pragma once
+
+#include <ReferenceCounter.h>
+#include <Vec3.h>
+
+#include "Area.h"
+#include "TypeRegistry.h"
+
+class Block;
+class Chunk;
+class BlockFilter;
+
+class OrientationConfig : public virtual Framework::ReferenceCounter
+{
+public:
+    OrientationConfig();
+    virtual Direction calculateOrientation(
+        Framework::Vec3<int> pos, int dimensionId, Chunk* zChunk) const
+        = 0;
+};
+
+class AttachToNeighborOrientationConfig : public OrientationConfig
+{
+private:
+    Directions validDirections;
+    BlockFilter* filter;
+
+public:
+    AttachToNeighborOrientationConfig();
+    ~AttachToNeighborOrientationConfig();
+    Direction calculateOrientation(Framework::Vec3<int> pos,
+        int dimensionId,
+        Chunk* zChunk) const override;
+    void setValidDirections(Directions validDirections);
+    Directions getValidDirections() const;
+    void setFilter(BlockFilter* filter);
+    BlockFilter* zFilter() const;
+};
+
+class AttachToNeighborOrientationConfigFactory
+    : public SubTypeFactory<OrientationConfig,
+          AttachToNeighborOrientationConfig>
+{
+public:
+    AttachToNeighborOrientationConfigFactory();
+    AttachToNeighborOrientationConfig* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        AttachToNeighborOrientationConfig* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};

+ 62 - 17
FactoryCraft/PlantConfig.cpp

@@ -15,7 +15,8 @@ PlantConfig::PlantConfig()
       plantBlockTypeName(""),
       plantblockTypeId(0),
       plantHeight(0),
-      direction(PlantDirection::UP)
+      direction(PlantDirection::UP),
+      orientationConfig(0)
 {}
 
 PlantConfig::~PlantConfig()
@@ -32,6 +33,10 @@ PlantConfig::~PlantConfig()
     {
         noiseConfig->release();
     }
+    if (orientationConfig)
+    {
+        orientationConfig->release();
+    }
 }
 
 void PlantConfig::initialize(JExpressionMemory* zMemory)
@@ -90,29 +95,41 @@ double PlantConfig::doesGeneratePlant(int x,
 void PlantConfig::generatePlantAt(
     int x, int y, int z, int dimensionId, Chunk* zChunk)
 {
+    Direction frontDirection = orientationConfig
+                                 ? orientationConfig->calculateOrientation(
+                                       {x, y, z}, dimensionId, zChunk)
+                                 : NO_DIRECTION;
     for (int i = 0; i < plantHeight; i++)
     {
+        Framework::Vec3<int> pos(x, y, z);
         if (direction == PlantDirection::UP)
         {
-            int currentType = zChunk->getBlockTypeAtWC(x, y, z + i);
-            if (currentType != BlockTypeEnum::AIR
-                && currentType != BlockTypeEnum::NO_BLOCK)
-            {
-                break;
-            }
-            zChunk->putBlockTypeAt(
-                Dimension::chunkCoordinates({x, y, z + i}), plantblockTypeId);
+            pos = {x, y, z + i};
         }
         else if (direction == PlantDirection::DOWN)
         {
-            int currentType = zChunk->getBlockTypeAtWC(x, y, z - i);
-            if (currentType != BlockTypeEnum::AIR
-                && currentType != BlockTypeEnum::NO_BLOCK)
-            {
-                break;
-            }
+            pos = {x, y, z - i};
+        }
+        int currentType = zChunk->getBlockTypeAtWC(pos.x, pos.y, pos.z);
+        if (currentType != BlockTypeEnum::AIR
+            && currentType != BlockTypeEnum::NO_BLOCK)
+        {
+            break;
+        }
+        if (orientationConfig
+            && frontDirection
+                   != Game::INSTANCE->zBlockType(plantblockTypeId)
+                       ->getDefaultFrontDirection())
+        {
+            Block* block = Game::INSTANCE->zBlockType(plantblockTypeId)
+                               ->createBlockAt(pos, dimensionId, 0);
+            block->setFrontDirection(frontDirection);
+            zChunk->putBlockAt(Dimension::chunkCoordinates(pos), block);
+        }
+        else
+        {
             zChunk->putBlockTypeAt(
-                Dimension::chunkCoordinates({x, y, z - i}), plantblockTypeId);
+                Dimension::chunkCoordinates(pos), plantblockTypeId);
         }
     }
 }
@@ -195,6 +212,20 @@ int PlantConfig::getDirection() const
     return direction;
 }
 
+void PlantConfig::setOrientationConfig(OrientationConfig* orientationConfig)
+{
+    if (this->orientationConfig)
+    {
+        this->orientationConfig->release();
+    }
+    this->orientationConfig = orientationConfig;
+}
+
+OrientationConfig* PlantConfig::zOrientationConfig() const
+{
+    return orientationConfig;
+}
+
 PlantConfigFactory::PlantConfigFactory()
     : ObjectTypeFactory<PlantConfig>()
 {}
@@ -249,6 +280,12 @@ PlantConfig* PlantConfigFactory::fromJson(
     {
         config->setDirection(PlantDirection::DOWN);
     }
+    if (zJson->hasValue("orientation"))
+    {
+        config->setOrientationConfig(
+            Game::INSTANCE->zTypeRegistry()->fromJson<OrientationConfig>(
+                zJson->zValue("orientation")));
+    }
     return config;
 }
 
@@ -298,6 +335,12 @@ Framework::JSON::JSONObject* PlantConfigFactory::toJsonObject(
     {
         zJson->addValue("direction", new Framework::JSON::JSONString("DOWN"));
     }
+    if (zObject->zOrientationConfig())
+    {
+        zJson->addValue("orientation",
+            Game::INSTANCE->zTypeRegistry()->toJson<OrientationConfig>(
+                zObject->zOrientationConfig()));
+    }
     return zJson;
 }
 
@@ -325,5 +368,7 @@ JSONObjectValidationBuilder* PlantConfigFactory::addToValidator(
         ->withRequiredString("direction")
         ->whichIsOneOf({"UP", "DOWN"})
         ->withDefault("UP")
-        ->finishString();
+        ->finishString()
+        ->withOptionalAttribute("orientation",
+            Game::INSTANCE->zTypeRegistry()->getValidator<OrientationConfig>());
 }

+ 4 - 0
FactoryCraft/PlantConfig.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "JsonExpression.h"
+#include "Orientation.h"
 #include "TypeRegistry.h"
 
 class PlantLocation
@@ -31,6 +32,7 @@ private:
     int plantblockTypeId;
     int plantHeight;
     int direction;
+    OrientationConfig* orientationConfig;
 
 public:
     PlantConfig();
@@ -61,6 +63,8 @@ public:
     int getPlantHeight() const;
     void setDirection(int direction);
     int getDirection() const;
+    void setOrientationConfig(OrientationConfig* orientationConfig);
+    OrientationConfig* zOrientationConfig() const;
 };
 
 class PlantConfigFactory : public ObjectTypeFactory<PlantConfig>

+ 4 - 0
FactoryCraft/TypeRegistry.cpp

@@ -27,6 +27,7 @@
 #include "LightSources.h"
 #include "ModelInfo.h"
 #include "OpenDialogInteractionConfig.h"
+#include "Orientation.h"
 #include "PlaceableProof.h"
 #include "PlantConfig.h"
 #include "Quest.h"
@@ -182,6 +183,9 @@ TypeRegistry::TypeRegistry()
 
     // block components
     registerSubType(new FireBasedProcessingBlockComponentFactory());
+
+    // orientations
+    registerSubType(new AttachToNeighborOrientationConfigFactory());
 }
 
 void TypeRegistry::writeSyntaxInfo(Framework::Text folderPath) const

+ 6 - 3
FactoryCraft/UIMLBuilder.h

@@ -640,8 +640,9 @@ public:
     {
         parentElement->addChild(result);
         result = 0;
+        UIMLTableBuilder* tmp = parent;
         delete this;
-        return parent;
+        return tmp;
     }
 };
 
@@ -1485,7 +1486,8 @@ private:
 public:
     UIMLShapedRecipieBuilder()
         : UIMLElementBuilder(),
-          ingredients(0)
+          ingredients(0),
+          outputs(0)
     {
         result->setName("recipie");
         result->setAttribute("type", "shaped");
@@ -1539,7 +1541,8 @@ private:
 public:
     UIMLUnshapedRecipieBuilder()
         : UIMLElementBuilder(),
-          ingredients(0)
+          ingredients(0),
+          outputs(0)
     {
         result->setName("recipie");
         result->setAttribute("type", "unshaped");

+ 2 - 0
Windows Version/Windows Version.vcxproj

@@ -240,6 +240,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\Noise.cpp" />
     <ClCompile Include="..\FactoryCraft\NoiseInterpolator.cpp" />
     <ClCompile Include="..\FactoryCraft\OpenDialogInteractionConfig.cpp" />
+    <ClCompile Include="..\FactoryCraft\Orientation.cpp" />
     <ClCompile Include="..\FactoryCraft\PlaceableProof.cpp" />
     <ClCompile Include="..\FactoryCraft\PlantConfig.cpp" />
     <ClCompile Include="..\FactoryCraft\Player.cpp" />
@@ -370,6 +371,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\NoBlock.h" />
     <ClInclude Include="..\FactoryCraft\NoiseInterpolator.h" />
     <ClInclude Include="..\FactoryCraft\OpenDialogInteractionConfig.h" />
+    <ClInclude Include="..\FactoryCraft\Orientation.h" />
     <ClInclude Include="..\FactoryCraft\PlaceableProof.h" />
     <ClInclude Include="..\FactoryCraft\PlantConfig.h" />
     <ClInclude Include="..\FactoryCraft\Player.h" />

+ 9 - 0
Windows Version/Windows Version.vcxproj.filters

@@ -130,6 +130,9 @@
     <Filter Include="UI\Builder">
       <UniqueIdentifier>{c98c24ed-2295-480b-a68a-0c119cf6ade4}</UniqueIdentifier>
     </Filter>
+    <Filter Include="world\orientation">
+      <UniqueIdentifier>{69d25bc2-afc8-4ebf-a45e-6eda2b6ed048}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -504,6 +507,9 @@
     <ClCompile Include="..\FactoryCraft\PlantConfig.cpp">
       <Filter>world\generator\biom\plants</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\Orientation.cpp">
+      <Filter>world\orientation</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -893,5 +899,8 @@
     <ClInclude Include="..\FactoryCraft\UIMLBuilder.h">
       <Filter>UI\Builder</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\Orientation.h">
+      <Filter>world\orientation</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 9 - 0
Windows Version/data/generator/overworld.json

@@ -668,6 +668,15 @@
                         "locations": [
                             "ABOVE_SURFACE"
                         ],
+                        "orientation": {
+                            "type": "attachToNeighbor",
+                            "condition": {
+                                "type": "groups",
+                                "groupNames": [
+                                    "Leaves"
+                                ]
+                            }
+                        },
                         "threshold": 0.8,
                         "noise": {
                             "type": "random",