Пример #1
0
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
	super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
	m_PickupState(psNoPickup),
	m_DamageCoeff(2),
	m_IsCritical(false)
{
	SetSpeed(a_Speed);
	SetMass(0.1);
	SetRotationFromSpeed();
	SetPitchFromSpeed();
	LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
		m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
		GetRotation(), GetPitch()
	);
}
Пример #2
0
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
	super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
	m_PickupState(psNoPickup),
	m_DamageCoeff(2),
	m_IsCritical(false),
	m_Timer(0),
	m_HitGroundTimer(0),
	m_bIsCollected(false),
	m_HitBlockPos(Vector3i(0, 0, 0))
{
	SetSpeed(a_Speed);
	SetMass(0.1);
	SetYawFromSpeed();
	SetPitchFromSpeed();
	LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
		m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
		GetYaw(), GetPitch()
	);
}
Пример #3
0
bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
{
    cMinecartCollisionCallback MinecartCollisionCallback(GetPosition(), GetHeight(), GetWidth(), GetUniqueID(), ((m_Attachee == NULL) ? -1 : m_Attachee->GetUniqueID()));
    int ChunkX, ChunkZ;
    cChunkDef::BlockToChunk(POSX_TOINT, POSZ_TOINT, ChunkX, ChunkZ);
    m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);

    if (!MinecartCollisionCallback.FoundIntersection())
    {
        return false;
    }

    switch (a_RailMeta)
    {
    case E_META_RAIL_ZM_ZP:
    {
        if (MinecartCollisionCallback.GetCollidedEntityPosition().z >= GetPosZ())
        {
            if ((-GetSpeedZ() * 0.4) < 0.01)
            {
                AddSpeedZ(-4);
            }
            else
            {
                SetSpeedZ(-GetSpeedZ() * 0.4);
            }
        }
        else
        {
            if ((GetSpeedZ() * 0.4) < 0.01)
            {
                AddSpeedZ(4);
            }
            else
            {
                SetSpeedZ(GetSpeedZ() * 0.4);
            }
        }
        return true;
    }
    case E_META_RAIL_XM_XP:
    {
        if (MinecartCollisionCallback.GetCollidedEntityPosition().x >= GetPosX())
        {
            if ((-GetSpeedX() * 0.4) < 0.01)
            {
                AddSpeedX(-4);
            }
            else
            {
                SetSpeedX(-GetSpeedX() * 0.4);
            }
        }
        else
        {
            if ((GetSpeedX() * 0.4) < 0.01)
            {
                AddSpeedX(4);
            }
            else
            {
                SetSpeedX(GetSpeedX() * 0.4);
            }
        }
        return true;
    }
    case E_META_RAIL_CURVED_ZM_XM:
    case E_META_RAIL_CURVED_ZP_XP:
    {
        Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());

        // Prevent division by small numbers
        if (abs(Distance.z) < 0.001)
        {
            Distance.z = 0.001;
        }

        /* Check to which side the minecart is to be pushed.
        Let's consider a z-x-coordinate system where the minecart is the center (0/0).
        The minecart moves along the line x = -z, the perpendicular line to this is x = z.
        In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
        if (
            ((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
            ((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
        )
        {
            // Moving -X +Z
            if ((-GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
            {
                // ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
                AddSpeedX(-4 / sqrt(2.0));
                AddSpeedZ(4 / sqrt(2.0));
            }
            else
            {
                // ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
                SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
                SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
            }
        }
        else if ((GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
        {
            // Moving +X -Z
            // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
            AddSpeedX(4 / sqrt(2.0));
            AddSpeedZ(-4 / sqrt(2.0));
        }
        else
        {
            // ~ SpeedX > 0 Moving in the "right" direction
            SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
            SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
        }
        break;
    }
    case E_META_RAIL_CURVED_ZM_XP:
    case E_META_RAIL_CURVED_ZP_XM:
    {
        Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());

        // Prevent division by small numbers
        if (abs(Distance.z) < 0.001)
        {
            Distance.z = 0.001;
        }

        /* Check to which side the minecart is to be pushed.
        Let's consider a z-x-coordinate system where the minecart is the center (0/0).
        The minecart moves along the line x = z, the perpendicular line to this is x = -z.
        In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
        if (
            ((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
            ((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
        )
        {
            // Moving +X +Z
            if ((GetSpeedX() * 0.4) < 0.01)
            {
                // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
                AddSpeedX(4 / sqrt(2.0));
                AddSpeedZ(4 / sqrt(2.0));
            }
            else
            {
                // ~ SpeedX > 0 Moving in the "right" direction
                SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
                SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
            }
        }
        else if ((-GetSpeedX() * 0.4) < 0.01)
        {
            // Moving -X -Z
            // ~ SpeedX >= 0 Immobile or not moving in the "right" direction
            AddSpeedX(-4 / sqrt(2.0));
            AddSpeedZ(-4 / sqrt(2.0));
        }
        else
        {
            // ~ SpeedX < 0 Moving in the "right" direction
            SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
            SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
        }
        break;
    }
    default:
        break;
    }

    return false;
}
Пример #4
0
bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
{
    switch (a_RailMeta)
    {
    case E_META_RAIL_ZM_ZP:
    {
        if (GetSpeedZ() > 0)
        {
            BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ()));
            if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
            {
                // We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P
                cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ())), 0.5, 1);
                cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());

                if (bbBlock.DoesIntersect(bbMinecart))
                {
                    SetSpeed(0, 0, 0);
                    SetPosZ(floor(GetPosZ()) + 0.4);
                    return true;
                }
            }
        }
        else if (GetSpeedZ() < 0)
        {
            BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1);
            if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
            {
                cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1), 0.5, 1);
                cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight());

                if (bbBlock.DoesIntersect(bbMinecart))
                {
                    SetSpeed(0, 0, 0);
                    SetPosZ(floor(GetPosZ()) + 0.65);
                    return true;
                }
            }
        }
        break;
    }
    case E_META_RAIL_XM_XP:
    {
        if (GetSpeedX() > 0)
        {
            BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT);
            if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
            {
                cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT), 0.5, 1);
                cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());

                if (bbBlock.DoesIntersect(bbMinecart))
                {
                    SetSpeed(0, 0, 0);
                    SetPosX(floor(GetPosX()) + 0.4);
                    return true;
                }
            }
        }
        else if (GetSpeedX() < 0)
        {
            BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT);
            if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
            {
                cBoundingBox bbBlock(Vector3d(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT), 0.5, 1);
                cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());

                if (bbBlock.DoesIntersect(bbMinecart))
                {
                    SetSpeed(0, 0, 0);
                    SetPosX(floor(GetPosX()) + 0.65);
                    return true;
                }
            }
        }
        break;
    }
    case E_META_RAIL_CURVED_ZM_XM:
    case E_META_RAIL_CURVED_ZM_XP:
    case E_META_RAIL_CURVED_ZP_XM:
    case E_META_RAIL_CURVED_ZP_XP:
    {
        BLOCKTYPE BlockXM = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT);
        BLOCKTYPE BlockXP = m_World->GetBlock(POSX_TOINT + 1, POSY_TOINT, POSZ_TOINT);
        BLOCKTYPE BlockZM = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1);
        BLOCKTYPE BlockZP = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT + 1);
        if (
            (!IsBlockRail(BlockXM) && cBlockInfo::IsSolid(BlockXM)) ||
            (!IsBlockRail(BlockXP) && cBlockInfo::IsSolid(BlockXP)) ||
            (!IsBlockRail(BlockZM) && cBlockInfo::IsSolid(BlockZM)) ||
            (!IsBlockRail(BlockZP) && cBlockInfo::IsSolid(BlockZP))
        )
        {
            SetSpeed(0, 0, 0);
            SetPosition(POSX_TOINT + 0.5, GetPosY(), POSZ_TOINT + 0.5);
            return true;
        }
        break;
    }
    default:
        break;
    }
    return false;
}
Пример #5
0
void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
{
    switch (a_RailMeta)
    {
    case E_META_RAIL_ASCEND_XM:
    case E_META_RAIL_ASCEND_XP:
    case E_META_RAIL_XM_XP:
    {
        SetSpeedZ(0);
        SetPosZ(floor(GetPosZ()) + 0.5);
        break;
    }
    case E_META_RAIL_ASCEND_ZM:
    case E_META_RAIL_ASCEND_ZP:
    case E_META_RAIL_ZM_ZP:
    {
        SetSpeedX(0);
        SetPosX(floor(GetPosX()) + 0.5);
        break;
    }
    // Curved rail physics: once minecart has reached more than half of the block in the direction that it is travelling in, jerk it in the direction of curvature
    case E_META_RAIL_CURVED_ZM_XM:
    {
        if (GetPosZ() > floor(GetPosZ()) + 0.5)
        {
            if (GetSpeedZ() > 0)
            {
                SetSpeedX(-GetSpeedZ() * 0.7);
            }

            SetSpeedZ(0);
            SetPosZ(floor(GetPosZ()) + 0.5);
        }
        else if (GetPosX() > floor(GetPosX()) + 0.5)
        {
            if (GetSpeedX() > 0)
            {
                SetSpeedZ(-GetSpeedX() * 0.7);
            }

            SetSpeedX(0);
            SetPosX(floor(GetPosX()) + 0.5);
        }
        SetSpeedY(0);
        break;
    }
    case E_META_RAIL_CURVED_ZM_XP:
    {
        if (GetPosZ() > floor(GetPosZ()) + 0.5)
        {
            if (GetSpeedZ() > 0)
            {
                SetSpeedX(GetSpeedZ() * 0.7);
            }

            SetSpeedZ(0);
            SetPosZ(floor(GetPosZ()) + 0.5);
        }
        else if (GetPosX() < floor(GetPosX()) + 0.5)
        {
            if (GetSpeedX() < 0)
            {
                SetSpeedZ(GetSpeedX() * 0.7);
            }

            SetSpeedX(0);
            SetPosX(floor(GetPosX()) + 0.5);
        }
        SetSpeedY(0);
        break;
    }
    case E_META_RAIL_CURVED_ZP_XM:
    {
        if (GetPosZ() < floor(GetPosZ()) + 0.5)
        {
            if (GetSpeedZ() < 0)
            {
                SetSpeedX(GetSpeedZ() * 0.7);
            }

            SetSpeedZ(0);
            SetPosZ(floor(GetPosZ()) + 0.5);
        }
        else if (GetPosX() > floor(GetPosX()) + 0.5)
        {
            if (GetSpeedX() > 0)
            {
                SetSpeedZ(GetSpeedX() * 0.7);
            }

            SetSpeedX(0);
            SetPosX(floor(GetPosX()) + 0.5);
        }
        SetSpeedY(0);
        break;
    }
    case E_META_RAIL_CURVED_ZP_XP:
    {
        if (GetPosZ() < floor(GetPosZ()) + 0.5)
        {
            if (GetSpeedZ() < 0)
            {
                SetSpeedX(-GetSpeedZ() * 0.7);
            }

            SetSpeedZ(0);
            SetPosZ(floor(GetPosZ()) + 0.5);
        }
        else if (GetPosX() < floor(GetPosX()) + 0.5)
        {
            if (GetSpeedX() < 0)
            {
                SetSpeedZ(-GetSpeedX() * 0.7);
            }

            SetSpeedX(0);
            SetPosX(floor(GetPosX()) + 0.5);
        }
        SetSpeedY(0);
        break;
    }
    default:
        break;
    }
}
Пример #6
0
void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
{
    // Initialise to 'slow down' values
    int AccelDecelSpeed = -2;
    int AccelDecelNegSpeed = 2;

    if ((a_RailMeta & 0x8) == 0x8)
    {
        // Rail powered - set variables to 'speed up' values
        AccelDecelSpeed = 1;
        AccelDecelNegSpeed = -1;
    }

    switch (a_RailMeta & 0x07)
    {
    case E_META_RAIL_ZM_ZP:  // NORTHSOUTH
    {
        SetYaw(270);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);
        SetSpeedX(0);

        bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
        if (EntCol || BlckCol) return;

        if (GetSpeedZ() != 0)
        {
            if (GetSpeedZ() > 0)
            {
                AddSpeedZ(AccelDecelSpeed);
            }
            else
            {
                AddSpeedZ(AccelDecelNegSpeed);
            }
        }
        break;
    }
    case E_META_RAIL_XM_XP:  // EASTWEST
    {
        SetYaw(180);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);
        SetSpeedZ(0);

        bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
        if (EntCol || BlckCol) return;

        if (GetSpeedX() != 0)
        {
            if (GetSpeedX() > 0)
            {
                AddSpeedX(AccelDecelSpeed);
            }
            else
            {
                AddSpeedX(AccelDecelNegSpeed);
            }
        }
        break;
    }
    case E_META_RAIL_ASCEND_XM:  // ASCEND EAST
    {
        SetYaw(180);
        SetSpeedZ(0);

        if (GetSpeedX() >= 0)
        {
            if (GetSpeedX() <= MAX_SPEED)
            {
                AddSpeedX(AccelDecelSpeed);
                SetSpeedY(-GetSpeedX());
            }
        }
        else
        {
            AddSpeedX(AccelDecelNegSpeed);
            SetSpeedY(-GetSpeedX());
        }
        break;
    }
    case E_META_RAIL_ASCEND_XP:  // ASCEND WEST
    {
        SetYaw(180);
        SetSpeedZ(0);

        if (GetSpeedX() > 0)
        {
            AddSpeedX(AccelDecelSpeed);
            SetSpeedY(GetSpeedX());
        }
        else
        {
            if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
            {
                AddSpeedX(AccelDecelNegSpeed);
                SetSpeedY(GetSpeedX());
            }
        }
        break;
    }
    case E_META_RAIL_ASCEND_ZM:  // ASCEND NORTH
    {
        SetYaw(270);
        SetSpeedX(0);

        if (GetSpeedZ() >= 0)
        {
            if (GetSpeedZ() <= MAX_SPEED)
            {
                AddSpeedZ(AccelDecelSpeed);
                SetSpeedY(-GetSpeedZ());
            }
        }
        else
        {
            AddSpeedZ(AccelDecelNegSpeed);
            SetSpeedY(-GetSpeedZ());
        }
        break;
    }
    case E_META_RAIL_ASCEND_ZP:  // ASCEND SOUTH
    {
        SetYaw(270);
        SetSpeedX(0);

        if (GetSpeedZ() > 0)
        {
            AddSpeedZ(AccelDecelSpeed);
            SetSpeedY(GetSpeedZ());
        }
        else
        {
            if (GetSpeedZ() >= MAX_SPEED_NEGATIVE)
            {
                AddSpeedZ(AccelDecelNegSpeed);
                SetSpeedY(GetSpeedZ());
            }
        }
        break;
    }
    default:
        ASSERT(!"Unhandled powered rail metadata!");
        break;
    }
}
Пример #7
0
void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
    /*
    NOTE: Please bear in mind that taking away from negatives make them even more negative,
    adding to negatives make them positive, etc.
    */

    switch (a_RailMeta)
    {
    case E_META_RAIL_ZM_ZP:  // NORTHSOUTH
    {
        SetYaw(270);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);  // Don't move vertically as on ground
        SetSpeedX(0);  // Correct diagonal movement from curved rails

        // Execute both the entity and block collision checks
        bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
        if (EntCol || BlckCol) return;

        if (GetSpeedZ() != 0)  // Don't do anything if cart is stationary
        {
            if (GetSpeedZ() > 0)
            {
                // Going SOUTH, slow down
                AddSpeedZ(-0.1);
            }
            else
            {
                // Going NORTH, slow down
                AddSpeedZ(0.1);
            }
        }
        break;
    }
    case E_META_RAIL_XM_XP:  // EASTWEST
    {
        SetYaw(180);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);
        SetSpeedZ(0);

        bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
        if (EntCol || BlckCol) return;

        if (GetSpeedX() != 0)
        {
            if (GetSpeedX() > 0)
            {
                AddSpeedX(-0.1);
            }
            else
            {
                AddSpeedX(0.1);
            }
        }
        break;
    }
    case E_META_RAIL_ASCEND_ZM:  // ASCEND NORTH
    {
        SetYaw(270);
        SetSpeedX(0);

        if (GetSpeedZ() >= 0)
        {
            // SpeedZ POSITIVE, going SOUTH
            if (GetSpeedZ() <= MAX_SPEED)  // Speed limit
            {
                AddSpeedZ(0.5);  // Speed up
                SetSpeedY(-GetSpeedZ());  // Downward movement is negative (0 minus positive numbers is negative)
            }
        }
        else
        {
            // SpeedZ NEGATIVE, going NORTH
            AddSpeedZ(1);  // Slow down
            SetSpeedY(-GetSpeedZ());  // Upward movement is positive (0 minus negative number is positive number)
        }
        break;
    }
    case E_META_RAIL_ASCEND_ZP:  // ASCEND SOUTH
    {
        SetYaw(270);
        SetSpeedX(0);

        if (GetSpeedZ() > 0)
        {
            // SpeedZ POSITIVE, going SOUTH
            AddSpeedZ(-1);  // Slow down
            SetSpeedY(GetSpeedZ());  // Upward movement positive
        }
        else
        {
            if (GetSpeedZ() >= MAX_SPEED_NEGATIVE)  // Speed limit
            {
                // SpeedZ NEGATIVE, going NORTH
                AddSpeedZ(-0.5);  // Speed up
                SetSpeedY(GetSpeedZ());  // Downward movement negative
            }
        }
        break;
    }
    case E_META_RAIL_ASCEND_XM:  // ASCEND EAST
    {
        SetYaw(180);
        SetSpeedZ(0);

        if (GetSpeedX() >= 0)
        {
            if (GetSpeedX() <= MAX_SPEED)
            {
                AddSpeedX(0.5);
                SetSpeedY(-GetSpeedX());
            }
        }
        else
        {
            AddSpeedX(1);
            SetSpeedY(-GetSpeedX());
        }
        break;
    }
    case E_META_RAIL_ASCEND_XP:  // ASCEND WEST
    {
        SetYaw(180);
        SetSpeedZ(0);

        if (GetSpeedX() > 0)
        {
            AddSpeedX(-1);
            SetSpeedY(GetSpeedX());
        }
        else
        {
            if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
            {
                AddSpeedX(-0.5);
                SetSpeedY(GetSpeedX());
            }
        }
        break;
    }
    case E_META_RAIL_CURVED_ZM_XM:  // Ends pointing NORTH and WEST
    {
        SetYaw(315);  // Set correct rotation server side
        SetPosY(floor(GetPosY()) + 0.55);  // Levitate dat cart
        SetSpeedY(0);

        TestBlockCollision(a_RailMeta);
        TestEntityCollision(a_RailMeta);

        // SnapToRail handles turning

        break;
    }
    case E_META_RAIL_CURVED_ZM_XP:  // Curved NORTH EAST
    {
        SetYaw(225);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);

        TestBlockCollision(a_RailMeta);
        TestEntityCollision(a_RailMeta);

        break;
    }
    case E_META_RAIL_CURVED_ZP_XM:  // Curved SOUTH WEST
    {
        SetYaw(135);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);

        TestBlockCollision(a_RailMeta);
        TestEntityCollision(a_RailMeta);

        break;
    }
    case E_META_RAIL_CURVED_ZP_XP:  // Curved SOUTH EAST
    {
        SetYaw(45);
        SetPosY(floor(GetPosY()) + 0.55);
        SetSpeedY(0);

        TestBlockCollision(a_RailMeta);
        TestEntityCollision(a_RailMeta);

        break;
    }
    default:
    {
        ASSERT(!"Unhandled rail meta!");  // Dun dun DUN!
        break;
    }
    }
}
Пример #8
0
void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	// GetWorld()->BroadcastTeleportEntity(*this);  // Test position
	
	int BlockX = POSX_TOINT;
	int BlockY = (int)(GetPosY() - 0.5);
	int BlockZ = POSZ_TOINT;
	
	if (BlockY < 0)
	{
		// Fallen out of this world, just continue falling until out of sight, then destroy:
		if (BlockY < VOID_BOUNDARY)
		{
			Destroy(true);
		}
		return;
	}
	
	if (BlockY >= cChunkDef::Height)
	{
		// Above the world, just wait for it to fall back down
		return;
	}
	
	BLOCKTYPE BlockBelow = a_Chunk.GetBlock(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
	NIBBLETYPE BelowMeta = a_Chunk.GetMeta(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
	if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
	{
		// Fallen onto a block that breaks this into pickups (e. g. half-slab)
		// Must finish the fall with coords one below the block:
		cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
		Destroy(true);
		return;
	}
	else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
	{
		// Fallen onto a solid block
		/*
		LOGD(
			"Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.",
			BlockX, BlockY, BlockZ,
			BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width,
			ItemTypeToString(BlockBelow).c_str()
		);
		*/

		if (BlockY < cChunkDef::Height - 1)
		{
			cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
		}
		Destroy(true);
		return;
	}
	
	float MilliDt = a_Dt.count() * 0.001f;
	AddSpeedY(MilliDt * -9.8f);
	AddPosition(GetSpeed() * MilliDt);

	// If not static (one billionth precision) broadcast movement
	if ((fabs(GetSpeedX()) > std::numeric_limits<double>::epsilon()) || (fabs(GetSpeedZ()) > std::numeric_limits<double>::epsilon()))
	{
		BroadcastMovementUpdate();
	}
}
Пример #9
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	int BlockX = POSX_TOINT;
	int BlockY = POSY_TOINT;
	int BlockZ = POSZ_TOINT;

	// Position changed -> super::HandlePhysics() called
	GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ)

	// TODO Add collision detection with entities.
	a_Dt /= 1000;  // Convert from msec to sec
	Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world		
		AddSpeedY(m_Gravity * a_Dt);
		AddPosition(GetSpeed() * a_Dt);
		return;
	}
	
	int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
	int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
	BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
	BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
	if (!cBlockInfo::IsSolid(BlockIn))  // Making sure we are not inside a solid block
	{
		if (m_bOnGround)  // check if it's still on the ground
		{
			if (!cBlockInfo::IsSolid(BlockBelow))  // Check if block below is air or water.
			{
				m_bOnGround = false;
			}
		}
	}
	else
	{
		// Push out entity.
		BLOCKTYPE GotBlock;

		static const struct
		{
			int x, y, z;
		} gCrossCoords[] =
		{
			{ 1, 0,  0},
			{-1, 0,  0},
			{ 0, 0,  1},
			{ 0, 0, -1},
		} ;

		bool IsNoAirSurrounding = true;
		for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
		{
			if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
			{
				// The pickup is too close to an unloaded chunk, bail out of any physics handling
				return;
			}
			if (!cBlockInfo::IsSolid(GotBlock))
			{
				NextPos.x += gCrossCoords[i].x;
				NextPos.z += gCrossCoords[i].z;
				IsNoAirSurrounding = false;
				break;
			}
		}  // for i - gCrossCoords[]
			
		if (IsNoAirSurrounding)
		{
			NextPos.y += 0.5;
		}

		m_bOnGround = true;

		/*
		// DEBUG:
		LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
			m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
		);
		*/
	}

	if (!m_bOnGround)
	{
		float fallspeed;
		if (IsBlockWater(BlockIn))
		{
			fallspeed = m_Gravity * a_Dt / 3;  // Fall 3x slower in water.
		}
		else if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.y *= 0.05;  // Reduce overall falling speed
			fallspeed = 0;  // No falling.
		}
		else
		{
			// Normal gravity
			fallspeed = m_Gravity * a_Dt;
		}
		NextSpeed.y += fallspeed;
	}
	else
	{
		// Friction
		if (NextSpeed.SqrLength() > 0.0004f)
		{
			NextSpeed.x *= 0.7f / (1 + a_Dt);
			if (fabs(NextSpeed.x) < 0.05)
			{
				NextSpeed.x = 0;
			}
			NextSpeed.z *= 0.7f / (1 + a_Dt);
			if (fabs(NextSpeed.z) < 0.05)
			{
				NextSpeed.z = 0;
			}
		}
	}

	// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
	// might have different speed modifiers according to terrain.
	if (BlockIn == E_BLOCK_COBWEB)
	{
		NextSpeed.x *= 0.25;
		NextSpeed.z *= 0.25;
	}
					
	//Get water direction
	Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

	m_WaterSpeed *= 0.9f;		//Reduce speed each tick

	switch(WaterDir)
	{
		case X_PLUS:
			m_WaterSpeed.x = 0.2f;
			m_bOnGround = false;
			break;
		case X_MINUS:
			m_WaterSpeed.x = -0.2f;
			m_bOnGround = false;
			break;
		case Z_PLUS:
			m_WaterSpeed.z = 0.2f;
			m_bOnGround = false;
			break;
		case Z_MINUS:
			m_WaterSpeed.z = -0.2f;
			m_bOnGround = false;
			break;
			
	default:
		break;
	}

	if (fabs(m_WaterSpeed.x) < 0.05)
	{
		m_WaterSpeed.x = 0;
	}

	if (fabs(m_WaterSpeed.z) < 0.05)
	{
		m_WaterSpeed.z = 0;
	}

	NextSpeed += m_WaterSpeed;

	if (NextSpeed.SqrLength() > 0.f)
	{
		cTracer Tracer(GetWorld());
		// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse
		int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2);
		bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace);

		if (HasHit)
		{
			// Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
			// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
			if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
			{
				// Block hit was within our projected path
				// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
				// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
				if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
				if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
				if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;

				if (Tracer.HitNormal.y == 1) // Hit BLOCK_FACE_YP, we are on the ground
				{
					m_bOnGround = true;
				}

				// Now, set our position to the hit block (i.e. move part way along our intended trajectory)
				NextPos.Set(Tracer.RealHit.x, Tracer.RealHit.y, Tracer.RealHit.z);
				NextPos.x += Tracer.HitNormal.x * 0.1;
				NextPos.y += Tracer.HitNormal.y * 0.05;
				NextPos.z += Tracer.HitNormal.z * 0.1;
			}
			else
			{
				// We have hit a block but overshot our intended trajectory, move normally, safe in the warm cocoon of knowledge that we won't appear to teleport forwards on clients,
				// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
				// be henceforth seen again in the time of programmers and man alike
				// </&sensationalist>
				NextPos += (NextSpeed * a_Dt);
			}
		}
		else
		{
			// We didn't hit anything, so move =]
			NextPos += (NextSpeed * a_Dt);
		}
	}

	SetPosition(NextPos);
	SetSpeed(NextSpeed);
}
Пример #10
0
void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	if (m_IsInGround)
	{
		// Already-grounded projectiles don't move at all
		return;
	}
	
	Vector3d PerTickSpeed = GetSpeed() / 20;
	Vector3d Pos = GetPosition();
	
	// Trace the tick's worth of movement as a line:
	Vector3d NextPos = Pos + PerTickSpeed;
	cProjectileTracerCallback TracerCallback(this);
	if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
	{
		// Something has been hit, abort all other processing
		return;
	}
	// The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
	
	// Test for entity collisions:
	cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
	a_Chunk.ForEachEntity(EntityCollisionCallback);
	if (EntityCollisionCallback.HasHit())
	{
		// An entity was hit:
		Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff();

		// DEBUG:
		LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)",
			m_UniqueID,
			EntityCollisionCallback.GetHitEntity()->GetUniqueID(),
			EntityCollisionCallback.GetHitEntity()->GetClass(),
			HitPos.x, HitPos.y, HitPos.z,
			EntityCollisionCallback.GetMinCoeff()
		);
		
		OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
	}
	// TODO: Test the entities in the neighboring chunks, too

	// Update the position:
	SetPosition(NextPos);
	
	// Add slowdown and gravity effect to the speed:
	Vector3d NewSpeed(GetSpeed());
	NewSpeed.y += m_Gravity / 20;
	NewSpeed *= TracerCallback.GetSlowdownCoeff();
	SetSpeed(NewSpeed);
	SetRotationFromSpeed();
	SetPitchFromSpeed();

	// DEBUG:
	LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
		m_UniqueID,
		GetPosX(), GetPosY(), GetPosZ(),
		GetSpeedX(), GetSpeedY(), GetSpeedZ(),
		GetRotation(), GetPitch()
	);
}
Пример #11
0
void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);

	if (m_Health <= 0)
	{
		// The mob is dead, but we're still animating the "puff" they leave when they die
		m_DestroyTimer += a_Dt / 1000;
		if (m_DestroyTimer > 1)
		{
			Destroy(true);
		}
		return;
	}

	if ((m_Target != NULL) && m_Target->IsDestroyed())
		m_Target = NULL;

	// Burning in daylight
	HandleDaylightBurning(a_Chunk);

	a_Dt /= 1000;

	if (m_bMovingToDestination)
	{
		if (m_bOnGround)
		{
			m_Destination.y = FindFirstNonAirBlockPosition(m_Destination.x, m_Destination.z);

			if (DoesPosYRequireJump((int)floor(m_Destination.y)))
			{
				m_bOnGround = false;
				AddPosY(1.5); // Jump!!
			}
		}

		Vector3f Distance = m_Destination - GetPosition();
		if(!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move
		{
			Distance.y = 0;
			Distance.Normalize();
			Distance *= 5;
			SetSpeedX(Distance.x);
			SetSpeedZ(Distance.z);

			if (m_EMState == ESCAPING)
			{	//Runs Faster when escaping :D otherwise they just walk away
				SetSpeedX (GetSpeedX() * 2.f);
				SetSpeedZ (GetSpeedZ() * 2.f);
			}
		}
		else
		{
			if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate
			{
				FinishPathFinding();
			}
			else
			{
				TickPathFinding(); // We have reached the next point in our path, calculate another point
			}
		}
	}

	SetPitchAndYawFromDestination();
	HandleFalling();

	switch (m_EMState)
	{
		case IDLE:
		{
			// If enemy passive we ignore checks for player visibility
			InStateIdle(a_Dt);
			break;
		}	
		case CHASING:
		{
			// If we do not see a player anymore skip chasing action
			InStateChasing(a_Dt);
			break;
		}	
		case ESCAPING:
		{
			InStateEscaping(a_Dt);
			break;
		}
	}  // switch (m_EMState)

	BroadcastMovementUpdate();
}
Пример #12
0
void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);

	if (m_Health <= 0)
	{
		// The mob is dead, but we're still animating the "puff" they leave when they die
		m_DestroyTimer += a_Dt / 1000;
		if (m_DestroyTimer > 1)
		{
			Destroy(true);
		}
		return;
	}

	// Burning in daylight
	HandleDaylightBurning(a_Chunk);
	
	HandlePhysics(a_Dt,a_Chunk);
	BroadcastMovementUpdate();

	a_Dt /= 1000;

	if (m_bMovingToDestination)
	{
		Vector3f Pos( GetPosition() );
		Vector3f Distance = m_Destination - Pos;
		if( !ReachedDestination() )
		{
			Distance.y = 0;
			Distance.Normalize();
			Distance *= 3;
			SetSpeedX( Distance.x );
			SetSpeedZ( Distance.z );

			if (m_EMState == ESCAPING)
			{	//Runs Faster when escaping :D otherwise they just walk away
				SetSpeedX (GetSpeedX() * 2.f);
				SetSpeedZ (GetSpeedZ() * 2.f);
			}
		}
		else
		{
			m_bMovingToDestination = false;
		}

		if( GetSpeed().SqrLength() > 0.f )
		{
			if( m_bOnGround )
			{
				Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy();
				Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed;
				int NextHeight;
				if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight))
				{
					// The chunk at NextBlock is not loaded
					return;
				}
				if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 )
				{
					m_bOnGround = false;
					SetSpeedY(5.f); // Jump!!
				}
			}
		}
	}

	Vector3d Distance = m_Destination - GetPosition();
	if (Distance.SqrLength() > 0.1f)
	{
		double Rotation, Pitch;
		Distance.Normalize();
		VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
		SetHeadYaw (Rotation);
		SetRotation( Rotation );
		SetPitch( -Pitch );
	}

	switch (m_EMState)
	{
		case IDLE:
		{
			// If enemy passive we ignore checks for player visibility
			InStateIdle(a_Dt);
			break;
		}
	
		case CHASING:
		{
			// If we do not see a player anymore skip chasing action
			InStateChasing(a_Dt);
			break;
		}
	
		case ESCAPING:
		{
			InStateEscaping(a_Dt);
			break;
		}
	}  // switch (m_EMState)
}
Пример #13
0
void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
{
	
	super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling

	/*
	NOTE: Please bear in mind that taking away from negatives make them even more negative,
	adding to negatives make them positive, etc.
	*/
	
	// Get block meta below the cart
	int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
	NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ);
	double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed
	
	switch (BelowMeta)
	{
		case E_META_RAIL_ZM_ZP: // NORTHSOUTH
		{
			SetRotation(270);
			SpeedY = 0; // Don't move vertically as on ground
			SpeedX = 0; // Correct diagonal movement from curved rails
			
			if (SpeedZ != 0) // Don't do anything if cart is stationary
			{
				if (SpeedZ > 0)
				{
					// Going SOUTH, slow down
					SpeedZ = SpeedZ - 0.1;
				}
				else
				{
					// Going NORTH, slow down
					SpeedZ = SpeedZ + 0.1;
				}
			}
			break;
		}

		case E_META_RAIL_XM_XP: // EASTWEST
		{
			SetRotation(180);
			SpeedY = 0;
			SpeedZ = 0;

			if (SpeedX != 0)
			{
				if (SpeedX > 0)
				{
					SpeedX = SpeedX - 0.1;
				}
				else
				{
					SpeedX = SpeedX + 0.1;
				}
			}
			break;
		}

		case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
		{
			SetRotation(270);
			SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/
			SpeedX = 0;

			if (SpeedZ >= 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				if (SpeedZ <= MAX_SPEED) // Speed limit
				{
					SpeedZ = SpeedZ + 0.5; // Speed up
					SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative)
				}
				else
				{
					SpeedZ = MAX_SPEED; // Enforce speed limit
					SpeedY = (0 - SpeedZ);
				}
			}
			else
			{
				// SpeedZ NEGATIVE, going NORTH
				SpeedZ = SpeedZ + 0.4; // Slow down
				SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number)
			}
			break;
		}

		case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
		{
			SetRotation(270);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedX = 0;

			if (SpeedZ > 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				SpeedZ = SpeedZ - 0.4; // Slow down
				SpeedY = SpeedZ; // Upward movement positive
			}
			else
			{
				if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit
				{
					// SpeedZ NEGATIVE, going NORTH
					SpeedZ = SpeedZ - 0.5; // Speed up
					SpeedY = SpeedZ; // Downward movement negative
				}
				else
				{
					SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit
					SpeedY = SpeedZ;
				}
			}
			break;
		}

		case E_META_RAIL_ASCEND_XM: // ASCEND EAST
		{
			SetRotation(180);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedZ = 0;

			if (SpeedX >= 0)
			{
				if (SpeedX <= MAX_SPEED)
				{
					SpeedX = SpeedX + 0.5;
					SpeedY = (0 - SpeedX);
				}
				else
				{
					SpeedX = MAX_SPEED;
					SpeedY = (0 - SpeedX);
				}
			}
			else
			{
				SpeedX = SpeedX + 0.4;
				SpeedY = (0 - SpeedX);
			}
			break;
		}

		case E_META_RAIL_ASCEND_XP: // ASCEND WEST
		{
			SetRotation(180);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedZ = 0;

			if (SpeedX > 0)
			{
				SpeedX = SpeedX - 0.4;
				SpeedY = SpeedX;
			}
			else
			{
				if (SpeedX >= MAX_SPEED_NEGATIVE)
				{
					SpeedX = SpeedX - 0.5;
					SpeedY = SpeedX;
				}
				else
				{
					SpeedX = MAX_SPEED_NEGATIVE;
					SpeedY = SpeedX;
				}
			}
			break;
		}

		case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
		{
			SetRotation(315); // Set correct rotation server side
			SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart

			if (SpeedZ > 0) // Cart moving south
			{
				SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail)
			}
			else if (SpeedX > 0) // Cart moving east
			{
				SpeedZ = (0 - SpeedX); // Diagonally move northeast
			}
			break;
		}

		case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
		{
			SetRotation(225);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ > 0)
			{
				SpeedX = SpeedZ;
			}
			else if (SpeedX < 0)
			{
				SpeedZ = SpeedX;
			}
			break;
		}

		case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
		{
			SetRotation(135);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ < 0)
			{
				SpeedX = SpeedZ;
			}
			else if (SpeedX > 0)
			{
				SpeedZ = SpeedX;
			}
			break;
		}

		case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
		{
			SetRotation(45);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ < 0)
			{
				SpeedX = (0 - SpeedZ);
			}
			else if (SpeedX < 0)
			{
				SpeedZ = (0 - SpeedX);
			}
			break;
		}

		default:
		{
			ASSERT(!"Unhandled rail meta!"); // Dun dun DUN!
			break;
		}
	}

	// Set speed to speed variables
	SetSpeedX(SpeedX);
	SetSpeedY(SpeedY);
	SetSpeedZ(SpeedZ);


	// Broadcast position to client
	BroadcastMovementUpdate();
}
Пример #14
0
void cFloater::Tick(float a_Dt, cChunk & a_Chunk)
{
	HandlePhysics(a_Dt, a_Chunk);
	if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0)
	{
		if ((!m_CanPickupItem) && (m_AttachedMobID == -1))  // Check if you can't already pickup a fish and if the floater isn't attached to a mob.
		{
			if (m_CountDownTime <= 0)
			{
				m_World->BroadcastSoundEffect("random.splash", GetPosX(), GetPosY(), GetPosZ(), 1, 1);
				SetPosY(GetPosY() - 1);
				m_CanPickupItem = true;
				m_PickupCountDown = 20;
				m_CountDownTime = 100 + m_World->GetTickRandomNumber(800);
				LOGD("Floater %i can be picked up", GetUniqueID());
			}
			else if (m_CountDownTime == 20)  // Calculate the position where the particles should spawn and start producing them.
			{
				LOGD("Started producing particles for floater %i", GetUniqueID());
				m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8)));
				m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
			}
			else if (m_CountDownTime < 20)
			{
				m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6);
				m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
			}
			
			m_CountDownTime--;
			if (m_World->GetHeight((int) GetPosX(), (int) GetPosZ()) == (int) GetPosY())
			{
				if (m_World->IsWeatherWet() && m_World->GetTickRandomNumber(3) == 0)  // 25% chance of an extra countdown when being rained on.
				{
					m_CountDownTime--;
				}
			}
			else  // if the floater is underground it has a 50% chance of not decreasing the countdown.
			{
				if (m_World->GetTickRandomNumber(1) == 0)
				{
					m_CountDownTime++;
				}
			}
		}
		SetSpeedY(0.7);
	}

	if (CanPickup())  // Make sure the floater "loses its fish"
	{
		m_PickupCountDown--;
		if (m_PickupCountDown == 0)
		{
			m_CanPickupItem = false;
			LOGD("The fish is gone. Floater %i can not pick an item up.", GetUniqueID());
		}
	}

	if ((GetSpeed().Length() > 4) && (m_AttachedMobID == -1))
	{
		cFloaterEntityCollisionCallback Callback(this, GetPosition(), GetPosition() + GetSpeed() / 20);
		
		a_Chunk.ForEachEntity(Callback);
		if (Callback.HasHit())
		{
			AttachTo(Callback.GetHitEntity());
			Callback.GetHitEntity()->TakeDamage(*this);  // TODO: the player attacked the mob not the floater.
			m_AttachedMobID = Callback.GetHitEntity()->GetUniqueID();
		}
	}

	cFloaterCheckEntityExist EntityCallback;
	m_World->DoWithEntityByID(m_PlayerID, EntityCallback);
	if (!EntityCallback.DoesExist())  // The owner doesn't exist anymore. Destroy the floater entity.
	{
		Destroy(true);
	}

	if (m_AttachedMobID != -1)
	{
		m_World->DoWithEntityByID(m_AttachedMobID, EntityCallback);  // The mob the floater was attached to doesn't exist anymore.
		if (!EntityCallback.DoesExist())
		{
			m_AttachedMobID = -1;
		}
	}

	SetSpeedX(GetSpeedX() * 0.95);
	SetSpeedZ(GetSpeedZ() * 0.95);

	BroadcastMovementUpdate();
}
Пример #15
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	// TODO Add collision detection with entities.
	a_Dt /= 1000;
	Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
	int BlockX = (int) floor(NextPos.x);
	int BlockY = (int) floor(NextPos.y);
	int BlockZ = (int) floor(NextPos.z);
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world
		// TODO: Current speed should still be added to the entity position
		// Otherwise TNT explosions in the void will still effect the bottommost layers of the world
		return;
	}
	
	// Make sure we got the correct chunk and a valid one. No one ever knows...
	cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX,BlockZ);
	if (NextChunk != NULL)
	{
		int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
		int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
		BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
		if (!g_BlockIsSolid[BlockIn])  // Making sure we are not inside a solid block
		{
			if (m_bOnGround)  // check if it's still on the ground
			{
				BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ );
				if (!g_BlockIsSolid[BlockBelow])  // Check if block below is air or water.
				{
					m_bOnGround = false;
				}
			}
		}
		else
		{
			//Push out entity.
			m_bOnGround = true;
			NextPos.y += 0.2;
			LOGD("Entity #%d (%s) is inside a block at {%d,%d,%d}",
				m_UniqueID, GetClass(), BlockX, BlockY, BlockZ);
		}

		if (!m_bOnGround)
		{
			float fallspeed;
			if (IsBlockWater(BlockIn))
			{
				fallspeed = -3.0f * a_Dt; //Fall slower in water.
			}
			else if (BlockIn == E_BLOCK_COBWEB)
			{
				NextSpeed.y *= 0.05; //Reduce overall falling speed
				fallspeed = 0; //No falling.
			}
			else
			{
				//Normal gravity
				fallspeed = m_Gravity * a_Dt;
			}
			NextSpeed.y += fallspeed;
		}
		else
		{
			//Friction
			if (NextSpeed.SqrLength() > 0.0004f)
			{
				NextSpeed.x *= 0.7f/(1+a_Dt);
				if ( fabs(NextSpeed.x) < 0.05 ) NextSpeed.x = 0;
				NextSpeed.z *= 0.7f/(1+a_Dt);
				if ( fabs(NextSpeed.z) < 0.05 ) NextSpeed.z = 0;
			}
		}

		//Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
		//might have different speed modifiers according to terrain.
		if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.x *= 0.25;
			NextSpeed.z *= 0.25;
		}
					
		//Get water direction
		Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

		m_WaterSpeed *= 0.9f;		//Reduce speed each tick

		switch(WaterDir)
		{
			case X_PLUS:
				m_WaterSpeed.x = 1.f;
				m_bOnGround = false;
				break;
			case X_MINUS:
				m_WaterSpeed.x = -1.f;
				m_bOnGround = false;
				break;
			case Z_PLUS:
				m_WaterSpeed.z = 1.f;
				m_bOnGround = false;
				break;
			case Z_MINUS:
				m_WaterSpeed.z = -1.f;
				m_bOnGround = false;
				break;
			
		default:
			break;
		}

		if (fabs(m_WaterSpeed.x) < 0.05)
		{
			m_WaterSpeed.x = 0;
		}

		if (fabs(m_WaterSpeed.z) < 0.05)
		{
			m_WaterSpeed.z = 0;
		}

		NextSpeed += m_WaterSpeed;

		if( NextSpeed.SqrLength() > 0.f )
		{
			cTracer Tracer( GetWorld() );
			int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
			if( Ret ) // Oh noez! we hit something
			{
				// Set to hit position
				if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
				{
					if( Ret == 1 )
					{

						if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
						if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
						if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;

						if( Tracer.HitNormal.y > 0 ) // means on ground
						{
							m_bOnGround = true;
						}
					}
					NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
					NextPos.x += Tracer.HitNormal.x * 0.5f;
					NextPos.z += Tracer.HitNormal.z * 0.5f;
				}
				else
					NextPos += (NextSpeed * a_Dt);
			}
			else
			{
				// We didn't hit anything, so move =]
				NextPos += (NextSpeed * a_Dt);
			}
		}
		BlockX = (int) floor(NextPos.x);
		BlockZ = (int) floor(NextPos.z);
		NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
			if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
			if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
			if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
			if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
			if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
		}
	}
}
Пример #16
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	// TODO Add collision detection with entities.
	a_Dt /= 1000;  // Convert from msec to sec
	Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
	int BlockX = (int) floor(NextPos.x);
	int BlockY = (int) floor(NextPos.y);
	int BlockZ = (int) floor(NextPos.z);
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world

		cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			SetSpeed(NextSpeed);
			NextPos += (NextSpeed * a_Dt);
			SetPosition(NextPos);
		}
		return;
	}
	
	// Make sure we got the correct chunk and a valid one. No one ever knows...
	cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
	if (NextChunk != NULL)
	{
		int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
		int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
		BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
		BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
		if (!g_BlockIsSolid[BlockIn])  // Making sure we are not inside a solid block
		{
			if (m_bOnGround)  // check if it's still on the ground
			{
				if (!g_BlockIsSolid[BlockBelow])  // Check if block below is air or water.
				{
					m_bOnGround = false;
				}
			}
		}
		else
		{
			// Push out entity.
			BLOCKTYPE GotBlock;

			static const struct
			{
				int x, y, z;
			} gCrossCoords[] =
			{
				{ 1, 0,  0},
				{-1, 0,  0},
				{ 0, 0,  1},
				{ 0, 0, -1},
			} ;

			bool IsNoAirSurrounding = true;
			for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
			{
				if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
				{
					// The pickup is too close to an unloaded chunk, bail out of any physics handling
					return;
				}
				if (!g_BlockIsSolid[GotBlock])
				{
					NextPos.x += gCrossCoords[i].x;
					NextPos.z += gCrossCoords[i].z;
					IsNoAirSurrounding = false;
					break;
				}
			}  // for i - gCrossCoords[]
			
			if (IsNoAirSurrounding)
			{
				NextPos.y += 0.5;
			}

			m_bOnGround = true;

			LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
				m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
			);
		}

		if (!m_bOnGround)
		{
			float fallspeed;
			if (IsBlockWater(BlockIn))
			{
				fallspeed = m_Gravity * a_Dt / 3;  // Fall 3x slower in water.
			}
			else if (IsBlockRail(BlockBelow) && IsMinecart())  // Rails aren't solid, except for Minecarts
			{
				fallspeed = 0;
				m_bOnGround = true;
			}
			else if (BlockIn == E_BLOCK_COBWEB)
			{
				NextSpeed.y *= 0.05;  // Reduce overall falling speed
				fallspeed = 0;  // No falling.
			}
			else
			{
				// Normal gravity
				fallspeed = m_Gravity * a_Dt;
			}
			NextSpeed.y += fallspeed;
		}
		else
		{
			if (IsMinecart())
			{
				if (!IsBlockRail(BlockBelow))
				{
					// Friction if minecart is off track, otherwise, Minecart.cpp handles this
					if (NextSpeed.SqrLength() > 0.0004f)
					{
						NextSpeed.x *= 0.7f / (1 + a_Dt);
						if (fabs(NextSpeed.x) < 0.05)
						{
							NextSpeed.x = 0;
						}
						NextSpeed.z *= 0.7f / (1 + a_Dt);
						if (fabs(NextSpeed.z) < 0.05)
						{
							NextSpeed.z = 0;
						}
					}
				}
			}
			else
			{
				// Friction for non-minecarts
				if (NextSpeed.SqrLength() > 0.0004f)
				{
					NextSpeed.x *= 0.7f / (1 + a_Dt);
					if (fabs(NextSpeed.x) < 0.05)
					{
						NextSpeed.x = 0;
					}
					NextSpeed.z *= 0.7f / (1 + a_Dt);
					if (fabs(NextSpeed.z) < 0.05)
					{
						NextSpeed.z = 0;
					}
				}
			}
		}

		// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
		// might have different speed modifiers according to terrain.
		if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.x *= 0.25;
			NextSpeed.z *= 0.25;
		}
					
		//Get water direction
		Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

		m_WaterSpeed *= 0.9f;		//Reduce speed each tick

		switch(WaterDir)
		{
			case X_PLUS:
				m_WaterSpeed.x = 0.2f;
				m_bOnGround = false;
				break;
			case X_MINUS:
				m_WaterSpeed.x = -0.2f;
				m_bOnGround = false;
				break;
			case Z_PLUS:
				m_WaterSpeed.z = 0.2f;
				m_bOnGround = false;
				break;
			case Z_MINUS:
				m_WaterSpeed.z = -0.2f;
				m_bOnGround = false;
				break;
			
		default:
			break;
		}

		if (fabs(m_WaterSpeed.x) < 0.05)
		{
			m_WaterSpeed.x = 0;
		}

		if (fabs(m_WaterSpeed.z) < 0.05)
		{
			m_WaterSpeed.z = 0;
		}

		NextSpeed += m_WaterSpeed;

		if( NextSpeed.SqrLength() > 0.f )
		{
			cTracer Tracer( GetWorld() );
			int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
			if( Ret ) // Oh noez! we hit something
			{
				// Set to hit position
				if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
				{
					if( Ret == 1 )
					{
						if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
						if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
						if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;

						if( Tracer.HitNormal.y > 0 ) // means on ground
						{
							m_bOnGround = true;
						}
					}
					NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
					NextPos.x += Tracer.HitNormal.x * 0.3f;
					NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
					NextPos.z += Tracer.HitNormal.z * 0.3f;
				}
				else
				{
					NextPos += (NextSpeed * a_Dt);
				}
			}
			else
			{
				// We didn't hit anything, so move =]
				NextPos += (NextSpeed * a_Dt);
			}
		}
		BlockX = (int) floor(NextPos.x);
		BlockZ = (int) floor(NextPos.z);
		NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
			if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
			if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
			if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
			if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
			if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
		}
	}
}