ソースを参照

add Components to BasicBlocks and add FireProcessingBlockComponent witch can process recipies fueled by fire over time

Kolja Strohm 4 ヶ月 前
コミット
abc4466df1

+ 52 - 1
FactoryCraft/BasicBlocks.cpp

@@ -16,9 +16,19 @@ BasicBlock::BasicBlock(
     : Block(typeId, pos, dimensionId, hasInventory)
 {}
 
+void BasicBlock::addComponent(BlockComponent* component)
+{
+    components.add(component);
+}
+
 bool BasicBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 {
-    return 0;
+    bool ative = false;
+    for (BlockComponent* component : components)
+    {
+        ative |= component->tick(numTicks);
+    }
+    return ative;
 }
 
 void BasicBlock::onPostTick() {}
@@ -45,6 +55,28 @@ bool BasicBlockType::initialize(Game* zGame)
     return itemTypeId >= 0 && BlockType::initialize(zGame);
 }
 
+void BasicBlockType::loadSuperBlock(
+    Block* zBlock, Framework::StreamReader* zReader, int dimensionId) const
+{
+    BasicBlock* block = dynamic_cast<BasicBlock*>(zBlock);
+    for (BlockComponent* component : block->components)
+    {
+        component->loadComponent(zReader);
+    }
+    BlockType::loadSuperBlock(zBlock, zReader, dimensionId);
+}
+
+void BasicBlockType::saveSuperBlock(
+    Block* zBlock, Framework::StreamWriter* zWriter) const
+{
+    BasicBlock* block = dynamic_cast<BasicBlock*>(zBlock);
+    for (BlockComponent* component : block->components)
+    {
+        component->saveComponent(zWriter);
+    }
+    BlockType::saveSuperBlock(zBlock, zWriter);
+}
+
 void BasicBlockType::createSuperBlock(Block* zBlock, Item* zItem) const
 {
     BasicBlock* block = dynamic_cast<BasicBlock*>(zBlock);
@@ -59,6 +91,14 @@ void BasicBlockType::createSuperBlock(Block* zBlock, Item* zItem) const
     {
         block->addSlot(new ItemSlot(*slot));
     }
+    for (Framework::JSON::JSONValue* component : components)
+    {
+        BlockComponent* blockComponent
+            = Game::INSTANCE->zTypeRegistry()->fromJson<BlockComponent>(
+                component);
+        blockComponent->initialize(block);
+        block->addComponent(blockComponent);
+    }
     BlockType::createSuperBlock(zBlock, zItem);
 }
 
@@ -161,4 +201,15 @@ const Framework::RCArray<ItemSlot>& BasicBlockType::getInventorySlots() const
 void BasicBlockType::addInventorySlot(ItemSlot* slot)
 {
     itemSlots.add(slot);
+}
+
+const Framework::RCArray<Framework::JSON::JSONValue>&
+BasicBlockType::getComponents() const
+{
+    return components;
+}
+
+void BasicBlockType::addComponent(Framework::JSON::JSONValue* component)
+{
+    components.add(component);
 }

+ 36 - 2
FactoryCraft/BasicBlocks.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "Block.h"
+#include "BlockComponent.h"
 #include "BlockType.h"
 #include "Item.h"
 #include "TypeRegistry.h"
@@ -11,12 +12,16 @@ class BasicBlockType;
 
 class BasicBlock : public Block
 {
+private:
+    Framework::RCArray<BlockComponent> components;
+
 public:
     BasicBlock(int typeId, Framework::Vec3<int> pos, int dimensionId);
     BasicBlock(int typeId,
         Framework::Vec3<int> pos,
         int dimensionId,
         bool hasInventory);
+    void addComponent(BlockComponent* component);
     virtual bool onTick(
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
@@ -34,13 +39,21 @@ private:
     float speedModifier;
     bool interactable;
     Framework::RCArray<ItemSlot> itemSlots;
+    Framework::RCArray<Framework::JSON::JSONValue> components;
+
+public:
+    BasicBlockType();
+    virtual bool initialize(Game* zGame) override;
 
 protected:
+    virtual void loadSuperBlock(Block* zBlock,
+        Framework::StreamReader* zReader,
+        int dimensionId) const override;
+    virtual void saveSuperBlock(
+        Block* zBlock, Framework::StreamWriter* zWriter) const override;
     virtual void createSuperBlock(Block* zBlock, Item* zItem) const override;
 
 public:
-    BasicBlockType();
-    virtual bool initialize(Game* zGame) override;
     virtual Block* createBlock(
         Framework::Vec3<int> position, int dimensionId) const override;
     virtual Item* createItem() const override;
@@ -58,6 +71,8 @@ public:
     bool isInteractable() const;
     const Framework::RCArray<ItemSlot>& getInventorySlots() const;
     void addInventorySlot(ItemSlot* slot);
+    const Framework::RCArray<Framework::JSON::JSONValue>& getComponents() const;
+    void addComponent(Framework::JSON::JSONValue* component);
 };
 
 template<class T,
@@ -101,6 +116,12 @@ public:
                 Game::INSTANCE->zTypeRegistry()->fromJson<ItemSlot>(
                     value->asObject()));
         }
+        for (Framework::JSON::JSONValue* component :
+            *zJson->zValue("components")->asArray())
+        {
+            result->addComponent(dynamic_cast<Framework::JSON::JSONValue*>(
+                component->getThis()));
+        }
         return result;
     }
 
@@ -133,6 +154,14 @@ public:
                 Game::INSTANCE->zTypeRegistry()->toJson<ItemSlot>(slot));
         }
         result->addValue("inventorySlots", inventorySlots);
+        Framework::JSON::JSONArray* components
+            = new Framework::JSON::JSONArray();
+        for (Framework::JSON::JSONValue* component : zObject->getComponents())
+        {
+            components->addValue(dynamic_cast<Framework::JSON::JSONValue*>(
+                component->getThis()));
+        }
+        result->addValue("components", components);
         return result;
     }
 
@@ -163,6 +192,11 @@ public:
             ->withDefault(new Framework::JSON::JSONArray())
             ->addAcceptedTypeInArray(
                 Game::INSTANCE->zTypeRegistry()->getValidator<ItemSlot>())
+            ->finishArray()
+            ->withRequiredArray("components")
+            ->withDefault(new Framework::JSON::JSONArray())
+            ->addAcceptedTypeInArray(
+                Game::INSTANCE->zTypeRegistry()->getValidator<BlockComponent>())
             ->finishArray();
     }
 

+ 5 - 0
FactoryCraft/BlockComponent.cpp

@@ -0,0 +1,5 @@
+#include "BlockComponent.h"
+
+BlockComponent::BlockComponent()
+    : Framework::ReferenceCounter()
+{}

+ 19 - 0
FactoryCraft/BlockComponent.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <Reader.h>
+#include <ReferenceCounter.h>
+#include <Writer.h>
+#include <XML.h>
+
+class Block;
+
+class BlockComponent : public virtual Framework::ReferenceCounter
+{
+public:
+    BlockComponent();
+    virtual void initialize(Block* zBlock) = 0;
+    virtual bool tick(int numTicks) = 0;
+    virtual Framework::XML::Element* getUIML() const = 0;
+    virtual void loadComponent(Framework::StreamReader* zReader) = 0;
+    virtual void saveComponent(Framework::StreamWriter* zWriter) const = 0;
+};

+ 16 - 37
FactoryCraft/CraftingStorage.cpp

@@ -144,41 +144,12 @@ bool BasicShapedCrafter::isAllAvailable(
 bool BasicShapedCrafter::isAllAvailable(
     Framework::RCArray<RecipieInput>& inputs)
 {
-    bool* used = new bool[craftingInput.getEintragAnzahl()];
-    memset(used, 0, sizeof(bool) * craftingInput.getEintragAnzahl());
-    for (RecipieInput* input : inputs)
-    {
-        bool found = 0;
-        for (int i = 0; i < craftingInput.getEintragAnzahl(); i++)
-        {
-            if (used[i]) continue;
-            ItemSlot* slot = craftingInput.get(i);
-            if (slot && slot->zStack()
-                && slot->zStack()->getSize() >= input->getAmount()
-                && slot->zStack()->zItem()
-                && input->zFilter()->matchItem(slot->zStack()->zItem()))
-            {
-                found = 1;
-                used[i] = 1;
-                break;
-            }
-        }
-        if (!found)
-        {
-            delete[] used;
-            return 0;
-        }
-    }
-    delete[] used;
-    return 1;
+    return zInventory->isAllAvailable(inputs, "CraftingGrid");
 }
 
 bool BasicShapedCrafter::hasFreeSpace(const Item* zItem, int amount)
 {
-    ItemStack* stack
-        = new ItemStack(zItem->zItemType()->cloneItem(zItem), amount);
-    int addable = zInventory->numberOfAddableItems(stack, NO_DIRECTION);
-    stack->release();
+    int addable = zInventory->numberOfAddableItems(zItem, NO_DIRECTION);
     return addable >= amount;
 }
 
@@ -296,7 +267,7 @@ bool BasicShapedCrafter::consume(
     return 1;
 }
 
-bool BasicShapedCrafter::consume(Framework::RCArray<RecipieInput>& inputs)
+void BasicShapedCrafter::consume(Framework::RCArray<RecipieInput>& inputs)
 {
     SourceSlotBlacklistFilter otherSlotsSource;
     TargetSlotBlacklistFilter otherSlotsTarget;
@@ -403,21 +374,19 @@ bool BasicShapedCrafter::consume(Framework::RCArray<RecipieInput>& inputs)
                             0, &tmp, &combinedFilter, 1, NO_DIRECTION, INSIDE);
                     }
                 }
-
                 break;
             }
         }
     }
     delete[] used;
-    return 1;
 }
 
-void BasicShapedCrafter::addCraftingResult(ItemStack* stack)
+void BasicShapedCrafter::addCraftingResult(ItemStack* zStack)
 {
     TargetSlotBlacklistFilter otherSlotsTarget;
     for (int i = 0; i < craftingInput.getEintragAnzahl(); i++)
         otherSlotsTarget.addBlackListSlotId(craftingInput.get(i)->getId());
-    zInventory->unsaveAddItem(stack, NO_DIRECTION, &otherSlotsTarget);
+    zInventory->unsaveAddItem(zStack, NO_DIRECTION, &otherSlotsTarget);
 }
 
 void BasicShapedCrafter::applyCurrentRecipie()
@@ -441,4 +410,14 @@ void BasicShapedCrafter::calculateOutputPreview()
             zInventory->notifyObservers(message);
         }
     }
-}
+}
+
+Framework::Vec3<float> BasicShapedCrafter::getStorageLocation() const
+{
+    return zInventory->getLocation();
+}
+
+int BasicShapedCrafter::getStorageDimensionId() const
+{
+    return zInventory->getDimensionId();
+}

+ 9 - 4
FactoryCraft/CraftingStorage.h

@@ -1,5 +1,6 @@
 #pragma once
 #include <Array.h>
+#include <Vec3.h>
 
 #include "Inventory.h"
 #include "ItemFilter.h"
@@ -12,8 +13,10 @@ class CraftingStorage
 public:
     virtual bool isAllAvailable(Framework::RCArray<RecipieInput>& inputs) = 0;
     virtual bool hasFreeSpace(const Item* zItem, int amount) = 0;
-    virtual bool consume(Framework::RCArray<RecipieInput>& inputs) = 0;
-    virtual void addCraftingResult(ItemStack* stack) = 0;
+    virtual void consume(Framework::RCArray<RecipieInput>& inputs) = 0;
+    virtual void addCraftingResult(ItemStack* zStack) = 0;
+    virtual Framework::Vec3<float> getStorageLocation() const = 0;
+    virtual int getStorageDimensionId() const = 0;
 };
 
 class ShapedCraftingStorage : public CraftingStorage
@@ -53,8 +56,10 @@ public:
     virtual bool consume(Framework::RCArray<RecipieInput>& inputs,
         int width,
         int height) override;
-    virtual bool consume(Framework::RCArray<RecipieInput>& inputs) override;
-    virtual void addCraftingResult(ItemStack* stack) override;
+    virtual void consume(Framework::RCArray<RecipieInput>& inputs) override;
+    virtual void addCraftingResult(ItemStack* zStack) override;
     void applyCurrentRecipie();
     void calculateOutputPreview();
+    virtual Framework::Vec3<float> getStorageLocation() const override;
+    virtual int getStorageDimensionId() const override;
 };

+ 4 - 0
FactoryCraft/FactoryCraft.vcxproj

@@ -99,6 +99,7 @@
     <ClInclude Include="Animal.h" />
     <ClInclude Include="AnimalAI.h" />
     <ClInclude Include="ArrayUtils.h" />
+    <ClInclude Include="BlockComponent.h" />
     <ClInclude Include="BlockFilter.h" />
     <ClInclude Include="BlockInfoCommand.h" />
     <ClInclude Include="BlockInstanceGeneratorRule.h" />
@@ -113,6 +114,7 @@
     <ClInclude Include="DropUsedItemCondition.h" />
     <ClInclude Include="EntityGenerator.h" />
     <ClInclude Include="FactorizeNoise.h" />
+    <ClInclude Include="FireBasedProcessingBlockComponent.h" />
     <ClInclude Include="FlattenNoise.h" />
     <ClInclude Include="FluidContainer.h" />
     <ClInclude Include="GameClient.h" />
@@ -229,6 +231,7 @@
     <ClCompile Include="BasicTool.cpp" />
     <ClCompile Include="BiomGenerator.cpp" />
     <ClCompile Include="Block.cpp" />
+    <ClCompile Include="BlockComponent.cpp" />
     <ClCompile Include="BlockFilter.cpp" />
     <ClCompile Include="BlockInfoCommand.cpp" />
     <ClCompile Include="BlockInstanceGeneratorRule.cpp" />
@@ -261,6 +264,7 @@
     <ClCompile Include="EntityType.cpp" />
     <ClCompile Include="FactorizeNoise.cpp" />
     <ClCompile Include="FastNoiseWrapper.cpp" />
+    <ClCompile Include="FireBasedProcessingBlockComponent.cpp" />
     <ClCompile Include="FlattenNoise.cpp" />
     <ClCompile Include="FluidBlock.cpp" />
     <ClCompile Include="FluidContainer.cpp" />

+ 15 - 0
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -121,6 +121,9 @@
     <Filter Include="Interaction">
       <UniqueIdentifier>{ca208791-fa0c-40d2-ad18-ddbef309fa4a}</UniqueIdentifier>
     </Filter>
+    <Filter Include="world\blocks\components">
+      <UniqueIdentifier>{fb4a3a5c-a42f-4674-97eb-825dfcc5d1b6}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Chunk.h">
@@ -489,6 +492,12 @@
     <ClInclude Include="OpenDialogInteractionConfig.h">
       <Filter>Interaction</Filter>
     </ClInclude>
+    <ClInclude Include="BlockComponent.h">
+      <Filter>world\blocks\components</Filter>
+    </ClInclude>
+    <ClInclude Include="FireBasedProcessingBlockComponent.h">
+      <Filter>world\blocks\components</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -845,5 +854,11 @@
     <ClCompile Include="OpenDialogInteractionConfig.cpp">
       <Filter>Interaction</Filter>
     </ClCompile>
+    <ClCompile Include="BlockComponent.cpp">
+      <Filter>world\blocks\components</Filter>
+    </ClCompile>
+    <ClCompile Include="FireBasedProcessingBlockComponent.cpp">
+      <Filter>world\blocks\components</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 388 - 0
FactoryCraft/FireBasedProcessingBlockComponent.cpp

@@ -0,0 +1,388 @@
+#include "FireBasedProcessingBlockComponent.h"
+
+#include "Block.h"
+#include "ItemFilter.h"
+#include "Recipie.h"
+#include "RecipieList.h"
+#include "RecipieLoader.h"
+
+FireBasedProcessingBlockComponent::FireBasedProcessingBlockComponent()
+    : BlockComponent()
+{}
+
+FireBasedProcessingBlockComponent::~FireBasedProcessingBlockComponent() {}
+
+void FireBasedProcessingBlockComponent::findRecipie()
+{
+    RecipieList* zRecipies
+        = Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup.getText());
+    if (zRecipies)
+    {
+        zBlock->lock();
+        Recipie* recipie = zRecipies->zFirstRecipie(this);
+        if (recipie)
+        {
+            recipie->consumeInputs(this);
+            currentRecipie = recipie;
+            ticksNeeded = recipie->getTicksNeeded();
+        }
+        zBlock->unlock();
+    }
+}
+
+void FireBasedProcessingBlockComponent::consumeFuel()
+{
+    zBlock->lock();
+    ItemSlot* fireStartingSlot = 0;
+    ItemSlot* fuelSlot = 0;
+    for (ItemSlot* slot : *zBlock)
+    {
+        if (slot->zStack())
+        {
+            if (!burning && !fireStartingSlot
+                && slot->getName().istGleich(fireStartingInventorySlotName)
+                && fireStartingItemFilter->matchItem(slot->zStack()->zItem()))
+            {
+                fireStartingSlot = slot;
+            }
+            if (!fuelSlot && slot->getName().istGleich(fuelInventorySlotName)
+                && fuelItemFilter->matchItem(slot->zStack()->zItem()))
+            {
+                fuelSlot = slot;
+            }
+            if (fuelSlot && fireStartingSlot)
+            {
+                break;
+            }
+        }
+    }
+    if (burning)
+    {
+        if (fuelSlot)
+        {
+            ItemStack* fuelStack
+                = fuelSlot->takeItemsOut(1, Direction::NO_DIRECTION);
+            if (fuelStack)
+            {
+                // TODO: check if item is burnable and how much fuel it provides
+                fuelBuffer += 100;
+                fuelStack->release();
+            }
+            else
+            {
+                if (fuelBuffer == 0)
+                {
+                    burning = false;
+                }
+            }
+        }
+        else
+        {
+            if (fuelBuffer == 0)
+            {
+                burning = false;
+            }
+        }
+    }
+    else
+    {
+        if (fuelSlot && fireStartingSlot)
+        {
+            ItemStack* fuelStack
+                = fuelSlot->takeItemsOut(1, Direction::NO_DIRECTION);
+            ItemStack* fireStartingStack
+                = fireStartingSlot->takeItemsOut(1, Direction::NO_DIRECTION);
+            if (fuelStack && fireStartingStack)
+            {
+                // TODO: check if item is burnable and how much fuel it provides
+                fuelBuffer += 100;
+                burning = true;
+                fuelStack->release();
+                fireStartingStack->release();
+            }
+        }
+    }
+    zBlock->unlock();
+}
+
+void FireBasedProcessingBlockComponent::initialize(Block* zBlock)
+{
+    this->zBlock = zBlock;
+}
+
+bool FireBasedProcessingBlockComponent::tick(int numTicks)
+{
+    bool active = 0;
+    while (numTicks > 0)
+    {
+        if (!fuelBuffer)
+        {
+            consumeFuel();
+        }
+        if (!burning)
+        {
+            break;
+        }
+        if (!currentRecipie)
+        {
+            findRecipie();
+        }
+        bool processed = false;
+        if (currentRecipie)
+        {
+            int possibleTicks
+                = fuelBuffer / currentRecipie->getFuelPerTickNeeded();
+            if (!possibleTicks)
+            {
+                consumeFuel();
+                possibleTicks
+                    = fuelBuffer / currentRecipie->getFuelPerTickNeeded();
+            }
+            if (possibleTicks >= numTicks)
+            {
+                if (numTicks >= ticksNeeded)
+                {
+                    numTicks -= ticksNeeded;
+                    fuelBuffer
+                        -= currentRecipie->getFuelPerTickNeeded() * ticksNeeded;
+                    zBlock->lock();
+                    currentRecipie->produceOutputs(this);
+                    zBlock->unlock();
+                    ticksNeeded = 0;
+                    currentRecipie = 0;
+                }
+                else
+                {
+                    ticksNeeded -= numTicks;
+                    fuelBuffer
+                        -= currentRecipie->getFuelPerTickNeeded() * numTicks;
+                    numTicks = 0;
+                }
+                processed = true;
+            }
+            else
+            {
+                if (possibleTicks >= ticksNeeded)
+                {
+                    numTicks -= ticksNeeded;
+                    fuelBuffer
+                        -= currentRecipie->getFuelPerTickNeeded() * ticksNeeded;
+                    zBlock->lock();
+                    currentRecipie->produceOutputs(this);
+                    zBlock->unlock();
+                    ticksNeeded = 0;
+                    currentRecipie = 0;
+                    processed = true;
+                }
+                else
+                {
+                    numTicks -= possibleTicks;
+                    fuelBuffer -= currentRecipie->getFuelPerTickNeeded()
+                                * possibleTicks;
+                    ticksNeeded -= possibleTicks;
+                    processed = possibleTicks > 0;
+                }
+            }
+        }
+        if (!processed)
+        {
+            // burning without recipie
+            if (fuelBuffer >= numTicks)
+            {
+                fuelBuffer -= numTicks;
+                numTicks = 0;
+            }
+            else
+            {
+                numTicks -= fuelBuffer;
+                fuelBuffer = 0;
+            }
+        }
+        active = 1;
+    }
+    return active;
+}
+
+Framework::XML::Element* FireBasedProcessingBlockComponent::getUIML() const
+{
+    return nullptr;
+}
+
+bool FireBasedProcessingBlockComponent::isAllAvailable(
+    Framework::RCArray<RecipieInput>& inputs)
+{
+    return zBlock->isAllAvailable(inputs, inputInventorySlotName);
+}
+
+bool FireBasedProcessingBlockComponent::hasFreeSpace(
+    const Item* zItem, int amount)
+{
+    int addable = zBlock->numberOfAddableItems(
+        zItem, NO_DIRECTION, inputInventorySlotName);
+    return addable >= amount;
+}
+
+void FireBasedProcessingBlockComponent::consume(
+    Framework::RCArray<RecipieInput>& inputs)
+{
+    zBlock->consume(inputs, inputInventorySlotName);
+}
+
+void FireBasedProcessingBlockComponent::addCraftingResult(ItemStack* zStack)
+{
+    TargetSlotNameItemFilter filter(outputInventorySlotName);
+    zBlock->unsaveAddItem(zStack, NO_DIRECTION, &filter);
+}
+
+Framework::Vec3<float>
+FireBasedProcessingBlockComponent::getStorageLocation() const
+{
+    return zBlock->getLocation();
+}
+
+int FireBasedProcessingBlockComponent::getStorageDimensionId() const
+{
+    return zBlock->getDimensionId();
+}
+
+void FireBasedProcessingBlockComponent::loadComponent(
+    Framework::StreamReader* zReader)
+{
+    zReader->lese((char*)&ticksNeeded, 4);
+    zReader->lese((char*)&fuelBuffer, 4);
+    zReader->lese((char*)&burning, 1);
+    int index = 0;
+    zReader->lese((char*)&index, 4);
+    if (index >= 0)
+    {
+        // TODO: add unique recipie ids to enshure correct loading after
+        // recipies were changed, added or removed
+        RecipieList* recipies
+            = Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup);
+        if (recipies && index < recipies->getRecipieCount())
+        {
+            currentRecipie = recipies->zRecipie(index);
+        }
+    }
+}
+
+void FireBasedProcessingBlockComponent::saveComponent(
+    Framework::StreamWriter* zWriter) const
+{
+    zWriter->schreibe((char*)&ticksNeeded, 4);
+    zWriter->schreibe((char*)&fuelBuffer, 4);
+    zWriter->schreibe((char*)&burning, 1);
+    int index = -1;
+    if (currentRecipie)
+    {
+        // TODO: add unique recipie ids to enshure correct loading after
+        // recipies were changed, added or removed
+        RecipieList* recipies
+            = Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup);
+        if (recipies)
+        {
+            index = recipies->getRecipieIndex(currentRecipie);
+        }
+    }
+    zWriter->schreibe((char*)&index, 4);
+}
+
+FireBasedProcessingBlockComponentFactory::
+    FireBasedProcessingBlockComponentFactory()
+    : SubTypeFactory()
+{}
+
+FireBasedProcessingBlockComponent*
+FireBasedProcessingBlockComponentFactory::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    FireBasedProcessingBlockComponent* component
+        = new FireBasedProcessingBlockComponent();
+    if (zJson->hasValue("fireStartingItemFilter"))
+    {
+        component->fireStartingItemFilter
+            = Game::INSTANCE->zTypeRegistry()->fromJson<ItemFilter>(
+                zJson->zValue("fireStartingItemFilter"));
+    }
+    else
+    {
+        component->fireStartingItemFilter = new AnyItemFilter();
+    }
+    if (zJson->hasValue("fuelItemFilter"))
+    {
+        component->fuelItemFilter
+            = Game::INSTANCE->zTypeRegistry()->fromJson<ItemFilter>(
+                zJson->zValue("fuelItemFilter"));
+    }
+    else
+    {
+        component->fuelItemFilter = new AnyItemFilter();
+    }
+    component->recipieGroup
+        = zJson->zValue("recipieGroup")->asString()->getString();
+    component->fuelInventorySlotName
+        = zJson->zValue("fuelInventorySlotName")->asString()->getString();
+    component->fireStartingInventorySlotName
+        = zJson->zValue("fireStartingInventorySlotName")
+              ->asString()
+              ->getString();
+    component->inputInventorySlotName
+        = zJson->zValue("inputInventorySlotName")->asString()->getString();
+    component->outputInventorySlotName
+        = zJson->zValue("outputInventorySlotName")->asString()->getString();
+    return component;
+}
+
+Framework::JSON::JSONObject*
+FireBasedProcessingBlockComponentFactory::toJsonObject(
+    FireBasedProcessingBlockComponent* zObject) const
+{
+    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
+    result->addValue("fireStartingItemFilter",
+        Game::INSTANCE->zTypeRegistry()->toJson(
+            zObject->fireStartingItemFilter));
+    result->addValue("fuelItemFilter",
+        Game::INSTANCE->zTypeRegistry()->toJson(zObject->fuelItemFilter));
+    result->addValue("recipieGroup",
+        new Framework::JSON::JSONString(zObject->recipieGroup.getText()));
+    result->addValue("fuelInventorySlotName",
+        new Framework::JSON::JSONString(
+            zObject->fuelInventorySlotName.getText()));
+    result->addValue("fireStartingInventorySlotName",
+        new Framework::JSON::JSONString(
+            zObject->fireStartingInventorySlotName.getText()));
+    result->addValue("inputInventorySlotName",
+        new Framework::JSON::JSONString(
+            zObject->inputInventorySlotName.getText()));
+    result->addValue("outputInventorySlotName",
+        new Framework::JSON::JSONString(
+            zObject->outputInventorySlotName.getText()));
+    return result;
+}
+
+JSONObjectValidationBuilder*
+FireBasedProcessingBlockComponentFactory::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return builder
+        ->withRequiredAttribute("fireStartingItemFilter",
+            Game::INSTANCE->zTypeRegistry()->getValidator<ItemFilter>())
+        ->withRequiredAttribute("fuelItemFilter",
+            Game::INSTANCE->zTypeRegistry()->getValidator<ItemFilter>())
+        ->withRequiredAttribute("recipieGroup",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>())
+        ->withRequiredAttribute("fuelInventorySlotName",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>())
+        ->withRequiredAttribute("fireStartingInventorySlotName",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>())
+        ->withRequiredAttribute("inputInventorySlotName",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>())
+        ->withRequiredAttribute("outputInventorySlotName",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>());
+    return builder;
+}
+
+const char* FireBasedProcessingBlockComponentFactory::getTypeToken() const
+{
+    return "fireBasedProcessing";
+}

+ 63 - 0
FactoryCraft/FireBasedProcessingBlockComponent.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include "BlockComponent.h"
+#include "CraftingStorage.h"
+#include "TypeRegistry.h"
+
+class ItemFilter;
+
+class FireBasedProcessingBlockComponent : public BlockComponent,
+                                          public CraftingStorage
+{
+private:
+    Block* zBlock;
+    ItemFilter* fireStartingItemFilter;
+    ItemFilter* fuelItemFilter;
+    Framework::Text recipieGroup;
+    Framework::Text fuelInventorySlotName;
+    Framework::Text fireStartingInventorySlotName;
+    Framework::Text inputInventorySlotName;
+    Framework::Text outputInventorySlotName;
+    int ticksNeeded;
+    Recipie* currentRecipie;
+    int fuelBuffer;
+    bool burning;
+
+public:
+    FireBasedProcessingBlockComponent();
+    virtual ~FireBasedProcessingBlockComponent();
+
+protected:
+    void findRecipie();
+    void consumeFuel();
+
+public:
+    virtual void initialize(Block* zBlock) override;
+    virtual bool tick(int numTicks) override;
+    virtual Framework::XML::Element* getUIML() const override;
+    virtual bool isAllAvailable(
+        Framework::RCArray<RecipieInput>& inputs) override;
+    virtual bool hasFreeSpace(const Item* zItem, int amount) override;
+    virtual void consume(Framework::RCArray<RecipieInput>& inputs) override;
+    virtual void addCraftingResult(ItemStack* zStack) override;
+    virtual Framework::Vec3<float> getStorageLocation() const override;
+    virtual int getStorageDimensionId() const override;
+    virtual void loadComponent(Framework::StreamReader* zReader) override;
+    virtual void saveComponent(Framework::StreamWriter* zWriter) const override;
+
+    friend class FireBasedProcessingBlockComponentFactory;
+};
+
+class FireBasedProcessingBlockComponentFactory
+    : public SubTypeFactory<BlockComponent, FireBasedProcessingBlockComponent>
+{
+public:
+    FireBasedProcessingBlockComponentFactory();
+    FireBasedProcessingBlockComponent* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        FireBasedProcessingBlockComponent* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};

+ 1 - 0
FactoryCraft/GrowingPlant.cpp

@@ -2,6 +2,7 @@
 
 #include "Dimension.h"
 #include "Game.h"
+#include "Recipie.h"
 
 GrowthState::GrowthState(float percentage, ModelInfo* model)
     : ReferenceCounter(),

+ 106 - 18
FactoryCraft/Inventory.cpp

@@ -25,7 +25,7 @@ InventoryInteraction::InventoryInteraction(
 InventoryInteraction::InventoryInteraction(
     const InventoryInteraction& interaction)
     : InventoryInteraction(
-        interaction.current, interaction.other, interaction.dir)
+          interaction.current, interaction.other, interaction.dir)
 {}
 
 InventoryInteraction::~InventoryInteraction()
@@ -566,7 +566,7 @@ void Inventory::addItems(ItemStack* zItems, Direction dir, ItemFilter* zFilter)
     {
         cs.lock();
         for (auto targetSlot = pushSlotsOrder->begin(); targetSlot;
-             targetSlot++)
+            targetSlot++)
         {
             if (!targetSlot->isFull()
                 && (!zFilter || zFilter->matchTargetSlot(targetSlot)))
@@ -576,9 +576,9 @@ void Inventory::addItems(ItemStack* zItems, Direction dir, ItemFilter* zFilter)
                     if (targetSlot->zStack()->zItem()->canBeStackedWith(
                             zItems->zItem()))
                     {
-                        int number
-                            = MIN(targetSlot->numberOfAddableItems(zItems, dir),
-                                zItems->getSize());
+                        int number = MIN(targetSlot->numberOfAddableItems(
+                                             zItems->zItem(), dir),
+                            zItems->getSize());
                         int tmp = number;
                         if (number > 0
                             && allowPushStack(
@@ -602,9 +602,9 @@ void Inventory::addItems(ItemStack* zItems, Direction dir, ItemFilter* zFilter)
                 }
                 else
                 {
-                    int number
-                        = MIN(targetSlot->numberOfAddableItems(zItems, dir),
-                            zItems->getSize());
+                    int number = MIN(
+                        targetSlot->numberOfAddableItems(zItems->zItem(), dir),
+                        zItems->getSize());
                     int tmp = number;
                     if (number > 0
                         && allowPushStack(
@@ -638,8 +638,8 @@ void Inventory::addItems(ItemSlot* zSlot, ItemStack* zItems, Direction dir)
         && !zSlot->zStack()->zItem()->canBeStackedWith(zItems->zItem()))
         return;
     bool needUpdate = !zSlot->zStack();
-    int number
-        = MIN(zSlot->numberOfAddableItems(zItems, dir), zItems->getSize());
+    int number = MIN(
+        zSlot->numberOfAddableItems(zItems->zItem(), dir), zItems->getSize());
     int tmp = number;
     if (number > 0 && allowPushStack(zSlot, dir, zItems->zItem(), tmp))
     {
@@ -684,20 +684,108 @@ void Inventory::unsaveAddItem(
     addItems(zStack, dir, zFilter);
 }
 
-int Inventory::numberOfAddableItems(
-    const ItemStack* zStack, Direction dir) const
+int Inventory::numberOfAddableItems(const Item* zItem, Direction dir) const
 {
     int count = 0;
     for (auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++)
     {
-        int maxCount = targetSlot->numberOfAddableItems(zStack, dir);
+        int maxCount = targetSlot->numberOfAddableItems(zItem, dir);
         int allowed = maxCount;
-        if (allowPushStack(targetSlot, dir, zStack->zItem(), allowed))
+        if (allowPushStack(targetSlot, dir, zItem, allowed))
             count += MIN(maxCount, allowed);
     }
     return count;
 }
 
+int Inventory::numberOfAddableItems(
+    const Item* zItem, Direction dir, const Framework::Text& slotName) const
+{
+    int count = 0;
+    for (auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++)
+    {
+        if (targetSlot->getName().istGleich(slotName))
+        {
+            int maxCount = targetSlot->numberOfAddableItems(zItem, dir);
+            int allowed = maxCount;
+            if (allowPushStack(targetSlot, dir, zItem, allowed))
+                count += MIN(maxCount, allowed);
+        }
+    }
+    return count;
+}
+
+bool Inventory::isAllAvailable(Framework::RCArray<RecipieInput>& inputs,
+    const Framework::Text& slotName) const
+{
+    int* used = new int[pullSlotsOrder->getEintragAnzahl()];
+    memset(used, 0, sizeof(int) * pullSlotsOrder->getEintragAnzahl());
+    for (RecipieInput* input : inputs)
+    {
+        int found = 0;
+        for (int i = 0; i < pullSlotsOrder->getEintragAnzahl(); i++)
+        {
+            ItemSlot* slot = pullSlotsOrder->get(i);
+            if (slot && slot->zStack() && slot->zStack()->zItem()
+                && slot->getNumberOfItems() > used[i]
+                && slot->getName().istGleich(slotName)
+                && input->zFilter()->matchItem(slot->zStack()->zItem()))
+            {
+                int usable = slot->getNumberOfItems() - used[i];
+                if (found + usable >= input->getAmount())
+                {
+                    used[i] += input->getAmount() - found;
+                    found = input->getAmount();
+                    break;
+                }
+                else
+                {
+                    used[i] += usable;
+                    found += usable;
+                }
+            }
+        }
+        if (found < input->getAmount())
+        {
+            delete[] used;
+            return 0;
+        }
+    }
+    delete[] used;
+    return 1;
+}
+
+void Inventory::consume(Framework::RCArray<RecipieInput>& inputs,
+    const Framework::Text& slotName) const
+{
+    for (RecipieInput* input : inputs)
+    {
+        int consumed = 0;
+        for (int i = 0; i < pullSlotsOrder->getEintragAnzahl(); i++)
+        {
+            ItemSlot* slot = pullSlotsOrder->get(i);
+            if (slot && slot->zStack() && slot->zStack()->zItem()
+                && slot->getName().istGleich(slotName)
+                && input->zFilter()->matchItem(slot->zStack()->zItem()))
+            {
+                if (consumed + slot->getNumberOfItems() >= input->getAmount())
+                {
+                    slot->takeItemsOut(
+                            input->getAmount() - consumed, NO_DIRECTION)
+                        ->release();
+                    consumed = input->getAmount();
+                    break;
+                }
+                else
+                {
+                    consumed += slot->getNumberOfItems();
+                    slot->takeItemsOut(slot->getNumberOfItems(), NO_DIRECTION)
+                        ->release();
+                }
+            }
+        }
+    }
+}
+
 Framework::ArrayIterator<ItemSlot*> Inventory::begin()
 {
     return pullSlotsOrder->begin();
@@ -857,8 +945,8 @@ bool Inventory::unsafeMove(Inventory* zSource,
         if (sourceSlot->zStack()->zItem()->canBeStackedWith(
                 targetSlot->zStack()->zItem()))
         {
-            int number = MIN(
-                targetSlot->numberOfAddableItems(sourceSlot->zStack(), outDir),
+            int number = MIN(targetSlot->numberOfAddableItems(
+                                 sourceSlot->zStack()->zItem(), outDir),
                 count);
             int tmp = number;
             if (number > 0 && zSource->allowPullStack(sourceSlot, outDir)
@@ -896,8 +984,8 @@ bool Inventory::unsafeMove(Inventory* zSource,
     }
     else
     {
-        int number = MIN(
-            targetSlot->numberOfAddableItems(sourceSlot->zStack(), outDir),
+        int number = MIN(targetSlot->numberOfAddableItems(
+                             sourceSlot->zStack()->zItem(), outDir),
             count);
         int tmp = number;
         if (number > 0 && zSource->allowPullStack(sourceSlot, outDir)

+ 18 - 7
FactoryCraft/Inventory.h

@@ -9,6 +9,7 @@
 
 #include "Area.h"
 #include "ItemSlot.h"
+#include "Recipie.h"
 
 class ItemFilter;
 class Inventory;
@@ -75,6 +76,13 @@ private:
         observerAddedCalls;
     int nextSlotId;
 
+public:
+    Inventory(const Framework::Vec3<float> location,
+        int dimensionId,
+        bool hasInventory);
+    virtual ~Inventory();
+
+private:
     void updateCache(ItemSlot* zSlot, int beforeKey);
 
 protected:
@@ -95,14 +103,10 @@ protected:
     void addObserver(Entity* zSource, Framework::Text id);
     virtual void addItems(
         ItemStack* zItems, Direction dir, ItemFilter* zFilter);
-    void lock();
-    void unlock();
 
 public:
-    Inventory(const Framework::Vec3<float> location,
-        int dimensionId,
-        bool hasInventory);
-    virtual ~Inventory();
+    void lock();
+    void unlock();
     void notifyObservers(NetworkMessage* msg);
     const ItemSlot* zSlot(int id) const;
     void addSlot(ItemSlot* slot);
@@ -116,7 +120,14 @@ public:
     virtual void addItems(ItemSlot* zSlot, ItemStack* zItems, Direction dir);
     InventoryInteraction interactWith(Inventory* zInventory, Direction dir);
     void unsaveAddItem(ItemStack* zStack, Direction dir, ItemFilter* zFilter);
-    int numberOfAddableItems(const ItemStack* zStack, Direction dir) const;
+    int numberOfAddableItems(const Item* zItem, Direction dir) const;
+    int numberOfAddableItems(const Item* zItem,
+        Direction dir,
+        const Framework::Text& slotName) const;
+    bool isAllAvailable(Framework::RCArray<RecipieInput>& inputs,
+        const Framework::Text& slotName) const;
+    void consume(Framework::RCArray<RecipieInput>& inputs,
+        const Framework::Text& slotName) const;
     Framework::ArrayIterator<ItemSlot*> begin();
     Framework::ArrayIterator<ItemSlot*> end();
     void inventoryApi(Framework::StreamReader* zRequest,

+ 3 - 2
FactoryCraft/ItemEntity.cpp

@@ -36,7 +36,7 @@ void ItemEntity::prepareTick(const Dimension* zDimension)
             dimensionId, location, [this](Entity* zOther) {
                 return zOther != this
                     && zOther->numberOfAddableItems(
-                        slot->zStack(), NO_DIRECTION)
+                        slot->zStack()->zItem(), NO_DIRECTION)
                     && (!this->slot->isFull()
                         || zOther->zType()->getId() != EntityTypeEnum::ITEM);
             });
@@ -59,7 +59,8 @@ void ItemEntity::tick(const Dimension* zDimension)
     Entity* zOther = Game::INSTANCE->zNearestEntity(
         dimensionId, location, [this](Entity* zOther) {
             return zOther != this
-                && zOther->numberOfAddableItems(slot->zStack(), NO_DIRECTION)
+                && zOther->numberOfAddableItems(
+                    slot->zStack()->zItem(), NO_DIRECTION)
                 && (!this->slot->isFull()
                     || zOther->zType()->getId() != EntityTypeEnum::ITEM);
         });

+ 42 - 0
FactoryCraft/ItemFilter.cpp

@@ -653,3 +653,45 @@ const char* GroupItemFilterFactory::getTypeToken() const
 {
     return "groups";
 }
+
+TargetSlotNameItemFilter::TargetSlotNameItemFilter(
+    const Framework::Text& slotName)
+    : ItemFilter(),
+      slotName(slotName)
+{}
+
+bool TargetSlotNameItemFilter::matchSourceSlot(ItemSlot* zSlot) const
+{
+    return 1;
+}
+
+bool TargetSlotNameItemFilter::matchTargetSlot(ItemSlot* zSlot) const
+{
+    return zSlot->getName().istGleich(slotName);
+}
+
+Framework::Text TargetSlotNameItemFilter::getLogicUIML() const
+{
+    return "<anyItem/>";
+}
+
+SourceSlotNameItemFilter::SourceSlotNameItemFilter(
+    const Framework::Text& slotName)
+    : ItemFilter(),
+      slotName(slotName)
+{}
+
+bool SourceSlotNameItemFilter::matchSourceSlot(ItemSlot* zSlot) const
+{
+    return zSlot->getName().istGleich(slotName);
+}
+
+bool SourceSlotNameItemFilter::matchTargetSlot(ItemSlot* zSlot) const
+{
+    return 1;
+}
+
+Framework::Text SourceSlotNameItemFilter::getLogicUIML() const
+{
+    return "<anyItem/>";
+}

+ 25 - 1
FactoryCraft/ItemFilter.h

@@ -162,4 +162,28 @@ public:
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override;
     const char* getTypeToken() const override;
-};
+};
+
+class TargetSlotNameItemFilter : public ItemFilter
+{
+private:
+    Framework::Text slotName;
+
+public:
+    TargetSlotNameItemFilter(const Framework::Text& slotName);
+    bool matchSourceSlot(ItemSlot* zSlot) const override;
+    bool matchTargetSlot(ItemSlot* zSlot) const override;
+    Framework::Text getLogicUIML() const override;
+};
+
+class SourceSlotNameItemFilter : public ItemFilter
+{
+private:
+    Framework::Text slotName;
+
+public:
+    SourceSlotNameItemFilter(const Framework::Text& slotName);
+    bool matchSourceSlot(ItemSlot* zSlot) const override;
+    bool matchTargetSlot(ItemSlot* zSlot) const override;
+    Framework::Text getLogicUIML() const override;
+};

+ 5 - 4
FactoryCraft/ItemSlot.cpp

@@ -80,7 +80,7 @@ void ItemSlot::update()
     }
 }
 
-int ItemSlot::numberOfAddableItems(const ItemStack* zStack, Direction dir) const
+int ItemSlot::numberOfAddableItems(const Item* zItem, Direction dir) const
 {
     if ((dir | allowedPushSides) == allowedPushSides)
     {
@@ -89,11 +89,12 @@ int ItemSlot::numberOfAddableItems(const ItemStack* zStack, Direction dir) const
             if (allowHigherStackSize)
                 return maxSize;
             else
-                return MIN(maxSize, zStack->zItem()->getMaxStackSize());
+                return MIN(maxSize, zItem->getMaxStackSize());
         }
-        else if (zStack->zItem()
-                 && items->zItem()->canBeStackedWith(zStack->zItem()))
+        else if (zItem && items->zItem()->canBeStackedWith(zItem))
+        {
             return items->getMaxSize() - items->getSize();
+        }
     }
     return 0;
 }

+ 2 - 1
FactoryCraft/ItemSlot.h

@@ -7,6 +7,7 @@
 
 class Inventory;
 class ItemStack;
+class Item;
 
 class ItemSlotIDSetter
 {
@@ -46,7 +47,7 @@ public:
     void addItems(ItemStack* zStack, Direction dir);
     void update();
 
-    int numberOfAddableItems(const ItemStack* zStack, Direction dir) const;
+    int numberOfAddableItems(const Item* zItem, Direction dir) const;
     const ItemStack* zStack() const;
     int getPullPriority() const;
     int getPushPriority() const;

+ 124 - 54
FactoryCraft/Recipie.cpp

@@ -219,6 +219,12 @@ Recipie::Recipie()
     : ReferenceCounter()
 {}
 
+void Recipie::apply(CraftingStorage* zStorage)
+{
+    consumeInputs(zStorage);
+    produceOutputs(zStorage);
+}
+
 void Recipie::addOutput(RecipieOutput* outputs)
 {
     this->outputs.add(outputs);
@@ -271,6 +277,16 @@ Framework::Text Recipie::getGroupName() const
     return groupName;
 }
 
+int Recipie::getTicksNeeded() const
+{
+    return 1;
+}
+
+int Recipie::getFuelPerTickNeeded() const
+{
+    return 0;
+}
+
 UnshapedRecipie::UnshapedRecipie()
     : Recipie()
 {}
@@ -293,9 +309,13 @@ bool UnshapedRecipie::testApplicability(CraftingStorage* zStorage)
     return zStorage->isAllAvailable(inputs);
 }
 
-void UnshapedRecipie::apply(CraftingStorage* zStorage)
+void UnshapedRecipie::consumeInputs(CraftingStorage* zStorage)
 {
     zStorage->consume(inputs);
+}
+
+void UnshapedRecipie::produceOutputs(CraftingStorage* zStorage)
+{
     for (RecipieOutput* output : outputs)
     {
         Item* item = output->createItem();
@@ -303,7 +323,16 @@ void UnshapedRecipie::apply(CraftingStorage* zStorage)
         {
             ItemStack* stack = new ItemStack(item, output->getAmount());
             zStorage->addCraftingResult(stack);
-            stack->release();
+            if (stack->getSize() > 0)
+            {
+                Game::INSTANCE->spawnItem(zStorage->getStorageLocation(),
+                    zStorage->getStorageDimensionId(),
+                    stack);
+            }
+            else
+            {
+                stack->release();
+            }
         }
     }
 }
@@ -353,56 +382,6 @@ const Framework::RCArray<RecipieInput>& UnshapedRecipie::getInputs() const
     return inputs;
 }
 
-UnshapedRecipieFactory::UnshapedRecipieFactory()
-    : RecipieFactory()
-{}
-
-UnshapedRecipie* UnshapedRecipieFactory::createValue(
-    Framework::JSON::JSONObject* zJson) const
-{
-    return new UnshapedRecipie();
-}
-
-UnshapedRecipie* UnshapedRecipieFactory::fromJson(
-    Framework::JSON::JSONObject* zJson) const
-{
-    UnshapedRecipie* result = RecipieFactory::fromJson(zJson);
-    for (Framework::JSON::JSONValue* input :
-        *zJson->zValue("inputs")->asArray())
-    {
-        result->addInput(
-            Game::INSTANCE->zTypeRegistry()->fromJson<RecipieInput>(input));
-    }
-    return result;
-}
-
-Framework::JSON::JSONObject* UnshapedRecipieFactory::toJsonObject(
-    UnshapedRecipie* zObject) const
-{
-    Framework::JSON::JSONObject* result = RecipieFactory::toJsonObject(zObject);
-    Framework::JSON::JSONArray* inputs = new Framework::JSON::JSONArray();
-    for (RecipieInput* input : zObject->getInputs())
-    {
-        inputs->addValue(Game::INSTANCE->zTypeRegistry()->toJson(input));
-    }
-    result->addValue("inputs", inputs);
-    return result;
-}
-
-JSONObjectValidationBuilder* UnshapedRecipieFactory::addToValidator(
-    JSONObjectValidationBuilder* builder) const
-{
-    return RecipieFactory::addToValidator(builder->withRequiredArray("inputs")
-            ->addAcceptedTypeInArray(
-                Game::INSTANCE->zTypeRegistry()->getValidator<RecipieInput>())
-            ->finishArray());
-}
-
-const char* UnshapedRecipieFactory::getTypeToken() const
-{
-    return "unshaped";
-}
-
 ShapedRecipie::ShapedRecipie()
     : Recipie(),
       width(0),
@@ -430,11 +409,15 @@ bool ShapedRecipie::testApplicability(CraftingStorage* zStorage)
     return zShapedStorage->isAllAvailable(inputs, width, height);
 }
 
-void ShapedRecipie::apply(CraftingStorage* zStorage)
+void ShapedRecipie::consumeInputs(CraftingStorage* zStorage)
 {
     ShapedCraftingStorage* zShapedStorage
         = dynamic_cast<ShapedCraftingStorage*>(zStorage);
     zShapedStorage->consume(inputs, width, height);
+}
+
+void ShapedRecipie::produceOutputs(CraftingStorage* zStorage)
+{
     for (RecipieOutput* output : outputs)
     {
         Item* item = output->createItem();
@@ -442,7 +425,16 @@ void ShapedRecipie::apply(CraftingStorage* zStorage)
         {
             ItemStack* stack = new ItemStack(item, output->getAmount());
             zStorage->addCraftingResult(stack);
-            stack->release();
+            if (stack->getSize() > 0)
+            {
+                Game::INSTANCE->spawnItem(zStorage->getStorageLocation(),
+                    zStorage->getStorageDimensionId(),
+                    stack);
+            }
+            else
+            {
+                stack->release();
+            }
         }
     }
 }
@@ -610,4 +602,82 @@ JSONObjectValidationBuilder* ShapedRecipieFactory::addToValidator(
 const char* ShapedRecipieFactory::getTypeToken() const
 {
     return "shaped";
+}
+
+MachineRecipie::MachineRecipie()
+    : UnshapedRecipie(),
+      neededTicks(1),
+      neededFuelPerTick(0)
+{}
+
+void MachineRecipie::setTicksNeeded(int ticks)
+{
+    neededTicks = ticks;
+}
+
+int MachineRecipie::getTicksNeeded() const
+{
+    return neededTicks;
+}
+
+void MachineRecipie::setFuelPerTickNeeded(int fuel)
+{
+    neededFuelPerTick = fuel;
+}
+
+int MachineRecipie::getFuelPerTickNeeded() const
+{
+    return neededFuelPerTick;
+}
+
+MachineRecipieFactory::MachineRecipieFactory()
+    : UnshapedRecipieFactory()
+{}
+
+MachineRecipie* MachineRecipieFactory::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new MachineRecipie();
+}
+
+MachineRecipie* MachineRecipieFactory::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    MachineRecipie* result = UnshapedRecipieFactory::fromJson(zJson);
+    result->setTicksNeeded(
+        (int)zJson->zValue("ticksNeeded")->asNumber()->getNumber());
+    result->setFuelPerTickNeeded(
+        (int)zJson->zValue("fuelPerTickNeeded")->asNumber()->getNumber());
+    return result;
+}
+
+Framework::JSON::JSONObject* MachineRecipieFactory::toJsonObject(
+    MachineRecipie* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = UnshapedRecipieFactory::toJsonObject(zObject);
+    result->addValue("ticksNeeded",
+        new Framework::JSON::JSONNumber(zObject->getTicksNeeded()));
+    result->addValue("fuelNeeded",
+        new Framework::JSON::JSONNumber(zObject->getFuelPerTickNeeded()));
+    return result;
+}
+
+JSONObjectValidationBuilder* MachineRecipieFactory::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return UnshapedRecipieFactory::addToValidator(builder)
+        ->withRequiredNumber("ticksNeeded")
+        ->whichIsGreaterOrEqual(1.0)
+        ->withDefault(1.0)
+        ->finishNumber()
+        ->withRequiredNumber("fuelNeeded")
+        ->whichIsGreaterOrEqual(0.0)
+        ->withDefault(0.0)
+        ->finishNumber();
+}
+
+const char* MachineRecipieFactory::getTypeToken() const
+{
+    return "machine";
 }

+ 88 - 13
FactoryCraft/Recipie.h

@@ -87,7 +87,9 @@ protected:
 public:
     Recipie();
     virtual bool testApplicability(CraftingStorage* zStorage) = 0;
-    virtual void apply(CraftingStorage* zStorage) = 0;
+    virtual void consumeInputs(CraftingStorage* zStorage) = 0;
+    virtual void produceOutputs(CraftingStorage* zStorage) = 0;
+    virtual void apply(CraftingStorage* zStorage);
     virtual Framework::Text getRecipieUIML() = 0;
     void addOutput(RecipieOutput* outputs);
     virtual Framework::Array<ItemInfo> getOutput() const;
@@ -95,6 +97,8 @@ public:
     void setGroupName(Framework::Text groupName);
     const Framework::RCArray<RecipieOutput>& getOutputs() const;
     Framework::Text getGroupName() const;
+    virtual int getTicksNeeded() const;
+    virtual int getFuelPerTickNeeded() const;
 };
 
 template<typename S> class RecipieFactory : public SubTypeFactory<Recipie, S>
@@ -157,25 +161,67 @@ private:
 public:
     UnshapedRecipie();
     bool testApplicability(CraftingStorage* zStorage) override;
-    void apply(CraftingStorage* zStorage) override;
+    void consumeInputs(CraftingStorage* zStorage) override;
+    void produceOutputs(CraftingStorage* zStorage) override;
     Framework::Text getRecipieUIML() override;
     void addInput(RecipieInput* input);
     const Framework::RCArray<RecipieInput>& getInputs() const;
 };
 
-class UnshapedRecipieFactory : public RecipieFactory<UnshapedRecipie>
+template<class T,
+    typename = std::enable_if<std::is_base_of<UnshapedRecipie, T>::value>>
+class UnshapedRecipieFactory : public RecipieFactory<T>
 {
 public:
-    UnshapedRecipieFactory();
-    UnshapedRecipie* createValue(
-        Framework::JSON::JSONObject* zJson) const override;
-    UnshapedRecipie* fromJson(
-        Framework::JSON::JSONObject* zJson) const override;
-    Framework::JSON::JSONObject* toJsonObject(
-        UnshapedRecipie* zObject) const override;
+    UnshapedRecipieFactory()
+        : RecipieFactory<T>()
+    {}
+
+    T* createValue(Framework::JSON::JSONObject* zJson) const override
+    {
+        return (T*)new UnshapedRecipie();
+    }
+
+    T* fromJson(Framework::JSON::JSONObject* zJson) const override
+    {
+        UnshapedRecipie* result = dynamic_cast<UnshapedRecipie*>(
+            RecipieFactory<T>::fromJson(zJson));
+        for (Framework::JSON::JSONValue* input :
+            *zJson->zValue("inputs")->asArray())
+        {
+            result->addInput(
+                Game::INSTANCE->zTypeRegistry()->fromJson<RecipieInput>(input));
+        }
+        return dynamic_cast<T*>(result);
+    }
+
+    Framework::JSON::JSONObject* toJsonObject(T* zObject) const override
+    {
+        Framework::JSON::JSONObject* result
+            = RecipieFactory<T>::toJsonObject(zObject);
+        Framework::JSON::JSONArray* inputs = new Framework::JSON::JSONArray();
+        for (RecipieInput* input : zObject->getInputs())
+        {
+            inputs->addValue(Game::INSTANCE->zTypeRegistry()->toJson(input));
+        }
+        result->addValue("inputs", inputs);
+        return result;
+    }
+
     JSONObjectValidationBuilder* addToValidator(
-        JSONObjectValidationBuilder* builder) const override;
-    const char* getTypeToken() const override;
+        JSONObjectValidationBuilder* builder) const override
+    {
+        return RecipieFactory<T>::addToValidator(
+            builder->withRequiredArray("inputs")
+                ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
+                        ->getValidator<RecipieInput>())
+                ->finishArray());
+    }
+
+    const char* getTypeToken() const override
+    {
+        return "unshaped";
+    }
 };
 
 class ShapedRecipie : public Recipie
@@ -188,7 +234,8 @@ private:
 public:
     ShapedRecipie();
     bool testApplicability(CraftingStorage* zStorage) override;
-    void apply(CraftingStorage* zStorage) override;
+    void consumeInputs(CraftingStorage* zStorage) override;
+    void produceOutputs(CraftingStorage* zStorage) override;
     Framework::Text getRecipieUIML() override;
     void setWidth(int width);
     int getWidth() const;
@@ -211,4 +258,32 @@ public:
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override;
     const char* getTypeToken() const override;
+};
+
+class MachineRecipie : public UnshapedRecipie
+{
+private:
+    int neededTicks;
+    int neededFuelPerTick;
+
+public:
+    MachineRecipie();
+    void setTicksNeeded(int ticks);
+    virtual int getTicksNeeded() const override;
+    void setFuelPerTickNeeded(int fuel);
+    virtual int getFuelPerTickNeeded() const override;
+};
+
+class MachineRecipieFactory : public UnshapedRecipieFactory<MachineRecipie>
+{
+public:
+    MachineRecipieFactory();
+    MachineRecipie* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+    MachineRecipie* fromJson(Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        MachineRecipie* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
 };

+ 16 - 1
FactoryCraft/RecipieList.cpp

@@ -34,4 +34,19 @@ void RecipieList::findRecipies(
             recipies.add(dynamic_cast<Recipie*>(recipie->getThis()));
         }
     }
-}
+}
+
+int RecipieList::getRecipieCount() const
+{
+    return recipies.getEintragAnzahl();
+}
+
+Recipie* RecipieList::zRecipie(int index) const
+{
+    return recipies.z(index);
+}
+
+int RecipieList::getRecipieIndex(Recipie* zRecipie) const
+{
+    return recipies.indexOf(zRecipie);
+}

+ 4 - 2
FactoryCraft/RecipieList.h

@@ -3,8 +3,7 @@
 
 #include "Recipie.h"
 
-class RecipieList
-    : public virtual Framework::ReferenceCounter
+class RecipieList : public virtual Framework::ReferenceCounter
 {
 private:
     Framework::RCArray<Recipie> recipies;
@@ -16,4 +15,7 @@ public:
     Recipie* zFirstRecipie(CraftingStorage* zStorage);
     const Framework::Text& getName() const;
     void findRecipies(int itemTypeId, Framework::RCArray<Recipie>& recipies);
+    int getRecipieCount() const;
+    Recipie* zRecipie(int index) const;
+    int getRecipieIndex(Recipie* zRecipie) const;
 };

+ 5 - 1
FactoryCraft/TypeRegistry.cpp

@@ -18,6 +18,7 @@
 #include "DropChanceCondition.h"
 #include "DropConditionOperator.h"
 #include "DropUsedItemCondition.h"
+#include "FireBasedProcessingBlockComponent.h"
 #include "FluidBlock.h"
 #include "FluidContainer.h"
 #include "GeneratorRule.h"
@@ -99,7 +100,7 @@ TypeRegistry::TypeRegistry()
     registerType(new RecipieInputFactory());
     registerType(new RecipieOutputFactory());
     registerSubType(new ShapedRecipieFactory());
-    registerSubType(new UnshapedRecipieFactory());
+    registerSubType(new UnshapedRecipieFactory<UnshapedRecipie>());
 
     // item modifiers
     registerSubType(new ConsumeItemModifierFactory());
@@ -164,6 +165,9 @@ TypeRegistry::TypeRegistry()
 
     // interactions
     registerSubType(new OpenDialogInteractionConfigFactory());
+
+    // block components
+    registerSubType(new FireBasedProcessingBlockComponentFactory());
 }
 
 void TypeRegistry::writeSyntaxInfo(Framework::Text folderPath) const

+ 4 - 0
Windows Version/Windows Version.vcxproj

@@ -167,6 +167,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\BasicTool.cpp" />
     <ClCompile Include="..\FactoryCraft\BiomGenerator.cpp" />
     <ClCompile Include="..\FactoryCraft\Block.cpp" />
+    <ClCompile Include="..\FactoryCraft\BlockComponent.cpp" />
     <ClCompile Include="..\FactoryCraft\BlockFilter.cpp" />
     <ClCompile Include="..\FactoryCraft\BlockInfoCommand.cpp" />
     <ClCompile Include="..\FactoryCraft\BlockInstanceGeneratorRule.cpp" />
@@ -199,6 +200,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\EntityType.cpp" />
     <ClCompile Include="..\FactoryCraft\FactorizeNoise.cpp" />
     <ClCompile Include="..\FactoryCraft\FastNoiseWrapper.cpp" />
+    <ClCompile Include="..\FactoryCraft\FireBasedProcessingBlockComponent.cpp" />
     <ClCompile Include="..\FactoryCraft\FlattenNoise.cpp" />
     <ClCompile Include="..\FactoryCraft\FluidBlock.cpp" />
     <ClCompile Include="..\FactoryCraft\FluidContainer.cpp" />
@@ -282,6 +284,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\Animal.h" />
     <ClInclude Include="..\FactoryCraft\AnimalAI.h" />
     <ClInclude Include="..\FactoryCraft\ArrayUtils.h" />
+    <ClInclude Include="..\FactoryCraft\BlockComponent.h" />
     <ClInclude Include="..\FactoryCraft\BlockFilter.h" />
     <ClInclude Include="..\FactoryCraft\BlockInfoCommand.h" />
     <ClInclude Include="..\FactoryCraft\BlockInstanceGeneratorRule.h" />
@@ -296,6 +299,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\DropUsedItemCondition.h" />
     <ClInclude Include="..\FactoryCraft\EntityGenerator.h" />
     <ClInclude Include="..\FactoryCraft\FactorizeNoise.h" />
+    <ClInclude Include="..\FactoryCraft\FireBasedProcessingBlockComponent.h" />
     <ClInclude Include="..\FactoryCraft\FlattenNoise.h" />
     <ClInclude Include="..\FactoryCraft\FluidContainer.h" />
     <ClInclude Include="..\FactoryCraft\GameClient.h" />

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

@@ -121,6 +121,9 @@
     <Filter Include="interaction">
       <UniqueIdentifier>{7d7c98b1-2773-42e7-8c68-6cdb7519fa0f}</UniqueIdentifier>
     </Filter>
+    <Filter Include="world\blocks\components">
+      <UniqueIdentifier>{a9dadb06-9971-42a7-82e0-81ac95faa38f}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -474,6 +477,12 @@
     <ClCompile Include="..\FactoryCraft\OpenDialogInteractionConfig.cpp">
       <Filter>interaction</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\BlockComponent.cpp">
+      <Filter>world\blocks\components</Filter>
+    </ClCompile>
+    <ClCompile Include="..\FactoryCraft\FireBasedProcessingBlockComponent.cpp">
+      <Filter>world\blocks\components</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -845,5 +854,11 @@
     <ClInclude Include="..\FactoryCraft\OpenDialogInteractionConfig.h">
       <Filter>interaction</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\BlockComponent.h">
+      <Filter>world\blocks\components</Filter>
+    </ClInclude>
+    <ClInclude Include="..\FactoryCraft\FireBasedProcessingBlockComponent.h">
+      <Filter>world\blocks\components</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>