Sfoglia il codice sorgente

add more quest requirements

Kolja Strohm 1 settimana fa
parent
commit
650fa40b98

+ 1 - 2
FactoryCraft/BasicTool.cpp

@@ -137,8 +137,7 @@ JSONObjectValidationBuilder* XPBasedLevelUpRuleFactory::addToValidator(
         ->whichIsGreaterThen(0.0)
         ->withDefault(1.0)
         ->finishNumber()
-        ->withRequiredNumber("maxLevel")
-        ->whichIsOptional()
+        ->withOptionalNumber("maxLevel")
         ->finishNumber();
 }
 

+ 9 - 0
FactoryCraft/Block.cpp

@@ -7,6 +7,7 @@
 #include "ItemType.h"
 #include "MultiblockStructure.h"
 #include "PlaceableProof.h"
+#include "Quest.h"
 #include "TickQueue.h"
 #include "WorldGenerator.h"
 
@@ -39,6 +40,10 @@ void Block::onDestroy(Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill)
 {
     if (!deadAndRemoved)
     {
+        Game::INSTANCE->zQuestManager()->processEvent(new QuestEventBlockBreak(
+            zActor ? dynamic_cast<Entity*>(zActor->getThis()) : 0,
+            typeId,
+            zUsedItem ? zUsedItem->getTypeId() : ItemTypeEnum::PLAYER_HAND));
         for (int i = 0; i < 6; i++)
         {
             Framework::Vec3<int> pos
@@ -240,6 +245,10 @@ void Block::sendModelInfo(NetworkMessage* zMessage)
 
 bool Block::interact(Item* zItem, Entity* zActor, bool& itemChanged)
 {
+    Game::INSTANCE->zQuestManager()->processEvent(new QuestEventBlockInteract(
+        zActor ? dynamic_cast<Entity*>(zActor->getThis()) : 0,
+        typeId,
+        zItem ? zItem->getTypeId() : ItemTypeEnum::PLAYER_HAND));
     bool result = 0;
     for (InteractionConfig* config : zBlockType()->getInteractionConfigs())
     {

+ 29 - 0
FactoryCraft/Entity.cpp

@@ -10,6 +10,7 @@
 #include "ItemSkill.h"
 #include "ItemStack.h"
 #include "ItemType.h"
+#include "Quest.h"
 
 ActionTarget::ActionTarget(Framework::Vec3<int> blockPos, Direction blockSide)
     : blockPos(blockPos),
@@ -107,6 +108,10 @@ bool ActionTarget::placeBlock(Entity* zActor, Item* zItem)
                 zItem);
             if (block)
             {
+                Game::INSTANCE->zQuestManager()->processEvent(
+                    new QuestEventBlockPlace(
+                        zActor ? dynamic_cast<Entity*>(zActor->getThis()) : 0,
+                        block->zBlockType()->getId()));
                 Game::INSTANCE->zDimension(zActor->getDimensionId())
                     ->placeBlock(block->getPos(), block);
                 zItem->onPlaced();
@@ -863,6 +868,18 @@ bool Entity::isCollidingWithBlock(
     return false;
 }
 
+void Entity::addItems(ItemStack* zItems, Direction dir, ItemFilter* zFilter)
+{
+    int size = zItems->getSize();
+    int type = zItems->zItem() ? zItems->zItem()->getTypeId() : -1;
+    Inventory::addItems(zItems, dir, zFilter);
+    if (zItems->getSize() < size && type >= 0)
+    {
+        Game::INSTANCE->zQuestManager()->processEvent(new QuestEventItemPickup(
+            dynamic_cast<Entity*>(getThis()), type, size - zItems->getSize()));
+    }
+}
+
 void Entity::prepareTick(const Dimension* zDimension, double seconds) {}
 
 void Entity::tick(const Dimension* zDimension, double seconds)
@@ -1608,3 +1625,15 @@ float Entity::getRotation() const
 {
     return rotation;
 }
+
+void Entity::addItems(ItemSlot* zSlot, ItemStack* zItems, Direction dir)
+{
+    int size = zItems->getSize();
+    int type = zItems->zItem() ? zItems->zItem()->getTypeId() : -1;
+    Inventory::addItems(zSlot, zItems, dir);
+    if (zItems->getSize() < size && type >= 0)
+    {
+        Game::INSTANCE->zQuestManager()->processEvent(new QuestEventItemPickup(
+            dynamic_cast<Entity*>(getThis()), type, size - zItems->getSize()));
+    }
+}

+ 3 - 0
FactoryCraft/Entity.h

@@ -109,6 +109,8 @@ protected:
     bool isCollidingWithBlock(const Dimension* zDimension);
     bool isCollidingWithBlock(
         const Dimension* zDimension, int x, int y, int xl, int yl);
+    void addItems(
+        ItemStack* zItems, Direction dir, ItemFilter* zFilter) override;
 
 public:
     virtual void prepareTick(const Dimension* zDimension, double seconds);
@@ -161,6 +163,7 @@ public:
     double getHitDistance(Framework::Vec3<float> rayOrigin,
         Framework::Vec3<float> rayDirection) const;
     float getRotation() const;
+    void addItems(ItemSlot* zSlot, ItemStack* zItems, Direction dir) override;
 
     friend EntityType;
 };

+ 1 - 2
FactoryCraft/EntityGenerator.cpp

@@ -116,8 +116,7 @@ JSONObjectValidationBuilder* EntityGeneratorFactory::addToValidator(
     }
     // TODO: add EntityTypeNameFactory
     return builder->withRequiredAttribute("noise", JNoise::getValidator(true))
-        ->withRequiredNumber("threshold")
-        ->whichIsOptional()
+        ->withOptionalNumber("threshold")
         ->whichIsGreaterOrEqual(0.0)
         ->whichIsLessOrEqual(1.0)
         ->finishNumber()

+ 7 - 0
FactoryCraft/Game.cpp

@@ -412,8 +412,15 @@ void Game::thread()
     double tickTime = 0;
     double sleepTime = 0;
     int nextTimeSync = MAX_TICKS_PER_SECOND;
+    int ticktToNextUpdate = MAX_TICKS_PER_SECOND * 5;
     while (!stop)
     {
+        ticktToNextUpdate--;
+        if (ticktToNextUpdate <= 0)
+        {
+            questManager->processEvent(new QuestEventTimeUpdate());
+            ticktToNextUpdate = MAX_TICKS_PER_SECOND * 5;
+        }
         m.messungStart();
         ticker->nextTick();
         actionsCs.lock();

+ 3 - 6
FactoryCraft/GeneratorRule.h

@@ -125,16 +125,13 @@ public:
                     ->getValidator<JBoolExpression>(),
                 false,
                 true)
-            ->withRequiredNumber("threshold")
-            ->whichIsOptional()
+            ->withOptionalNumber("threshold")
             ->whichIsGreaterOrEqual(0.0)
             ->whichIsLessOrEqual(1.0)
             ->finishNumber()
-            ->withRequiredString("topLayer")
-            ->whichIsOptional()
+            ->withOptionalString("topLayer")
             ->finishString()
-            ->withRequiredString("bottomLayer")
-            ->whichIsOptional()
+            ->withOptionalString("bottomLayer")
             ->finishString();
     }
 

+ 22 - 2
FactoryCraft/Inventory.cpp

@@ -327,15 +327,35 @@ void Inventory::addSlot(ItemSlot* slot)
     cs.unlock();
 }
 
+int Inventory::countAccessableItems(ItemFilter* zFilter, Direction dir) const
+{
+    int count = 0;
+    if (itemCache)
+    {
+        for (auto slot : *pullSlotsOrder)
+        {
+            if (slot->getNumberOfItems() > 0
+                && (!zFilter || zFilter->matchSourceSlot(slot)))
+            {
+                if (allowPullStack(slot, dir))
+                {
+                    count += slot->getNumberOfItems();
+                }
+            }
+        }
+    }
+    return count;
+}
+
 bool Inventory::allowPullStack(ItemSlot* zSlot, Direction dir) const
 {
-    return pullSlotsOrder;
+    return pullSlotsOrder != 0;
 }
 
 bool Inventory::allowPushStack(
     ItemSlot* zSlot, Direction dir, const Item* zItem, int& count) const
 {
-    return pushSlotsOrder;
+    return pushSlotsOrder != 0;
 }
 
 void Inventory::afterPullStack(

+ 1 - 0
FactoryCraft/Inventory.h

@@ -113,6 +113,7 @@ public:
     void notifyObservers(NetworkMessage* msg);
     const ItemSlot* zSlot(int id) const;
     void addSlot(ItemSlot* slot);
+    int countAccessableItems(ItemFilter* zFilter, Direction dir) const;
     void localTransaction(Framework::Array<ItemSlot*>* zSourceSlots,
         Framework::Array<ItemSlot*>* zTargetSlots,
         ItemFilter* zFilter,

+ 1 - 2
FactoryCraft/ItemType.h

@@ -151,8 +151,7 @@ public:
                 Game::INSTANCE->zTypeRegistry()->getValidator<ModelInfo>())
             ->withRequiredString("name")
             ->finishString()
-            ->withRequiredString("tooltipUIML")
-            ->whichIsOptional()
+            ->withOptionalString("tooltipUIML")
             ->finishString()
             ->withRequiredNumber("maxStack")
             ->withDefault(50)

+ 12 - 24
FactoryCraft/JNoise.cpp

@@ -403,15 +403,12 @@ Validator::DataValidator* JNoise::getValidator(bool optional)
                 ->withRequiredAttribute("seed",
                     Game::INSTANCE->zTypeRegistry()
                         ->getValidator<JFloatExpression>())
-                ->withRequiredString("rotationType3D")
-                ->whichIsOptional()
+                ->withOptionalString("rotationType3D")
                 ->whichIsOneOf({"None", "ImproveXYPlanes", "ImproveXZPlanes"})
                 ->finishString()
-                ->withRequiredNumber("frequency")
-                ->whichIsOptional()
+                ->withOptionalNumber("frequency")
                 ->finishNumber()
-                ->withRequiredString("fractalType")
-                ->whichIsOptional()
+                ->withOptionalString("fractalType")
                 ->whichIsOneOf({"None",
                     "FBm",
                     "Ridged",
@@ -419,22 +416,17 @@ Validator::DataValidator* JNoise::getValidator(bool optional)
                     "DomainWarpProgressive",
                     "DomainWarpIndependent"})
                 ->finishString()
-                ->withRequiredNumber("fractalOctaves")
-                ->whichIsOptional()
+                ->withOptionalNumber("fractalOctaves")
                 ->finishNumber()
-                ->withRequiredNumber("fractalLacunarity")
-                ->whichIsOptional()
+                ->withOptionalNumber("fractalLacunarity")
                 ->finishNumber()
-                ->withRequiredNumber("fractalGain")
-                ->whichIsOptional()
+                ->withOptionalNumber("fractalGain")
                 ->finishNumber()
-                ->withRequiredString("cellularDistanceFunction")
-                ->whichIsOptional()
+                ->withOptionalString("cellularDistanceFunction")
                 ->whichIsOneOf(
                     {"Hybrid", "Manhattan", "EuclideanSq", "Euclidean"})
                 ->finishString()
-                ->withRequiredString("cellularReturnType")
-                ->whichIsOptional()
+                ->withOptionalString("cellularReturnType")
                 ->whichIsOneOf({"CellValue",
                     "Distance",
                     "Distance2",
@@ -443,19 +435,15 @@ Validator::DataValidator* JNoise::getValidator(bool optional)
                     "Distance2Mul",
                     "Distance2Div"})
                 ->finishString()
-                ->withRequiredNumber("cellularJitter")
-                ->whichIsOptional()
+                ->withOptionalNumber("cellularJitter")
                 ->finishNumber()
-                ->withRequiredString("domainWarpType")
-                ->whichIsOptional()
+                ->withOptionalString("domainWarpType")
                 ->whichIsOneOf(
                     {"BasicGrid", "OpenSimplex2", "OpenSimplex2Reduced"})
                 ->finishString()
-                ->withRequiredNumber("domainWarpAmp")
-                ->whichIsOptional()
+                ->withOptionalNumber("domainWarpAmp")
                 ->finishNumber()
-                ->withRequiredNumber("multiplier")
-                ->whichIsOptional()
+                ->withOptionalNumber("multiplier")
                 ->whichIsGreaterThen(0)
                 ->finishNumber()
                 ->finishObject())

+ 1 - 2
FactoryCraft/ModelInfo.cpp

@@ -189,8 +189,7 @@ JSONObjectValidationBuilder* ModelInfoFactory::addToValidator(
         ->whichIsGreaterThen(0)
         ->withDefault(1.0)
         ->finishNumber()
-        ->withRequiredArray("boundingBox")
-        ->whichIsOptional()
+        ->withOptionalArray("boundingBox")
         ->withRequiredSize(3)
         ->addAcceptedNumberInArray()
         ->finishNumber()

+ 39 - 6
FactoryCraft/Quest.cpp

@@ -4,6 +4,7 @@
 #include <Logging.h>
 
 #include "Game.h"
+#include "ItemType.h"
 
 QuestRequirementStorage::QuestRequirementStorage()
     : ReferenceCounter(),
@@ -461,8 +462,25 @@ Quest* QuestType::fromJson(Framework::JSON::JSONObject* zJson) const
             Game::INSTANCE->zTypeRegistry()->fromJson<QuestReward>(
                 rewardsArray->zValue(i)));
     }
-    result->imagePath
-        = zJson->asObject()->zValue("imagePath")->asString()->getString();
+    if (zJson->asObject()->hasValue("imagePath"))
+    {
+        result->imagePath
+            = zJson->asObject()->zValue("imagePath")->asString()->getString();
+    }
+    else if (zJson->asObject()->hasValue("imageItemType"))
+    {
+        result->imagePath = Framework::Text("itemType:")
+                          + zJson->asObject()
+                                ->zValue("imageItemType")
+                                ->asString()
+                                ->getString();
+    }
+    else
+    {
+        result->imagePath
+            = Framework::Text("itemType:")
+            + Game::INSTANCE->zItemType(ItemTypeEnum::PLAYER_HAND)->getName();
+    }
     return result;
 }
 
@@ -517,8 +535,18 @@ Framework::JSON::JSONObject* QuestType::toJsonObject(Quest* zObject) const
         rewardsArray->addValue(Game::INSTANCE->zTypeRegistry()->toJson(reward));
     }
     result->addValue("rewards", rewardsArray);
-    result->addValue(
-        "imagePath", new Framework::JSON::JSONString(zObject->imagePath));
+    if (zObject->imagePath.beginnsWith("itemType:"))
+    {
+        Framework::Text* itemTypeName = zObject->imagePath.getTeilText(9);
+        result->addValue(
+            "imageItemType", new Framework::JSON::JSONString(*itemTypeName));
+        itemTypeName->release();
+    }
+    else
+    {
+        result->addValue(
+            "imagePath", new Framework::JSON::JSONString(zObject->imagePath));
+    }
     return result;
 }
 
@@ -546,8 +574,13 @@ JSONObjectValidationBuilder* QuestType::addToValidator(
         ->addAcceptedTypeInArray(
             Game::INSTANCE->zTypeRegistry()->getValidator<QuestReward>())
         ->finishArray()
-        ->withRequiredString("imagePath")
-        ->finishString();
+        ->withOptionalString("imagePath")
+        ->finishString()
+        ->withRequiredAttribute("imageItemType",
+            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                ItemTypeNameFactory::TYPE_ID),
+            0,
+            1);
 }
 
 QuestCollection::QuestCollection()

+ 75 - 6
FactoryCraft/QuestEvent.cpp

@@ -7,7 +7,10 @@ QuestEvent::QuestEvent(Entity* actingEntity)
 
 QuestEvent::~QuestEvent()
 {
-    actingEntity->release();
+    if (actingEntity)
+    {
+        actingEntity->release();
+    }
 }
 
 Entity* QuestEvent::zActingEntity() const
@@ -15,13 +18,79 @@ Entity* QuestEvent::zActingEntity() const
     return actingEntity;
 }
 
-QuestEventOpenDialog::QuestEventOpenDialog(Entity* actingEntity,
-    	Framework::Text dialogId)
-	: QuestEvent(actingEntity),
-	  dialogId(dialogId)
+QuestEventOpenDialog::QuestEventOpenDialog(
+    Entity* actingEntity, Framework::Text dialogId)
+    : QuestEvent(actingEntity),
+      dialogId(dialogId)
 {}
 
 const Framework::Text& QuestEventOpenDialog::getDialogId() const
 {
-	return dialogId;
+    return dialogId;
+}
+
+QuestEventBlockBreak::QuestEventBlockBreak(
+    Entity* actingEntity, int blockTypeId, int usedItemTypeId)
+    : QuestEvent(actingEntity),
+      blockTypeId(blockTypeId),
+      usedItemTypeId(usedItemTypeId)
+{}
+
+int QuestEventBlockBreak::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+int QuestEventBlockBreak::getUsedItemTypeId() const
+{
+    return usedItemTypeId;
+}
+
+QuestEventBlockPlace::QuestEventBlockPlace(
+    Entity* actingEntity, int blockTypeId)
+    : QuestEvent(actingEntity),
+      blockTypeId(blockTypeId)
+{}
+
+int QuestEventBlockPlace::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+QuestEventBlockInteract::QuestEventBlockInteract(
+    Entity* actingEntity, int blockTypeId, int usedItemTypeId)
+    : QuestEvent(actingEntity),
+      blockTypeId(blockTypeId),
+      usedItemTypeId(usedItemTypeId)
+{}
+
+int QuestEventBlockInteract::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+int QuestEventBlockInteract::getUsedItemTypeId() const
+{
+    return usedItemTypeId;
+}
+
+QuestEventTimeUpdate::QuestEventTimeUpdate()
+    : QuestEvent(0)
+{}
+
+QuestEventItemPickup::QuestEventItemPickup(
+    Entity* actingEntity, int itemTypeId, int amount)
+    : QuestEvent(actingEntity),
+      itemTypeId(itemTypeId),
+      amount(amount)
+{}
+
+int QuestEventItemPickup::getItemTypeId() const
+{
+    return itemTypeId;
+}
+
+int QuestEventItemPickup::getAmount() const
+{
+    return amount;
 }

+ 44 - 8
FactoryCraft/QuestEvent.h

@@ -25,19 +25,55 @@ public:
 };
 
 class QuestEventBlockBreak : public QuestEvent
-{};
+{
+private:
+    int blockTypeId;
+    int usedItemTypeId;
+
+public:
+    QuestEventBlockBreak(
+        Entity* actingEntity, int blockTypeId, int usedItemTypeId);
+    int getBlockTypeId() const;
+    int getUsedItemTypeId() const;
+};
 
 class QuestEventBlockPlace : public QuestEvent
-{};
+{
+private:
+    int blockTypeId;
+
+public:
+    QuestEventBlockPlace(Entity* actingEntity, int blockTypeId);
+    int getBlockTypeId() const;
+};
 
 class QuestEventBlockInteract : public QuestEvent
-{};
+{
+private:
+    int blockTypeId;
+    int usedItemTypeId;
 
-class QuestEventTimeUpdate : public QuestEvent
-{};
+public:
+    QuestEventBlockInteract(
+        Entity* actingEntity, int blockTypeId, int usedItemTypeId);
+    int getBlockTypeId() const;
+    int getUsedItemTypeId() const;
+};
 
-class QuestEventCraft : public QuestEvent
-{};
+class QuestEventTimeUpdate : public QuestEvent
+{
+public:
+    QuestEventTimeUpdate();
+};
 
 class QuestEventItemPickup : public QuestEvent
-{};
+{
+private:
+    int itemTypeId;
+    int amount;
+
+public:
+    QuestEventItemPickup(Entity* actingEntity, int itemTypeId, int amount);
+    int getItemTypeId() const;
+    int getAmount() const;
+};

+ 632 - 0
FactoryCraft/QuestRequirement.cpp

@@ -2,6 +2,10 @@
 
 #include <Fenster.h>
 
+#include "BlockType.h"
+#include "Game.h"
+#include "ItemFilter.h"
+#include "ItemType.h"
 #include "Quest.h"
 
 QuestRequirement::QuestRequirement()
@@ -137,4 +141,632 @@ JSONObjectValidationBuilder* QuestRequirementOpenDialogType::addToValidator(
 const char* QuestRequirementOpenDialogType::getTypeToken() const
 {
     return "open_dialog";
+}
+
+QuestRequirementBlockBreak::QuestRequirementBlockBreak()
+    : QuestRequirement(),
+      blockTypeId(0),
+      amount(1),
+      usedItemTypeId(-1)
+{}
+
+void QuestRequirementBlockBreak::processEvent(
+    QuestEvent* zEvent, QuestStorage* zStorage)
+{
+    QuestEventBlockBreak* event = dynamic_cast<QuestEventBlockBreak*>(zEvent);
+    if (event)
+    {
+        if (event->getBlockTypeId() == blockTypeId)
+        {
+            if (usedItemTypeId != -1
+                && event->getUsedItemTypeId() != usedItemTypeId)
+            {
+                return;
+            }
+            int currentAmount = zStorage->containsKey(getRequirementId())
+                                  ? (int)zStorage->zValue(getRequirementId())
+                                        ->asNumber()
+                                        ->getNumber()
+                                  : 0;
+            currentAmount += 1;
+            zStorage->putValue(getRequirementId(),
+                new Framework::JSON::JSONNumber(currentAmount));
+            if (currentAmount >= amount)
+            {
+                zStorage->zStorage(getRequirementId())->setFullfilled(true);
+            }
+        }
+    }
+}
+
+void QuestRequirementBlockBreak::addRequirementUIML(QuestStorage* zStorage,
+    Framework::XML::Element* zParent,
+    Framework::Text onClickPrefix)
+{
+    Framework::XML::Element* container = new Framework::XML::Element(
+        "<frame width=\"100%\" height=\"50\" display=\"row\" gap=\"10\"/>");
+    container->setAttribute("style",
+        Framework::Text() += (Framework::Fenster::Style::Sichtbar
+                              | Framework::Fenster::Style::Erlaubt));
+    container->setAttribute(
+        "id", Framework::Text("requirement_") += getRequirementId());
+    // TODO: add icon of block
+    auto text
+        = new Framework::XML::Element("<text width=\"auto\" height=\"auto\"/>");
+    text->setAttribute("id",
+        Framework::Text("requirement_description_") += getRequirementId());
+    text->setText(description);
+    container->addChild(text);
+    auto status = new Framework::XML::Element(
+        "<text margin-top=\"10\" width=\"auto\" height=\"auto\"/>");
+    status->setAttribute("align-top", text->getAttributeValue("id"));
+    status->setAttribute("align-x", text->getAttributeValue("id"));
+    if (zStorage->zStorage(getRequirementId())->isFullfilled())
+    {
+        Framework::Text completedText = "Completed (";
+        completedText.append() << amount << "/" << amount << ")";
+        status->setText(completedText.getText());
+        status->setAttribute("text-color", "0xFF00FF00");
+    }
+    else
+    {
+        int currentAmount = zStorage->containsKey(getRequirementId())
+                              ? (int)zStorage->zValue(getRequirementId())
+                                    ->asNumber()
+                                    ->getNumber()
+                              : 0;
+        Framework::Text completedText = "Not completed (";
+        completedText.append() << currentAmount << "/" << amount << ")";
+        status->setAttribute("text-color", "0xFFFF0000");
+    }
+    container->addChild(status);
+    zParent->addChild(container);
+}
+
+void QuestRequirementBlockBreak::setBlockTypeId(int blockTypeId)
+{
+    this->blockTypeId = blockTypeId;
+}
+
+int QuestRequirementBlockBreak::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+void QuestRequirementBlockBreak::setAmount(int amount)
+{
+    this->amount = amount;
+}
+
+int QuestRequirementBlockBreak::getAmount() const
+{
+    return amount;
+}
+
+void QuestRequirementBlockBreak::setUsedItemTypeId(int usedItemTypeId)
+{
+    this->usedItemTypeId = usedItemTypeId;
+}
+
+int QuestRequirementBlockBreak::getUsedItemTypeId() const
+{
+    return usedItemTypeId;
+}
+
+QuestRequirementBlockBreakType::QuestRequirementBlockBreakType()
+    : QuestRequirementFactoryBase()
+{}
+
+QuestRequirementBlockBreak* QuestRequirementBlockBreakType::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new QuestRequirementBlockBreak();
+}
+
+QuestRequirementBlockBreak* QuestRequirementBlockBreakType::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    QuestRequirementBlockBreak* result
+        = QuestRequirementFactoryBase::fromJson(zJson);
+    result->setBlockTypeId(Game::INSTANCE->getBlockTypeId(
+        zJson->zValue("blockType")->asString()->getString()));
+    result->setAmount((int)zJson->zValue("amount")->asNumber()->getNumber());
+    if (zJson->hasValue("usedItemTypeId"))
+    {
+        result->setUsedItemTypeId(Game::INSTANCE->getItemTypeId(
+            zJson->zValue("usedItemType")->asString()->getString()));
+    }
+    else
+    {
+        result->setUsedItemTypeId(-1);
+    }
+    return result;
+}
+
+Framework::JSON::JSONObject* QuestRequirementBlockBreakType::toJsonObject(
+    QuestRequirementBlockBreak* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = QuestRequirementFactoryBase::toJsonObject(zObject);
+    result->addValue("blockType",
+        new Framework::JSON::JSONString(
+            Game::INSTANCE->zBlockType(zObject->getBlockTypeId())->getName()));
+    result->addValue("amount",
+        new Framework::JSON::JSONNumber((double)zObject->getAmount()));
+    if (zObject->getUsedItemTypeId() != -1)
+    {
+        result->addValue("usedItemType",
+            new Framework::JSON::JSONString(
+                Game::INSTANCE->zItemType(zObject->getUsedItemTypeId())
+                    ->getName()));
+    }
+    return result;
+}
+
+JSONObjectValidationBuilder* QuestRequirementBlockBreakType::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return QuestRequirementFactoryBase::addToValidator(builder
+            ->withRequiredAttribute("blockType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    BlockTypeNameFactory::TYPE_ID))
+            ->withRequiredNumber("amount")
+            ->whichIsGreaterOrEqual(1.0)
+            ->withDefault(1.0)
+            ->finishNumber()
+            ->withOptionalAttribute("usedItemType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    ItemTypeNameFactory::TYPE_ID)));
+}
+
+const char* QuestRequirementBlockBreakType::getTypeToken() const
+{
+    return "block_break";
+}
+
+QuestRequirementBlockPlace::QuestRequirementBlockPlace()
+    : QuestRequirement(),
+      blockTypeId(0),
+      amount(1)
+{}
+
+void QuestRequirementBlockPlace::processEvent(
+    QuestEvent* zEvent, QuestStorage* zStorage)
+{
+    QuestEventBlockPlace* event = dynamic_cast<QuestEventBlockPlace*>(zEvent);
+    if (event)
+    {
+        if (event->getBlockTypeId() == blockTypeId)
+        {
+            int currentAmount = zStorage->containsKey(getRequirementId())
+                                  ? (int)zStorage->zValue(getRequirementId())
+                                        ->asNumber()
+                                        ->getNumber()
+                                  : 0;
+            currentAmount += 1;
+            zStorage->putValue(getRequirementId(),
+                new Framework::JSON::JSONNumber(currentAmount));
+            if (currentAmount >= amount)
+            {
+                zStorage->zStorage(getRequirementId())->setFullfilled(true);
+            }
+        }
+    }
+}
+
+void QuestRequirementBlockPlace::addRequirementUIML(QuestStorage* zStorage,
+    Framework::XML::Element* zParent,
+    Framework::Text onClickPrefix)
+{
+    Framework::XML::Element* container = new Framework::XML::Element(
+        "<frame width=\"100%\" height=\"50\" display=\"row\" gap=\"10\"/>");
+    container->setAttribute("style",
+        Framework::Text() += (Framework::Fenster::Style::Sichtbar
+                              | Framework::Fenster::Style::Erlaubt));
+    container->setAttribute(
+        "id", Framework::Text("requirement_") += getRequirementId());
+    // TODO: add icon of block
+    auto text
+        = new Framework::XML::Element("<text width=\"auto\" height=\"auto\"/>");
+    text->setAttribute("id",
+        Framework::Text("requirement_description_") += getRequirementId());
+    text->setText(description);
+    container->addChild(text);
+    auto status = new Framework::XML::Element(
+        "<text margin-top=\"10\" width=\"auto\" height=\"auto\"/>");
+    status->setAttribute("align-top", text->getAttributeValue("id"));
+    status->setAttribute("align-x", text->getAttributeValue("id"));
+    if (zStorage->zStorage(getRequirementId())->isFullfilled())
+    {
+        Framework::Text completedText = "Completed (";
+        completedText.append() << amount << "/" << amount << ")";
+        status->setText(completedText.getText());
+        status->setAttribute("text-color", "0xFF00FF00");
+    }
+    else
+    {
+        int currentAmount = zStorage->containsKey(getRequirementId())
+                              ? (int)zStorage->zValue(getRequirementId())
+                                    ->asNumber()
+                                    ->getNumber()
+                              : 0;
+        Framework::Text completedText = "Not completed (";
+        completedText.append() << currentAmount << "/" << amount << ")";
+        status->setAttribute("text-color", "0xFFFF0000");
+    }
+    container->addChild(status);
+    zParent->addChild(container);
+}
+
+void QuestRequirementBlockPlace::setBlockTypeId(int blockTypeId)
+{
+    this->blockTypeId = blockTypeId;
+}
+
+int QuestRequirementBlockPlace::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+void QuestRequirementBlockPlace::setAmount(int amount)
+{
+    this->amount = amount;
+}
+
+int QuestRequirementBlockPlace::getAmount() const
+{
+    return amount;
+}
+
+QuestRequirementBlockPlaceType::QuestRequirementBlockPlaceType()
+    : QuestRequirementFactoryBase()
+{}
+
+QuestRequirementBlockPlace* QuestRequirementBlockPlaceType::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new QuestRequirementBlockPlace();
+}
+
+QuestRequirementBlockPlace* QuestRequirementBlockPlaceType::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    QuestRequirementBlockPlace* result
+        = QuestRequirementFactoryBase::fromJson(zJson);
+    result->setBlockTypeId(Game::INSTANCE->getBlockTypeId(
+        zJson->zValue("blockType")->asString()->getString()));
+    result->setAmount((int)zJson->zValue("amount")->asNumber()->getNumber());
+    return result;
+}
+
+Framework::JSON::JSONObject* QuestRequirementBlockPlaceType::toJsonObject(
+    QuestRequirementBlockPlace* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = QuestRequirementFactoryBase::toJsonObject(zObject);
+    result->addValue("blockType",
+        new Framework::JSON::JSONString(
+            Game::INSTANCE->zBlockType(zObject->getBlockTypeId())->getName()));
+    result->addValue("amount",
+        new Framework::JSON::JSONNumber((double)zObject->getAmount()));
+    return result;
+}
+
+JSONObjectValidationBuilder* QuestRequirementBlockPlaceType::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return QuestRequirementFactoryBase::addToValidator(builder
+            ->withRequiredAttribute("blockType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    BlockTypeNameFactory::TYPE_ID))
+            ->withRequiredNumber("amount")
+            ->whichIsGreaterOrEqual(1.0)
+            ->withDefault(1.0)
+            ->finishNumber());
+}
+
+const char* QuestRequirementBlockPlaceType::getTypeToken() const
+{
+    return "block_place";
+}
+
+QuestRequirementBlockInteract::QuestRequirementBlockInteract()
+    : QuestRequirement(),
+      blockTypeId(0),
+      itemTypeId(-1)
+{}
+
+void QuestRequirementBlockInteract::processEvent(
+    QuestEvent* zEvent, QuestStorage* zStorage)
+{
+    QuestEventBlockInteract* event
+        = dynamic_cast<QuestEventBlockInteract*>(zEvent);
+    if (event)
+    {
+        if (event->getBlockTypeId() == blockTypeId)
+        {
+            if (itemTypeId != -1 && event->getUsedItemTypeId() != itemTypeId)
+            {
+                return;
+            }
+            QuestRequirementStorage* zRequirementStorage
+                = zStorage->zStorage(getRequirementId());
+            zRequirementStorage->setFullfilled(true);
+        }
+    }
+}
+
+void QuestRequirementBlockInteract::addRequirementUIML(QuestStorage* zStorage,
+    Framework::XML::Element* zParent,
+    Framework::Text onClickPrefix)
+{
+    Framework::XML::Element* container = new Framework::XML::Element(
+        "<frame width=\"100%\" height=\"50\" display=\"row\" gap=\"10\"/>");
+    container->setAttribute("style",
+        Framework::Text() += (Framework::Fenster::Style::Sichtbar
+                              | Framework::Fenster::Style::Erlaubt));
+    container->setAttribute(
+        "id", Framework::Text("requirement_") += getRequirementId());
+    // TODO: add icon of block
+    auto text
+        = new Framework::XML::Element("<text width=\"auto\" height=\"auto\"/>");
+    text->setAttribute("id",
+        Framework::Text("requirement_description_") += getRequirementId());
+    text->setText(description);
+    container->addChild(text);
+    auto status = new Framework::XML::Element(
+        "<text margin-top=\"10\" width=\"auto\" height=\"auto\"/>");
+    status->setAttribute("align-top", text->getAttributeValue("id"));
+    status->setAttribute("align-x", text->getAttributeValue("id"));
+    if (zStorage->zStorage(getRequirementId())->isFullfilled())
+    {
+        status->setText("Completed");
+        status->setAttribute("text-color", "0xFF00FF00");
+    }
+    else
+    {
+        status->setText("Not completed");
+        status->setAttribute("text-color", "0xFFFF0000");
+    }
+    container->addChild(status);
+    zParent->addChild(container);
+}
+
+void QuestRequirementBlockInteract::setBlockTypeId(int blockTypeId)
+{
+    this->blockTypeId = blockTypeId;
+}
+
+int QuestRequirementBlockInteract::getBlockTypeId() const
+{
+    return blockTypeId;
+}
+
+void QuestRequirementBlockInteract::setItemTypeId(int itemTypeId)
+{
+    this->itemTypeId = itemTypeId;
+}
+
+int QuestRequirementBlockInteract::getItemTypeId() const
+{
+    return itemTypeId;
+}
+
+QuestRequirementBlockInteractType::QuestRequirementBlockInteractType()
+    : QuestRequirementFactoryBase()
+{}
+
+QuestRequirementBlockInteract* QuestRequirementBlockInteractType::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new QuestRequirementBlockInteract();
+}
+
+QuestRequirementBlockInteract* QuestRequirementBlockInteractType::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    QuestRequirementBlockInteract* result
+        = QuestRequirementFactoryBase::fromJson(zJson);
+    result->setBlockTypeId(Game::INSTANCE->getBlockTypeId(
+        zJson->zValue("blockType")->asString()->getString()));
+    if (zJson->hasValue("itemType"))
+    {
+        result->setItemTypeId(Game::INSTANCE->getItemTypeId(
+            zJson->zValue("itemType")->asString()->getString()));
+    }
+    else
+    {
+        result->setItemTypeId(-1);
+    }
+    return result;
+}
+
+Framework::JSON::JSONObject* QuestRequirementBlockInteractType::toJsonObject(
+    QuestRequirementBlockInteract* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = QuestRequirementFactoryBase::toJsonObject(zObject);
+    result->addValue("blockType",
+        new Framework::JSON::JSONString(
+            Game::INSTANCE->zBlockType(zObject->getBlockTypeId())->getName()));
+    if (zObject->getItemTypeId() != -1)
+    {
+        result->addValue("itemType",
+            new Framework::JSON::JSONString(
+                Game::INSTANCE->zItemType(zObject->getItemTypeId())
+                    ->getName()));
+    }
+    return result;
+}
+
+JSONObjectValidationBuilder* QuestRequirementBlockInteractType::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return QuestRequirementFactoryBase::addToValidator(builder
+            ->withRequiredAttribute("blockType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    BlockTypeNameFactory::TYPE_ID))
+            ->withOptionalAttribute("itemType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    ItemTypeNameFactory::TYPE_ID)));
+}
+
+const char* QuestRequirementBlockInteractType::getTypeToken() const
+{
+    return "block_interact";
+}
+
+QuestRequirementItemInInventory::QuestRequirementItemInInventory()
+    : QuestRequirement(),
+      itemTypeId(0),
+      amount(1)
+{}
+
+void QuestRequirementItemInInventory::processEvent(
+    QuestEvent* zEvent, QuestStorage* zStorage)
+{
+    QuestEventItemPickup* event = dynamic_cast<QuestEventItemPickup*>(zEvent);
+    if (event)
+    {
+        if (event->getItemTypeId() == itemTypeId)
+        {
+            TypeItemFilter filter;
+            filter.setType(Game::INSTANCE->zItemType(itemTypeId));
+            int current = event->zActingEntity()->countAccessableItems(
+                &filter, Direction::NO_DIRECTION);
+            int currentAmount = zStorage->containsKey(getRequirementId())
+                                  ? (int)zStorage->zValue(getRequirementId())
+                                        ->asNumber()
+                                        ->getNumber()
+                                  : 0;
+            if (currentAmount < current)
+            {
+                currentAmount = current;
+            }
+            zStorage->putValue(getRequirementId(),
+                new Framework::JSON::JSONNumber(currentAmount));
+            if (currentAmount >= amount)
+            {
+                zStorage->zStorage(getRequirementId())->setFullfilled(true);
+            }
+        }
+    }
+}
+
+void QuestRequirementItemInInventory::addRequirementUIML(QuestStorage* zStorage,
+    Framework::XML::Element* zParent,
+    Framework::Text onClickPrefix)
+{
+    Framework::XML::Element* container = new Framework::XML::Element(
+        "<frame width=\"100%\" height=\"50\" display=\"row\" gap=\"10\"/>");
+    container->setAttribute("style",
+        Framework::Text() += (Framework::Fenster::Style::Sichtbar
+                              | Framework::Fenster::Style::Erlaubt));
+    container->setAttribute(
+        "id", Framework::Text("requirement_") += getRequirementId());
+    // TODO: add icon of item
+    auto text
+        = new Framework::XML::Element("<text width=\"auto\" height=\"auto\"/>");
+    text->setAttribute("id",
+        Framework::Text("requirement_description_") += getRequirementId());
+    text->setText(description);
+    container->addChild(text);
+    auto status = new Framework::XML::Element(
+        "<text margin-top=\"10\" width=\"auto\" height=\"auto\"/>");
+    status->setAttribute("align-top", text->getAttributeValue("id"));
+    status->setAttribute("align-x", text->getAttributeValue("id"));
+    if (zStorage->zStorage(getRequirementId())->isFullfilled())
+    {
+        Framework::Text completedText = "Completed (";
+        completedText.append() << amount << "/" << amount << ")";
+        status->setText(completedText.getText());
+        status->setAttribute("text-color", "0xFF00FF00");
+    }
+    else
+    {
+        int currentAmount = zStorage->containsKey(getRequirementId())
+                              ? (int)zStorage->zValue(getRequirementId())
+                                    ->asNumber()
+                                    ->getNumber()
+                              : 0;
+        Framework::Text completedText = "Not completed (";
+        completedText.append() << currentAmount << "/" << amount << ")";
+        status->setAttribute("text-color", "0xFFFF0000");
+    }
+    container->addChild(status);
+    zParent->addChild(container);
+}
+
+void QuestRequirementItemInInventory::setItemTypeId(int itemTypeId)
+{
+    this->itemTypeId = itemTypeId;
+}
+
+int QuestRequirementItemInInventory::getItemTypeId() const
+{
+    return itemTypeId;
+}
+
+void QuestRequirementItemInInventory::setAmount(int amount)
+{
+    this->amount = amount;
+}
+
+int QuestRequirementItemInInventory::getAmount() const
+{
+    return amount;
+}
+
+QuestRequirementItemInInventoryType::QuestRequirementItemInInventoryType()
+    : QuestRequirementFactoryBase()
+{}
+
+QuestRequirementItemInInventory*
+QuestRequirementItemInInventoryType::createValue(
+    Framework::JSON::JSONObject* zJson) const
+{
+    return new QuestRequirementItemInInventory();
+}
+
+QuestRequirementItemInInventory* QuestRequirementItemInInventoryType::fromJson(
+    Framework::JSON::JSONObject* zJson) const
+{
+    QuestRequirementItemInInventory* result
+        = QuestRequirementFactoryBase::fromJson(zJson);
+    result->setItemTypeId(Game::INSTANCE->getItemTypeId(
+        zJson->zValue("itemType")->asString()->getString()));
+    result->setAmount((int)zJson->zValue("amount")->asNumber()->getNumber());
+    return result;
+}
+
+Framework::JSON::JSONObject* QuestRequirementItemInInventoryType::toJsonObject(
+    QuestRequirementItemInInventory* zObject) const
+{
+    Framework::JSON::JSONObject* result
+        = QuestRequirementFactoryBase::toJsonObject(zObject);
+    result->addValue("itemType",
+        new Framework::JSON::JSONString(
+            Game::INSTANCE->zItemType(zObject->getItemTypeId())->getName()));
+    result->addValue("amount",
+        new Framework::JSON::JSONNumber((double)zObject->getAmount()));
+    return result;
+}
+
+JSONObjectValidationBuilder*
+QuestRequirementItemInInventoryType::addToValidator(
+    JSONObjectValidationBuilder* builder) const
+{
+    return QuestRequirementFactoryBase::addToValidator(builder
+            ->withRequiredAttribute("itemType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    ItemTypeNameFactory::TYPE_ID))
+            ->withRequiredNumber("amount")
+            ->whichIsGreaterOrEqual(1.0)
+            ->withDefault(1.0)
+            ->finishNumber());
+}
+
+const char* QuestRequirementItemInInventoryType::getTypeToken() const
+{
+    return "item_in_inventory";
 }

+ 129 - 6
FactoryCraft/QuestRequirement.h

@@ -108,16 +108,139 @@ public:
 };
 
 class QuestRequirementBlockBreak : public QuestRequirement
-{};
+{
+private:
+    int blockTypeId;
+    int amount;
+    int usedItemTypeId;
+
+public:
+    QuestRequirementBlockBreak();
+    void processEvent(QuestEvent* zEvent, QuestStorage* zStorage) override;
+    void addRequirementUIML(QuestStorage* zStorage,
+        Framework::XML::Element* zParent,
+        Framework::Text onClickPrefix) override;
+    void setBlockTypeId(int blockTypeId);
+    int getBlockTypeId() const;
+    void setAmount(int amount);
+    int getAmount() const;
+    void setUsedItemTypeId(int usedItemTypeId);
+    int getUsedItemTypeId() const;
+};
+
+class QuestRequirementBlockBreakType
+    : public QuestRequirementFactoryBase<QuestRequirementBlockBreak>
+{
+public:
+    QuestRequirementBlockBreakType();
+    QuestRequirementBlockBreak* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+    QuestRequirementBlockBreak* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        QuestRequirementBlockBreak* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};
 
 class QuestRequirementBlockPlace : public QuestRequirement
-{};
+{
+private:
+    int blockTypeId;
+    int amount;
+
+public:
+    QuestRequirementBlockPlace();
+    void processEvent(QuestEvent* zEvent, QuestStorage* zStorage) override;
+    void addRequirementUIML(QuestStorage* zStorage,
+        Framework::XML::Element* zParent,
+        Framework::Text onClickPrefix) override;
+    void setBlockTypeId(int blockTypeId);
+    int getBlockTypeId() const;
+    void setAmount(int amount);
+    int getAmount() const;
+};
+
+class QuestRequirementBlockPlaceType
+    : public QuestRequirementFactoryBase<QuestRequirementBlockPlace>
+{
+public:
+    QuestRequirementBlockPlaceType();
+    QuestRequirementBlockPlace* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+    QuestRequirementBlockPlace* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        QuestRequirementBlockPlace* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};
 
 class QuestRequirementBlockInteract : public QuestRequirement
-{};
+{
+    int blockTypeId;
+    int itemTypeId;
+
+public:
+    QuestRequirementBlockInteract();
+    void processEvent(QuestEvent* zEvent, QuestStorage* zStorage) override;
+    void addRequirementUIML(QuestStorage* zStorage,
+        Framework::XML::Element* zParent,
+        Framework::Text onClickPrefix) override;
+    void setBlockTypeId(int blockTypeId);
+    int getBlockTypeId() const;
+    void setItemTypeId(int itemTypeId);
+    int getItemTypeId() const;
+};
 
-class QuestRequirementCraft : public QuestRequirement
-{};
+class QuestRequirementBlockInteractType
+    : public QuestRequirementFactoryBase<QuestRequirementBlockInteract>
+{
+public:
+    QuestRequirementBlockInteractType();
+    QuestRequirementBlockInteract* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+    QuestRequirementBlockInteract* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        QuestRequirementBlockInteract* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};
 
 class QuestRequirementItemInInventory : public QuestRequirement
-{};
+{
+private:
+    int itemTypeId;
+    int amount;
+
+public:
+    QuestRequirementItemInInventory();
+    void processEvent(QuestEvent* zEvent, QuestStorage* zStorage) override;
+    void addRequirementUIML(QuestStorage* zStorage,
+        Framework::XML::Element* zParent,
+        Framework::Text onClickPrefix) override;
+    void setItemTypeId(int itemTypeId);
+    int getItemTypeId() const;
+    void setAmount(int amount);
+    int getAmount() const;
+};
+
+class QuestRequirementItemInInventoryType
+    : public QuestRequirementFactoryBase<QuestRequirementItemInInventory>
+{
+public:
+    QuestRequirementItemInInventoryType();
+    QuestRequirementItemInInventory* createValue(
+        Framework::JSON::JSONObject* zJson) const override;
+    QuestRequirementItemInInventory* fromJson(
+        Framework::JSON::JSONObject* zJson) const override;
+    Framework::JSON::JSONObject* toJsonObject(
+        QuestRequirementItemInInventory* zObject) const override;
+    JSONObjectValidationBuilder* addToValidator(
+        JSONObjectValidationBuilder* builder) const override;
+    const char* getTypeToken() const override;
+};

+ 4 - 0
FactoryCraft/TypeRegistry.cpp

@@ -53,6 +53,10 @@ TypeRegistry::TypeRegistry()
     registerType(new QuestType());
     registerType(new QuestCollectionType());
     registerSubType(new QuestRequirementOpenDialogType());
+    registerSubType(new QuestRequirementBlockBreakType());
+    registerSubType(new QuestRequirementBlockPlaceType());
+    registerSubType(new QuestRequirementBlockInteractType());
+    registerSubType(new QuestRequirementItemInInventoryType());
     registerType(new ItemJsonType());
     registerType(new ItemStackInfoType());
     registerSubType(new QuestRewardGiveItemsType());

+ 1 - 2
FactoryCraft/UIInventory.cpp

@@ -104,8 +104,7 @@ JSONObjectValidationBuilder* UIInventoryElementFactory::addToValidator(
         ->withRequiredNumber("numSlots")
         ->whichIsGreaterThen(0)
         ->finishNumber()
-        ->withRequiredString("slotNameFilter")
-        ->whichIsOptional()
+        ->withOptionalString("slotNameFilter")
         ->finishString();
 }