Procházet zdrojové kódy

Add Chest block with inventory

Kolja Strohm před 1 rokem
rodič
revize
b75a7ec635

+ 2 - 2
FactoryCraft/Axe.h

@@ -1,8 +1,8 @@
 #pragma once
 
-#include "ItemType.h"
-#include "ItemSkill.h"
 #include "BasicTool.h"
+#include "ItemSkill.h"
+#include "ItemType.h"
 
 class AxeToolItemType : public BasicToolItemType
 {

+ 18 - 2
FactoryCraft/BasicBlock.cpp

@@ -5,7 +5,12 @@
 #include "TreeSeblingBlock.h"
 
 BasicBlock::BasicBlock(int typeId, ItemType* zTool, Framework::Vec3<int> pos)
-    : Block(typeId, zTool, pos, false)
+    : BasicBlock(typeId, zTool, pos, false)
+{}
+
+BasicBlock::BasicBlock(
+    int typeId, ItemType* zTool, Framework::Vec3<int> pos, bool hasInventory)
+    : Block(typeId, zTool, pos, hasInventory)
 {}
 
 bool BasicBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
@@ -79,7 +84,18 @@ BasicBlockType::BasicBlockType(int typeId,
     std::function<Block*(Framework::Vec3<int>)> creatBlockCustom,
     const char* name,
     int mapColor)
-    : BlockType(typeId, 0, model, 1, 100, 0, name, false, mapColor),
+    : BasicBlockType(
+        typeId, itemTypeId, model, creatBlockCustom, name, mapColor, 0)
+{}
+
+BasicBlockType::BasicBlockType(int typeId,
+    int itemTypeId,
+    ModelInfo model,
+    std::function<Block* (Framework::Vec3<int>)> creatBlockCustom,
+    const char* name,
+    int mapColor,
+    bool modelSubscription)
+    : BlockType(typeId, 0, model, 1, 100, 0, name, modelSubscription, mapColor),
       itemType(itemTypeId),
       transparent(0),
       passable(0),

+ 8 - 0
FactoryCraft/BasicBlocks.h

@@ -12,6 +12,7 @@ class BasicBlock : public Block
 {
 public:
     BasicBlock(int typeId, ItemType* zTool, Framework::Vec3<int> pos);
+    BasicBlock(int typeId, ItemType* zTool, Framework::Vec3<int> pos, bool hasInventory);
     virtual bool onTick(
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
@@ -63,6 +64,13 @@ public:
         std::function<Block*(Framework::Vec3<int>)> creatBlockCustom,
         const char* name,
         int mapColor);
+    BasicBlockType(int typeId,
+        int itemTypeId,
+        ModelInfo model,
+        std::function<Block*(Framework::Vec3<int>)> creatBlockCustom,
+        const char* name,
+        int mapColor,
+        bool modelSubscription);
     virtual Block* createBlock(Framework::Vec3<int> position) const override;
     virtual Item* createItem() const override;
     BasicBlockType* setHardness(float hardness);

+ 17 - 2
FactoryCraft/Block.cpp

@@ -84,6 +84,8 @@ void Block::onDestroy()
     }
 }
 
+void Block::onDialogClosed(Text dialogId) {}
+
 void Block::tick(TickQueue* zQueue)
 {
     if (wasTicked) return;
@@ -194,10 +196,13 @@ Framework::Text Block::getTargetUIML()
         ->getTargetUIML();
 }
 
-void Block::sendModelInfo(NetworkMessage* zMessage) {
+void Block::sendModelInfo(NetworkMessage* zMessage)
+{
     // overwritten by some blocks
 }
 
+void Block::interact(Item* zItem, Entity* zActor) {}
+
 void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse)
 {
     // TODO: answer api requests
@@ -208,6 +213,16 @@ void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse)
     case 0:
         // request model state
         sendModelInfo(zResponse);
+        break;
+    case 1: // dialog closed
+        short nameLen;
+        zRequest->lese((char*)&nameLen, 2);
+        char* name = new char[nameLen + 1];
+        zRequest->lese(name, nameLen);
+        name[nameLen] = 0;
+        onDialogClosed(name);
+        delete[] name;
+        break;
     }
 }
 
@@ -231,7 +246,7 @@ bool Block::isPassable() const
     return passable;
 }
 
-bool Block::isInteractable() const
+bool Block::isInteractable(const Item* zItem) const
 {
     return interactable;
 }

+ 4 - 1
FactoryCraft/Block.h

@@ -74,6 +74,8 @@ protected:
     virtual void onPostTick() = 0;
     virtual void onDestroy();
 
+    virtual void onDialogClosed(Framework::Text dialogId);
+
 public:
     Block(int typeId,
         const ItemType* zTool,
@@ -93,13 +95,14 @@ public:
     virtual void setNeighbourType(Direction dir, int type);
     virtual Framework::Text getTargetUIML();
     virtual void sendModelInfo(NetworkMessage* zMessage);
+    virtual void interact(Item* zItem, Entity *zActor);
     void api(Framework::StreamReader* zRequest, NetworkMessage* zResponse);
 
     bool isTickSource() const;
     const BlockType* zBlockType() const;
     bool isTransparent() const;
     bool isPassable() const;
-    bool isInteractable() const;
+    virtual bool isInteractable(const Item *zItem) const;
     float getHP() const;
     float getMaxHP() const;
     float getHardness() const;

+ 2 - 1
FactoryCraft/BlockType.h

@@ -42,8 +42,9 @@ public:
     static const int WHEAT = 25;
     static const int WATER = 26;
     static const int CRAFTING_TABLE = 27;
+    static const int CHEST = 28;
 
-    static const int MAX_VALUE = 27;
+    static const int MAX_VALUE = 28;
 };
 
 class BlockType : public virtual Framework::ReferenceCounter

+ 137 - 0
FactoryCraft/Chest.cpp

@@ -0,0 +1,137 @@
+#include "Chest.h"
+
+#include <TextFeld.h>
+
+#include "Game.h"
+
+Chest::Chest(int typeId, ItemType* zTool, Framework::Vec3<int> pos)
+    : BasicBlock(typeId, zTool, pos, 1),
+      open(0),
+      userEntityId(0)
+{
+    for (int i = 0; i < 30; i++)
+    {
+        ItemSlot* slot = new ItemSlot(
+            "Inventory", 50, i, i, ANY_DIRECTION, ANY_DIRECTION, 0);
+        addSlot(slot);
+    }
+}
+
+void Chest::onDestroy()
+{
+    // TODO: drop inventory
+}
+
+void Chest::onDialogClosed(Framework::Text dialogId)
+{
+    if (dialogId.istGleich(getDialogId()))
+    {
+        open = 0;
+        userEntityId = 0;
+        NetworkMessage* msg = new NetworkMessage();
+        msg->animateBlockBone(getDimensionId(),
+            getPos(),
+            1,
+            1.0,
+            Vec3<float>(1.f, 1.f, 0.45f),
+            Vec3<float>(0.0f, 0.f, 0.f)); // close the chest over one second
+        Game::INSTANCE->broadcastMessage(msg);
+    }
+}
+
+Framework::Text Chest::getDialogId() const
+{
+    Text dialogId = "chest_inventory_";
+    dialogId.append() << getDimensionId() << "," << getPos().x << ","
+                      << getPos().y << "," << getPos().z;
+    return dialogId;
+}
+
+bool Chest::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
+{
+    if (open)
+    {
+        if (!Game::INSTANCE->zEntity(userEntityId))
+        {
+            onDialogClosed(getDialogId());
+        }
+    }
+    return open;
+}
+
+void Chest::interact(Item* zItem, Entity* zActor)
+{
+    lock();
+    if (open)
+    {
+        if (!Game::INSTANCE->zEntity(userEntityId)) open = 0;
+    }
+    if (!open)
+    {
+        userEntityId = zActor->getId();
+        open = 1;
+        NetworkMessage* msg = new NetworkMessage();
+        msg->openDialog(getDialogId());
+        Text uiml = "";
+        uiml.append()
+            << "<dialog id=\"" << getDialogId()
+            << "\" title=\"Chest\" "
+               "notifyOnClose=\""
+            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
+            << getPos().z
+            << "\" "
+               "width=\"610\" "
+               "height=\"470\">"
+            << "<inventory id=\"chest_inventory\" margin-bottom=\"9\" "
+               "align-bottom=\"player_label\" align-left=\"start\" "
+               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
+               "numSlots=\"30\" slotNameFilter=\"\" target=\""
+            << getDimensionId() << "," << getPos().x << "," << getPos().y << ","
+            << getPos().z << "\"/>"
+            << "<text id=\"player_label\" width=\"100%\" style=\""
+            << std::uppercase << std::hex
+            << (TextFeld::Style::Text | TextFeld::Style::Center) << std::dec
+            << std::nouppercase
+            << "\"margin-bottom=\"9\" align-bottom=\"player_inventory\">Player "
+               "Inventory</text>"
+            << "<inventory id=\"player_inventory\" margin-bottom=\"18\" "
+               "align-bottom=\"item_bar\" align-left=\"start\" "
+               "margin-left=\"9\" width=\"592\" height=\"172\" rowSize=\"10\" "
+               "numSlots=\"30\" slotNameFilter=\"Inventory\" target=\""
+            << zActor->getId() << "\"/>"
+            << "<inventory id=\"item_bar\" margin-bottom=\"9\" "
+               "align-bottom=\"end\" align-left=\"start\" margin-left=\"9\" "
+               "width=\"592\" height=\"52\" rowSize=\"10\" numSlots=\"10\" "
+               "slotNameFilter=\"ItemBar\" target=\""
+            << zActor->getId() << "\"/>"
+            << "</dialog>";
+        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();
+        msg->animateBlockBone(getDimensionId(),
+            getPos(),
+            1,
+            1.0,
+            Vec3<float>(1.f, 1.f, 0.45f),
+            Vec3<float>(0.0f, (float)(PI / 2.0), 0.f)); // open the chest over 1 second
+        Game::INSTANCE->broadcastMessage(msg);
+    }
+    unlock();
+}
+
+void Chest::sendModelInfo(NetworkMessage* zMessage)
+{
+    if (open)
+    {
+        zMessage->animateBlockBone(getDimensionId(),
+            getPos(),
+            1,
+            0.0,
+            Vec3<float>(1.f, 1.f, 0.45f),
+            Vec3<float>(0.0f, (float)(PI / 2.0), 0.f)); // open the chest instantly
+    }
+}

+ 22 - 0
FactoryCraft/Chest.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "BasicBlocks.h"
+
+class Chest : public BasicBlock
+{
+private:
+    bool open;
+    int userEntityId;
+
+    virtual void onDestroy() override;
+    virtual void onDialogClosed(Framework::Text dialogId) override;
+    Framework::Text getDialogId() const;
+
+protected:
+    virtual bool onTick(TickQueue* zQueue, int numTicks, bool& blocked) override;
+
+public:
+    Chest(int typeId, ItemType* zTool, Framework::Vec3<int> pos);
+    virtual void interact(Item* zItem, Entity* zActor) override;
+    virtual void sendModelInfo(NetworkMessage* zMessage) override;
+};

+ 100 - 30
FactoryCraft/Entity.cpp

@@ -32,7 +32,7 @@ bool ActionTarget::isEntity(int entityId) const
     return this->entityId == entityId;
 }
 
-void ActionTarget::applyItemSkillOnTarget(
+void ActionTarget::useItemSkillOnTarget(
     Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem)
 {
     if (entityId >= 0)
@@ -48,6 +48,40 @@ void ActionTarget::applyItemSkillOnTarget(
     }
 }
 
+void ActionTarget::interactItemSkillOnTarget(
+    Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem)
+{
+    if (zItemSkill)
+    {
+        if (entityId >= 0)
+        {
+            Entity* target = Game::INSTANCE->zEntity(entityId);
+            if (target) zItemSkill->interact(zActor, zUsedItem, target);
+        }
+        else
+        {
+            Block* block = Game::INSTANCE->zRealBlockInstance(
+                blockPos, zActor->getCurrentDimensionId());
+            if (block) zItemSkill->interact(zActor, zUsedItem, block);
+        }
+    }
+    else
+    {
+        if (entityId >= 0)
+        {
+            Block* block = Game::INSTANCE->zRealBlockInstance(
+                blockPos, zActor->getCurrentDimensionId());
+            if (block) block->interact(zUsedItem, zActor);
+        }
+        else
+        {
+            Block* block = Game::INSTANCE->zRealBlockInstance(
+                blockPos, zActor->getCurrentDimensionId());
+            if (block) block->interact(zUsedItem, zActor);
+        }
+    }
+}
+
 void ActionTarget::placeBlock(Entity* zActor, Item* zItem)
 {
     if (zActor->getStamina() > 0.2f)
@@ -190,53 +224,75 @@ void Entity::onDeath()
         new EntityRemovedUpdate(id, currentDimensionId, location));
 }
 
-void Entity::useItem(int typeId, Item* zItem)
+void Entity::useItem(int typeId, Item* zItem, bool left)
 {
-    if (zItem && zItem->isEatable())
-    { // eat item
-        zItem->applyFoodEffects(this);
-    }
-    else if (zItem && zItem->isPlaceable())
-    { // place item
-        if (placeBlockCooldown <= 0)
+    if (left)
+    {
+        if (!zItem || zItem->isUsable())
         {
             cs.lock();
             if (target)
             {
-                target->placeBlock(this, zItem);
-                placeBlockCooldown = 15;
+                ItemSkill* selected = zSkill(typeId);
+                if (!selected)
+                {
+                    selected
+                        = StaticRegistry<ItemType>::INSTANCE.zElement(typeId)
+                              ->createDefaultItemSkill();
+                    if (selected) skills.add(selected);
+                }
+                if (!selected)
+                {
+                    selected = zSkill(ItemTypeEnum::PLAYER_HAND);
+                }
+                target->useItemSkillOnTarget(this, selected, zItem);
             }
             cs.unlock();
         }
     }
-    else if (!zItem || zItem->isUsable())
-    { // use item skill
-        cs.lock();
-        if (target)
-        {
-            ItemSkill* selected = 0;
-            for (ItemSkill* skill : skills)
+    else
+    {
+        if (zItem && zItem->isEatable())
+        { // eat item
+            zItem->applyFoodEffects(this);
+        }
+        else if (zItem && zItem->isPlaceable())
+        { // place item
+            if (placeBlockCooldown <= 0)
             {
-                if (skill->getTypeId() == typeId)
+                cs.lock();
+                if (target)
                 {
-                    selected = skill;
-                    break;
+                    target->placeBlock(this, zItem);
+                    placeBlockCooldown = 15;
                 }
+                cs.unlock();
             }
-            if (!selected)
+        }
+        else if (!zItem || zItem->isUsable())
+        { // use item skill
+            cs.lock();
+            if (target)
             {
-                selected = StaticRegistry<ItemType>::INSTANCE.zElement(typeId)
-                               ->createDefaultItemSkill();
-                skills.add(selected);
+                ItemSkill* selected = zSkill(typeId);
+                if (!selected)
+                {
+                    selected
+                        = StaticRegistry<ItemType>::INSTANCE.zElement(typeId)
+                              ->createDefaultItemSkill();
+                    if (selected) skills.add(selected);
+                }
+                target->interactItemSkillOnTarget(this, selected, zItem);
             }
-            target->applyItemSkillOnTarget(this, selected, zItem);
+            cs.unlock();
         }
-        cs.unlock();
     }
 }
 
 void Entity::onTargetChange() {}
 
+void Entity::interact(Item* zItem, Entity* zActor) {}
+
 void Entity::addMovementFrame(MovementFrame& frame)
 {
     cs.lock();
@@ -260,8 +316,9 @@ void Entity::addMovementFrame(MovementFrame& frame)
     // TODO implement subscription system to notify only interested clients
 }
 
-void Entity::calculateTarget(
-    Framework::Vec3<float> basePos, Framework::Vec3<float> direction)
+void Entity::calculateTarget(Framework::Vec3<float> basePos,
+    Framework::Vec3<float> direction,
+    const Item* zItem)
 {
     Vec3<float> headPosition = basePos + faceOffset;
     int px = (int)floor(headPosition.x);
@@ -273,7 +330,7 @@ void Entity::calculateTarget(
     {
         if (getDefaultBlock(Game::INSTANCE->zBlockAt(
                                 Vec3<int>{px, py, pz}, currentDimensionId))
-                ->isInteractable())
+                ->isInteractable(zItem))
         {
             if (!target || !target->isBlock({px, py, pz}, dir))
             {
@@ -432,6 +489,19 @@ void Entity::notifyStatusBarObservers(NetworkMessage* msg)
     msg->release();
 }
 
+ItemSkill* Entity::zSkill(int itemType)
+{
+    ItemSkill* selected = 0;
+    for (ItemSkill* skill : skills)
+    {
+        if (skill->getTypeId() == typeId)
+        {
+            return skill;
+        }
+    }
+    return 0;
+}
+
 void Entity::prepareTick(const Dimension* zDimension) {}
 
 void Entity::tick(const Dimension* zDimension)

+ 11 - 5
FactoryCraft/Entity.h

@@ -28,7 +28,9 @@ public:
     bool isBlock(Framework::Vec3<int> blockPos, Direction blockSide) const;
     bool isEntity(int entityId) const;
 
-    void applyItemSkillOnTarget(
+    void useItemSkillOnTarget(
+        Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem);
+    void interactItemSkillOnTarget(
         Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem);
     void placeBlock(Entity* zActor, Item* zItem);
 
@@ -80,26 +82,30 @@ protected:
         statusBarObservers;
 
     virtual void onDeath();
-    virtual void useItem(int typeId, Item* zItem);
+    virtual void useItem(int typeId, Item* zItem, bool left);
     Entity(int typeId,
         Framework::Vec3<float> location,
         int dimensionId,
         int entityId);
     void addMovementFrame(MovementFrame& frame);
-    void calculateTarget(
-        Framework::Vec3<float> basePos, Framework::Vec3<float> direction);
+    void calculateTarget(Framework::Vec3<float> basePos,
+        Framework::Vec3<float> direction,
+        const Item* zItem);
     void removeStatusBarObserver(Entity* zSource, Framework::Text id);
     void addStatusBarObserver(Entity* zSource, Framework::Text id);
     void notifyStatusBarObservers(NetworkMessage* msg);
+    ItemSkill* zSkill(int itemType);
 
 public:
     virtual void prepareTick(const Dimension* zDimension);
     virtual void tick(const Dimension* zDimension);
 
     virtual void api(Framework::StreamReader* zRequest,
-        NetworkMessage* zResponse, Entity* zSource);
+        NetworkMessage* zResponse,
+        Entity* zSource);
     virtual void onTargetChange();
 
+    virtual void interact(Item* zItem, Entity* zActor);
     virtual void onFall(float collisionSpeed);
     void setChatSecurityLevel(int level);
     void setPosition(Framework::Vec3<float> pos);

+ 12 - 2
FactoryCraft/Inventory.cpp

@@ -487,6 +487,16 @@ void Inventory::addObserver(Entity* zSource, Framework::Text id)
         call(zSource, id);
 }
 
+void Inventory::lock()
+{
+    cs.lock();
+}
+
+void Inventory::unlock()
+{
+    cs.unlock();
+}
+
 const ItemSlot* Inventory::zSlot(int id) const
 {
     if (itemCache)
@@ -715,13 +725,13 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest,
             char filterLen;
             zRequest->lese(&filterLen, 1);
             char* filter = new char[filterLen + 1];
-            zRequest->lese(filter, filterLen);
+            if (filterLen) zRequest->lese(filter, filterLen);
             filter[(int)filterLen] = 0;
             InMemoryBuffer buffer;
             int count = 0;
             for (ItemSlot* slot : *this)
             {
-                if (slot->getName().istGleich(filter))
+                if (filterLen == 0 || slot->getName().istGleich(filter))
                 {
                     count++;
                     int id = slot->getId();

+ 2 - 0
FactoryCraft/Inventory.h

@@ -90,6 +90,8 @@ protected:
     void addObserver(Entity* zSource, Framework::Text id);
     virtual void addItems(
         ItemStack* zItems, Direction dir, ItemFilter* zFilter);
+    void lock();
+    void unlock();
 
 public:
     Inventory(const Framework::Vec3<float> location, bool hasInventory);

+ 19 - 1
FactoryCraft/ItemSkill.cpp

@@ -1,5 +1,7 @@
 #include "ItemSkill.h"
 
+#include "Block.h"
+#include "Entity.h"
 #include "ItemType.h"
 #include "StaticRegistry.h"
 
@@ -18,6 +20,16 @@ int ItemSkill::getTypeId()
     return itemTypeId;
 }
 
+void ItemSkill::interact(Entity* zActor, Item* zUsedItem, Block* zTarget)
+{
+    zTarget->interact(zUsedItem, zActor);
+}
+
+void ItemSkill::interact(Entity* zActor, Item* zUsedItem, Entity* zTarget)
+{
+    zTarget->interact(zUsedItem, zActor);
+}
+
 BasicItemSkill::BasicItemSkill(int itemTypeId,
     float maxXP,
     float durabilityModifier,
@@ -39,4 +51,10 @@ BasicItemSkill::BasicItemSkill(int itemTypeId,
 
 void BasicItemSkill::use(Entity* zActor, Item* zUsedItem, Block* zTarget) {}
 
-void BasicItemSkill::use(Entity* zActor, Item* zUsedItem, Entity* zTarget) {}
+void BasicItemSkill::use(Entity* zActor, Item* zUsedItem, Entity* zTarget) {}
+
+void BasicItemSkill::interact(Entity* zActor, Item* zUsedItem, Block* zTarget)
+{}
+
+void BasicItemSkill::interact(Entity* zActor, Item* zUsedItem, Entity* zTarget)
+{}

+ 6 - 0
FactoryCraft/ItemSkill.h

@@ -27,6 +27,8 @@ public:
     ItemSkill(int itemTypeId);
     virtual void use(Entity* zActor, Item* zUsedItem, Block* zTarget) = 0;
     virtual void use(Entity* zActor, Item* zUsedItem, Entity* zTarget) = 0;
+    virtual void interact(Entity* zActor, Item* zUsedItem, Block* zTarget);
+    virtual void interact(Entity* zActor, Item* zUsedItem, Entity* zTarget);
     const ItemType* zSkillType();
     int getTypeId();
 };
@@ -55,6 +57,10 @@ protected:
 public:
     virtual void use(Entity* zActor, Item* zUsedItem, Block* zTarget) override;
     virtual void use(Entity* zActor, Item* zUsedItem, Entity* zTarget) override;
+    virtual void interact(
+        Entity* zActor, Item* zUsedItem, Block* zTarget) override;
+    virtual void interact(
+        Entity* zActor, Item* zUsedItem, Entity* zTarget) override;
 
     friend ItemType;
 };

+ 29 - 0
FactoryCraft/NetworkMessage.cpp

@@ -154,6 +154,35 @@ void NetworkMessage::sendToAll()
     broadcast = true;
 }
 
+void NetworkMessage::animateBlockBone(int dimensionId,
+    Framework::Vec3<int> position,
+    int boneId,
+    double time,
+    Framework::Vec3<float> pos,
+    Framework::Vec3<float> rotation)
+{
+    delete[] address;
+    addressLength = 19;
+    address = new char[addressLength];
+    address[0] = 1; // dimension api
+    *(int*)(address + 1) = dimensionId;
+    address[5] = 3; // block api
+    *(int*)(address + 6) = position.x;
+    *(int*)(address + 10) = position.y;
+    *(int*)(address + 14) = position.z;
+    address[18] = 3; // animate bone
+    char* msg = new char[36];
+    *(int*)msg = boneId;
+    *(double*)(msg + 4) = boneId;
+    *(float*)(msg + 12) = pos.x;
+    *(float*)(msg + 16) = pos.y;
+    *(float*)(msg + 20) = pos.z;
+    *(float*)(msg + 24) = rotation.x;
+    *(float*)(msg + 28) = rotation.y;
+    *(float*)(msg + 32) = rotation.z;
+    setMessage(msg, 36);
+}
+
 void NetworkMessage::writeTo(Framework::StreamWriter* zWriter) const
 {
     int total = msgLength + addressLength;

+ 6 - 0
FactoryCraft/NetworkMessage.h

@@ -40,6 +40,12 @@ public:
     void sendPlayerPositions(char *msg, int length);
     void setUseBackground();
     void sendToAll();
+    void animateBlockBone(int dimensionId,
+        Framework::Vec3<int> position,
+        int boneId,
+        double time,
+        Framework::Vec3<float> pos,
+        Framework::Vec3<float> rotation);
 
     void writeTo(Framework::StreamWriter* zWriter) const;
     bool isBroadcast() const;

+ 11 - 6
FactoryCraft/Player.cpp

@@ -83,7 +83,7 @@ Framework::Text Player::getPlayerGUI()
     return result;
 }
 
-void Player::useItemSlot(ItemSlot* zSlot)
+void Player::useItemSlot(ItemSlot* zSlot, bool left)
 {
     if (zSlot->zStack())
     {
@@ -91,7 +91,7 @@ void Player::useItemSlot(ItemSlot* zSlot)
         if (stack)
         {
             Item* item = stack->extractFromStack();
-            Entity::useItem(item->getTypeId(), item);
+            Entity::useItem(item->getTypeId(), item, left);
             if (item->getHp() > 0)
             {
                 if (item->getDurability() > 0)
@@ -153,7 +153,7 @@ void Player::useItemSlot(ItemSlot* zSlot)
         }
     }
     else
-        Entity::useItem(ItemTypeEnum::PLAYER_HAND, 0); // hand usage
+        Entity::useItem(ItemTypeEnum::PLAYER_HAND, 0, left); // hand usage
 }
 
 void Player::setName(Framework::Text name)
@@ -169,10 +169,10 @@ const char* Player::getName() const
 void Player::tick(const Dimension* zDimension)
 {
     if ((keyState | Key::LEFT_HAND_ACTION) == keyState)
-        useItemSlot(itemBar.get(leftHandPosition));
+        useItemSlot(itemBar.get(leftHandPosition), true);
     if ((keyState | Key::RIGHT_HAND_ACTION) == keyState)
         useItemSlot(
-            itemBar.get((leftHandPosition + 1) % itemBar.getEintragAnzahl()));
+            itemBar.get(leftHandPosition), false);
     return Entity::tick(zDimension);
 }
 
@@ -222,7 +222,12 @@ void Player::playerApi(
             zRequest->lese((char*)&frame.movementFlags, 4);
             zRequest->lese((char*)&frame.duration, 8);
             addMovementFrame(frame);
-            calculateTarget(frame.targetPosition, frame.direction);
+            calculateTarget(frame.targetPosition,
+                frame.direction,
+                !itemBar.get(leftHandPosition)->isEmpty()
+                    ? itemBar.get(leftHandPosition)
+                    ->zStack()
+                    ->zItem() : 0);
             break;
         }
     case 3:

+ 1 - 1
FactoryCraft/Player.h

@@ -27,7 +27,7 @@ private:
     bool jumping;
     __int64 keyState;
 
-    void useItemSlot(ItemSlot* zSlot);
+    void useItemSlot(ItemSlot* zSlot, bool left);
     Player(Framework::Vec3<float> location, int dimensionId, int entityId);
 
     Framework::Text getInventoryUIML();

+ 19 - 0
FactoryCraft/StaticInitializerOrder.cpp

@@ -7,6 +7,7 @@
 #include "GrowingPlant.h"
 #include "NoBlock.h"
 #include "TreeSeblingBlock.h"
+#include "Chest.h"
 // fluid blocks
 #include "FluidBlock.h"
 // dimensions
@@ -295,6 +296,16 @@ void initializeBlockTypes()
          "Crafting Table",
          0xFFC4A783))
         ->initializeDefault(); // TODO: implement crafting table block type
+    (new BasicBlockType(BlockTypeEnum::CHEST,
+         ItemTypeEnum::CHEST,
+         ModelInfo(
+             "chest", {"blocks.ltdb/chest.png", "blocks.ltdb/chestcover.png"}),
+         [](Framework::Vec3<int> pos) {
+             return new Chest(BlockTypeEnum::CHEST, 0, pos);
+         },
+         "Chest",
+         0xFFE2C292, 1))
+        ->initializeDefault();
 }
 
 void initializeItemTypes()
@@ -604,6 +615,14 @@ void initializeItemTypes()
                 "blocks.ltdb/woodplanks.png"}),
         BlockTypeEnum::CRAFTING_TABLE)); // TODO: implement crafting table item
                                          // type
+
+    (new BasicBlockItemType(ItemTypeEnum::CHEST,
+        "Chest",
+        0,
+        0,
+        ModelInfo(
+            "chest", {"blocks.ltdb/chest.png", "blocks.ltdb/chestcover.png"}),
+        BlockTypeEnum::CHEST));
 }
 
 void initializeEntityTypes()

+ 2 - 0
Windows Version/Windows Version.vcxproj

@@ -172,6 +172,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\ChatCommandExecutor.cpp" />
     <ClCompile Include="..\FactoryCraft\ChatMessage.cpp" />
     <ClCompile Include="..\FactoryCraft\ChatObserver.cpp" />
+    <ClCompile Include="..\FactoryCraft\Chest.cpp" />
     <ClCompile Include="..\FactoryCraft\Chunk.cpp" />
     <ClCompile Include="..\FactoryCraft\ChunkMap.cpp" />
     <ClCompile Include="..\FactoryCraft\CraftingStorage.cpp" />
@@ -233,6 +234,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClCompile Include="..\FactoryCraft\WormCaveGenerator.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\FactoryCraft\Chest.h" />
     <ClInclude Include="..\FactoryCraft\ChunkMap.h" />
     <ClInclude Include="..\FactoryCraft\AddEntityUpdate.h" />
     <ClInclude Include="..\FactoryCraft\Area.h" />

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

@@ -85,6 +85,9 @@
     <Filter Include="world\map">
       <UniqueIdentifier>{cdafc254-a7ce-4911-8534-20ebcee5bec8}</UniqueIdentifier>
     </Filter>
+    <Filter Include="world\blocks\storage">
+      <UniqueIdentifier>{f0a18928-ee44-40d9-8872-e09b57585ac0}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\FactoryCraft\Server.cpp">
@@ -309,6 +312,9 @@
     <ClCompile Include="..\FactoryCraft\DimensionMap.cpp">
       <Filter>world\map</Filter>
     </ClCompile>
+    <ClCompile Include="..\FactoryCraft\Chest.cpp">
+      <Filter>world\blocks\storage</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\FactoryCraft\Chunk.h">
@@ -545,5 +551,8 @@
     <ClInclude Include="..\FactoryCraft\ChunkMap.h">
       <Filter>world\map</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\Chest.h">
+      <Filter>world\blocks\storage</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>