Selaa lähdekoodia

make block interactions configurable for all block types

Kolja Strohm 5 kuukautta sitten
vanhempi
commit
e5daa55bd9

+ 0 - 106
FactoryCraft/BasicBlocks.cpp

@@ -161,110 +161,4 @@ const Framework::RCArray<ItemSlot>& BasicBlockType::getInventorySlots() const
 void BasicBlockType::addInventorySlot(ItemSlot* slot)
 {
     itemSlots.add(slot);
-}
-
-BasicBlockTypeFactory::BasicBlockTypeFactory()
-    : BlockTypeFactoryBase()
-{}
-
-BasicBlockType* BasicBlockTypeFactory::createValue(
-    Framework::JSON::JSONObject* zJson) const
-{
-    return new BasicBlockType();
-}
-
-BasicBlockType* BasicBlockTypeFactory::fromJson(
-    Framework::JSON::JSONObject* zJson) const
-{
-    BasicBlockType* result = BlockTypeFactoryBase::fromJson(zJson);
-    if (zJson->zValue("itemType")->getType() == Framework::AbstractType::STRING)
-    {
-        result->setItemTypeName(
-            zJson->zValue("itemType")->asString()->getString());
-    }
-    else
-    {
-        result->setItemTypeName("");
-    }
-    result->setTransparent(zJson->zValue("transparent")->asBool()->getBool());
-    result->setPassable(zJson->zValue("passable")->asBool()->getBool());
-    result->setSpeedModifier(
-        (float)zJson->zValue("speedModifier")->asNumber()->getNumber());
-    result->setInteractable(
-        (float)zJson->zValue("interactable")->asBool()->getBool());
-    for (Framework::JSON::JSONValue* value :
-        *zJson->zValue("inventorySlots")->asArray())
-    {
-        result->addInventorySlot(
-            Game::INSTANCE->zTypeRegistry()->fromJson<ItemSlot>(
-                value->asObject()));
-    }
-    return result;
-}
-
-Framework::JSON::JSONObject* BasicBlockTypeFactory::toJsonObject(
-    BasicBlockType* zObject) const
-{
-    Framework::JSON::JSONObject* result
-        = BlockTypeFactoryBase::toJsonObject(zObject);
-    if (zObject->getItemTypeName().istGleich(""))
-    {
-        result->addValue("itemType", new Framework::JSON::JSONValue());
-    }
-    else
-    {
-        result->addValue("itemType",
-            new Framework::JSON::JSONString(zObject->getItemTypeName()));
-    }
-    result->addValue(
-        "transparent", new Framework::JSON::JSONBool(zObject->isTransparent()));
-    result->addValue(
-        "passable", new Framework::JSON::JSONBool(zObject->isPassable()));
-    result->addValue("speedModifier",
-        new Framework::JSON::JSONNumber(zObject->getSpeedModifier()));
-    result->addValue("interactable",
-        new Framework::JSON::JSONBool(zObject->isInteractable()));
-    Framework::JSON::JSONArray* inventorySlots
-        = new Framework::JSON::JSONArray();
-    for (ItemSlot* slot : zObject->getInventorySlots())
-    {
-        inventorySlots->addValue(
-            Game::INSTANCE->zTypeRegistry()->toJson<ItemSlot>(slot));
-    }
-    result->addValue("inventorySlots", inventorySlots);
-    return result;
-}
-
-JSONObjectValidationBuilder* BasicBlockTypeFactory::addToValidator(
-    JSONObjectValidationBuilder* builder) const
-{
-    return BlockTypeFactoryBase::addToValidator(
-        builder
-            ->withRequiredAttribute("itemType",
-                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
-                    ItemTypeNameFactory::TYPE_ID),
-                true,
-                false)
-            ->withRequiredBool("transparent")
-            ->withDefault(false)
-            ->finishBool()
-            ->withRequiredBool("passable")
-            ->withDefault(false)
-            ->finishBool()
-            ->withRequiredNumber("speedModifier")
-            ->withDefault(1.0)
-            ->finishNumber()
-            ->withRequiredBool("interactable")
-            ->withDefault(true)
-            ->finishBool())
-        ->withRequiredArray("inventorySlots")
-        ->withDefault(new Framework::JSON::JSONArray())
-        ->addAcceptedTypeInArray(
-            Game::INSTANCE->zTypeRegistry()->getValidator<ItemSlot>())
-        ->finishArray();
-}
-
-const char* BasicBlockTypeFactory::getTypeToken() const
-{
-    return "basicBlock";
 }

+ 107 - 10
FactoryCraft/BasicBlocks.h

@@ -60,17 +60,114 @@ public:
     void addInventorySlot(ItemSlot* slot);
 };
 
-class BasicBlockTypeFactory : public BlockTypeFactoryBase<BasicBlockType>
+template<class T,
+    typename = std::enable_if<std::is_base_of<BasicBlockType, T>::value>>
+class BasicBlockTypeFactory : public BlockTypeFactoryBase<T>
 {
 public:
-    BasicBlockTypeFactory();
-    virtual BasicBlockType* createValue(
-        Framework::JSON::JSONObject* zJson) const override;
-    virtual BasicBlockType* fromJson(
-        Framework::JSON::JSONObject* zJson) const override;
-    virtual Framework::JSON::JSONObject* toJsonObject(
-        BasicBlockType* zObject) const override;
+    BasicBlockTypeFactory()
+        : BlockTypeFactoryBase<T>()
+    {}
+
+    virtual T* createValue(Framework::JSON::JSONObject* zJson) const override
+    {
+        return (T*)new BasicBlockType();
+    }
+
+    virtual T* fromJson(Framework::JSON::JSONObject* zJson) const override
+    {
+        T* result = BlockTypeFactoryBase<T>::fromJson(zJson);
+        if (zJson->zValue("itemType")->getType()
+            == Framework::AbstractType::STRING)
+        {
+            result->setItemTypeName(
+                zJson->zValue("itemType")->asString()->getString());
+        }
+        else
+        {
+            result->setItemTypeName("");
+        }
+        result->setTransparent(
+            zJson->zValue("transparent")->asBool()->getBool());
+        result->setPassable(zJson->zValue("passable")->asBool()->getBool());
+        result->setSpeedModifier(
+            (float)zJson->zValue("speedModifier")->asNumber()->getNumber());
+        result->setInteractable(
+            (float)zJson->zValue("interactable")->asBool()->getBool());
+        for (Framework::JSON::JSONValue* value :
+            *zJson->zValue("inventorySlots")->asArray())
+        {
+            result->addInventorySlot(
+                Game::INSTANCE->zTypeRegistry()->fromJson<ItemSlot>(
+                    value->asObject()));
+        }
+        return result;
+    }
+
+    virtual Framework::JSON::JSONObject* toJsonObject(T* zObject) const override
+    {
+        Framework::JSON::JSONObject* result
+            = BlockTypeFactoryBase<T>::toJsonObject(zObject);
+        if (zObject->getItemTypeName().istGleich(""))
+        {
+            result->addValue("itemType", new Framework::JSON::JSONValue());
+        }
+        else
+        {
+            result->addValue("itemType",
+                new Framework::JSON::JSONString(zObject->getItemTypeName()));
+        }
+        result->addValue("transparent",
+            new Framework::JSON::JSONBool(zObject->isTransparent()));
+        result->addValue(
+            "passable", new Framework::JSON::JSONBool(zObject->isPassable()));
+        result->addValue("speedModifier",
+            new Framework::JSON::JSONNumber(zObject->getSpeedModifier()));
+        result->addValue("interactable",
+            new Framework::JSON::JSONBool(zObject->isInteractable()));
+        Framework::JSON::JSONArray* inventorySlots
+            = new Framework::JSON::JSONArray();
+        for (ItemSlot* slot : zObject->getInventorySlots())
+        {
+            inventorySlots->addValue(
+                Game::INSTANCE->zTypeRegistry()->toJson<ItemSlot>(slot));
+        }
+        result->addValue("inventorySlots", inventorySlots);
+        return result;
+    }
+
     virtual JSONObjectValidationBuilder* addToValidator(
-        JSONObjectValidationBuilder* builder) const override;
-    virtual const char* getTypeToken() const override;
+        JSONObjectValidationBuilder* builder) const override
+    {
+        return BlockTypeFactoryBase<T>::addToValidator(
+            builder
+                ->withRequiredAttribute("itemType",
+                    Game::INSTANCE->zTypeRegistry()
+                        ->getValidator<Framework::Text>(
+                            ItemTypeNameFactory::TYPE_ID),
+                    true,
+                    false)
+                ->withRequiredBool("transparent")
+                ->withDefault(false)
+                ->finishBool()
+                ->withRequiredBool("passable")
+                ->withDefault(false)
+                ->finishBool()
+                ->withRequiredNumber("speedModifier")
+                ->withDefault(1.0)
+                ->finishNumber()
+                ->withRequiredBool("interactable")
+                ->withDefault(true)
+                ->finishBool())
+            ->withRequiredArray("inventorySlots")
+            ->withDefault(new Framework::JSON::JSONArray())
+            ->addAcceptedTypeInArray(
+                Game::INSTANCE->zTypeRegistry()->getValidator<ItemSlot>())
+            ->finishArray();
+    }
+
+    virtual const char* getTypeToken() const override
+    {
+        return "basicBlock";
+    }
 };

+ 7 - 1
FactoryCraft/Block.cpp

@@ -2,6 +2,7 @@
 
 #include "Dimension.h"
 #include "Game.h"
+#include "InteractionConfig.h"
 #include "Inventory.h"
 #include "ItemEntity.h"
 #include "MultiblockStructure.h"
@@ -255,8 +256,13 @@ void Block::sendModelInfo(NetworkMessage* zMessage)
     // overwritten by some blocks
 }
 
-bool Block::interact(Item* zItem, Entity* zActor)
+bool Block::interact(Item* zItem, Entity* zActor, bool& itemChanged)
 {
+    bool result = 0;
+    for (InteractionConfig* config : zBlockType()->getInteractionConfigs())
+    {
+        result |= config->doInteraction(this, zItem, zActor, itemChanged);
+    }
     return false;
 }
 

+ 1 - 1
FactoryCraft/Block.h

@@ -95,7 +95,7 @@ public:
     virtual void setNeighbourType(Direction dir, int type);
     virtual Framework::Text getTargetUIML();
     virtual void sendModelInfo(NetworkMessage* zMessage);
-    virtual bool interact(Item* zItem, Entity* zActor);
+    virtual bool interact(Item* zItem, Entity* zActor, bool& itemChanged);
     void api(Framework::StreamReader* zRequest, NetworkMessage* zResponse);
     virtual TickSourceType isTickSource() const;
     virtual bool needsTick() const;

+ 11 - 0
FactoryCraft/BlockType.cpp

@@ -321,6 +321,17 @@ bool BlockType::isDamagableByHand() const
     return damagableByHand;
 }
 
+void BlockType::addInteractionConfig(InteractionConfig* config)
+{
+    interactionConfigs.add(config);
+}
+
+const Framework::RCArray<InteractionConfig>&
+BlockType::getInteractionConfigs() const
+{
+    return interactionConfigs;
+}
+
 int BlockType::getTypeId(const char* name)
 {
     Text n = name;

+ 27 - 3
FactoryCraft/BlockType.h

@@ -6,6 +6,7 @@
 #include <Writer.h>
 
 #include "DropConfig.h"
+#include "InteractionConfig.h"
 #include "ModelInfo.h"
 
 class Item;
@@ -36,6 +37,7 @@ private:
     float hardness;
     Framework::RCArray<DropConfig> dropConfigs;
     bool damagableByHand;
+    Framework::RCArray<InteractionConfig> interactionConfigs;
 
 protected:
     BlockType();
@@ -96,6 +98,8 @@ public:
     float getHardness() const;
     void setDamagableByHand(bool damagableByHand);
     bool isDamagableByHand() const;
+    void addInteractionConfig(InteractionConfig* config);
+    const Framework::RCArray<InteractionConfig>& getInteractionConfigs() const;
 
     static int getTypeId(const char* name);
     static Framework::Text getTypeName(int id);
@@ -141,11 +145,17 @@ public:
             *zJson->zValue("drops")->asArray())
         {
             zType->addDropConfig(
-                Game::INSTANCE->zTypeRegistry()->fromJson<DropConfig>(
-                    value->asObject()));
+                Game::INSTANCE->zTypeRegistry()->fromJson<DropConfig>(value));
         }
         zType->setDamagableByHand(
             zJson->zValue("damagableByHand")->asBool()->getBool());
+        for (Framework::JSON::JSONValue* value :
+            *zJson->zValue("interactions")->asArray())
+        {
+            zType->addInteractionConfig(
+                Game::INSTANCE->zTypeRegistry()->fromJson<InteractionConfig>(
+                    value));
+        }
         return result;
     }
 
@@ -187,6 +197,15 @@ public:
             "hardness", new Framework::JSON::JSONNumber(zType->getHardness()));
         result->addValue("damagableByHand",
             new Framework::JSON::JSONBool(zType->isDamagableByHand()));
+        Framework::JSON::JSONArray* interactions
+            = new Framework::JSON::JSONArray();
+        for (InteractionConfig* interaction : zType->getInteractionConfigs())
+        {
+            interactions->addValue(
+                Game::INSTANCE->zTypeRegistry()->toJson<InteractionConfig>(
+                    interaction));
+        }
+        result->addValue("interactions", interactions);
         return result;
     }
 
@@ -240,7 +259,12 @@ public:
                     ->finishArray())
             ->withRequiredBool("damagableByHand")
             ->withDefault(false)
-            ->finishBool();
+            ->finishBool()
+            ->withRequiredArray("interactions")
+            ->withDefault(new Framework::JSON::JSONArray())
+            ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
+                    ->getValidator<InteractionConfig>())
+            ->finishArray();
     }
 
 protected:

+ 7 - 43
FactoryCraft/Chest.cpp

@@ -55,9 +55,10 @@ bool Chest::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
     return open;
 }
 
-bool Chest::interact(Item* zItem, Entity* zActor)
+bool Chest::interact(Item* zItem, Entity* zActor, bool& itemChanged)
 {
     lock();
+    bool result = 0;
     if (open)
     {
         if (!Game::INSTANCE->zEntity(userEntityId)) open = 0;
@@ -66,44 +67,7 @@ bool Chest::interact(Item* zItem, Entity* zActor)
     {
         userEntityId = zActor->getId();
         open = 1;
-        Framework::Text uiml = "";
-        uiml.append()
-            << "<dialog id=\"" << getDialogId()
-            << "\" title=\"Chest\" "
-               "notifyOnClose=\""
-            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
-            << getPos().z
-            << "\" "
-               "width=\"610\" "
-               "height=\"480\">"
-            << "<inventory id=\"chest_inventory\" margin-bottom=\"18\" "
-               "align-bottom=\"player_label\" align-left=\"start\" "
-               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
-               "numSlots=\"30\" slotNameFilter=\"\" target=\""
-            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
-            << getPos().z << "\"/>"
-            << "<text id=\"player_label\" width=\"100%\" height=\"auto\" "
-               "style=\""
-            << std::uppercase << std::hex
-            << (Framework::TextFeld::Style::Text
-                   | Framework::TextFeld::Style::Center)
-            << std::dec << std::nouppercase
-            << "\" margin-bottom=\"9\" "
-               "align-bottom=\"player_inventory\">Player "
-               "Inventory</text>"
-            << "<inventory id=\"player_inventory\" margin-bottom=\"18\" "
-               "align-bottom=\"item_bar\" align-left=\"start\" "
-               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
-               "numSlots=\"30\" slotNameFilter=\"Inventory\" target=\""
-            << zActor->getId() << "\"/>"
-            << "<inventory id=\"item_bar\" margin-bottom=\"9\" "
-               "align-bottom=\"end\" align-left=\"start\" margin-left=\"9\" "
-               "width=\"592\" height=\"52\" rowSize=\"10\" numSlots=\"10\" "
-               "slotNameFilter=\"ItemBar\" target=\""
-            << zActor->getId() << "\"/>"
-            << "</dialog>";
-        Game::INSTANCE->zUIController()->addDialog(new UIDialog(
-            getDialogId(), zActor->getId(), new Framework::XML::Element(uiml)));
+        result = Block::interact(zItem, zActor, itemChanged);
         NetworkMessage* msg = new NetworkMessage();
         msg->animateBlockBone(getDimensionId(),
             Game::getChunkCenter(getPos().x, getPos().y),
@@ -116,7 +80,7 @@ bool Chest::interact(Item* zItem, Entity* zActor)
         broadcastMessage(msg);
     }
     unlock();
-    return false; // item was not changed
+    return result;
 }
 
 void Chest::sendModelInfo(NetworkMessage* zMessage)
@@ -148,20 +112,20 @@ ChestBlockTypeFactory::ChestBlockTypeFactory()
     : BasicBlockTypeFactory()
 {}
 
-BasicBlockType* ChestBlockTypeFactory::createValue(
+ChestBlockType* ChestBlockTypeFactory::createValue(
     Framework::JSON::JSONObject* zJson) const
 {
     return new ChestBlockType();
 }
 
-BasicBlockType* ChestBlockTypeFactory::fromJson(
+ChestBlockType* ChestBlockTypeFactory::fromJson(
     Framework::JSON::JSONObject* zJson) const
 {
     return BasicBlockTypeFactory::fromJson(zJson);
 }
 
 Framework::JSON::JSONObject* ChestBlockTypeFactory::toJsonObject(
-    BasicBlockType* zObject) const
+    ChestBlockType* zObject) const
 {
     return BasicBlockTypeFactory::toJsonObject(zObject);
 }

+ 6 - 5
FactoryCraft/Chest.h

@@ -17,7 +17,8 @@ protected:
 
 public:
     Chest(int typeId, Framework::Vec3<int> pos, int dimensionId);
-    virtual bool interact(Item* zItem, Entity* zActor) override;
+    virtual bool interact(
+        Item* zItem, Entity* zActor, bool& itemChanged) override;
     virtual void sendModelInfo(NetworkMessage* zMessage) override;
 };
 
@@ -29,15 +30,15 @@ public:
         Framework::Vec3<int> position, int dimensionId) const override;
 };
 
-class ChestBlockTypeFactory : public BasicBlockTypeFactory
+class ChestBlockTypeFactory : public BasicBlockTypeFactory<ChestBlockType>
 {
 public:
     ChestBlockTypeFactory();
-    BasicBlockType* createValue(
+    ChestBlockType* createValue(
         Framework::JSON::JSONObject* zJson) const override;
-    BasicBlockType* fromJson(Framework::JSON::JSONObject* zJson) const override;
+    ChestBlockType* fromJson(Framework::JSON::JSONObject* zJson) const override;
     Framework::JSON::JSONObject* toJsonObject(
-        BasicBlockType* zObject) const override;
+        ChestBlockType* zObject) const override;
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override;
     const char* getTypeToken() const override;

+ 4 - 2
FactoryCraft/Entity.cpp

@@ -75,18 +75,20 @@ bool ActionTarget::interactItemSkillOnTarget(
     }
     else
     {
+        bool itemChanged = 0;
         if (entityId >= 0)
         {
             Block* block = Game::INSTANCE->zRealBlockInstance(
                 blockPos, zActor->getDimensionId());
-            if (block) return block->interact(zUsedItem, zActor);
+            if (block) block->interact(zUsedItem, zActor, itemChanged);
         }
         else
         {
             Block* block = Game::INSTANCE->zRealBlockInstance(
                 blockPos, zActor->getDimensionId());
-            if (block) return block->interact(zUsedItem, zActor);
+            if (block) block->interact(zUsedItem, zActor, itemChanged);
         }
+        return itemChanged;
     }
     return 0;
 }

+ 5 - 1
FactoryCraft/FactoryCraft.vcxproj

@@ -151,6 +151,7 @@
     <ClInclude Include="Grass.h" />
     <ClInclude Include="Chat.h" />
     <ClInclude Include="InformationObserver.h" />
+    <ClInclude Include="InteractionConfig.h" />
     <ClInclude Include="Inventory.h" />
     <ClInclude Include="Item.h" />
     <ClInclude Include="ItemEntity.h" />
@@ -177,6 +178,7 @@
     <ClInclude Include="Noise.h" />
     <ClInclude Include="NoBlock.h" />
     <ClInclude Include="NoiseInterpolator.h" />
+    <ClInclude Include="OpenDialogInteractionConfig.h" />
     <ClInclude Include="PlaceableProof.h" />
     <ClInclude Include="Player.h" />
     <ClInclude Include="PlayerHand.h" />
@@ -270,6 +272,7 @@
     <ClCompile Include="GrantCommand.cpp" />
     <ClCompile Include="Grass.cpp" />
     <ClCompile Include="InformationObserver.cpp" />
+    <ClCompile Include="InteractionConfig.cpp" />
     <ClCompile Include="Inventory.cpp" />
     <ClCompile Include="Item.cpp" />
     <ClCompile Include="ItemEntity.cpp" />
@@ -295,6 +298,7 @@
     <ClCompile Include="NoBlock.cpp" />
     <ClCompile Include="Noise.cpp" />
     <ClCompile Include="NoiseInterpolator.cpp" />
+    <ClCompile Include="OpenDialogInteractionConfig.cpp" />
     <ClCompile Include="PlaceableProof.cpp" />
     <ClCompile Include="Player.cpp" />
     <ClCompile Include="PlayerHand.cpp" />
@@ -327,7 +331,7 @@
     <ClCompile Include="UICraftingGrid.cpp" />
     <ClCompile Include="UIDialog.cpp" />
     <ClCompile Include="UIDialogElement.cpp" />
-    <ClCompile Include="UIelement.cpp" />
+    <ClCompile Include="UIElement.cpp" />
     <ClCompile Include="UIInventory.cpp" />
     <ClCompile Include="UIReference.cpp" />
     <ClCompile Include="UIText.cpp" />

+ 16 - 1
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -118,6 +118,9 @@
     <Filter Include="UI\UIElements">
       <UniqueIdentifier>{b000db10-bafc-49f7-b4a3-c4c26e82ca6b}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Interaction">
+      <UniqueIdentifier>{ca208791-fa0c-40d2-ad18-ddbef309fa4a}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Chunk.h">
@@ -480,6 +483,12 @@
     <ClInclude Include="UIReference.h">
       <Filter>UI\UIElements</Filter>
     </ClInclude>
+    <ClInclude Include="InteractionConfig.h">
+      <Filter>Interaction</Filter>
+    </ClInclude>
+    <ClInclude Include="OpenDialogInteractionConfig.h">
+      <Filter>Interaction</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -824,11 +833,17 @@
     <ClCompile Include="UIText.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
-    <ClCompile Include="UIelement.cpp">
+    <ClCompile Include="UIElement.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
     <ClCompile Include="UIReference.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
+    <ClCompile Include="InteractionConfig.cpp">
+      <Filter>Interaction</Filter>
+    </ClCompile>
+    <ClCompile Include="OpenDialogInteractionConfig.cpp">
+      <Filter>Interaction</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 3 - 3
FactoryCraft/Grass.cpp

@@ -70,20 +70,20 @@ GrassBlockTypeFactory::GrassBlockTypeFactory()
     : BasicBlockTypeFactory()
 {}
 
-BasicBlockType* GrassBlockTypeFactory::createValue(
+GrassBlockType* GrassBlockTypeFactory::createValue(
     Framework::JSON::JSONObject* zJson) const
 {
     return new GrassBlockType();
 }
 
-BasicBlockType* GrassBlockTypeFactory::fromJson(
+GrassBlockType* GrassBlockTypeFactory::fromJson(
     Framework::JSON::JSONObject* zJson) const
 {
     return BasicBlockTypeFactory::fromJson(zJson);
 }
 
 Framework::JSON::JSONObject* GrassBlockTypeFactory::toJsonObject(
-    BasicBlockType* zObject) const
+    GrassBlockType* zObject) const
 {
     return BasicBlockTypeFactory::toJsonObject(zObject);
 }

+ 4 - 4
FactoryCraft/Grass.h

@@ -38,15 +38,15 @@ public:
     virtual ItemType* createItemType() const override;
 };
 
-class GrassBlockTypeFactory : public BasicBlockTypeFactory
+class GrassBlockTypeFactory : public BasicBlockTypeFactory<GrassBlockType>
 {
 public:
     GrassBlockTypeFactory();
-    BasicBlockType* createValue(
+    GrassBlockType* createValue(
         Framework::JSON::JSONObject* zJson) const override;
-    BasicBlockType* fromJson(Framework::JSON::JSONObject* zJson) const override;
+    GrassBlockType* fromJson(Framework::JSON::JSONObject* zJson) const override;
     Framework::JSON::JSONObject* toJsonObject(
-        BasicBlockType* zObject) const override;
+        GrassBlockType* zObject) const override;
     JSONObjectValidationBuilder* addToValidator(
         JSONObjectValidationBuilder* builder) const override;
     const char* getTypeToken() const override;

+ 40 - 0
FactoryCraft/InteractionConfig.cpp

@@ -0,0 +1,40 @@
+#include "InteractionConfig.h"
+
+InteractionConfig::InteractionConfig()
+    : Framework::ReferenceCounter(),
+      filter(0)
+{}
+
+InteractionConfig::~InteractionConfig()
+{
+    if (filter)
+    {
+        filter->release();
+    }
+}
+
+void InteractionConfig::setItemFilter(ItemFilter* filter)
+{
+    if (this->filter)
+    {
+        this->filter->release();
+    }
+    this->filter = filter;
+}
+
+ItemFilter* InteractionConfig::zItemFilter() const
+{
+    return filter;
+}
+
+bool InteractionConfig::doInteraction(Framework::Either<Block*, Entity*> target,
+    Item* zItem,
+    Entity* actor,
+    bool& itemChanged)
+{
+    if (!filter || (zItem && filter->matchItem(zItem)))
+    {
+        return onInteraction(target, zItem, actor, itemChanged);
+    }
+    return false;
+}

+ 77 - 0
FactoryCraft/InteractionConfig.h

@@ -0,0 +1,77 @@
+#pragma once
+
+#include <Either.h>
+#include <ReferenceCounter.h>
+
+#include "Game.h"
+#include "ItemFilter.h"
+#include "TypeRegistry.h"
+
+class Block;
+class Entity;
+class Item;
+
+class InteractionConfig : public virtual Framework::ReferenceCounter
+{
+private:
+    ItemFilter* filter;
+
+public:
+    InteractionConfig();
+    ~InteractionConfig();
+    void setItemFilter(ItemFilter* filter);
+    ItemFilter* zItemFilter() const;
+    bool doInteraction(Framework::Either<Block*, Entity*> target,
+        Item* zItem,
+        Entity* actor,
+        bool& itemChanged);
+
+protected:
+    virtual bool onInteraction(Framework::Either<Block*, Entity*> target,
+        Item* zItem,
+        Entity* actor,
+        bool& itemChanged)
+        = 0;
+};
+
+template<class T> class InteractionConfigFactory
+    : public SubTypeFactory<InteractionConfig, T>
+{
+public:
+    virtual JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const
+    {
+        return builder->withRequiredAttribute("itemFilter",
+            Game::INSTANCE->zTypeRegistry()->getValidator<ItemFilter>(),
+            false,
+            true);
+    }
+
+    virtual T* fromJson(Framework::JSON::JSONObject* zJson) const
+    {
+        T* result = createValue(zJson);
+        if (zJson->hasValue("itemFilter"))
+        {
+            ItemFilter* itemFilter
+                = Game::INSTANCE->zTypeRegistry()->fromJson<ItemFilter>(
+                    zJson->zValue("itemFilter"));
+            result->setItemFilter(itemFilter);
+        }
+        return result;
+    }
+
+    virtual Framework::JSON::JSONObject* toJsonObject(T* zObject) const
+    {
+        Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
+        if (zObject->zItemFilter())
+        {
+            result->addValue("itemFilter",
+                Game::INSTANCE->zTypeRegistry()->toJson(
+                    zObject->zItemFilter()));
+        }
+        return result;
+    }
+
+protected:
+    virtual T* createValue(Framework::JSON::JSONObject* zJson) const = 0;
+};

+ 3 - 1
FactoryCraft/ItemSkill.cpp

@@ -30,7 +30,9 @@ void ItemSkill::save(Framework::StreamWriter* zWriter)
 
 bool ItemSkill::interact(Entity* zActor, Item* zUsedItem, Block* zTarget)
 {
-    return zTarget->interact(zUsedItem, zActor);
+    bool itemChanged = 0;
+    zTarget->interact(zUsedItem, zActor, itemChanged);
+    return itemChanged;
 }
 
 bool ItemSkill::interact(Entity* zActor, Item* zUsedItem, Entity* zTarget)

+ 95 - 0
FactoryCraft/OpenDialogInteractionConfig.cpp

@@ -0,0 +1,95 @@
+#include "OpenDialogInteractionConfig.h"
+
+#include <XML.h>
+
+#include "Entity.h"
+#include "UIController.h"
+#include "UIDialog.h"
+#include "UIDialogElement.h"
+
+OpenDialogInteractionConfig::OpenDialogInteractionConfig()
+    : InteractionConfig(),
+      dialogElement(0)
+{}
+
+OpenDialogInteractionConfig::~OpenDialogInteractionConfig()
+{
+    if (dialogElement)
+    {
+        dialogElement->release();
+    }
+}
+
+void OpenDialogInteractionConfig::setDialogElement(
+    UIDialogElement* dialogElement)
+{
+    if (this->dialogElement)
+    {
+        this->dialogElement->release();
+    }
+    this->dialogElement = dialogElement;
+}
+
+UIDialogElement* OpenDialogInteractionConfig::zDialogElement() const
+{
+    return dialogElement;
+}
+
+bool OpenDialogInteractionConfig::onInteraction(
+    Framework::Either<Block*, Entity*> target,
+    Item* zItem,
+    Entity* actor,
+    bool& itemChanged)
+{
+    Framework::XML::Element* uiml = dialogElement->toUIML(target, actor);
+    Game::INSTANCE->zUIController()->addDialog(
+        new UIDialog(uiml->getAttributeValue("id"), actor->getId(), uiml));
+    return true;
+}
+
+OpenDialogInteractionConfigFactory::OpenDialogInteractionConfigFactory()
+    : InteractionConfigFactory<OpenDialogInteractionConfig>()
+{}
+
+JSONObjectValidationBuilder* OpenDialogInteractionConfigFactory::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return InteractionConfigFactory<
+        OpenDialogInteractionConfig>::addToValidator(builder)
+        ->withRequiredAttribute("dialogElement",
+            Game::INSTANCE->zTypeRegistry()->getValidator<UIDialogElement>());
+}
+
+OpenDialogInteractionConfig* OpenDialogInteractionConfigFactory::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    OpenDialogInteractionConfig* result
+        = InteractionConfigFactory<OpenDialogInteractionConfig>::fromJson(
+            zJson);
+    result->setDialogElement(
+        Game::INSTANCE->zTypeRegistry()->fromJson<UIDialogElement>(
+            zJson->zValue("dialogElement")));
+    return result;
+}
+
+Framework::JSON::JSONObject* OpenDialogInteractionConfigFactory::toJsonObject(
+    OpenDialogInteractionConfig* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = InteractionConfigFactory<OpenDialogInteractionConfig>::toJsonObject(
+            zObject);
+    result->addValue("dialogElement",
+        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zDialogElement()));
+    return result;
+}
+
+const char* OpenDialogInteractionConfigFactory::getTypeToken() const
+{
+    return "OpenDialogInteractionConfig";
+}
+
+OpenDialogInteractionConfig* OpenDialogInteractionConfigFactory::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new OpenDialogInteractionConfig();
+}

+ 41 - 0
FactoryCraft/OpenDialogInteractionConfig.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include "InteractionConfig.h"
+
+class UIDialogElement;
+
+class OpenDialogInteractionConfig : public InteractionConfig
+{
+private:
+    UIDialogElement* dialogElement;
+
+public:
+    OpenDialogInteractionConfig();
+    ~OpenDialogInteractionConfig();
+    void setDialogElement(UIDialogElement* dialogElement);
+    UIDialogElement* zDialogElement() const;
+
+protected:
+    virtual bool onInteraction(Framework::Either<Block*, Entity*> target,
+        Item* zItem,
+        Entity* actor,
+        bool& itemChanged) override;
+};
+
+class OpenDialogInteractionConfigFactory
+    : public InteractionConfigFactory<OpenDialogInteractionConfig>
+{
+public:
+    OpenDialogInteractionConfigFactory();
+    virtual JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    virtual OpenDialogInteractionConfig* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    virtual Framework::JSON::JSONObject* toJsonObject(
+        OpenDialogInteractionConfig* zObject) const override;
+    virtual const char* getTypeToken() const override;
+
+protected:
+    virtual OpenDialogInteractionConfig* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+};

+ 5 - 1
FactoryCraft/TypeRegistry.cpp

@@ -27,6 +27,7 @@
 #include "JsonExpression.h"
 #include "LightSources.h"
 #include "ModelInfo.h"
+#include "OpenDialogInteractionConfig.h"
 #include "PlaceableProof.h"
 #include "Quest.h"
 #include "Recipie.h"
@@ -55,7 +56,7 @@ TypeRegistry::TypeRegistry()
 
     // block types
     registerType(new ModelInfoFactory());
-    registerSubType(new BasicBlockTypeFactory());
+    registerSubType(new BasicBlockTypeFactory<BasicBlockType>());
     registerSubType(new BasicLightSourceBlockTypeFactory());
     registerSubType(new ChestBlockTypeFactory());
     registerSubType(new FluidBlockTypeFactory());
@@ -160,6 +161,9 @@ TypeRegistry::TypeRegistry()
     registerSubType(new UIInventoryElementFactory());
     registerSubType(new UIDialogElementFactory());
     registerSubType(new UICraftingGridElementFactory());
+
+    // interactions
+    registerSubType(new OpenDialogInteractionConfigFactory());
 }
 
 void TypeRegistry::writeSyntaxInfo(Framework::Text folderPath) const

+ 3 - 0
FactoryCraft/TypeRegistry.h

@@ -352,6 +352,9 @@ public:
                 + typeId;
         }
         polymorphFactory->addFactory<S>(factory);
+        Framework::Text typeIdS = typeid(S).name();
+        parsableTypeNames.add(new Framework::Text(typeIdS));
+        registerType(factory);
     }
 
     template<typename T> void registerType(ObjectTypeFactory<T>* factory)

+ 15 - 0
FactoryCraft/UIDialogElement.cpp

@@ -1,5 +1,7 @@
 #include "UIDialogElement.h"
 
+#include "Block.h"
+#include "Entity.h"
 #include "UIReference.h"
 
 UIDialogElement::UIDialogElement()
@@ -46,6 +48,19 @@ Framework::XML::Element* UIDialogElement::toUIML(
         = UIContainerElement::toUIML(zTarget, zActor);
     result->setName("dialog");
     result->setAttribute("title", title);
+    Framework::Text id = getId();
+    if (zTarget.isA())
+    {
+        id.append() << "_" + zTarget.getA()->getDimensionId() << ","
+                    << zTarget.getA()->getPos().x << ","
+                    << zTarget.getA()->getPos().y << ","
+                    << zTarget.getA()->getPos().z << "_" << zActor->getId();
+    }
+    else
+    {
+        id.append() << "_" << zTarget.getB()->getId() << "_" << zActor->getId();
+    }
+    result->setAttribute("id", id);
     if (notifyOnClose)
     {
         result->setAttribute(

+ 36 - 22
FactoryCraft/UIElement.cpp

@@ -20,62 +20,62 @@ const Framework::Text& UIElement::getId() const
     return id;
 }
 
-void UIElement::setMarginLeft(int marginLeft)
+void UIElement::setMarginLeft(const Framework::Text& marginLeft)
 {
     this->marginLeft = marginLeft;
 }
 
-int UIElement::getMarginLeft() const
+const Framework::Text& UIElement::getMarginLeft() const
 {
     return marginLeft;
 }
 
-void UIElement::setMarginRight(int marginRight)
+void UIElement::setMarginRight(const Framework::Text& marginRight)
 {
     this->marginRight = marginRight;
 }
 
-int UIElement::getMarginRight() const
+const Framework::Text& UIElement::getMarginRight() const
 {
     return marginRight;
 }
 
-void UIElement::setMarginTop(int marginTop)
+void UIElement::setMarginTop(const Framework::Text& marginTop)
 {
     this->marginTop = marginTop;
 }
 
-int UIElement::getMarginTop() const
+const Framework::Text& UIElement::getMarginTop() const
 {
     return marginTop;
 }
 
-void UIElement::setMarginBottom(int marginBottom)
+void UIElement::setMarginBottom(const Framework::Text& marginBottom)
 {
     this->marginBottom = marginBottom;
 }
 
-int UIElement::getMarginBottom() const
+const Framework::Text& UIElement::getMarginBottom() const
 {
     return marginBottom;
 }
 
-void UIElement::setWidth(int width)
+void UIElement::setWidth(const Framework::Text& width)
 {
     this->width = width;
 }
 
-int UIElement::getWidth() const
+const Framework::Text& UIElement::getWidth() const
 {
     return width;
 }
 
-void UIElement::setHeight(int height)
+void UIElement::setHeight(const Framework::Text& height)
 {
     this->height = height;
 }
 
-int UIElement::getHeight() const
+const Framework::Text& UIElement::getHeight() const
 {
     return height;
 }
@@ -120,29 +120,39 @@ const Framework::Text& UIElement::getAlignBottom() const
     return alignBottom;
 }
 
+void UIElement::setStyle(const Framework::Text& style)
+{
+    this->style = style;
+}
+
+const Framework::Text& UIElement::getStyle() const
+{
+    return style;
+}
+
 Framework::XML::Element* UIElement::toUIML(
     Framework::Either<Block*, Entity*> zTarget, Entity* zActor) const
 {
     Framework::XML::Element* result = new Framework::XML::Element();
     result->setAttribute("id", id);
-    if (marginLeft != 0)
+    if (!marginLeft.istGleich("0"))
     {
-        result->setAttribute("marginLeft", Framework::Text(marginLeft));
+        result->setAttribute("marginLeft", marginLeft);
     }
-    if (marginRight != 0)
+    if (!marginRight.istGleich("0"))
     {
-        result->setAttribute("marginRight", Framework::Text(marginRight));
+        result->setAttribute("marginRight", marginRight);
     }
-    if (marginTop != 0)
+    if (!marginTop.istGleich("0"))
     {
-        result->setAttribute("marginTop", Framework::Text(marginTop));
+        result->setAttribute("marginTop", marginTop);
     }
-    if (marginBottom != 0)
+    if (!marginBottom.istGleich("0"))
     {
-        result->setAttribute("marginBottom", Framework::Text(marginBottom));
+        result->setAttribute("marginBottom", marginBottom);
     }
-    result->setAttribute("width", Framework::Text(width));
-    result->setAttribute("height", Framework::Text(height));
+    result->setAttribute("width", width);
+    result->setAttribute("height", height);
     if (alignLeft.getLength() > 0)
     {
         result->setAttribute("alignLeft", alignLeft);
@@ -159,6 +169,10 @@ Framework::XML::Element* UIElement::toUIML(
     {
         result->setAttribute("alignBottom", alignBottom);
     }
+    if (style.getLength() > 0)
+    {
+        result->setAttribute("style", style);
+    }
     return result;
 }
 

+ 121 - 35
FactoryCraft/UIElement.h

@@ -14,33 +14,34 @@ class UIElement : public virtual Framework::ReferenceCounter
 {
 private:
     Framework::Text id;
-    int marginLeft;
-    int marginRight;
-    int marginTop;
-    int marginBottom;
-    int width;
-    int height;
+    Framework::Text marginLeft;
+    Framework::Text marginRight;
+    Framework::Text marginTop;
+    Framework::Text marginBottom;
+    Framework::Text width;
+    Framework::Text height;
     Framework::Text alignLeft;
     Framework::Text alignRight;
     Framework::Text alignTop;
     Framework::Text alignBottom;
+    Framework::Text style;
 
 public:
     UIElement();
     void setId(const Framework::Text& id);
     const Framework::Text& getId() const;
-    void setMarginLeft(int marginLeft);
-    int getMarginLeft() const;
-    void setMarginRight(int marginRight);
-    int getMarginRight() const;
-    void setMarginTop(int marginTop);
-    int getMarginTop() const;
-    void setMarginBottom(int marginBottom);
-    int getMarginBottom() const;
-    void setWidth(int width);
-    int getWidth() const;
-    void setHeight(int height);
-    int getHeight() const;
+    void setMarginLeft(const Framework::Text& marginLeft);
+    const Framework::Text& getMarginLeft() const;
+    void setMarginRight(const Framework::Text& marginRight);
+    const Framework::Text& getMarginRight() const;
+    void setMarginTop(const Framework::Text& marginTop);
+    const Framework::Text& getMarginTop() const;
+    void setMarginBottom(const Framework::Text& marginBottom);
+    const Framework::Text& getMarginBottom() const;
+    void setWidth(const Framework::Text& width);
+    const Framework::Text& getWidth() const;
+    void setHeight(const Framework::Text& height);
+    const Framework::Text& getHeight() const;
     void setAlignLeft(const Framework::Text& alignLeft);
     const Framework::Text& getAlignLeft() const;
     void setAlignRight(const Framework::Text& alignRight);
@@ -49,6 +50,8 @@ public:
     const Framework::Text& getAlignTop() const;
     void setAlignBottom(const Framework::Text& alignBottom);
     const Framework::Text& getAlignBottom() const;
+    void setStyle(const Framework::Text& style);
+    const Framework::Text& getStyle() const;
     virtual Framework::XML::Element* toUIML(
         Framework::Either<Block*, Entity*> zTarget, Entity* zActor) const;
 };
@@ -82,19 +85,43 @@ public:
             ->withRequiredNumber("marginLeft")
             ->withDefault(0.0)
             ->finishNumber()
+            ->withRequiredString("marginLeft")
+            ->whichEndsWithMatch("%")
+            ->finishString()
             ->withRequiredNumber("marginRight")
             ->withDefault(0.0)
             ->finishNumber()
+            ->withRequiredString("marginRight")
+            ->whichEndsWithMatch("%")
+            ->finishString()
             ->withRequiredNumber("marginTop")
             ->withDefault(0.0)
             ->finishNumber()
+            ->withRequiredString("marginTop")
+            ->whichEndsWithMatch("%")
+            ->finishString()
             ->withRequiredNumber("marginBottom")
             ->withDefault(0.0)
             ->finishNumber()
+            ->withRequiredString("marginBottom")
+            ->whichEndsWithMatch("%")
+            ->finishString()
             ->withRequiredNumber("width")
             ->finishNumber()
+            ->withRequiredString("width")
+            ->whichEndsWithMatch("%")
+            ->finishString()
+            ->withRequiredString("width")
+            ->withExactMatch("auto")
+            ->finishString()
             ->withRequiredNumber("height")
             ->finishNumber()
+            ->withRequiredString("height")
+            ->whichEndsWithMatch("%")
+            ->finishString()
+            ->withRequiredString("height")
+            ->withExactMatch("auto")
+            ->finishString()
             ->withRequiredString("alignLeft")
             ->withDefault("")
             ->finishString()
@@ -106,6 +133,9 @@ public:
             ->finishString()
             ->withRequiredString("alignBottom")
             ->withDefault("")
+            ->finishString()
+            ->withRequiredString("style")
+            ->withDefault("")
             ->finishString();
     }
 
@@ -113,17 +143,70 @@ public:
     {
         T* result = createElement(zJson);
         result->setId(zJson->zValue("id")->asString()->getString());
-        result->setMarginLeft(
-            (int)zJson->zValue("marginLeft")->asNumber()->getNumber());
-        result->setMarginRight(
-            (int)zJson->zValue("marginRight")->asNumber()->getNumber());
-        result->setMarginTop(
-            (int)zJson->zValue("marginTop")->asNumber()->getNumber());
-        result->setMarginBottom(
-            (int)zJson->zValue("marginBottom")->asNumber()->getNumber());
-        result->setWidth((int)zJson->zValue("width")->asNumber()->getNumber());
-        result->setHeight(
-            (int)zJson->zValue("height")->asNumber()->getNumber());
+        if (zJson->zValue("marginLeft")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setMarginLeft(
+                (int)zJson->zValue("marginLeft")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setMarginLeft(
+                zJson->zValue("marginLeft")->asString()->getString());
+        }
+        if (zJson->zValue("marginRight")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setMarginRight(
+                (int)zJson->zValue("marginRight")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setMarginRight(
+                zJson->zValue("marginRight")->asString()->getString());
+        }
+        if (zJson->zValue("marginTop")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setMarginTop(
+                (int)zJson->zValue("marginTop")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setMarginTop(
+                zJson->zValue("marginTop")->asString()->getString());
+        }
+        if (zJson->zValue("marginBottom")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setMarginBottom(
+                (int)zJson->zValue("marginBottom")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setMarginBottom(
+                zJson->zValue("marginBottom")->asString()->getString());
+        }
+        if (zJson->zValue("width")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setWidth(
+                (int)zJson->zValue("width")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setWidth(zJson->zValue("width")->asString()->getString());
+        }
+        if (zJson->zValue("height")->getType()
+            == Framework::AbstractType::NUMBER)
+        {
+            result->setHeight(
+                (int)zJson->zValue("height")->asNumber()->getNumber());
+        }
+        else
+        {
+            result->setHeight(zJson->zValue("height")->asString()->getString());
+        }
         result->setAlignLeft(
             zJson->zValue("alignLeft")->asString()->getString());
         result->setAlignRight(
@@ -131,6 +214,7 @@ public:
         result->setAlignTop(zJson->zValue("alignTop")->asString()->getString());
         result->setAlignBottom(
             zJson->zValue("alignBottom")->asString()->getString());
+        result->setStyle(zJson->zValue("style")->asString()->getString());
         return result;
     }
 
@@ -140,17 +224,17 @@ public:
         result->addValue(
             "id", new Framework::JSON::JSONString(zObject->getId()));
         result->addValue("marginLeft",
-            new Framework::JSON::JSONNumber(zObject->getMarginLeft()));
+            new Framework::JSON::JSONString(zObject->getMarginLeft()));
         result->addValue("marginRight",
-            new Framework::JSON::JSONNumber(zObject->getMarginRight()));
+            new Framework::JSON::JSONString(zObject->getMarginRight()));
         result->addValue("marginTop",
-            new Framework::JSON::JSONNumber(zObject->getMarginTop()));
+            new Framework::JSON::JSONString(zObject->getMarginTop()));
         result->addValue("marginBottom",
-            new Framework::JSON::JSONNumber(zObject->getMarginBottom()));
+            new Framework::JSON::JSONString(zObject->getMarginBottom()));
         result->addValue(
-            "width", new Framework::JSON::JSONNumber(zObject->getWidth()));
+            "width", new Framework::JSON::JSONString(zObject->getWidth()));
         result->addValue(
-            "height", new Framework::JSON::JSONNumber(zObject->getHeight()));
+            "height", new Framework::JSON::JSONString(zObject->getHeight()));
         result->addValue("alignLeft",
             new Framework::JSON::JSONString(zObject->getAlignLeft()));
         result->addValue("alignRight",
@@ -159,6 +243,8 @@ public:
             new Framework::JSON::JSONString(zObject->getAlignTop()));
         result->addValue("alignBottom",
             new Framework::JSON::JSONString(zObject->getAlignBottom()));
+        result->addValue(
+            "style", new Framework::JSON::JSONString(zObject->getStyle()));
         return result;
     }
 

+ 10 - 3
FactoryCraft/UIInventory.cpp

@@ -6,7 +6,8 @@ UIInventoryElement::UIInventoryElement()
     : UIElement(),
       reference(nullptr),
       rowSize(0),
-      numSlots(0)
+      numSlots(0),
+      hasFilter(0)
 {}
 
 UIInventoryElement::~UIInventoryElement()
@@ -55,6 +56,7 @@ void UIInventoryElement::setSlotNameFilter(
     const Framework::Text& slotNameFilter)
 {
     this->slotNameFilter = slotNameFilter;
+    hasFilter = 1;
 }
 
 const Framework::Text& UIInventoryElement::getSlotNameFilter() const
@@ -62,6 +64,11 @@ const Framework::Text& UIInventoryElement::getSlotNameFilter() const
     return slotNameFilter;
 }
 
+bool UIInventoryElement::hasSlotNameFilter() const
+{
+    return hasFilter;
+}
+
 Framework::XML::Element* UIInventoryElement::toUIML(
     Framework::Either<Block*, Entity*> zTarget, Entity* zActor) const
 {
@@ -74,7 +81,7 @@ Framework::XML::Element* UIInventoryElement::toUIML(
     }
     result->setAttribute("rowSize", rowSize);
     result->setAttribute("numSlots", numSlots);
-    if (slotNameFilter.getLength())
+    if (hasFilter)
     {
         result->setAttribute("slotNameFilter", slotNameFilter);
     }
@@ -132,7 +139,7 @@ Framework::JSON::JSONObject* UIInventoryElementFactory::toJsonObject(
         "rowSize", new Framework::JSON::JSONNumber(zObject->getRowSize()));
     result->addValue(
         "numSlots", new Framework::JSON::JSONNumber(zObject->getNumSlots()));
-    if (zObject->getSlotNameFilter().getLength() > 0)
+    if (zObject->hasSlotNameFilter())
     {
         result->addValue("slotNameFilter",
             new Framework::JSON::JSONString(zObject->getSlotNameFilter()));

+ 2 - 0
FactoryCraft/UIInventory.h

@@ -11,6 +11,7 @@ private:
     int rowSize;
     int numSlots;
     Framework::Text slotNameFilter;
+    bool hasFilter;
 
 public:
     UIInventoryElement();
@@ -23,6 +24,7 @@ public:
     int getNumSlots() const;
     void setSlotNameFilter(const Framework::Text& slotNameFilter);
     const Framework::Text& getSlotNameFilter() const;
+    bool hasSlotNameFilter() const;
     Framework::XML::Element* toUIML(Framework::Either<Block*, Entity*> zTarget,
         Entity* zActor) const override;
 };

+ 7 - 1
Windows Version/Windows Version.vcxproj

@@ -210,6 +210,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\GrantCommand.cpp" />
     <ClCompile Include="..\FactoryCraft\Grass.cpp" />
     <ClCompile Include="..\FactoryCraft\InformationObserver.cpp" />
+    <ClCompile Include="..\FactoryCraft\InteractionConfig.cpp" />
     <ClCompile Include="..\FactoryCraft\Inventory.cpp" />
     <ClCompile Include="..\FactoryCraft\Item.cpp" />
     <ClCompile Include="..\FactoryCraft\ItemEntity.cpp" />
@@ -235,6 +236,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\NoBlock.cpp" />
     <ClCompile Include="..\FactoryCraft\Noise.cpp" />
     <ClCompile Include="..\FactoryCraft\NoiseInterpolator.cpp" />
+    <ClCompile Include="..\FactoryCraft\OpenDialogInteractionConfig.cpp" />
     <ClCompile Include="..\FactoryCraft\PlaceableProof.cpp" />
     <ClCompile Include="..\FactoryCraft\Player.cpp" />
     <ClCompile Include="..\FactoryCraft\PlayerHand.cpp" />
@@ -261,7 +263,9 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\TreeSeblingBlock.cpp" />
     <ClCompile Include="..\FactoryCraft\TreeTemplate.cpp" />
     <ClCompile Include="..\FactoryCraft\GrowingPlant.cpp" />
-    <ClCompile Include="..\FactoryCraft\TypeRegistry.cpp" />
+    <ClCompile Include="..\FactoryCraft\TypeRegistry.cpp">
+      <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/bigobj</AdditionalOptions>
+    </ClCompile>
     <ClCompile Include="..\FactoryCraft\UIController.cpp" />
     <ClCompile Include="..\FactoryCraft\UICraftingGrid.cpp" />
     <ClCompile Include="..\FactoryCraft\UIDialog.cpp" />
@@ -330,6 +334,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\Grass.h" />
     <ClInclude Include="..\FactoryCraft\Chat.h" />
     <ClInclude Include="..\FactoryCraft\InformationObserver.h" />
+    <ClInclude Include="..\FactoryCraft\InteractionConfig.h" />
     <ClInclude Include="..\FactoryCraft\Inventory.h" />
     <ClInclude Include="..\FactoryCraft\Item.h" />
     <ClInclude Include="..\FactoryCraft\ItemEntity.h" />
@@ -356,6 +361,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\Noise.h" />
     <ClInclude Include="..\FactoryCraft\NoBlock.h" />
     <ClInclude Include="..\FactoryCraft\NoiseInterpolator.h" />
+    <ClInclude Include="..\FactoryCraft\OpenDialogInteractionConfig.h" />
     <ClInclude Include="..\FactoryCraft\PlaceableProof.h" />
     <ClInclude Include="..\FactoryCraft\Player.h" />
     <ClInclude Include="..\FactoryCraft\PlayerHand.h" />

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

@@ -118,6 +118,9 @@
     <Filter Include="UI\UIElements">
       <UniqueIdentifier>{327469e3-2704-4e52-b31a-2e3823386ad9}</UniqueIdentifier>
     </Filter>
+    <Filter Include="interaction">
+      <UniqueIdentifier>{7d7c98b1-2773-42e7-8c68-6cdb7519fa0f}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -465,6 +468,12 @@
     <ClCompile Include="..\FactoryCraft\UIReference.cpp">
       <Filter>UI\UIElements</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\InteractionConfig.cpp">
+      <Filter>interaction</Filter>
+    </ClCompile>
+    <ClCompile Include="..\FactoryCraft\OpenDialogInteractionConfig.cpp">
+      <Filter>interaction</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -830,5 +839,11 @@
     <ClInclude Include="..\FactoryCraft\UIReference.h">
       <Filter>UI\UIElements</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\InteractionConfig.h">
+      <Filter>interaction</Filter>
+    </ClInclude>
+    <ClInclude Include="..\FactoryCraft\OpenDialogInteractionConfig.h">
+      <Filter>interaction</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 72 - 0
Windows Version/data/blocks/blockTypes.json

@@ -1209,6 +1209,78 @@
         "pullPriority": 30,
         "pushPriority": 30
       }
+    ],
+    "interactions": [
+      {
+        "type": "OpenDialogInteractionConfig",
+        "dialogElement": {
+          "type": "dialog",
+          "id": "chest_inventory",
+          "title": "Chest",
+          "width": 610,
+          "height": 480,
+          "children": [
+            {
+              "type": "inventory",
+              "id": "chest_inventory",
+              "marginBottom": 18,
+              "alignBottom": "player_label",
+              "alignLeft": "start",
+              "marginLeft": 9,
+              "width": 592,
+              "height": 172,
+              "rowSize": 10,
+              "numSlots": 30,
+              "slotNameFilter": "",
+              "reference": {
+                "type": "target"
+              }
+            },
+            {
+              "type": "text",
+              "id": "player_label",
+              "width": "100%",
+              "height": "auto",
+              "style": "7003",
+              "marginBottom": 9,
+              "alignBottom": "player_inventory",
+              "text": "Player Inventory"
+            },
+            {
+              "type": "inventory",
+              "id": "player_inventory",
+              "marginBottom": 18,
+              "alignBottom": "item_bar",
+              "alignLeft": "start",
+              "marginLeft": 9,
+              "width": 592,
+              "height": 172,
+              "rowSize": 10,
+              "numSlots": 30,
+              "slotNameFilter": "Inventory",
+              "reference": {
+                "type": "actor"
+              }
+            },
+            {
+              "type": "inventory",
+              "id": "item_bar",
+              "marginBottom": 9,
+              "alignBottom": "end",
+              "alignLeft": "start",
+              "marginLeft": 9,
+              "width": 592,
+              "height": 52,
+              "rowSize": 10,
+              "numSlots": 10,
+              "slotNameFilter": "ItemBar",
+              "reference": {
+                "type": "actor"
+              }
+            }
+          ]
+        }
+      }
     ]
   },
   {