Bladeren bron

implement dynamic uiml dialogs

Kolja Strohm 9 maanden geleden
bovenliggende
commit
988fdcadf0

+ 3 - 9
FactoryCraft/Chest.cpp

@@ -81,8 +81,6 @@ void Chest::interact(Item* zItem, Entity* zActor)
     {
         userEntityId = zActor->getId();
         open = 1;
-        NetworkMessage* msg = new NetworkMessage();
-        msg->openDialog(getDialogId());
         Text uiml = "";
         uiml.append()
             << "<dialog id=\"" << getDialogId()
@@ -116,13 +114,9 @@ void Chest::interact(Item* zItem, Entity* zActor)
                "slotNameFilter=\"ItemBar\" target=\""
             << zActor->getId() << "\"/>"
             << "</dialog>";
-        int msgSize = 4 + uiml.getLength();
-        char* data = new char[msgSize];
-        *(int*)data = uiml.getLength();
-        memcpy(data + 4, uiml.getText(), uiml.getLength());
-        msg->setMessage(data, msgSize);
-        Game::INSTANCE->sendMessage(msg, zActor);
-        msg = new NetworkMessage();
+        Game::INSTANCE->zUIController()->addDialog(
+            new UIDialog(getDialogId(), zActor->getId(), new Framework::XML::Element(uiml)));
+        NetworkMessage *msg = new NetworkMessage();
         msg->animateBlockBone(getDimensionId(),
             Game::getChunkCenter(getPos().x, getPos().y),
             Chunk::index(Dimension::chunkCoordinates(getPos())),

+ 1 - 1
FactoryCraft/CraftingStorage.cpp

@@ -47,7 +47,7 @@ BasicShapedCrafter::BasicShapedCrafter(
             {
                 NetworkMessage* message = new NetworkMessage();
                 getOutputPreview(message);
-                message->addressGui(id);
+                message->addressUIElement(id);
                 Game::INSTANCE->sendMessage(message, zSource);
             }
         });

+ 2 - 2
FactoryCraft/Entity.cpp

@@ -480,7 +480,7 @@ void Entity::notifyStatusBarObservers(NetworkMessage* msg)
         Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
         if (e)
         {
-            msg->addressGui(observer.getSecond());
+            msg->addressUIElement(observer.getSecond());
             Game::INSTANCE->sendMessage(msg->clone(), e);
         }
         else
@@ -587,7 +587,7 @@ void Entity::api(Framework::StreamReader* zRequest,
             char* guiId = new char[(int)len + 1];
             zRequest->lese(guiId, len);
             guiId[(int)len] = 0;
-            zResponse->addressGui(guiId);
+            zResponse->addressUIElement(guiId);
             addStatusBarObserver(zSource, guiId);
             char* msg = new char[33];
             msg[0] = 0;

+ 6 - 0
FactoryCraft/FactoryCraft.vcxproj

@@ -161,6 +161,7 @@
     <ClInclude Include="PlayerHand.h" />
     <ClInclude Include="PlayerRegister.h" />
     <ClInclude Include="Quest.h" />
+    <ClInclude Include="QuestDialog.h" />
     <ClInclude Include="QuestEvent.h" />
     <ClInclude Include="QuestRequirement.h" />
     <ClInclude Include="QuestReward.h" />
@@ -183,6 +184,8 @@
     <ClInclude Include="TreeTemplate.h" />
     <ClInclude Include="GrowingPlant.h" />
     <ClInclude Include="TypeRegistry.h" />
+    <ClInclude Include="UIController.h" />
+    <ClInclude Include="UIDialog.h" />
     <ClInclude Include="WorldGenerator.h" />
     <ClInclude Include="WorldLoader.h" />
     <ClInclude Include="WorldUpdate.h" />
@@ -251,6 +254,7 @@
     <ClCompile Include="PlayerHand.cpp" />
     <ClCompile Include="PlayerRegister.cpp" />
     <ClCompile Include="Quest.cpp" />
+    <ClCompile Include="QuestDialog.cpp" />
     <ClCompile Include="QuestEvent.cpp" />
     <ClCompile Include="QuestRequirement.cpp" />
     <ClCompile Include="QuestReward.cpp" />
@@ -272,6 +276,8 @@
     <ClCompile Include="TreeTemplate.cpp" />
     <ClCompile Include="GrowingPlant.cpp" />
     <ClCompile Include="TypeRegistry.cpp" />
+    <ClCompile Include="UIController.cpp" />
+    <ClCompile Include="UIDialog.cpp" />
     <ClCompile Include="WorldGenerator.cpp" />
     <ClCompile Include="WorldLoader.cpp" />
     <ClCompile Include="WorldUpdate.cpp" />

+ 21 - 0
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -97,6 +97,9 @@
     <Filter Include="quests">
       <UniqueIdentifier>{5e0c1d26-c9fa-4935-9e93-67915bd2c0a9}</UniqueIdentifier>
     </Filter>
+    <Filter Include="UI">
+      <UniqueIdentifier>{cfbafb3b-9692-4bf4-b38c-937cf0d3ddd2}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Chunk.h">
@@ -372,6 +375,15 @@
     <ClInclude Include="BlockInfoCommand.h">
       <Filter>chat\commands</Filter>
     </ClInclude>
+    <ClInclude Include="UIController.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="UIDialog.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="QuestDialog.h">
+      <Filter>quests</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -635,5 +647,14 @@
     <ClCompile Include="BlockInfoCommand.cpp">
       <Filter>chat\commands</Filter>
     </ClCompile>
+    <ClCompile Include="UIController.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="UIDialog.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="QuestDialog.cpp">
+      <Filter>quests</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 16 - 6
FactoryCraft/Game.cpp

@@ -304,6 +304,7 @@ Game::Game(Framework::Text name, Framework::Text worldsDir)
       loader(0),
       chat(0),
       playerRegister(new PlayerRegister(path)),
+      uiController(new UIController()),
       totalTickTime(0),
       tickCounter(0),
       averageTickTime(0),
@@ -332,6 +333,7 @@ Game::~Game()
     chat->release();
     playerRegister->release();
     typeRegistry->release();
+    uiController->release();
 }
 
 void Game::initialize()
@@ -385,6 +387,7 @@ void Game::thread()
         {
             if (!player->isOnline())
             {
+                uiController->removePlayerDialogs(player->zEntity()->getId());
                 chat->removeObserver(player->zEntity()->getId());
                 chat->broadcastMessage(
                     Framework::Text(player->zEntity()->getName())
@@ -603,12 +606,9 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
                 StaticRegistry<ItemType>::INSTANCE.zElement(id));
             Text dialogId = "crafting_";
             dialogId += id;
-            response->openDialog(dialogId);
-            int msgSize = 4 + uiml.getLength();
-            char* msg = new char[msgSize];
-            *(int*)msg = uiml.getLength();
-            memcpy(msg + 4, uiml.getText(), uiml.getLength());
-            response->setMessage(msg, msgSize);
+            uiController
+                ->addDialog(new UIDialog(
+                    dialogId, zOrigin->zEntity()->getId(), new Framework::XML::Element(uiml)));
             break;
         }
     case 6:
@@ -627,6 +627,11 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
             }
             break;
         }
+    case 8: // ui message
+        {
+            uiController->api(zRequest, response, zOrigin->zEntity());
+            break;
+        }
     default:
         std::cout << "received unknown api request in game with type "
                   << (int)type << "\n";
@@ -1065,6 +1070,11 @@ QuestManager* Game::zQuestManager() const
     return questManager;
 }
 
+UIController* Game::zUIController() const
+{
+    return uiController;
+}
+
 double Game::getAverageTickTime() const
 {
     return averageTickTime;

+ 3 - 0
FactoryCraft/Game.h

@@ -20,6 +20,7 @@
 #include "WorldGenerator.h"
 #include "WorldLoader.h"
 #include "WorldUpdate.h"
+#include "UIController.h"
 
 class FCKlient;
 
@@ -94,6 +95,7 @@ private:
     RecipieLoader recipies;
     Chat* chat;
     PlayerRegister* playerRegister;
+    UIController* uiController;
     double totalTickTime;
     int tickCounter;
     double averageTickTime;
@@ -152,6 +154,7 @@ public:
     TypeRegistry* zTypeRegistry() const;
     int getPlayerId(const char* name) const;
     QuestManager* zQuestManager() const;
+    UIController* zUIController() const;
 
     double getAverageTickTime() const;
     int getTicksPerSecond() const;

+ 3 - 3
FactoryCraft/Inventory.cpp

@@ -440,7 +440,7 @@ void Inventory::notifyObservers(NetworkMessage* msg)
         Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
         if (e)
         {
-            msg->addressGui(observer.getSecond());
+            msg->addressUIElement(observer.getSecond());
             Game::INSTANCE->sendMessage(msg->clone(), e);
         }
         else
@@ -720,7 +720,7 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char* id = new char[idLen + 1];
             zRequest->lese(id, idLen);
             id[(int)idLen] = 0;
-            zResponse->addressGui(id);
+            zResponse->addressUIElement(id);
             addObserver(zSource, id);
             delete[] id;
             char filterLen;
@@ -788,7 +788,7 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char* id = new char[idLen + 1];
             zRequest->lese(id, idLen);
             id[(int)idLen] = 0;
-            zResponse->addressGui(id);
+            zResponse->addressUIElement(id);
             delete[] id;
             int slotId;
             zRequest->lese((char*)&slotId, 4);

+ 26 - 3
FactoryCraft/NetworkMessage.cpp

@@ -3,6 +3,7 @@
 #include "Chunk.h"
 #include "Entity.h"
 #include "Game.h"
+#include "ChunkMap.h"
 
 NetworkMessage::NetworkMessage()
     : Framework::ReferenceCounter(),
@@ -76,7 +77,7 @@ void NetworkMessage::addressBlock(const Block* zBlock)
     *(int*)(address + 14) = pos.z;
 }
 
-void NetworkMessage::openDialog(Framework::Text dialogName)
+void NetworkMessage::openDialog(Framework::Text dialogName, Framework::Text uiml)
 {
     delete[] address;
     addressLength = (char)(4 + dialogName.getLength());
@@ -85,16 +86,38 @@ void NetworkMessage::openDialog(Framework::Text dialogName)
     address[1] = 0;                                         // open dialog
     *(short*)(address + 2) = (short)dialogName.getLength(); // block
     memcpy(address + 4, dialogName.getText(), dialogName.getLength());
+    int msgSize = 4 + uiml.getLength();
+    char* msg = new char[msgSize];
+    *(int*)msg = uiml.getLength();
+    memcpy(msg + 4, uiml.getText(), uiml.getLength());
+    setMessage(msg, msgSize);
 }
 
-void NetworkMessage::addressGui(Framework::Text elementId)
+void NetworkMessage::updateDialog(
+    Framework::Text dialogName, Framework::Text uiml)
+{
+    delete[] address;
+    addressLength = (char)(4 + dialogName.getLength());
+    address = new char[addressLength];
+    address[0] = 2;                                         // gui message
+    address[1] = 3;                                         // update dialog
+    *(short*)(address + 2) = (short)dialogName.getLength(); // dialog name
+    memcpy(address + 4, dialogName.getText(), dialogName.getLength());
+    int msgSize = 4 + uiml.getLength();
+    char* msg = new char[msgSize];
+    *(int*)msg = uiml.getLength();
+    memcpy(msg + 4, uiml.getText(), uiml.getLength());
+    setMessage(msg, msgSize);
+}
+
+void NetworkMessage::addressUIElement(Framework::Text elementId)
 {
     delete[] address;
     addressLength = (char)(4 + elementId.getLength());
     address = new char[addressLength];
     address[0] = 2;                                        // gui message
     address[1] = 1;                                        // element message
-    *(short*)(address + 2) = (short)elementId.getLength(); // block
+    *(short*)(address + 2) = (short)elementId.getLength(); // element id
     memcpy(address + 4, elementId.getText(), elementId.getLength());
 }
 

+ 3 - 2
FactoryCraft/NetworkMessage.h

@@ -34,8 +34,9 @@ public:
     void addressEntity(const Entity* zEntity);
     void addressBlock(const Block* zBlock);
     void addressDimension(const Dimension* zDim);
-    void openDialog(Framework::Text dialogName);
-    void addressGui(Framework::Text elementId);
+    void openDialog(Framework::Text dialogName, Framework::Text uiml);
+    void updateDialog(Framework::Text dialogName, Framework::Text uiml);
+    void addressUIElement(Framework::Text elementId);
     void setMessage(char* msg, int length);
     void sendChatMessage(ChatMessage* zMsg);
     void sendChatOptions(ChatObserver* zOptions);

+ 14 - 26
FactoryCraft/Player.cpp

@@ -3,6 +3,8 @@
 #include "Game.h"
 #include "ItemFilter.h"
 #include "PlayerHand.h"
+#include "QuestDialog.h"
+
 
 Player::Player(Framework::Vec3<float> location, int dimensionId, int entityId)
     : Entity(EntityTypeEnum::PLAYER, location, dimensionId, entityId),
@@ -240,7 +242,7 @@ void Player::playerApi(
             zRequest->lese((char*)&leftHandPosition, 4);
             leftHandPosition = leftHandPosition % itemBar.getEintragAnzahl();
             NetworkMessage* msg = new NetworkMessage();
-            msg->addressGui("gui_item_bar");
+            msg->addressUIElement("gui_item_bar");
             char* message = new char[5];
             message[0] = 3; // set selected slot
             *(int*)(message + 1) = leftHandPosition;
@@ -250,17 +252,10 @@ void Player::playerApi(
         }
     case 4:
         {
-            Game::INSTANCE->zQuestManager()->processEvent(
-                new QuestEventOpenDialog(
-                    dynamic_cast<Entity*>(getThis()), "player_inventory"));
-            // open inventory
-            zResponse->openDialog("player_inventory");
-            Text uiml = getInventoryUIML();
-            int msgSize = 4 + uiml.getLength();
-            char* msg = new char[msgSize];
-            *(int*)msg = uiml.getLength();
-            memcpy(msg + 4, uiml.getText(), uiml.getLength());
-            zResponse->setMessage(msg, msgSize);
+            Game::INSTANCE->zUIController()->addDialog(
+                new UIDialog("player_inventory",
+                    getId(),
+                    new Framework::XML::Element(getInventoryUIML())));
             break;
         }
     case 5:
@@ -357,7 +352,7 @@ void Player::playerApi(
     case 8: // request left hand position
         {
             NetworkMessage* msg = new NetworkMessage();
-            msg->addressGui("gui_item_bar");
+            msg->addressUIElement("gui_item_bar");
             char* message = new char[5];
             message[0] = 3; // set selected slot
             *(int*)(message + 1) = leftHandPosition;
@@ -367,20 +362,13 @@ void Player::playerApi(
         }
     case 9: // open quest dialog
         {
-            Game::INSTANCE->zQuestManager()->processEvent(
-                new QuestEventOpenDialog(
-                    dynamic_cast<Entity*>(getThis()), "quests"));
-            zResponse->openDialog("quests");
-            Text uiml = Game::INSTANCE->zQuestManager()->getDialogUIML(this);
-            int msgSize = 4 + uiml.getLength();
-            char* msg = new char[msgSize];
-            *(int*)msg = uiml.getLength();
-            memcpy(msg + 4, uiml.getText(), uiml.getLength());
-            zResponse->setMessage(msg, msgSize);
+            Game::INSTANCE->zUIController()->addDialog(
+                new QuestDialog(getId()));
             break;
         }
     case 10: // request quest graph
         {
+            /*
             unsigned char length;
             zRequest->lese((char*)&length, 1);
             char* collectionName = new char[length + 1];
@@ -400,12 +388,12 @@ void Player::playerApi(
             char* msg = new char[msgSize];
             *(int*)msg = uiml.getLength();
             memcpy(msg + 4, uiml.getText(), uiml.getLength());
-            zResponse->setMessage(msg, msgSize);
+            zResponse->setMessage(msg, msgSize);*/
             break;
         }
     case 11: // request quest view
         {
-            unsigned char length;
+            /* unsigned char length;
             zRequest->lese((char*)&length, 1);
             char* questName = new char[length + 1];
             zRequest->lese(questName, length);
@@ -424,7 +412,7 @@ void Player::playerApi(
             char* msg = new char[msgSize];
             *(int*)msg = uiml.getLength();
             memcpy(msg + 4, uiml.getText(), uiml.getLength());
-            zResponse->setMessage(msg, msgSize);
+            zResponse->setMessage(msg, msgSize);*/
             break;
         }
     }

+ 138 - 94
FactoryCraft/Quest.cpp

@@ -142,6 +142,16 @@ const Framework::Text& QuestStorage::getQuestId() const
     return questId;
 }
 
+bool QuestStorage::isVisible() const
+{
+    return visible;
+}
+
+void QuestStorage::setVisible(bool visible)
+{
+    this->visible = visible;
+}
+
 QuestStorageType::QuestStorageType()
     : TypeFactory<QuestStorage>()
 {}
@@ -320,7 +330,8 @@ Framework::JSON::Validator::JSONValidator* QuestPartyType::getValidator() const
 
 Quest::Quest(Framework::Text questId)
     : ReferenceCounter(),
-      questId(questId)
+      questId(questId),
+      mainQuest(0)
 {}
 
 void Quest::processEvent(QuestEvent* zEvent, QuestStorage* zStorage)
@@ -342,16 +353,38 @@ void Quest::processEvent(QuestEvent* zEvent, QuestStorage* zStorage)
     }
 }
 
-bool Quest::isVisible(QuestParty* zParty)
+bool Quest::isVisible(QuestParty* zParty, QuestManager* zManager)
 {
-    bool visible = 1;
+    if (zParty->zQuestStorage(questId)->isVisible())
+    {
+        return 1;
+    }
+    bool visible = 0;
+    for (Framework::Text* requiredQuestId : requiredQuestsIds)
+    {
+        visible |= zParty->zQuestStorage(*requiredQuestId)->isQuestFinished();
+    }
+    if (visible)
+    {
+        zParty->zQuestStorage(questId)->setVisible(visible);
+    }
     for (Framework::Text* requiredQuestId : requiredQuestsIds)
     {
-        visible &= zParty->zQuestStorage(*requiredQuestId)->isQuestFinished();
+        zManager->setQuestVisible(true, *requiredQuestId, zParty);
     }
     return visible;
 }
 
+bool Quest::isActive(QuestParty* zParty)
+{
+    bool active = 1;
+    for (Framework::Text* requiredQuestId : requiredQuestsIds)
+    {
+        active &= zParty->zQuestStorage(*requiredQuestId)->isQuestFinished();
+    }
+    return active;
+}
+
 void Quest::giveReward(
     int choice, Entity* zTargetEntity, QuestStorage* zStorage)
 {
@@ -368,45 +401,41 @@ const Framework::Text& Quest::getQuestId() const
     return questId;
 }
 
-Framework::Text Quest::getQuestGraphItemUIML(QuestStorage* zStorage)
-{
-    Framework::Text requirements = "";
-    for (Framework::Text* requiredQuest : requiredQuestsIds)
-    {
-        if (requirements.getLength() > 0)
-        {
-            requirements += ",";
-        }
-        requirements += *requiredQuest;
-    }
-
-    Framework::Text result = "<questGraphItem id=\"";
-    result.append() << questId << "\" name=\"" << questName + "\" image=\""
-                    << imagePath << "\" description=\"" << description
-                    << "\" finished=\"" << zStorage->isQuestFinished()
-                    << "\" mainQuest=\"" << mainQuest << "\" requirements=\""
-                    << requirements << "\"/>";
-    return result;
-}
-
 Framework::Text Quest::getQuestViewUIML(QuestStorage* zStorage)
 {
     Framework::Text result = "<questView id=\"";
-    result.append() << questId << "\" name=\"" << questName + "\" description=\"" << description
-					<< "\" finished=\"" << zStorage->isQuestFinished()
-                    << "\" rewarded=\"" << zStorage ->isQuestRewarded() << "\">";
+    result.append() << questId << "\" name=\""
+                    << questName + "\" description=\"" << description
+                    << "\" finished=\"" << zStorage->isQuestFinished()
+                    << "\" rewarded=\"" << zStorage->isQuestRewarded() << "\">";
     for (QuestRequirement* requirement : requirements)
     {
         result += requirement->getRequirementUIML(
-			zStorage->zStorage(requirement->getRequirementId()));
+            zStorage->zStorage(requirement->getRequirementId()));
     }
     for (QuestReward* reward : rewards)
     {
         result += reward->getRewardUIML();
     }
 
-	result += "</questView>";
-	return result;
+    result += "</questView>";
+    return result;
+}
+
+void Quest::setVisible(bool visible, QuestParty* zParty, QuestManager* zManager)
+{
+    if (zParty->zQuestStorage(questId)->isVisible() == visible)
+    {
+        return;
+    }
+    zParty->zQuestStorage(questId)->setVisible(visible);
+    if (visible)
+    {
+        for (Framework::Text* requiredQuestId : requiredQuestsIds)
+        {
+            zManager->setQuestVisible(visible, *requiredQuestId, zParty);
+        }
+    }
 }
 
 QuestType::QuestType()
@@ -421,12 +450,18 @@ Quest* QuestType::fromJson(Framework::JSON::JSONValue* zJson) const
         = zJson->asObject()->zValue("questName")->asString()->getString();
     result->description
         = zJson->asObject()->zValue("description")->asString()->getString();
-    Framework::JSON::JSONArray* requiredQuestIdsArray
+    Framework::JSON::JSONArray* requiredQuestGroups
         = zJson->asObject()->zValue("requiredQuestIds")->asArray();
-    for (int i = 0; i < requiredQuestIdsArray->getLength(); i++)
+    for (int i = 0; i < requiredQuestGroups->getLength(); i++)
     {
-        result->requiredQuestsIds.add(new Framework::Text(
-            requiredQuestIdsArray->zValue(i)->asString()->getString()));
+        Framework::JSON::JSONArray* requiredQuestIdsArray
+            = requiredQuestGroups->zValue(i)->asArray();
+        for (int j = 0; j < requiredQuestIdsArray->getLength(); j++)
+        {
+            result->requiredQuestsIds.add(new Framework::Text(
+                requiredQuestIdsArray->zValue(i)->asString()->getString()));
+            result->requiredQuestsGroups.add(i);
+        }
     }
     Framework::JSON::JSONArray* requirementsArray
         = zJson->asObject()->zValue("requirements")->asArray();
@@ -444,6 +479,8 @@ Quest* QuestType::fromJson(Framework::JSON::JSONValue* zJson) const
             Game::INSTANCE->zTypeRegistry()->fromJson<QuestReward>(
                 rewardsArray->zValue(i)));
     }
+    result->imagePath
+        = zJson->asObject()->zValue("imagePath")->asString()->getString();
     return result;
 }
 
@@ -456,14 +493,34 @@ Framework::JSON::JSONValue* QuestType::toJson(Quest* zObject) const
         "questName", new Framework::JSON::JSONString(zObject->questName));
     result->addValue(
         "description", new Framework::JSON::JSONString(zObject->description));
+    Framework::JSON::JSONArray* requiredQuestGroupArray
+        = new Framework::JSON::JSONArray();
     Framework::JSON::JSONArray* requiredQuestIdsArray
         = new Framework::JSON::JSONArray();
+    int index = 0;
+    int lastGroup = 0;
     for (Framework::Text* requiredQuestId : zObject->requiredQuestsIds)
     {
+        int group = zObject->requiredQuestsGroups.get(index);
+        if (lastGroup != group)
+        {
+            if (requiredQuestIdsArray->getLength())
+            {
+                requiredQuestGroupArray->addValue(requiredQuestIdsArray);
+                requiredQuestIdsArray = new Framework::JSON::JSONArray();
+            }
+            lastGroup = group;
+        }
         requiredQuestIdsArray->addValue(
             new Framework::JSON::JSONString(*requiredQuestId));
+        index++;
+    }
+    if (requiredQuestIdsArray->getLength())
+    {
+        requiredQuestGroupArray->addValue(requiredQuestIdsArray);
+        requiredQuestIdsArray = new Framework::JSON::JSONArray();
     }
-    result->addValue("requiredQuestIds", requiredQuestIdsArray);
+    result->addValue("requiredQuestIds", requiredQuestGroupArray);
     Framework::JSON::JSONArray* requirementsArray
         = new Framework::JSON::JSONArray();
     for (QuestRequirement* requirement : zObject->requirements)
@@ -478,6 +535,8 @@ Framework::JSON::JSONValue* QuestType::toJson(Quest* zObject) const
         rewardsArray->addValue(Game::INSTANCE->zTypeRegistry()->toJson(reward));
     }
     result->addValue("rewards", rewardsArray);
+    result->addValue(
+        "imagePath", new Framework::JSON::JSONString(zObject->imagePath));
     return result;
 }
 
@@ -491,10 +550,12 @@ Framework::JSON::Validator::JSONValidator* QuestType::getValidator() const
         ->withRequiredString("description")
         ->finishString()
         ->withRequiredArray("requiredQuestIds")
-        ->withDefault(new Framework::JSON::JSONArray())
+        ->addAcceptedArrayInArray()
         ->addAcceptedStringInArray()
         ->finishString()
         ->finishArray()
+        ->withDefault(new Framework::JSON::JSONArray())
+        ->finishArray()
         ->withRequiredArray("requirements")
         ->addAcceptedTypeInArray(
             Game::INSTANCE->zTypeRegistry()->getValidator<QuestRequirement>())
@@ -503,6 +564,8 @@ Framework::JSON::Validator::JSONValidator* QuestType::getValidator() const
         ->addAcceptedTypeInArray(
             Game::INSTANCE->zTypeRegistry()->getValidator<QuestReward>())
         ->finishArray()
+        ->withRequiredString("imagePath")
+        ->finishString()
         ->finishObject();
 }
 
@@ -511,24 +574,25 @@ QuestCollection::QuestCollection(Framework::Text name)
       name(name)
 {}
 
-void QuestCollection::processEvent(QuestEvent* zEvent, QuestParty* zParty)
+void QuestCollection::processEvent(
+    QuestEvent* zEvent, QuestParty* zParty)
 {
     for (Quest* quest : quests)
     {
         QuestStorage* zStorage = zParty->zQuestStorage(quest->getQuestId());
-        if (quest->isVisible(zParty) && !zStorage->isQuestFinished())
+        if (quest->isActive(zParty) && !zStorage->isQuestFinished())
         {
             quest->processEvent(zEvent, zStorage);
         }
     }
 }
 
-bool QuestCollection::isVisible(QuestParty* zParty)
+bool QuestCollection::isVisible(QuestParty* zParty, QuestManager* zManager)
 {
     bool visible = 1;
     for (Quest* quest : quests)
     {
-        visible &= quest->isVisible(zParty);
+        visible &= quest->isVisible(zParty, zManager);
     }
     return visible;
 }
@@ -563,32 +627,32 @@ const Framework::Text& QuestCollection::getName() const
     return name;
 }
 
-Framework::Text QuestCollection::getQuestGraphUIML(QuestParty* zParty)
+Framework::Text QuestCollection::getQuestViewUIML(
+    QuestParty* zParty, Framework::Text questId)
 {
-    Framework::Text result = "";
     for (Quest* quest : quests)
     {
-        if (quest->isVisible(zParty))
+        if (quest->getQuestId().istGleich(questId))
         {
             QuestStorage* zStorage = zParty->zQuestStorage(quest->getQuestId());
-            result += quest->getQuestGraphItemUIML(zStorage);
+            return quest->getQuestViewUIML(zStorage);
         }
     }
-    return result;
+    return "";
 }
 
-Framework::Text QuestCollection::getQuestViewUIML(
-    QuestParty* zParty, Framework::Text questId)
+void QuestCollection::setQuestVisible(bool visible,
+    Framework::Text questId,
+    QuestParty* zParty,
+    QuestManager* zManager)
 {
     for (Quest* quest : quests)
     {
         if (quest->getQuestId().istGleich(questId))
         {
-            QuestStorage* zStorage = zParty->zQuestStorage(quest->getQuestId());
-            return quest->getQuestViewUIML(zStorage);
+            return quest->setVisible(visible, zParty, zManager);
         }
     }
-    return "";
 }
 
 QuestCollectionType::QuestCollectionType()
@@ -651,7 +715,22 @@ QuestParty* QuestManager::zParty(int entityId)
             return party;
         }
     }
-    return 0;
+    QuestParty* result = new QuestParty();
+    result->addMember(entityId);
+    parties.add(result);
+    return result;
+}
+
+QuestCollection* QuestManager::zCollection(Framework::Text collectionName)
+{
+    for (QuestCollection* collection : questCollections)
+	{
+		if (collection->getName().istGleich(collectionName))
+		{
+			return collection;
+		}
+	}
+	return 0;
 }
 
 void QuestManager::loadQuests()
@@ -804,58 +883,23 @@ void QuestManager::giveReward(
     }
 }
 
-Framework::Text QuestManager::getDialogUIML(Entity* zTargetEntity)
+Framework::Text QuestManager::getQuestViewUIML(
+    Entity* zTargetEntity, Framework::Text questId)
 {
-    Framework::Text result = "<dialog id=\"quests\" title=\"Quests\" "
-                             "width=\"1200\" height=\"800\">";
-    result.append()
-        << "<listView id=\"collectionList\" width=\"100%\" height=\"100%\" "
-           "member-width=\"200\" member-height=\"40\" align-top=\"start\" "
-           "align-left=\"start\">";
+    Framework::Text result = "";
     QuestParty* party = zParty(zTargetEntity->getId());
     for (QuestCollection* questCollection : questCollections)
     {
-        if (questCollection->isVisible(party))
-        {
-            result.append()
-                << "<listItem id=\"" << questCollection->getName() << "\">"
-                << "<itemTitle>"
-                << "<text>" << questCollection->getName() << "</text>"
-                << "</itemTitle>"
-                << "<itemContent>"
-                << "<questGraph collectionName=\"" << questCollection->getName()
-                << "\"/>"
-                << "</itemContent>"
-                << "</listItem>";
-        }
+        result += questCollection->getQuestViewUIML(party, questId);
     }
-    result.append() << "</listView>"
-                    << "</dialog>";
     return result;
 }
 
-Framework::Text QuestManager::getQuestGraphUIML(
-    Entity* zTargetEntity, Framework::Text collectionName)
+void QuestManager::setQuestVisible(
+    bool visible, Framework::Text questId, QuestParty* zParty)
 {
-    QuestParty* party = zParty(zTargetEntity->getId());
     for (QuestCollection* questCollection : questCollections)
     {
-        if (questCollection->getName().istGleich(collectionName))
-        {
-            return questCollection->getQuestGraphUIML(party);
-        }
+        questCollection->setQuestVisible(visible, questId, zParty, this);
     }
-    return "";
-}
-
-Framework::Text QuestManager::getQuestViewUIML(
-    Entity* zTargetEntity, Framework::Text questId)
-{
-    Framework::Text result = "";
-    QuestParty* party = zParty(zTargetEntity->getId());
-    for (QuestCollection* questCollection : questCollections)
-    {
-        result += questCollection->getQuestViewUIML(party, questId);
-    }
-    return result;
 }

+ 21 - 7
FactoryCraft/Quest.h

@@ -50,6 +50,7 @@ private:
     Framework::Text questId;
     bool finished;
     bool rewarded;
+    bool visible;
     Framework::RCArray<QuestRequirementStorage> requirements;
 
 public:
@@ -61,6 +62,8 @@ public:
     bool isQuestRewarded() const;
     QuestRequirementStorage* zStorage(Framework::Text requirementId);
     const Framework::Text& getQuestId() const;
+    bool isVisible() const;
+    void setVisible(bool visible);
 
     friend QuestStorageType;
 };
@@ -105,6 +108,8 @@ public:
 };
 
 class QuestType;
+class QuestManager;
+class QuestDialog;
 
 class Quest : public virtual Framework::ReferenceCounter
 {
@@ -115,6 +120,7 @@ private:
     Framework::Text imagePath;
     bool mainQuest;
     Framework::RCArray<Framework::Text> requiredQuestsIds;
+    Framework::Array<int> requiredQuestsGroups;
     Framework::RCArray<QuestRequirement> requirements;
     Framework::RCArray<QuestReward> rewards;
 
@@ -122,13 +128,15 @@ public:
     Quest(Framework::Text questId);
 
     void processEvent(QuestEvent* zEvent, QuestStorage* zStorage);
-    bool isVisible(QuestParty* zParty);
+    bool isVisible(QuestParty* zParty, QuestManager* zManager);
+    bool isActive(QuestParty* zParty);
     void giveReward(int choice, Entity* zTargetEntity, QuestStorage* zStorage);
     const Framework::Text& getQuestId() const;
-    Framework::Text getQuestGraphItemUIML(QuestStorage* zStorage);
     Framework::Text getQuestViewUIML(QuestStorage* zStorage);
+    void setVisible(bool visible, QuestParty* zParty, QuestManager* zManager);
 
     friend QuestType;
+    friend QuestDialog;
 };
 
 class QuestType : public TypeFactory<Quest>
@@ -152,7 +160,7 @@ public:
     QuestCollection(Framework::Text name);
 
     void processEvent(QuestEvent* zEvent, QuestParty* zParty);
-    bool isVisible(QuestParty* zParty);
+    bool isVisible(QuestParty* zParty, QuestManager* zManager);
     void giveReward(Framework::Text questId,
         int choice,
         Entity* zTargetEntity,
@@ -160,11 +168,15 @@ public:
     void addQuest(Quest* zQuest);
 
     const Framework::Text& getName() const;
-    Framework::Text getQuestGraphUIML(QuestParty* zParty);
     Framework::Text getQuestViewUIML(
         QuestParty* zParty, Framework::Text questId);
+    void setQuestVisible(bool visible,
+        Framework::Text questId,
+        QuestParty* zParty,
+        QuestManager* zManager);
 
     friend QuestCollectionType;
+    friend QuestDialog;
 };
 
 class QuestCollectionType : public TypeFactory<QuestCollection>
@@ -183,6 +195,7 @@ private:
     Framework::RCArray<QuestParty> parties;
 
     QuestParty* zParty(int entityId);
+    QuestCollection* zCollection(Framework::Text collectionName);
 
 public:
     QuestManager();
@@ -192,9 +205,10 @@ public:
 
     void processEvent(QuestEvent* event);
     void giveReward(Framework::Text questId, int choice, Entity* zTargetEntity);
-    Framework::Text getDialogUIML(Entity* zTargetEntity);
-    Framework::Text getQuestGraphUIML(
-        Entity* zTargetEntity, Framework::Text collectionName);
     Framework::Text getQuestViewUIML(
         Entity* zTargetEntity, Framework::Text questId);
+    void setQuestVisible(
+        bool visible, Framework::Text questId, QuestParty* zParty);
+
+    friend QuestDialog;
 };

+ 134 - 0
FactoryCraft/QuestDialog.cpp

@@ -0,0 +1,134 @@
+#include "QuestDialog.h"
+
+#include "Game.h"
+
+QuestDialog::QuestDialog(int playerId)
+    : UIDialog("quests", playerId, 0)
+{
+
+    QuestManager* zManager = Game::INSTANCE->zQuestManager();
+    Framework::Text uiml = "<dialog id=\"quests\" title=\"Quests\" "
+                           "width=\"1200\" height=\"800\">";
+    uiml.append()
+        << "<listView id=\"collectionList\" width=\"200\" height=\"100%\" "
+           "member-height=\"40\" align-top=\"start\" "
+           "align-left=\"start\" onSelectMessage=\"quests;0\">";
+    QuestParty* party = zManager->zParty(playerId);
+    int index = 0;
+    for (QuestCollection* questCollection : zManager->questCollections)
+    {
+        if (questCollection->isVisible(party, zManager))
+        {
+            uiml.append() << "<listItem id=\"quest_collection_" << index++
+                          << "\" collectionName=\""
+                          << questCollection->getName() << "\">"
+                          << questCollection->getName()
+                          << "</listItem>";
+        }
+    }
+    uiml.append()
+        << "</listView><questGraph id=\"visible_quest_graph\" width=\"1000\" "
+           "align-left=\"collectionList\" "
+           "height=\"100%\"/></dialog>";
+    this->uiml = new Framework::XML::Element(uiml);
+}
+
+void QuestDialog::api(
+    Framework::StreamReader* zRequest, NetworkMessage* zResponse)
+{
+    char typ;
+    zRequest->lese(&typ, 1);
+    switch (typ)
+    {
+    case 0: // onSelect Message of listView
+        {
+            int selection;
+            zRequest->lese((char*)&selection, 4);
+            auto questGraph = this->uiml->selectChildsByName("questGraph");
+            questGraph.selectChildren().remove();
+            auto listItems
+                = uiml->selectChildsByName("listView").selectChildren();
+            listItems.removeAttribute("selected");
+            listItems
+                .whereAttributeEquals(
+                    "id", Framework::Text("quest_collection_") + selection)
+                .forEach([this, &questGraph](
+                             Framework::XML::Element* zElement) {
+                    zElement->setAttribute("selected", "");
+                    auto collectionName
+                        = zElement->getAttributeValue("collectionName");
+                    QuestManager* zManager = Game::INSTANCE->zQuestManager();
+                    auto questCollection
+                        = zManager->zCollection(collectionName);
+                    QuestParty* zParty = zManager->zParty(getPlayerId());
+                    for (Quest* quest : questCollection->quests)
+                    {
+                        if (quest->isVisible(zParty, zManager))
+                        {
+                            questGraph.addChild(getQuestGraphItem(quest, zParty));
+                        }
+                    }
+                });
+            update();
+            break;
+        }
+    }
+}
+
+Framework::XML::Element* QuestDialog::getQuestGraphItem(
+    Quest* zQuest, QuestParty* zParty)
+{
+    Framework::Text requirements = "";
+    int index = 0;
+    int currentGroup = 0;
+    for (Framework::Text* requiredQuest : zQuest->requiredQuestsIds)
+    {
+        int group = zQuest->requiredQuestsGroups.get(index);
+        if (index != 0)
+        {
+            if (group != currentGroup)
+            {
+                requirements += "||";
+                currentGroup = group;
+            }
+            else
+            {
+                requirements += "&&";
+            }
+        }
+        else
+        {
+            currentGroup = group;
+        }
+        requirements += *requiredQuest;
+        index++;
+    }
+    if (!zQuest->isActive(zParty))
+    {
+        Framework::Text result = "<questGraphItem id=\"quest_";
+        result.append()
+            << zQuest->questId << "\" name=\"Unknown Quest\" image=\""
+            << zQuest->imagePath
+            << "\" description=\"You have to finish other quests befor this "
+               "quest will be available.\" finished=\"false\" mainQuest=\""
+            << zQuest->mainQuest << "\" requirements=\"" << requirements
+            << "\" width=\"" << (zQuest->mainQuest ? "50" : "30")
+            << "\" height=\"" << (zQuest->mainQuest ? "50" : "30") << "\"/>";
+        return new Framework::XML::Element(result);
+    }
+    else
+    {
+        QuestStorage* zStorage = zParty->zQuestStorage(zQuest->questId);
+        Framework::Text result = "<questGraphItem id=\"quest_";
+        result.append() << zQuest->questId << "\" name=\""
+                        << zQuest->questName + "\" image=\""
+                        << zQuest->imagePath << "\" description=\""
+                        << zQuest->description
+                        << "\" finished=\"" << zStorage->isQuestFinished() << "\" mainQuest=\""
+                        << zQuest->mainQuest
+                        << "\" requirements=\"" << requirements << "\" width=\""
+                        << (zQuest->mainQuest ? "50" : "30") << "\" height=\""
+                        << (zQuest->mainQuest ? "50" : "30") << "\"/>";
+        return new Framework::XML::Element(result);
+    }
+}

+ 18 - 0
FactoryCraft/QuestDialog.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include "UIDialog.h"
+
+class Quest;
+class QuestParty;
+
+class QuestDialog : public UIDialog
+{
+public:
+    QuestDialog(int playerId);
+
+    virtual void api(
+        Framework::StreamReader* zRequest, NetworkMessage* zResponse) override;
+
+private:
+    Framework::XML::Element *getQuestGraphItem(Quest *zQuest, QuestParty *zParty);
+};

+ 73 - 0
FactoryCraft/UIController.cpp

@@ -0,0 +1,73 @@
+#include "UIController.h"
+
+#include "Entity.h"
+
+UIController::UIController()
+    : ReferenceCounter()
+{}
+
+void UIController::addDialog(UIDialog* dialog)
+{
+    if (dialog->open())
+    {
+        activeDialogs.add(dialog);
+    }
+    else
+    {
+        dialog->release();
+    }
+}
+
+void UIController::api(Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse,
+    Entity* zSource)
+{
+    unsigned short length;
+    zRequest->lese((char*)&length, 2);
+    char* id = new char[length + 1];
+    zRequest->lese(id, length);
+    id[length] = 0;
+    char type;
+    zRequest->lese(&type, 1);
+    switch (type)
+    {
+    case 0: // element message
+        {
+            int entityId = zSource->getId();
+            for (auto dialog = activeDialogs.begin(); dialog; dialog++)
+            {
+                if (dialog->getId().istGleich(id)
+                    && dialog->getPlayerId() == entityId)
+                {
+                    dialog->api(zRequest, zResponse);
+                }
+            }
+            break;
+        }
+    case 1: // close dialog
+        {
+            int entityId = zSource->getId();
+            for (auto dialog = activeDialogs.begin(); dialog; dialog++)
+            {
+                if (dialog->getId().istGleich(id)
+                    && dialog->getPlayerId() == entityId)
+                {
+                    dialog.remove();
+                }
+            }
+            break;
+        }
+    }
+    delete[] id;
+}
+
+void UIController::removePlayerDialogs(int playerId)
+{
+    for (auto dialog = activeDialogs.begin(); dialog; dialog++)
+    {
+        if (dialog->getPlayerId() == playerId)
+        {
+            dialog.remove();
+        }
+    }
+}

+ 20 - 0
FactoryCraft/UIController.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include <Array.h>
+
+#include "UIDialog.h"
+
+class UIController : public Framework::ReferenceCounter
+{
+private:
+    Framework::RCArray<UIDialog> activeDialogs;
+
+public:
+    UIController();
+
+    void addDialog(UIDialog* dialog);
+    void api(Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse,
+        Entity* zSource);
+    void removePlayerDialogs(int playerId);
+};

+ 60 - 0
FactoryCraft/UIDialog.cpp

@@ -0,0 +1,60 @@
+#include "UIDialog.h"
+
+#include "Game.h"
+#include "NetworkMessage.h"
+
+UIDialog::UIDialog(
+    Framework::Text id, int playerId, Framework::XML::Element* uiml)
+    : ReferenceCounter(),
+      id(id),
+      playerId(playerId),
+      uiml(uiml)
+{}
+
+UIDialog::~UIDialog()
+{
+    uiml->release();
+}
+
+void UIDialog::api(Framework::StreamReader* zRequest,
+    NetworkMessage* zResponse)
+{}
+
+bool UIDialog::open() const
+{
+    Entity* zEntity = Game::INSTANCE->zEntity(playerId);
+    if (zEntity)
+    {
+        Game::INSTANCE->zQuestManager()->processEvent(new QuestEventOpenDialog(
+            dynamic_cast<Entity*>(zEntity->getThis()), id));
+        NetworkMessage* msg = new NetworkMessage();
+        msg->openDialog(id, uiml->toString());
+        Game::INSTANCE->sendMessage(msg, zEntity);
+        return 1;
+    }
+    return 0;
+}
+
+bool UIDialog::update() const {
+    Entity* zEntity = Game::INSTANCE->zEntity(playerId);
+    if (zEntity)
+    {
+        Game::INSTANCE->zQuestManager()->processEvent(new QuestEventOpenDialog(
+            dynamic_cast<Entity*>(zEntity->getThis()), id));
+        NetworkMessage* msg = new NetworkMessage();
+        msg->updateDialog(id, uiml->toString());
+        Game::INSTANCE->sendMessage(msg, zEntity);
+        return 1;
+    }
+    return 0;
+}
+
+const Framework::Text& UIDialog::getId() const
+{
+    return id;
+}
+
+int UIDialog::getPlayerId() const
+{
+    return playerId;
+}

+ 28 - 0
FactoryCraft/UIDialog.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <Text.h>
+#include <XML.h>
+
+class NetworkMessage;
+class Entity;
+
+class UIDialog : public Framework::ReferenceCounter
+{
+private:
+    Framework::Text id;
+    int playerId;
+
+protected:
+    Framework::XML::Element* uiml;
+
+public:
+    UIDialog(
+        Framework::Text id, int playerId, Framework::XML::Element* uiml);
+    virtual ~UIDialog();
+    virtual void api(Framework::StreamReader* zRequest,
+        NetworkMessage* zResponse);
+    bool open() const;
+    bool update() const;
+    const Framework::Text& getId() const;
+    int getPlayerId() const;
+};

+ 6 - 0
Windows Version/Windows Version.vcxproj

@@ -225,6 +225,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\PlayerHand.cpp" />
     <ClCompile Include="..\FactoryCraft\PlayerRegister.cpp" />
     <ClCompile Include="..\FactoryCraft\Quest.cpp" />
+    <ClCompile Include="..\FactoryCraft\QuestDialog.cpp" />
     <ClCompile Include="..\FactoryCraft\QuestEvent.cpp" />
     <ClCompile Include="..\FactoryCraft\QuestRequirement.cpp" />
     <ClCompile Include="..\FactoryCraft\QuestReward.cpp" />
@@ -247,6 +248,8 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\TreeTemplate.cpp" />
     <ClCompile Include="..\FactoryCraft\GrowingPlant.cpp" />
     <ClCompile Include="..\FactoryCraft\TypeRegistry.cpp" />
+    <ClCompile Include="..\FactoryCraft\UIController.cpp" />
+    <ClCompile Include="..\FactoryCraft\UIDialog.cpp" />
     <ClCompile Include="..\FactoryCraft\WorldGenerator.cpp" />
     <ClCompile Include="..\FactoryCraft\WorldLoader.cpp" />
     <ClCompile Include="..\FactoryCraft\WorldUpdate.cpp" />
@@ -324,6 +327,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\PlayerHand.h" />
     <ClInclude Include="..\FactoryCraft\PlayerRegister.h" />
     <ClInclude Include="..\FactoryCraft\Quest.h" />
+    <ClInclude Include="..\FactoryCraft\QuestDialog.h" />
     <ClInclude Include="..\FactoryCraft\QuestEvent.h" />
     <ClInclude Include="..\FactoryCraft\QuestRequirement.h" />
     <ClInclude Include="..\FactoryCraft\QuestReward.h" />
@@ -347,6 +351,8 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\TreeTemplate.h" />
     <ClInclude Include="..\FactoryCraft\GrowingPlant.h" />
     <ClInclude Include="..\FactoryCraft\TypeRegistry.h" />
+    <ClInclude Include="..\FactoryCraft\UIController.h" />
+    <ClInclude Include="..\FactoryCraft\UIDialog.h" />
     <ClInclude Include="..\FactoryCraft\WorldGenerator.h" />
     <ClInclude Include="..\FactoryCraft\WorldLoader.h" />
     <ClInclude Include="..\FactoryCraft\WorldUpdate.h" />

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

@@ -100,6 +100,9 @@
     <Filter Include="quests">
       <UniqueIdentifier>{58a901d6-aa56-4225-b9cd-9b12552701cc}</UniqueIdentifier>
     </Filter>
+    <Filter Include="UI">
+      <UniqueIdentifier>{a59326ac-f962-411e-b62d-6ad23ddd6506}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -384,6 +387,15 @@
     <ClCompile Include="..\FactoryCraft\QuestReward.cpp">
       <Filter>quests</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\UIController.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\FactoryCraft\UIDialog.cpp">
+      <Filter>UI</Filter>
+    </ClCompile>
+    <ClCompile Include="..\FactoryCraft\QuestDialog.cpp">
+      <Filter>quests</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -680,5 +692,14 @@
     <ClInclude Include="..\FactoryCraft\QuestReward.h">
       <Filter>quests</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\UIController.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\FactoryCraft\UIDialog.h">
+      <Filter>UI</Filter>
+    </ClInclude>
+    <ClInclude Include="..\FactoryCraft\QuestDialog.h">
+      <Filter>quests</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

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

@@ -5,7 +5,8 @@
       {
         "questId": "tutorial_1",
         "questName": "Quest Dialog",
-        "description": "Welcome to Factory Craft! This is a tutorial quest to get you started. You have already completed it by opening the quest dialog. Here you can see all current open or completed quests. Completing a quest can make other quests visible. You can view all current quests by clicking on a quest category on the left.",
+        "description": "Welcome to Factory Craft!\nThis is a tutorial quest to get you started.\nYou have already completed it by opening the quest dialog.\nHere you can see all current open or completed quests.\nCompleting a quest can make other quests visible.\nYou can view all current quests by clicking on a quest category on the left.",
+        "imagePath": "data/images/gui_icons.ltdb/questdialog.png",
         "requirements": [
           {
             "id": "1",