Ejemplo n.º 1
0
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	int PosY = (int)floor(GetPosY());
	if ((PosY <= 0) || (PosY >= cChunkDef::Height))
	{
		// Outside the world, just process normal falling physics
		super::HandlePhysics(a_Dt, a_Chunk);
		BroadcastMovementUpdate();
		return;
	}
	
	int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
	cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
	if (Chunk == NULL)
	{
		// Inside an unloaded chunk, bail out all processing
		return;
	}
	BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ);
	BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ);

	if (IsBlockRail(BelowType))
	{
		HandleRailPhysics(a_Dt, *Chunk);
	}
	else
	{
		if (IsBlockRail(InsideType))
		{
			SetPosY(PosY + 1);
			HandleRailPhysics(a_Dt, *Chunk);
		}
		else
		{
			super::HandlePhysics(a_Dt, *Chunk);
			BroadcastMovementUpdate();
		}
	}
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
    if (IsDestroyed())  // Mainly to stop detector rails triggering again after minecart is dead
    {
        return;
    }

    int PosY = POSY_TOINT;
    if ((PosY <= 0) || (PosY >= cChunkDef::Height))
    {
        // Outside the world, just process normal falling physics
        super::HandlePhysics(a_Dt, a_Chunk);
        BroadcastMovementUpdate();
        return;
    }

    int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
    int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
    cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
    if (Chunk == NULL)
    {
        // Inside an unloaded chunk, bail out all processing
        return;
    }

    BLOCKTYPE InsideType;
    NIBBLETYPE InsideMeta;
    Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta);

    if (!IsBlockRail(InsideType))
    {
        Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);  // When an descending minecart hits a flat rail, it goes through the ground; check for this
        if (IsBlockRail(InsideType)) AddPosY(1);  // Push cart upwards
    }

    bool WasDetectorRail = false;
    if (IsBlockRail(InsideType))
    {
        if (InsideType == E_BLOCK_RAIL)
        {
            SnapToRail(InsideMeta);
        }
        else
        {
            SnapToRail(InsideMeta & 0x07);
        }

        switch (InsideType)
        {
        case E_BLOCK_RAIL:
            HandleRailPhysics(InsideMeta, a_Dt);
            break;
        case E_BLOCK_ACTIVATOR_RAIL:
            break;
        case E_BLOCK_POWERED_RAIL:
            HandlePoweredRailPhysics(InsideMeta);
            break;
        case E_BLOCK_DETECTOR_RAIL:
        {
            HandleDetectorRailPhysics(InsideMeta, a_Dt);
            WasDetectorRail = true;
            break;
        }
        default:
            VERIFY(!"Unhandled rail type despite checking if block was rail!");
            break;
        }

        AddPosition(GetSpeed() * (a_Dt / 1000));  // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
    }
    else
    {
        // Not on rail, default physics
        SetPosY(floor(GetPosY()) + 0.35);  // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
        super::HandlePhysics(a_Dt, *Chunk);
    }

    if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition))
    {
        m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
        m_bIsOnDetectorRail = false;
    }
    else if (WasDetectorRail)
    {
        m_bIsOnDetectorRail = true;
        m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
    }

    // Broadcast positioning changes to client
    BroadcastMovementUpdate();
}
Ejemplo n.º 4
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);
		}
	}
}