Ver código fonte

implement server side collision detection

Kolja Strohm 1 mês atrás
pai
commit
17f97ab884

+ 2 - 2
FactoryCraft/Animal.cpp

@@ -41,9 +41,9 @@ void Animal::takeDamage(
     Entity::takeDamage(zSource, zUsedItem, zUsedSkill, damage);
 }
 
-void Animal::tick(const Dimension* zDimension)
+void Animal::tick(const Dimension* zDimension, double seconds)
 {
-    Entity::tick(zDimension);
+    Entity::tick(zDimension, seconds);
 }
 
 AnimalEntityType::AnimalEntityType()

+ 1 - 1
FactoryCraft/Animal.h

@@ -21,7 +21,7 @@ public:
         Item* zUsedItem,
         ItemSkill* zUsedSkill,
         float damage) override;
-    void tick(const Dimension* zDimension) override;
+    void tick(const Dimension* zDimension, double seconds) override;
 };
 
 class AnimalEntityTypeFactory;

+ 31 - 18
FactoryCraft/Chunk.cpp

@@ -465,6 +465,7 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
     {
         if (observer->getEntityId() == zEntity->getId()) return;
     }
+    cs.lock();
     int id = zEntity->getId();
     InformationObserver* observer = new InformationObserver(id);
     observers.add(observer);
@@ -505,6 +506,7 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
             observer->sendMessage(msg);
         }
     }
+    cs.unlock();
 }
 
 void Chunk::removeObserver(Entity* zEntity)
@@ -1126,25 +1128,38 @@ void Chunk::save(Framework::StreamWriter* zWriter,
             }
         }
     }
-    unsigned short len = (unsigned short)entitiesInChunk.getEintragAnzahl();
+    unsigned short len = 0;
+    for (Entity* entity : entitiesInChunk)
+    {
+        if (entity->zType()->getId() != EntityTypeEnum::PLAYER)
+        {
+            len++;
+        }
+    }
     zWriter->schreibe((char*)&len, 2);
     for (Entity* entity : entitiesInChunk)
     {
-        int type = entity->zType()->getId();
-        zWriter->schreibe((char*)&type, 4);
-        entity->zType()->saveEntity(entity, zWriter);
-        if (lastSavedEntityIds.getWertIndex(entity->getId()) < 0)
+        if (entity->zType()->getId() != EntityTypeEnum::PLAYER)
         {
-            entity->getLastSavedChunkCenter().ifPresent(
-                [&otherChunksToSave](
-                    Framework::Punkt p) { otherChunksToSave.add(p); });
+            int type = entity->zType()->getId();
+            zWriter->schreibe((char*)&type, 4);
+            entity->zType()->saveEntity(entity, zWriter);
+            if (lastSavedEntityIds.getWertIndex(entity->getId()) < 0)
+            {
+                entity->getLastSavedChunkCenter().ifPresent(
+                    [&otherChunksToSave](
+                        Framework::Punkt p) { otherChunksToSave.add(p); });
+            }
+            entity->setLastSavedChunkCenter(getCenter());
         }
-        entity->setLastSavedChunkCenter(getCenter());
     }
     lastSavedEntityIds.leeren();
     for (Entity* entity : entitiesInChunk)
     {
-        lastSavedEntityIds.add(entity->getId());
+        if (entity->zType()->getId() != EntityTypeEnum::PLAYER)
+        {
+            lastSavedEntityIds.add(entity->getId());
+        }
     }
 }
 
@@ -1309,10 +1324,8 @@ int Chunk::getBlockTypeAtWC(int x, int y, int z) const
 
 void Chunk::onEntityEnters(Entity* zEntity, Chunk* lastChunk)
 {
-    if (zEntity->zType()->getId() != EntityTypeEnum::PLAYER)
-    {
-        this->entitiesInChunk.add(dynamic_cast<Entity*>(zEntity->getThis()));
-    }
+    cs.lock();
+    this->entitiesInChunk.add(dynamic_cast<Entity*>(zEntity->getThis()));
     NetworkMessage* msg = 0;
     for (InformationObserver* observer : observers)
     {
@@ -1329,14 +1342,13 @@ void Chunk::onEntityEnters(Entity* zEntity, Chunk* lastChunk)
         }
     }
     if (msg) msg->release();
+    cs.unlock();
 }
 
 void Chunk::onEntityLeaves(Entity* zEntity, Chunk* zNextChunk)
 {
-    if (zEntity->zType()->getId() != EntityTypeEnum::PLAYER)
-    {
-        this->entitiesInChunk.remove(this->entitiesInChunk.indexOf(zEntity));
-    }
+    cs.lock();
+    this->entitiesInChunk.remove(this->entitiesInChunk.indexOf(zEntity));
     NetworkMessage* msg = 0;
     for (InformationObserver* observer : observers)
     {
@@ -1353,6 +1365,7 @@ void Chunk::onEntityLeaves(Entity* zEntity, Chunk* zNextChunk)
         }
     }
     if (msg) msg->release();
+    cs.unlock();
 }
 
 bool Chunk::hasObserver(int entityId) const

+ 38 - 21
FactoryCraft/Dimension.cpp

@@ -1,5 +1,6 @@
 #include "Dimension.h"
 
+#include <AsynchronCall.h>
 #include <Logging.h>
 
 #include "ChunkMap.h"
@@ -42,6 +43,7 @@ Dimension::Dimension(int id)
         d.close();
     }
     start();
+    new AsynchronCall([this]() { this->entityTickLoop(); });
 }
 
 Dimension::~Dimension()
@@ -117,7 +119,7 @@ void Dimension::api(Framework::InMemoryBuffer* zRequest,
     }
 }
 
-void Dimension::tickEntities()
+void Dimension::tick()
 {
     this->currentDayTime += 1.0 / MAX_TICKS_PER_SECOND;
     if (this->currentDayTime
@@ -126,26 +128,6 @@ void Dimension::tickEntities()
         this->currentDayTime
             -= dayDuration + nightDuration + nightTransitionDuration * 2;
     }
-    entityCs.lock();
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved()
-            && (entity->isMoving()
-                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
-                    (int)entity->getPosition().y))))
-            entity->prepareTick(this);
-    }
-    int index = 0;
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved()
-            && (entity->isMoving()
-                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
-                    (int)entity->getPosition().y))))
-            entity->tick(this);
-        index++;
-    }
-    entityCs.unlock();
 }
 
 void Dimension::thread()
@@ -344,6 +326,41 @@ void Dimension::saveStructure(MultiblockStructure* zStructure) const
     structureManager->saveStructure(zStructure);
 }
 
+void Dimension::entityTickLoop()
+{
+    ZeitMesser zm;
+    zm.messungStart();
+    while (!stop)
+    {
+        Sleep(32);
+        zm.messungEnde();
+        double seconds = zm.getSekunden();
+        zm.messungStart();
+        Logging::debug() << "Dimension " << this->getDimensionId()
+                         << " entity tick loop took " << seconds << " seconds.";
+        entityCs.lock();
+        chunkCs.lock();
+        for (auto entity : *entities)
+        {
+            if (!entity->isRemoved())
+            {
+                entity->prepareTick(this, seconds);
+            }
+        }
+        int index = 0;
+        for (auto entity : *entities)
+        {
+            if (!entity->isRemoved())
+            {
+                entity->tick(this, seconds);
+            }
+            index++;
+        }
+        chunkCs.unlock();
+        entityCs.unlock();
+    }
+}
+
 Chunk* Dimension::zChunk(Punkt wPos) const
 {
     char addr[8];

+ 2 - 1
FactoryCraft/Dimension.h

@@ -61,6 +61,7 @@ private:
     void getAddrOf(Framework::Punkt cPos, char* addr) const;
     void getAddrOfWorld(Framework::Punkt wPos, char* addr) const;
     void saveStructure(MultiblockStructure* zStructure) const;
+    void entityTickLoop();
 
 public:
     Dimension(int id);
@@ -72,7 +73,7 @@ public:
     void api(Framework::InMemoryBuffer* zRequest,
         NetworkMessage* zResponse,
         Entity* zSource);
-    void tickEntities();
+    void tick();
 
     void thread() override;
 

+ 552 - 85
FactoryCraft/Entity.cpp

@@ -245,6 +245,7 @@ Entity::Entity(
       lastChunkCenter(0, 0),
       lastSavedChunkCenter(Framework::Maybe<Framework::Punkt>::empty()),
       lastDimensionId(-1),
+      maxMovementSpeed(0.f),
       speed(0, 0, 0),
       faceDir(1, 0, 0),
       target(0),
@@ -253,7 +254,8 @@ Entity::Entity(
       gravityMultiplier(1.f),
       jumpSpeed(0.f),
       id(entityId),
-      placeBlockCooldown(0)
+      placeBlockCooldown(0.0),
+      movementFlags(0)
 {}
 
 void Entity::onDeath(Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill)
@@ -350,7 +352,7 @@ bool Entity::useItem(int typeId, ItemStack* zStack, bool left)
                     }
                     if (result)
                     {
-                        placeBlockCooldown = 15;
+                        placeBlockCooldown = 1.0;
                     }
                     cs.unlock();
                     return result;
@@ -423,44 +425,13 @@ bool Entity::interact(Item* zItem, Entity* zActor)
     return false;
 }
 
-void Entity::addMovementFrame(MovementFrame& frame)
+void Entity::calculateTarget(const Item* zItem)
 {
-    cs.lock();
-    movements.add(frame);
-    cs.unlock();
-    Dimension* dim = Game::INSTANCE->zDimension(lastDimensionId);
-    if (dim)
-    {
-        Chunk* chunk = dim->zChunk(lastChunkCenter);
-        if (chunk)
-        {
-            NetworkMessage* message = new NetworkMessage();
-            message->addressEntity(this);
-            char* msg = new char[37];
-            msg[0] = 0;
-            *(float*)(msg + 1) = frame.direction.x;
-            *(float*)(msg + 5) = frame.direction.y;
-            *(float*)(msg + 9) = frame.direction.z;
-            *(float*)(msg + 13) = frame.targetPosition.x;
-            *(float*)(msg + 17) = frame.targetPosition.y;
-            *(float*)(msg + 21) = frame.targetPosition.z;
-            *(int*)(msg + 25) = frame.movementFlags;
-            *(double*)(msg + 29) = frame.duration;
-            message->setMessage(msg, 37);
-            chunk->notifyObservers(message);
-        }
-    }
-    faceDir = frame.direction;
-}
-
-void Entity::calculateTarget(Framework::Vec3<float> basePos,
-    Framework::Vec3<float> direction,
-    const Item* zItem)
-{
-    Framework::Vec3<float> headPosition = basePos + faceOffset;
+    Framework::Vec3<float> headPosition = location + faceOffset;
     int px = (int)floor(headPosition.x);
     int py = (int)floor(headPosition.y);
     int pz = (int)floor(headPosition.z);
+    Framework::Vec3<float> direction = faceDir;
     direction.normalize();
     Direction dir = BOTTOM;
     bool found = false;
@@ -617,78 +588,555 @@ ItemSkill* Entity::zSkill(int itemType)
     return 0;
 }
 
-void Entity::prepareTick(const Dimension* zDimension) {}
+void Entity::calcBlockCollision(int& x, int& y, int& xl, int& yl, bool** map)
+{
+    Framework::Vec3<float> minXY = -boundingBox;
+    Framework::Vec3<float> maxXY = boundingBox;
+    Framework::Vec3<float> minXmaxY(minXY.x, maxXY.y, minXY.z);
+    Framework::Vec3<float> maxXminY(maxXY.x, minXY.y, minXY.z);
+    maxXY.z = minXY.z;
+    minXY.rotateZ(rotation);
+    maxXY.rotateZ(rotation);
+    minXmaxY.rotateZ(rotation);
+    maxXminY.rotateZ(rotation);
+    minXY += location;
+    maxXY += location;
+    minXmaxY += location;
+    maxXminY += location;
+    x = (int)minXY.x;
+    y = (int)minXY.y;
+    if (x > (int)minXmaxY.x) x = (int)minXmaxY.x;
+    if (x > (int)maxXminY.x) x = (int)maxXminY.x;
+    if (x > (int)maxXY.x) x = (int)maxXY.x;
+    if (y > (int)minXmaxY.y) y = (int)minXmaxY.y;
+    if (y > (int)maxXminY.y) y = (int)maxXminY.y;
+    if (y > (int)maxXY.y) y = (int)maxXY.y;
+    xl = (int)minXY.x;
+    if (xl < (int)minXmaxY.x) xl = (int)minXmaxY.x;
+    if (xl < (int)maxXminY.x) xl = (int)maxXminY.x;
+    if (xl < (int)maxXY.x) xl = (int)maxXY.x;
+    yl = (int)minXY.y;
+    if (yl < (int)minXmaxY.y) yl = (int)minXmaxY.y;
+    if (yl < (int)maxXminY.y) yl = (int)maxXminY.y;
+    if (yl < (int)maxXY.y) yl = (int)maxXY.y;
+    xl = xl - x + 1;
+    yl = yl - y + 1;
+    *map = new bool[xl * yl];
+    memset(*map, 0, xl * yl * sizeof(bool));
+    int xi = (int)minXY.x - x;
+    int yi = (int)minXY.y - y;
+    (*map)[xi + yi * xl] = 1;
+    xi = (int)maxXY.x - x;
+    yi = (int)maxXY.y - y;
+    (*map)[xi + yi * xl] = 1;
+    xi = (int)minXmaxY.x - x;
+    yi = (int)minXmaxY.y - y;
+    (*map)[xi + yi * xl] = 1;
+    xi = (int)maxXminY.x - x;
+    yi = (int)maxXminY.y - y;
+    (*map)[xi + yi * xl] = 1;
+    auto dir = minXmaxY - minXY;
+    float m = 0;
+    if (abs(dir.x) >= abs(dir.y))
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.y / dir.x;
+        }
+        float yy = minXY.y;
+        for (int xx = (int)minXY.x; xx < (int)minXmaxY.x; xx++)
+        {
+            int xi = xx - x;
+            int yi = (int)yy - y;
+            (*map)[xi + yi * xl] = 1;
+            yy += m;
+        }
+    }
+    else
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.x / dir.y;
+        }
+        float xx = minXY.x;
+        for (int yy = (int)minXY.y; yy < (int)minXmaxY.y; yy++)
+        {
+            int xi = (int)xx - x;
+            int yi = yy - y;
+            (*map)[xi + yi * xl] = 1;
+            xx += m;
+        }
+    }
+    dir = maxXminY - minXY;
+    m = 0;
+    if (abs(dir.x) >= abs(dir.y))
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.y / dir.x;
+        }
+        float yy = minXY.y;
+        for (int xx = (int)minXY.x; xx < (int)maxXminY.x; xx++)
+        {
+            int xi = xx - x;
+            int yi = (int)yy - y;
+            (*map)[xi + yi * xl] = 1;
+            yy += m;
+        }
+    }
+    else
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.x / dir.y;
+        }
+        float xx = minXY.x;
+        for (int yy = (int)minXY.y; yy < (int)maxXminY.y; yy++)
+        {
+            int xi = (int)xx - x;
+            int yi = yy - y;
+            (*map)[xi + yi * xl] = 1;
+            xx += m;
+        }
+    }
+    dir = maxXY - maxXminY;
+    m = 0;
+    if (abs(dir.x) >= abs(dir.y))
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.y / dir.x;
+        }
+        float yy = maxXminY.y;
+        for (int xx = (int)maxXminY.x; xx < (int)maxXY.x; xx++)
+        {
+            int xi = xx - x;
+            int yi = (int)yy - y;
+            (*map)[xi + yi * xl] = 1;
+            yy += m;
+        }
+    }
+    else
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.x / dir.y;
+        }
+        float xx = maxXminY.x;
+        for (int yy = (int)maxXminY.y; yy < (int)maxXY.y; yy++)
+        {
+            int xi = (int)xx - x;
+            int yi = yy - y;
+            (*map)[xi + yi * xl] = 1;
+            xx += m;
+        }
+    }
+    dir = maxXY - minXmaxY;
+    m = 0;
+    if (abs(dir.x) >= abs(dir.y))
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.y / dir.x;
+        }
+        float yy = minXmaxY.y;
+        for (int xx = (int)minXmaxY.x; xx < (int)maxXY.x; xx++)
+        {
+            int xi = xx - x;
+            int yi = (int)yy - y;
+            (*map)[xi + yi * xl] = 1;
+            yy += m;
+        }
+    }
+    else
+    {
+        if (dir.x != 0 && dir.y != 0)
+        {
+            m = dir.x / dir.y;
+        }
+        float xx = minXmaxY.x;
+        for (int yy = (int)minXmaxY.y; yy < (int)maxXY.y; yy++)
+        {
+            int xi = (int)xx - x;
+            int yi = yy - y;
+            (*map)[xi + yi * xl] = 1;
+            xx += m;
+        }
+    }
+    for (int yy = 0; yy < yl; yy++)
+    {
+        int min = xl;
+        int max = -1;
+        for (int xx = 0; xx < xl; xx++)
+        {
+            if ((*map)[xx + yy * xl])
+            {
+                if (xx < min) min = xx;
+                if (xx > max) max = xx;
+            }
+        }
+        for (int xx = min; xx <= max; xx++)
+        {
+            (*map)[xx + yy * xl] = 1;
+        }
+    }
+}
+
+bool Entity::isCollidingWithBlock(const Dimension* zDimension)
+{
+    int x, y, xl, yl;
+    bool* map;
+    calcBlockCollision(x, y, xl, yl, &map);
+    bool result = isCollidingWithBlock(zDimension, x, y, xl, yl, map);
+    delete[] map;
+    return result;
+}
+
+bool Entity::isCollidingWithBlock(
+    const Dimension* zDimension, int x, int y, int xl, int yl, bool* map)
+{
+    for (int yy = 0; yy < yl; yy++)
+    {
+        for (int xx = 0; xx < xl; xx++)
+        {
+            if (map[xx + yy * xl])
+            {
+                for (int zz = (int)(location.z - boundingBox.z);
+                    zz <= (int)(location.z + boundingBox.z);
+                    zz++)
+                {
+                    const Block* block = zDimension->zBlockOrDefault(
+                        Framework::Vec3<int>(x + xx, y + yy, zz));
+                    if (!block->isPassable())
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
 
-void Entity::tick(const Dimension* zDimension)
+void Entity::prepareTick(const Dimension* zDimension, double seconds) {}
+
+void Entity::tick(const Dimension* zDimension, double seconds)
 {
     if (removed) return;
     if (placeBlockCooldown > 0)
     {
-        placeBlockCooldown--;
+        placeBlockCooldown -= seconds;
+    }
+    bool changed = 0;
+    float rotSpeed = 0.f;
+    if (movementFlags & MovementFlags::ROTATE_LEFT)
+    {
+        rotSpeed += 3.f * (float)seconds;
+    }
+    if (movementFlags & MovementFlags::ROTATE_RIGHT)
+    {
+        rotSpeed -= 3.f * (float)seconds;
     }
-    placeBlockCooldown--;
-    if (time.isMeasuring())
+    if (movementFlags & MovementFlags::ROTATE_TO_FACE)
     {
-        time.messungEnde();
-        if (movements.getEintragAnzahl() > 0)
+        float rot = Framework::Vec2<float>(0, -1).angle({faceDir.x, faceDir.y});
+        rotSpeed = rot - rotation;
+    }
+    if (rotSpeed != 0.f)
+    {
+        rotation += rotSpeed;
+        if (isCollidingWithBlock(zDimension))
+        {
+            rotation -= rotSpeed;
+        }
+        else
+        {
+            changed = 1;
+        }
+        while (rotation > 2.f * (float)PI)
         {
-            MovementFrame currentFrame = movements.get(0);
-            double seconds = time.getSekunden();
-            while (seconds > 0)
+            rotation -= 2.f * (float)PI;
+        }
+        while (rotation < 0.f)
+        {
+            rotation += 2.f * (float)PI;
+        }
+    }
+    Framework::Vec3<float> moveDir(0.f, 0.f, 0.f);
+    if (movementFlags & MovementFlags::WALK_FORWARD)
+    {
+        moveDir.x += cosf(rotation - (float)PI / 2.f);
+        moveDir.y += sinf(rotation - (float)PI / 2.f);
+    }
+    if (movementFlags & MovementFlags::WALK_BACKWARD)
+    {
+        moveDir.x -= cosf(rotation - (float)PI / 2.f);
+        moveDir.y -= sinf(rotation - (float)PI / 2.f);
+    }
+    if (movementFlags & MovementFlags::WALK_LEFT)
+    {
+        moveDir.x += cosf(rotation + (float)PI);
+        moveDir.y += sinf(rotation + (float)PI);
+    }
+    if (movementFlags & MovementFlags::WALK_RIGHT)
+    {
+        moveDir.x += cosf(rotation);
+        moveDir.y += sinf(rotation);
+    }
+    if (movementFlags & MovementFlags::FLYING
+        && movementFlags & MovementFlags::JUMPING)
+    {
+        moveDir.z += 1.f;
+        movementFlags &= ~MovementFlags::GROUND_CONTACT;
+    }
+    if (movementFlags & MovementFlags::FLYING
+        && movementFlags & MovementFlags::SNEAKING)
+    {
+        moveDir.z -= 1.f;
+    }
+    float staminaCost = 0.f;
+    if (moveDir.x != 0.f || moveDir.y != 0.f)
+    {
+        moveDir.normalize();
+        moveDir *= maxMovementSpeed * (float)seconds;
+        if (movementFlags & MovementFlags::SPRINTING)
+        {
+            moveDir *= 2.f;
+        }
+        if (movementFlags & MovementFlags::SNEAKING
+            && !(movementFlags & MovementFlags::FLYING))
+        {
+            moveDir *= 0.5f;
+        }
+        staminaCost = moveDir.getLength() / (maxMovementSpeed * 10);
+        if (stamina < staminaCost)
+        {
+            staminaCost = stamina;
+            moveDir *= stamina / staminaCost;
+        }
+    }
+    if (!(movementFlags & MovementFlags::FLYING)
+        && movementFlags & MovementFlags::JUMPING
+        && movementFlags & MovementFlags::GROUND_CONTACT)
+    {
+        if (stamina > staminaCost + 0.02f)
+        {
+            staminaCost += 0.02f;
+            speed.z += jumpSpeed;
+            movementFlags &= ~MovementFlags::GROUND_CONTACT;
+        }
+    }
+    if (!(movementFlags & MovementFlags::FLYING))
+    {
+        speed.z
+            -= zDimension->getGravity() * gravityMultiplier * (float)seconds;
+    }
+    moveDir += speed * (float)seconds;
+    bool staminaChanged = staminaCost != 0.f;
+    stamina -= staminaCost;
+    if (stamina <= maxStamina - (float)seconds)
+    {
+        if (getThirst() > 0 && getHunger() > 0)
+        { // TODO: modify regen rate based on hunger/thirst
+            stamina += (float)seconds;
+            staminaChanged = 1;
+            setHunger(hunger - (float)seconds / 5.f);
+            setThirst(thirst - (float)seconds / 2.5f);
+        }
+    }
+    if (staminaChanged)
+    {
+        setStamina(stamina); // notify observers
+    }
+    if (moveDir.x != 0.f || moveDir.y != 0.f || moveDir.z != 0.f)
+    {
+        while (abs(moveDir.x) > 1.f)
+        {
+            if (moveDir.x > 0.f)
             {
-                if (currentFrame.duration <= 0)
+                location.x += 1.f;
+                if (isCollidingWithBlock(zDimension))
                 {
-                    cs.lock();
-                    movements.remove(0);
-                    cs.unlock();
-                    if (movements.getEintragAnzahl() > 0)
-                        currentFrame = movements.get(0);
-                    else
-                        break;
+                    moveDir.x = 0.f;
+                    speed.x = 0.f;
+                    location.x -= 1.f;
+                }
+                else
+                {
+                    moveDir.x -= 1.f;
+                    changed = 1;
                 }
-                double t = MIN(currentFrame.duration, seconds);
-                // TODO: add collision detection to reduce cheating capability
-                location += (currentFrame.targetPosition - location)
-                          * (float)(t / currentFrame.duration);
-                currentFrame.duration -= t;
-                seconds -= t;
-                if (currentFrame.duration <= 0)
+            }
+            else
+            {
+                location.x -= 1.f;
+                if (isCollidingWithBlock(zDimension))
+                {
+                    moveDir.x = 0.f;
+                    speed.x = 0.f;
+                    location.x += 1.f;
+                }
+                else
+                {
+                    moveDir.x += 1.f;
+                    changed = 1;
+                }
+            }
+        }
+        if (moveDir.x != 0.f)
+        {
+            location.x += moveDir.x;
+            if (isCollidingWithBlock(zDimension))
+            {
+                location.x -= moveDir.x;
+                speed.x = 0.f;
+            }
+            else
+            {
+                changed = 1;
+            }
+        }
+        while (abs(moveDir.y) > 1.f)
+        {
+            if (moveDir.y > 0.f)
+            {
+                location.y += 1.f;
+                if (isCollidingWithBlock(zDimension))
+                {
+                    moveDir.y = 0.f;
+                    location.y -= 1.f;
+                    speed.y = 0.f;
+                }
+                else
                 {
-                    location = currentFrame.targetPosition;
+                    moveDir.y -= 1.f;
+                    changed = 1;
                 }
             }
-            if (currentFrame.duration > 0) movements.set(currentFrame, 0);
-            if (getStamina() <= getMaxStamina() - 0.0025f)
+            else
             {
-                if (getThirst() > 0 && getHunger() > 0)
+                location.y -= 1.f;
+                if (isCollidingWithBlock(zDimension))
                 {
-                    setStamina(getStamina() + 0.0025f);
-                    setHunger(getHunger() - 0.0005f);
-                    setThirst(getThirst() - 0.0015f);
+                    moveDir.y = 0.f;
+                    location.y += 1.f;
+                    speed.y = 0.f;
+                }
+                else
+                {
+                    moveDir.y += 1.f;
+                    changed = 1;
                 }
             }
         }
-        else
+        if (moveDir.y != 0.f)
+        {
+            location.y += moveDir.y;
+            if (isCollidingWithBlock(zDimension))
+            {
+                location.y -= moveDir.y;
+                speed.y = 0.f;
+            }
+            else
+            {
+                changed = 1;
+            }
+        }
+        while (abs(moveDir.z) > 1.f)
+        {
+            if (moveDir.z > 0.f)
+            {
+                location.z += 1.f;
+                if (isCollidingWithBlock(zDimension))
+                {
+                    moveDir.z = 0.f;
+                    location.z -= 1.f;
+                    speed.z = 0.f;
+                }
+                else
+                {
+                    moveDir.z -= 1.f;
+                    changed = 1;
+                }
+            }
+            else
+            {
+                location.z -= 1.f;
+                if (isCollidingWithBlock(zDimension))
+                {
+                    moveDir.z = 0.f;
+                    location.z += 1.f;
+                    onFall(speed.z);
+                    speed.z = 0.f;
+                    movementFlags |= MovementFlags::GROUND_CONTACT;
+                }
+                else
+                {
+                    moveDir.z += 1.f;
+                    changed = 1;
+                }
+            }
+        }
+        if (moveDir.z != 0.f)
         {
-            if (getStamina() <= getMaxStamina() - 0.005f)
+            location.z += moveDir.z;
+            if (isCollidingWithBlock(zDimension))
             {
-                if (getThirst() > 0 && getHunger() > 0)
+                if (speed.z < 0.f)
                 {
-                    setStamina(getStamina() + 0.005f);
-                    setHunger(getHunger() - 0.001f);
-                    setThirst(getThirst() - 0.003f);
+                    onFall(speed.z);
+                    speed.z = 0.f;
+                    movementFlags |= MovementFlags::GROUND_CONTACT;
                 }
+                else
+                {
+                    changed = 1;
+                }
+                location.z -= moveDir.z;
+            }
+        }
+    }
+    if (movementFlags & MovementFlags::GROUND_CONTACT)
+    {
+        if (speed.x != 0)
+        {
+            if (speed.x > (float)seconds)
+            {
+                speed.x -= (float)seconds;
+            }
+            else if (speed.x < -(float)seconds)
+            {
+                speed.x += (float)seconds;
+            }
+            else
+            {
+                speed.x = 0;
+            }
+        }
+        if (speed.y != 0)
+        {
+            if (speed.y > (float)seconds)
+            {
+                speed.y -= (float)seconds;
+            }
+            else if (speed.y < -(float)seconds)
+            {
+                speed.y += (float)seconds;
+            }
+            else
+            {
+                speed.y = 0;
             }
         }
     }
-    time.messungStart();
     Framework::Punkt chunkCenter
         = Game::INSTANCE->getChunkCenter((int)location.x, (int)location.y);
+    Chunk* zCurrentChunk = 0;
     if (dimensionId != lastDimensionId || chunkCenter != lastChunkCenter)
     {
         Dimension* lastDimension = Game::INSTANCE->zDimension(lastDimensionId);
         Dimension* currentDimension = Game::INSTANCE->zDimension(dimensionId);
-        Chunk* zCurrentChunk
+        zCurrentChunk
             = currentDimension ? currentDimension->zChunk(chunkCenter) : 0;
         Chunk* zLastChunk
             = lastDimension ? lastDimension->zChunk(lastChunkCenter) : 0;
@@ -708,6 +1156,22 @@ void Entity::tick(const Dimension* zDimension)
         lastDimensionId = dimensionId;
         lastChunkCenter = chunkCenter;
     }
+    if (changed)
+    {
+        if (!zCurrentChunk)
+        {
+            Dimension* currentDimension
+                = Game::INSTANCE->zDimension(dimensionId);
+            zCurrentChunk
+                = currentDimension ? currentDimension->zChunk(chunkCenter) : 0;
+        }
+        if (zCurrentChunk)
+        {
+            NetworkMessage* msg = new NetworkMessage();
+            msg->sendEntityMovement(this, (float)seconds);
+            zCurrentChunk->notifyObservers(msg);
+        }
+    }
 }
 
 void Entity::api(Framework::StreamReader* zRequest,
@@ -763,9 +1227,9 @@ void Entity::api(Framework::StreamReader* zRequest,
 
 void Entity::onFall(float collisionSpeed)
 {
-    if (collisionSpeed > 10)
+    if (collisionSpeed > 20)
     {
-        setHP(this, 0, 0, getCurrentHP() - (collisionSpeed - 10.f) / 2.5f);
+        setHP(this, 0, 0, getCurrentHP() - (collisionSpeed - 20.f) / 2.5f);
     }
 }
 
@@ -964,7 +1428,7 @@ float Entity::getMaxSpeed() const
 
 bool Entity::isMoving() const
 {
-    return movements.getEintragAnzahl() > 0;
+    return movementFlags > 0;
 }
 
 int Entity::getChatSecurityLevel() const
@@ -990,12 +1454,10 @@ void Entity::setRemoved()
 double Entity::getHitDistance(
     Framework::Vec3<float> rayOrigin, Framework::Vec3<float> rayDirection) const
 {
-    Framework::Mat4<float> rotMat
-        = Framework::Mat4<float>::rotationX(-rotation.x)
-        * Framework::Mat4<float>::rotationY(-rotation.y)
-        * Framework::Mat4<float>::rotationZ(-rotation.z);
-    Framework::Vec3<float> rotatedRayOrigin = rotMat * rayOrigin;
-    Framework::Vec3<float> rotatedRayDirection = rotMat * rayDirection;
+    Framework::Vec3<float> rotatedRayOrigin
+        = Framework::Vec3<float>(rayOrigin).rotateZ(-rotation);
+    Framework::Vec3<float> rotatedRayDirection
+        = Framework::Vec3<float>(rayDirection).rotateZ(-rotation);
     rotatedRayDirection.normalize();
     if (rotatedRayDirection.x != 0)
     {
@@ -1077,3 +1539,8 @@ double Entity::getHitDistance(
     }
     return NAN;
 }
+
+float Entity::getRotation() const
+{
+    return rotation;
+}

+ 25 - 15
FactoryCraft/Entity.h

@@ -40,12 +40,21 @@ public:
     static ActionTarget* load(Framework::StreamReader* zReader);
 };
 
-struct MovementFrame
+class MovementFlags
 {
-    Framework::Vec3<float> direction;
-    Framework::Vec3<float> targetPosition;
-    int movementFlags;
-    double duration;
+public:
+    static const int SNEAKING = 0x00001;
+    static const int SPRINTING = 0x00002;
+    static const int FLYING = 0x00004;
+    static const int JUMPING = 0x00008;
+    static const int WALK_FORWARD = 0x00010;
+    static const int WALK_BACKWARD = 0x00020;
+    static const int WALK_LEFT = 0x00040;
+    static const int WALK_RIGHT = 0x00080;
+    static const int ROTATE_LEFT = 0x00100;
+    static const int ROTATE_RIGHT = 0x00200;
+    static const int GROUND_CONTACT = 0x01000;
+    static const int ROTATE_TO_FACE = 0x02000;
 };
 
 class Entity : public Inventory
@@ -78,12 +87,11 @@ protected:
     float gravityMultiplier;
     float jumpSpeed;
     int id;
-    int placeBlockCooldown;
-    Framework::ZeitMesser time;
-    Framework::Array<MovementFrame> movements;
+    double placeBlockCooldown;
     Framework::Critical cs;
     Framework::Vec3<float> boundingBox;
-    Framework::Vec3<float> rotation;
+    float rotation;
+    int movementFlags;
 
     virtual void onDeath(
         Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill);
@@ -92,16 +100,17 @@ protected:
         Framework::Vec3<float> location,
         int dimensionId,
         int entityId);
-    void addMovementFrame(MovementFrame& frame);
-    void calculateTarget(Framework::Vec3<float> basePos,
-        Framework::Vec3<float> direction,
-        const Item* zItem);
+    void calculateTarget(const Item* zItem);
     void notifyStatusBarObservers(NetworkMessage* msg);
     ItemSkill* zSkill(int itemType);
+    void calcBlockCollision(int& x, int& y, int& xl, int& yl, bool** map);
+    bool isCollidingWithBlock(const Dimension* zDimension);
+    bool isCollidingWithBlock(
+        const Dimension* zDimension, int x, int y, int xl, int yl, bool* map);
 
 public:
-    virtual void prepareTick(const Dimension* zDimension);
-    virtual void tick(const Dimension* zDimension);
+    virtual void prepareTick(const Dimension* zDimension, double seconds);
+    virtual void tick(const Dimension* zDimension, double seconds);
 
     virtual void api(Framework::StreamReader* zRequest,
         NetworkMessage* zResponse,
@@ -149,6 +158,7 @@ public:
     void setRemoved();
     double getHitDistance(Framework::Vec3<float> rayOrigin,
         Framework::Vec3<float> rayDirection) const;
+    float getRotation() const;
 
     friend EntityType;
 };

+ 1 - 0
FactoryCraft/EntityType.cpp

@@ -133,6 +133,7 @@ const Framework::RCArray<DropConfig>& EntityType::getDropConfigs() const
 Entity* EntityType::loadEntity(Framework::StreamReader* zReader) const
 {
     Entity* entity = createEntity(Framework::Vec3<float>(0, 0, 0), 0, 0);
+    createSuperEntity(entity);
     loadSuperEntity(entity, zReader);
     return entity;
 }

+ 3 - 3
FactoryCraft/EntityType.h

@@ -35,6 +35,9 @@ protected:
     virtual void saveSuperEntity(
         Entity* zEntity, Framework::StreamWriter* zWriter) const;
     virtual void createSuperEntity(Entity* zEntity) const;
+    virtual Entity* createEntity(
+        Framework::Vec3<float> position, int dimensionId, int entityId) const
+        = 0;
 
 public:
     virtual bool initialize(Game* zGame);
@@ -47,9 +50,6 @@ public:
         Entity* zEntity, Framework::StreamWriter* zWriter) const;
     virtual Entity* createEntityAt(
         Framework::Vec3<float> position, int dimensionId) const;
-    virtual Entity* createEntity(
-        Framework::Vec3<float> position, int dimensionId, int entityId) const
-        = 0;
 
     int getId() const;
     ModelInfo* zModel() const;

+ 12 - 34
FactoryCraft/Game.cpp

@@ -404,8 +404,6 @@ void Game::thread()
 {
     ZeitMesser waitForLock;
     ZeitMesser removeOldClients;
-    ZeitMesser tickEntities;
-    ZeitMesser worldUpdates;
     ZeitMesser clientReply;
     ZeitMesser removeOldChunks;
     ZeitMesser m;
@@ -494,17 +492,10 @@ void Game::thread()
         for (auto i : removed)
             clients->remove(i);
         removeOldClients.messungEnde();
-        cs.unlock();
-        tickEntities.messungStart();
-        for (auto dim : *dimensions)
-            dim->tickEntities();
-        tickEntities.messungEnde();
-        waitForLock.messungStart();
-        cs.lock();
-        waitForLock.messungEnde();
-        waitTotal += waitForLock.getSekunden();
-        worldUpdates.messungStart();
-        worldUpdates.messungEnde();
+        for (Dimension* dim : *dimensions)
+        {
+            dim->tick();
+        }
         cs.unlock();
         clientReply.messungStart();
         for (auto client : *clients)
@@ -548,8 +539,6 @@ void Game::thread()
             Framework::Logging::trace()
                 << "waiting: " << waitTotal
                 << "\nremoveOldClients: " << removeOldClients.getSekunden()
-                << "\ntickEntities:" << tickEntities.getSekunden()
-                << "\nworldUpdates: " << worldUpdates.getSekunden()
                 << "\nclientReply: " << clientReply.getSekunden()
                 << "\nremoveOldChunks:" << removeOldChunks.getSekunden();
         }
@@ -797,9 +786,8 @@ GameClient* Game::addPlayer(FCKlient* client, Framework::Text name)
     buffer->schreibe("\0", 1);
     dim->api(buffer, 0, player);
     buffer->release();
-    while (isNew
-           && !dim->zChunk(getChunkCenter(
-               (int)player->getPosition().x, (int)player->getPosition().y)))
+    while (!dim->zChunk(getChunkCenter(
+        (int)player->getPosition().x, (int)player->getPosition().y)))
     {
         cs.unlock();
         Sleep(1000);
@@ -820,18 +808,7 @@ GameClient* Game::addPlayer(FCKlient* client, Framework::Text name)
         player->setPosition(
             {player->getPosition().x, player->getPosition().y, (float)h + 2.f});
     }
-    Dimension* zDim = zDimension(player->getDimensionId());
-    if (zDim)
-    {
-        zDim->addEntity(player);
-    }
-    else
-    {
-        Framework::Logging::error()
-            << "could not add player to dimension "
-            << (int)player->getDimensionId() << ". Dimension not loaded.";
-        player->release();
-    }
+    dim->addEntity(player);
     chat->addObserver(gameClient->zEntity()->getId());
     chat->broadcastMessage(name + " joined the game.", Chat::CHANNEL_INFO);
     cs.unlock();
@@ -887,16 +864,17 @@ void Game::spawnItem(
 void Game::spawnItem(
     Framework::Vec3<float> location, int dimensionId, ItemStack* stack)
 {
-    ItemEntity* itemEntity
-        = (ItemEntity*)zEntityType(EntityTypeEnum::ITEM)
-              ->createEntity(
-                  location, dimensionId, Game::INSTANCE->getNextEntityId());
+    ItemEntity* itemEntity = (ItemEntity*)zEntityType(EntityTypeEnum::ITEM)
+                                 ->createEntityAt(location, dimensionId);
     itemEntity->unsaveAddItem(stack, NO_DIRECTION, 0);
     stack->release();
     Dimension* dim = zDimension(dimensionId);
     if (dim)
     {
         dim->addEntity(itemEntity);
+        dim->zChunk(Game::getChunkCenter((int)itemEntity->getLocation().x,
+                        (int)itemEntity->getLocation().y))
+            ->onEntityEnters(itemEntity, 0);
     }
     else
     {

+ 0 - 9
FactoryCraft/GameClient.cpp

@@ -104,15 +104,6 @@ void GameClient::reply()
         client->zForegroundWriter()->schreibe((char*)&id, 4);
         id = zPlayer->getDimensionId();
         client->zForegroundWriter()->schreibe((char*)&id, 4);
-        client->zForegroundWriter()->schreibe((char*)&Message::API_MESSAGE, 1);
-        int len = 10;
-        client->zForegroundWriter()->schreibe((char*)&len, 4);
-        client->zForegroundWriter()->schreibe("\1", 1);
-        client->zForegroundWriter()->schreibe((char*)&id, 4);
-        client->zForegroundWriter()->schreibe("\6", 1);
-        float gravity = Game::INSTANCE->zDimension(zPlayer->getDimensionId())
-                            ->getGravity();
-        client->zForegroundWriter()->schreibe((char*)&gravity, 4);
         foreground.unlock();
         first = 0;
     }

+ 8 - 6
FactoryCraft/ItemEntity.cpp

@@ -26,12 +26,14 @@ ItemEntity::ItemEntity(
     setThirst(10);
     targetDistanceLimit = 4;
     maxMovementSpeed = 1;
+    movementFlags |= MovementFlags::ROTATE_TO_FACE;
 }
 
-void ItemEntity::prepareTick(const Dimension* zDimension)
+void ItemEntity::prepareTick(const Dimension* zDimension, double seconds)
 {
     if (slot->zStack() == 0 && !removed) throw "Illegal State exception";
-    if (movements.getEintragAnzahl() <= 1)
+    // TODO: implement movement towards other item entities
+    /* if (movements.getEintragAnzahl() <= 1)
     {
         Entity* zOther = Game::INSTANCE->zNearestEntity(
             dimensionId, location, [this](Entity* zOther) {
@@ -51,11 +53,11 @@ void ItemEntity::prepareTick(const Dimension* zDimension)
                 = getPosition() + frame.direction * (0.5f * maxMovementSpeed);
             addMovementFrame(frame);
         }
-    }
-    Entity::prepareTick(zDimension);
+    }*/
+    Entity::prepareTick(zDimension, seconds);
 }
 
-void ItemEntity::tick(const Dimension* zDimension)
+void ItemEntity::tick(const Dimension* zDimension, double seconds)
 {
     Entity* zOther = Game::INSTANCE->zNearestEntity(
         dimensionId, location, [this](Entity* zOther) {
@@ -76,7 +78,7 @@ void ItemEntity::tick(const Dimension* zDimension)
             if (slot->getNumberOfItems() == 0) onDeath(0, 0, 0);
         }
     }
-    Entity::tick(zDimension);
+    Entity::tick(zDimension, seconds);
 }
 
 void ItemEntity::onFall(float collisionSpeed)

+ 2 - 2
FactoryCraft/ItemEntity.h

@@ -13,8 +13,8 @@ private:
     ItemEntity(Framework::Vec3<float> location, int dimensionId, int entityId);
 
 public:
-    void prepareTick(const Dimension* zDimension) override;
-    void tick(const Dimension* zDimension) override;
+    void prepareTick(const Dimension* zDimension, double seconds) override;
+    void tick(const Dimension* zDimension, double seconds) override;
 
     void onFall(float collisionSpeed) override;
     bool hasDefaultModel() const override;

+ 28 - 34
FactoryCraft/ModelInfo.cpp

@@ -48,6 +48,34 @@ void ModelInfo::writeTo(Framework::StreamWriter* zWriter) const
 void ModelInfo::setModelPath(Framework::Text path)
 {
     modelPath = path;
+    bool loades = 0;
+    if (path.positionVon(".m3/") > 0)
+    {
+        int pos = path.positionVon(".m3/", path.anzahlVon(".m3/") - 1) + 3;
+        Framework::M3Datei m3File(path.getTeilText(0, pos));
+        m3File.leseDaten();
+        Model3DData* data
+            = m3File.ladeModel(path.getTeilText(pos + 1), 0, new Text());
+        if (data)
+        {
+            auto tmp = data->getMaxPos();
+            boundingBox.x = abs(tmp.x);
+            boundingBox.y = abs(tmp.y);
+            boundingBox.z = abs(tmp.z);
+            tmp = data->getMinPos();
+            boundingBox.x = max(boundingBox.x, abs(tmp.x));
+            boundingBox.y = max(boundingBox.y, abs(tmp.y));
+            boundingBox.z = max(boundingBox.z, abs(tmp.z));
+            data->release();
+            loades = 1;
+        }
+    }
+    if (!loades)
+    {
+        boundingBox.x = 0.5f;
+        boundingBox.y = 0.5f;
+        boundingBox.z = 0.5f;
+    }
 }
 
 void ModelInfo::addTexturePath(Framework::Text path)
@@ -119,40 +147,6 @@ ModelInfo* ModelInfoFactory::fromJson(Framework::JSON::JSONObject* zJson) const
         result->boundingBox.z
             = (float)bbArr->getValue(2)->asNumber()->getNumber();
     }
-    else
-    {
-        Text path = "data/models/";
-        path += result->getModelPath();
-        bool loades = 0;
-        if (path.positionVon(".m3/") > 0)
-        {
-            int pos = path.positionVon(".m3/", path.anzahlVon(".m3/") - 1) + 3;
-            Framework::M3Datei m3File(path.getTeilText(0, pos));
-            m3File.leseDaten();
-            Model3DData* data
-                = m3File.ladeModel(path.getTeilText(pos + 1), 0, new Text());
-            if (data)
-            {
-                auto tmp = data->getMaxPos();
-                result->boundingBox.x = abs(tmp.x);
-                result->boundingBox.y = abs(tmp.y);
-                result->boundingBox.z = abs(tmp.z);
-                tmp = data->getMinPos();
-                result->boundingBox.x = max(result->boundingBox.x, abs(tmp.x));
-                result->boundingBox.y = max(result->boundingBox.y, abs(tmp.y));
-                result->boundingBox.z = max(result->boundingBox.z, abs(tmp.z));
-                data->release();
-                loades = 1;
-            }
-        }
-        if (!loades)
-        {
-            result->boundingBox.x = 0.5f;
-            result->boundingBox.y = 0.5f;
-            result->boundingBox.z = 0.5f;
-        }
-    }
-
     return result;
 }
 

+ 14 - 6
FactoryCraft/NetworkMessage.cpp

@@ -257,12 +257,6 @@ void NetworkMessage::addEntityMessage(const Entity* zEntity)
         buffer.schreibe((char*)&pos.x, 4);
         buffer.schreibe((char*)&pos.y, 4);
         buffer.schreibe((char*)&pos.z, 4);
-        float maxSpeed = zEntity->getMaxSpeed();
-        buffer.schreibe((char*)&maxSpeed, 4);
-        float gravityMultiplier = zEntity->getGravityMultiplier();
-        buffer.schreibe((char*)&gravityMultiplier, 4);
-        float jumpSpeed = zEntity->getJumpSpeed();
-        buffer.schreibe((char*)&jumpSpeed, 4);
         bool special = !zEntity->hasDefaultModel();
         buffer.schreibe((char*)&special, 1);
         if (special) zEntity->zSpecialModel()->writeTo(&buffer);
@@ -285,6 +279,20 @@ void NetworkMessage::removeEntityMessage(const Entity* zEntity)
     }
 }
 
+void NetworkMessage::sendEntityMovement(const Entity* zEntity, float seconds)
+{
+    addressEntity(zEntity);
+    msgLength = 21;
+    message = new char[msgLength];
+    Framework::Vec3<float> pos = zEntity->getPosition();
+    message[0] = 0;
+    *(float*)(message + 1) = pos.x;
+    *(float*)(message + 5) = pos.y;
+    *(float*)(message + 9) = pos.z;
+    *(float*)(message + 13) = zEntity->getRotation();
+    *(float*)(message + 17) = seconds;
+}
+
 void NetworkMessage::writeTo(Framework::StreamWriter* zWriter) const
 {
     int total = msgLength + addressLength;

+ 1 - 0
FactoryCraft/NetworkMessage.h

@@ -57,6 +57,7 @@ public:
         double dayLength);
     void addEntityMessage(const Entity* zEntity);
     void removeEntityMessage(const Entity* zEntity);
+    void sendEntityMovement(const Entity* zEntity, float seconds);
 
     void writeTo(Framework::StreamWriter* zWriter) const;
     bool isBroadcast() const;

+ 20 - 22
FactoryCraft/Player.cpp

@@ -40,7 +40,8 @@ Player::Player(Framework::Vec3<float> location, int dimensionId, int entityId)
     jumping = 0;
     faceOffset = {0.f, 0.f, 1.5f};
     targetDistanceLimit = 4;
-    maxMovementSpeed = 4;
+    maxMovementSpeed = 1.f;
+    movementFlags |= MovementFlags::ROTATE_TO_FACE;
 }
 
 void Player::onTargetChange()
@@ -160,13 +161,13 @@ const char* Player::getName() const
     return name;
 }
 
-void Player::tick(const Dimension* zDimension)
+void Player::tick(const Dimension* zDimension, double seconds)
 {
     if ((keyState | Key::LEFT_HAND_ACTION) == keyState)
         useItemSlot(itemBar.get(leftHandPosition), true);
     if ((keyState | Key::RIGHT_HAND_ACTION) == keyState)
         useItemSlot(itemBar.get(leftHandPosition), false);
-    return Entity::tick(zDimension);
+    return Entity::tick(zDimension, seconds);
 }
 
 void Player::playerApi(
@@ -203,20 +204,13 @@ void Player::playerApi(
         }
         break;
     case 2:
-        // set movement
+        // set face direction
         {
-            MovementFrame frame;
-            zRequest->lese((char*)&frame.direction.x, 4);
-            zRequest->lese((char*)&frame.direction.y, 4);
-            zRequest->lese((char*)&frame.direction.z, 4);
-            zRequest->lese((char*)&frame.targetPosition.x, 4);
-            zRequest->lese((char*)&frame.targetPosition.y, 4);
-            zRequest->lese((char*)&frame.targetPosition.z, 4);
-            zRequest->lese((char*)&frame.movementFlags, 4);
-            zRequest->lese((char*)&frame.duration, 8);
-            addMovementFrame(frame);
-            calculateTarget(frame.targetPosition,
-                frame.direction,
+            zRequest->lese((char*)&faceDir.x, 4);
+            zRequest->lese((char*)&faceDir.y, 4);
+            zRequest->lese((char*)&faceDir.z, 4);
+            // TODO: calculate rotation and try to rotate without collision
+            calculateTarget(
                 !itemBar.get(leftHandPosition)->isEmpty()
                     ? itemBar.get(leftHandPosition)->zStack()->zItem()
                     : 0);
@@ -351,11 +345,13 @@ void Player::playerApi(
                 new QuestDialog(getId()));
             break;
         }
-    case 10: // fall damage
+    case 10: // change movement flags
         {
-            float speed = 0.f;
-            zRequest->lese((char*)&speed, 4);
-            onFall(speed);
+            int flags;
+            zRequest->lese((char*)&flags, 4);
+            flags &= 0xFFF; // only lower 12 bits are valid
+            flags |= movementFlags & 0xFFFFF000;
+            movementFlags = flags;
             break;
         }
     }
@@ -379,8 +375,10 @@ PlayerEntityType::PlayerEntityType()
     : EntityType()
 {
     setName("Player");
-    setModel(new ModelInfo(
-        "data/models/entities.m3/player", toArray("data/textures/entities.ltdb/player.png", 6), 0, 1.f));
+    setModel(new ModelInfo("data/models/entities.m3/player",
+        toArray("data/textures/entities.ltdb/player.png", 6),
+        0,
+        1.f));
 }
 
 void PlayerEntityType::loadSuperEntity(

+ 1 - 1
FactoryCraft/Player.h

@@ -37,7 +37,7 @@ public:
     virtual void onTargetChange() override;
     void setName(Framework::Text name);
     const char* getName() const;
-    void tick(const Dimension* zDimension) override;
+    void tick(const Dimension* zDimension, double seconds) override;
 
     void playerApi(
         Framework::StreamReader* zRequest, NetworkMessage* zResponse);

+ 1 - 1
FactoryCraft/Server.cpp

@@ -222,7 +222,7 @@ void FCKlient::onInitialized()
         int count = 0;
         while (count < length)
         {
-            int l = MIN(4096, length - count);
+            int l = MIN(4096, (int)length - count);
             file.lese(buffer, l);
             foreground->sende(buffer, l);
             count += l;