void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { m_PathfinderActivated = false; // Disable Pathfinding until it's fixed. TODO // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk // that is not where the entity currently resides (FS #411) Vector3d Pos = GetPosition(); // TODO: Not a real behavior, but cool :D int RelY = FloorC(Pos.y); if ((RelY < 0) || (RelY >= cChunkDef::Height)) { return; } int RelX = FloorC(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; int RelZ = FloorC(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; BLOCKTYPE BlockType; if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire()) { // Burn for 10 ticks, then decide again StartBurning(10); } super::Tick(a_Dt, a_Chunk); }
void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging) { m_Writer.AddInt("TileX", FloorC(a_Hanging->GetPosX())); m_Writer.AddInt("TileY", FloorC(a_Hanging->GetPosY())); m_Writer.AddInt("TileZ", FloorC(a_Hanging->GetPosZ())); m_Writer.AddByte("Facing", a_Hanging->GetProtocolFacing()); }
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override { int BlockStartX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; int BlockStartZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; int BlockEndX = BlockStartX + cChunkDef::Width; int BlockEndZ = BlockStartZ + cChunkDef::Width; for (sRavineDefPoints::const_iterator itr = m_DefPoints.begin(), end = m_DefPoints.end(); itr != end; ++itr) { if ( (ceilf (itr->m_X + itr->m_Radius + 2) < BlockStartX) || (floorf(itr->m_X - itr->m_Radius - 2) > BlockEndX) || (ceilf (itr->m_Z + itr->m_Radius + 2) < BlockStartZ) || (floorf(itr->m_Z - itr->m_Radius - 2) > BlockEndZ) ) { // Cannot intersect, bail out early continue; } // Carve out a cylinder around the xz point, up to (m_Radius + 2) in diameter, from Bottom to Top: // On each height level, use m_PerHeightRadius[] to modify the actual radius used // EnlargedRadiusSq is the square of the radius enlarged by the maximum m_PerHeightRadius offset - anything outside it will never be touched. float RadiusSq = (itr->m_Radius + 2) * (itr->m_Radius + 2); float DifX = BlockStartX - itr->m_X; // substitution for faster calc float DifZ = BlockStartZ - itr->m_Z; // substitution for faster calc for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) { #ifdef _DEBUG // DEBUG: Make the roughravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) if ((FloorC(DifX + x) == 0) && (FloorC(DifZ + z) == 0)) { a_ChunkDesc.SetBlockType(x, 4, z, E_BLOCK_LAPIS_ORE); } #endif // _DEBUG // If the column is outside the enlarged radius, bail out completely float DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); if (DistSq > RadiusSq) { continue; } int Top = std::min(CeilC(itr->m_Top), +cChunkDef::Height); for (int y = std::max(FloorC(itr->m_Bottom), 1); y <= Top; y++) { if ((itr->m_Radius + m_PerHeightRadius[y]) * (itr->m_Radius + m_PerHeightRadius[y]) < DistSq) { continue; } if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z))) { a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); } } // for y } // for x, z - a_BlockTypes } // for itr - m_Points[] }
void cSplashPotionEntity::Splash(const Vector3d & a_HitPos) { cSplashPotionCallback Callback(a_HitPos, m_EntityEffectType, m_EntityEffect); m_World->ForEachEntity(Callback); m_World->BroadcastSoundParticleEffect( EffectID::PARTICLE_SPLASH_POTION, FloorC(a_HitPos.x), FloorC(a_HitPos.y), FloorC(a_HitPos.z), m_PotionColor ); }
void cEntityEffectPoison::OnTick(cPawn & a_Target) { super::OnTick(a_Target); if (a_Target.IsMob()) { cMonster & Target = reinterpret_cast<cMonster &>(a_Target); // Doesn't effect undead mobs, spiders if ( Target.IsUndead() || (Target.GetMobType() == mtSpider) || (Target.GetMobType() == mtCaveSpider) ) { return; } } // Poison frequency = 25 ticks, divided by potion level (Poison II = 12 ticks) int frequency = FloorC(25.0 / static_cast<double>(m_Intensity + 1)); if ((m_Ticks % frequency) == 0) { // Cannot take poison damage when health is at 1 if (a_Target.GetHealth() > 1) { a_Target.TakeDamage(dtPoisoning, nullptr, 1, 0); } } }
void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks) { Vector3d CurrentPos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); Vector3d Direction = a_StartDirection; for (int i = 0; i < a_BranchLength; i++) { CurrentPos += Direction; if (CurrentPos.y >= a_TreeHeight) { return; } Direction -= a_Direction; Direction.clamp(-1.0, 1.0); a_LogBlocks.push_back(sSetBlock(FloorC(CurrentPos.x), FloorC(CurrentPos.y), FloorC(CurrentPos.z), E_BLOCK_LOG, GetLogMetaFromDirection(E_META_LOG_APPLE, Direction))); } }
void cMonster::MoveToWayPoint(cChunk & a_Chunk) { if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) { return; } if (m_JumpCoolDown == 0) { if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) { if (((IsOnGround()) && (GetSpeed().SqrLength() == 0.0f)) || (IsSwimming())) { m_bOnGround = false; m_JumpCoolDown = 20; // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport AddPosY(1.6); // Jump!! SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect. } } } else { --m_JumpCoolDown; } Vector3d Distance = m_NextWayPointPosition - GetPosition(); if ((Distance.x != 0.0f) || (Distance.z != 0.0f)) { Distance.y = 0; Distance.Normalize(); if (m_bOnGround) { Distance *= 2.5f; } else if (IsSwimming()) { Distance *= 1.3f; } else { // Don't let the mob move too much if he's falling. Distance *= 0.25f; } // Apply walk speed: Distance *= m_RelativeWalkSpeed; /* Reduced default speed. Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence better pathfinding. */ Distance *= 0.5; AddSpeedX(Distance.x); AddSpeedZ(Distance.z); } }
int cFurnaceEntity::GetAndResetReward(void) { int Reward = FloorC(m_RewardCounter); float Remainder = m_RewardCounter - static_cast<float>(Reward); // Remainder is used as the percent chance of getting an extra xp point if (GetRandomProvider().RandBool(Remainder)) { Reward++; } m_RewardCounter = 0.0; return Reward; }
void cEntityEffectWither::OnTick(cPawn & a_Target) { super::OnTick(a_Target); // Damage frequency = 40 ticks, divided by effect level (Wither II = 20 ticks) int frequency = FloorC(25.0 / static_cast<double>(m_Intensity + 1)); if ((m_Ticks % frequency) == 0) { a_Target.TakeDamage(dtWither, nullptr, 1, 0); } }
bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Location.x), FloorC(a_Location.z)); if ((Chunk == nullptr) || (!Chunk->IsValid())) { return false; } int RelX = FloorC(a_Location.x) - Chunk->GetPosX() * cChunkDef::Width; int RelY = FloorC(a_Location.y); int RelZ = FloorC(a_Location.z) - Chunk->GetPosZ() * cChunkDef::Width; if ( (Chunk->GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight (Chunk->GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining ) { return true; } return false; }
cNetherPortalScanner::cNetherPortalScanner(cEntity * a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY) : m_Entity(a_MovingEntity), m_World(a_DestinationWorld), m_FoundPortal(false), m_BuildPlatform(true), m_Dir(Direction::X), m_PortalLoc(a_DestPosition.Floor()), m_Position(a_DestPosition), m_MaxY(a_MaxY) { int MinX = FloorC((m_Position.x - SearchRadius) / cChunkDef::Width); int MinZ = FloorC((m_Position.z - SearchRadius) / cChunkDef::Width); int MaxX = CeilC((m_Position.x + SearchRadius) / cChunkDef::Width); int MaxZ = CeilC((m_Position.z + SearchRadius) / cChunkDef::Width); for (int x = MinX; x < MaxX; x++) { for (int z = MinZ; z < MaxZ; z++) { Add(x, z); } } Enable(*a_DestinationWorld->GetChunkMap()); }
int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) { int PosY = POSY_TOINT; PosY = Clamp(PosY, 0, cChunkDef::Height); if (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ)))) { while (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ))) && (PosY > 0)) { PosY--; } return PosY + 1; } else { while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock(static_cast<int>(floor(a_PosX)), PosY, static_cast<int>(floor(a_PosZ))))) { PosY++; } return PosY; } }
void cEntityEffectRegeneration::OnTick(cPawn & a_Target) { super::OnTick(a_Target); if (a_Target.IsMob() && reinterpret_cast<cMonster &>(a_Target).IsUndead()) { return; } // Regen frequency = 50 ticks, divided by potion level (Regen II = 25 ticks) int frequency = FloorC(50.0 / static_cast<double>(m_Intensity + 1)); if ((m_Ticks % frequency) != 0) { return; } a_Target.Heal(1); }
/* cPath implementation */ cPath::cPath( cChunk & a_Chunk, const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, double a_BoundingBoxWidth, double a_BoundingBoxHeight, int a_MaxUp, int a_MaxDown ) : m_StepsLeft(a_MaxSteps), m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint m_Chunk(&a_Chunk), m_BadChunkFound(false) { // TODO: if src not walkable OR dest not walkable, then abort. // Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable a_BoundingBoxWidth = 1; // Until we improve physics, if ever. m_BoundingBoxWidth = CeilC(a_BoundingBoxWidth); m_BoundingBoxHeight = CeilC(a_BoundingBoxHeight); m_HalfWidth = a_BoundingBoxWidth / 2; int HalfWidthInt = FloorC(a_BoundingBoxWidth / 2); m_Source.x = FloorC(a_StartingPoint.x - HalfWidthInt); m_Source.y = FloorC(a_StartingPoint.y); m_Source.z = FloorC(a_StartingPoint.z - HalfWidthInt); m_Destination.x = FloorC(a_EndingPoint.x - HalfWidthInt); m_Destination.y = FloorC(a_EndingPoint.y); m_Destination.z = FloorC(a_EndingPoint.z - HalfWidthInt); if (GetCell(m_Source)->m_IsSolid || GetCell(m_Destination)->m_IsSolid) { m_Status = ePathFinderStatus::PATH_NOT_FOUND; return; } m_NearestPointToTarget = GetCell(m_Source); m_Status = ePathFinderStatus::CALCULATING; ProcessCell(GetCell(m_Source), nullptr, 0); }
void cVillager::HandleFarmerPrepareFarmCrops() { if (!m_World->VillagersShouldHarvestCrops()) { return; } cBlockArea Surrounding; // Read a 11x7x11 area: Surrounding.Read( m_World, FloorC(GetPosX()) - 5, FloorC(GetPosX()) + 6, FloorC(GetPosY()) - 3, FloorC(GetPosY()) + 4, FloorC(GetPosZ()) - 5, FloorC(GetPosZ()) + 6 ); for (int I = 0; I < 5; I++) { for (int Y = 0; Y < 6; Y++) { // Pick random coordinates and check for crops. int X = m_World->GetTickRandomNumber(11); int Z = m_World->GetTickRandomNumber(11); // A villager can't farm this. if (!IsBlockFarmable(Surrounding.GetRelBlockType(X, Y, Z))) { continue; } if (Surrounding.GetRelBlockMeta(X, Y, Z) != 0x7) { continue; } m_VillagerAction = true; m_CropsPos = Vector3i(static_cast<int>(GetPosX()) + X - 5, static_cast<int>(GetPosY()) + Y - 3, static_cast<int>(GetPosZ()) + Z - 5); MoveToPosition(Vector3f(static_cast<float>(m_CropsPos.x + 0.5), static_cast<float>(m_CropsPos.y), static_cast<float>(m_CropsPos.z + 0.5))); return; } // for Y loop. } // Repeat the procces 5 times. }
/* cPath implementation */ cPath::cPath( cChunk & a_Chunk, const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, double a_BoundingBoxWidth, double a_BoundingBoxHeight ) : m_StepsLeft(a_MaxSteps), m_IsValid(true), m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint m_Chunk(&a_Chunk), m_BadChunkFound(false) { a_BoundingBoxWidth = 1; // Treat all mobs width as 1 until physics is improved. m_BoundingBoxWidth = CeilC(a_BoundingBoxWidth); m_BoundingBoxHeight = CeilC(a_BoundingBoxHeight); m_HalfWidth = a_BoundingBoxWidth / 2; int HalfWidthInt = FloorC(a_BoundingBoxWidth / 2); m_Source.x = FloorC(a_StartingPoint.x - HalfWidthInt); m_Source.y = FloorC(a_StartingPoint.y); m_Source.z = FloorC(a_StartingPoint.z - HalfWidthInt); m_Destination.x = FloorC(a_EndingPoint.x - HalfWidthInt); m_Destination.y = FloorC(a_EndingPoint.y); m_Destination.z = FloorC(a_EndingPoint.z - HalfWidthInt); if (!IsWalkable(m_Source, m_Source)) { m_Status = ePathFinderStatus::PATH_NOT_FOUND; return; } m_NearestPointToTarget = GetCell(m_Source); m_Status = ePathFinderStatus::CALCULATING; ProcessCell(GetCell(m_Source), nullptr, 0); }
bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { // If the Y coord is out of range, return the most logical result without considering anything else: int RelY = FloorC(a_Location.y); if (RelY < 0) { // Never burn under the world return false; } if (RelY >= cChunkDef::Height) { // Always burn above the world return true; } if (RelY <= 0) { // The mob is about to die, no point in burning return false; } PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); if (!RelSuccess) { return false; } if ( (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining ) { int MobHeight = FloorC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head if (MobHeight >= cChunkDef::Height) { return true; } // Start with the highest block and scan down to the mob's head. // If a non transparent is found, return false (do not burn). Otherwise return true. // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out // instantly.(An exception is e.g. standing under a long column of glass). int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z); while (CurrentBlock >= MobHeight) { BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z); if ( // Do not burn if a block above us meets one of the following conditions: (!cBlockInfo::IsTransparent(Block)) || (Block == E_BLOCK_LEAVES) || (Block == E_BLOCK_NEW_LEAVES) || (IsBlockWater(Block)) ) { return false; } --CurrentBlock; } return true; } return false; }
bool cPathFinder::EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk) { cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x), FloorC(a_Vector.z)); BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; if ((Chunk == nullptr) || !Chunk->IsValid()) { return false; } int RelX = FloorC(a_Vector.x) - Chunk->GetPosX() * cChunkDef::Width; int RelZ = FloorC(a_Vector.z) - Chunk->GetPosZ() * cChunkDef::Width; // If destination in the air, first try to go 1 block north, or east, or west. // This fixes the player leaning issue. // If that failed, we instead go down to the lowest air block. Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta); if (!(IsWaterOrSolid(BlockType))) { bool InTheAir = true; int x, z; for (z = -1; z <= 1; ++z) { for (x = -1; x <= 1; ++x) { if ((x == 0) && (z == 0)) { continue; } Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x+x), FloorC(a_Vector.z+z)); if ((Chunk == nullptr) || !Chunk->IsValid()) { return false; } RelX = FloorC(a_Vector.x+x) - Chunk->GetPosX() * cChunkDef::Width; RelZ = FloorC(a_Vector.z+z) - Chunk->GetPosZ() * cChunkDef::Width; Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta); if (IsWaterOrSolid((BlockType))) { a_Vector.x += x; a_Vector.z += z; InTheAir = false; goto breakBothLoops; } } } breakBothLoops: // Go down to the lowest air block. if (InTheAir) { while (a_Vector.y > 0) { Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta); if (IsWaterOrSolid(BlockType)) { break; } a_Vector.y -= 1; } } } // If destination in water or solid, go up to the first air block. while (a_Vector.y < cChunkDef::Height) { Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y), RelZ, BlockType, BlockMeta); if (!IsWaterOrSolid(BlockType)) { break; } a_Vector.y += 1; } return true; }
bool cNetherPortalScanner::OnAllChunksAvailable(void) { if (m_FoundPortal) { // Find the bottom of this portal while (m_World->GetBlock(m_PortalLoc.x, m_PortalLoc.y, m_PortalLoc.z) == E_BLOCK_NETHER_PORTAL) { m_PortalLoc.y -= 1; } m_PortalLoc.y += 1; // Figure out which way the portal is facing int BXP = m_World->GetBlock(m_PortalLoc.x + 1, m_PortalLoc.y, m_PortalLoc.z); int BXM = m_World->GetBlock(m_PortalLoc.x - 1, m_PortalLoc.y, m_PortalLoc.z); if ((BXP == E_BLOCK_NETHER_PORTAL) || (BXM == E_BLOCK_NETHER_PORTAL)) { // The long axis is along X m_Dir = Direction::X; } else { // The long axis is along Z m_Dir = Direction::Y; } } else { // Scan the area for a suitable location int minx = FloorC(m_Position.x) - BuildSearchRadius; int minz = FloorC(m_Position.z) - BuildSearchRadius; int maxx = FloorC(m_Position.x) + BuildSearchRadius; int maxz = FloorC(m_Position.z) + BuildSearchRadius; int maxy = m_MaxY; std::vector<Vector3i> Possibilities; int x, y, z; for (y = 0; y < maxy - PortalHeight; y++) { for (x = minx; x < maxx - PortalLength; x++) { for (z = minz; z < maxz - SearchSolidBaseWidth; z++) { Vector3i Location = Vector3i(x, y, z); if (IsValidBuildLocation(Location)) { Possibilities.push_back(Vector3i(x, y, z)); } } } } if (Possibilities.size() > 0) { m_BuildPlatform = false; // Find the nearest double DistanceToClosest = (Possibilities[0] - m_Position).SqrLength(); Vector3i Closest = Possibilities[0]; for (const auto & itr : Possibilities) { double Distance = (itr - m_Position).SqrLength(); if (Distance < DistanceToClosest) { DistanceToClosest = Distance; Closest = itr; } } m_PortalLoc = Closest; } } return true; }
void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (!IsTicking()) { // The base class tick destroyed us return; } if (!m_bIsMouthOpen) { if (m_World->GetTickRandomNumber(50) == 25) { m_bIsMouthOpen = true; } } else { if (m_World->GetTickRandomNumber(10) == 5) { m_bIsMouthOpen = false; } } if ((m_Attachee != nullptr) && (!m_bIsTame)) { if (m_TameAttemptTimes < m_TimesToTame) { if (m_World->GetTickRandomNumber(50) == 25) { m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); m_Attachee->Detach(); m_bIsRearing = true; } } else { // TODO: emit hearts here m_bIsTame = true; } } if (m_bIsRearing) { if (m_RearTickCount == 20) { m_bIsRearing = false; m_RearTickCount = 0; } else { m_RearTickCount++; } } m_World->BroadcastEntityMetadata(*this); }
bool cMonster::EnsureProperDestination(cChunk & a_Chunk) { cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z)); BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; if ((Chunk == nullptr) || !Chunk->IsValid()) { return false; } int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width; int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width; // If destination in the air, first try to go 1 block north, or east, or west. // This fixes the player leaning issue. // If that failed, we instead go down to the lowest air block. Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); if (!cBlockInfo::IsSolid(BlockType)) { bool InTheAir = true; int x, z; for (z = -1; z <= 1; ++z) { for (x = -1; x <= 1; ++x) { if ((x==0) && (z==0)) { continue; } Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z)); if ((Chunk == nullptr) || !Chunk->IsValid()) { return false; } RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width; RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width; Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); if (cBlockInfo::IsSolid(BlockType)) { m_FinalDestination.x += x; m_FinalDestination.z += z; InTheAir = false; goto breakBothLoops; } } } breakBothLoops: // Go down to the lowest air block. if (InTheAir) { while (m_FinalDestination.y > 0) { Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); if (cBlockInfo::IsSolid(BlockType)) { break; } m_FinalDestination.y -= 1; } } } // If destination in water, go up to the highest water block. // If destination in solid, go up to first air block. bool InWater = false; while (m_FinalDestination.y < cChunkDef::Height) { Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta); if (BlockType == E_BLOCK_STATIONARY_WATER) { InWater = true; } else if (cBlockInfo::IsSolid(BlockType)) { InWater = false; } else { break; } m_FinalDestination.y += 1; } if (InWater) { m_FinalDestination.y -= 1; } return true; }
void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) { HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); const int SEGMENT_HEIGHT = 8; const int INTERPOL_X = 16; // Must be a divisor of 16 const int INTERPOL_Z = 16; // Must be a divisor of 16 // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. int FloorBuf1[17 * 17]; int FloorBuf2[17 * 17]; int * FloorHi = FloorBuf1; int * FloorLo = FloorBuf2; int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; // Interpolate the lowest floor: for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) { //* FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) * m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / 256; //*/ /* FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / 256; //*/ } // for x, z - FloorLo[] LinearUpscale2DArrayInPlace<17, 17, INTERPOL_X, INTERPOL_Z>(FloorLo); // Interpolate segments: for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) { // First update the high floor: for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) { //* FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / 256; //*/ /* FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / 256; //*/ } // for x, z - FloorLo[] LinearUpscale2DArrayInPlace<17, 17, INTERPOL_X, INTERPOL_Z>(FloorHi); // Interpolate between FloorLo and FloorHi: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) { int Threshold = static_cast<int>(m_Noise1.CubicNoise2D(static_cast<float>(BaseX + x) / 75, static_cast<float>(BaseZ + z) / 75) * m_MaxThreshold); int Lo = FloorLo[x + 17 * z] / 256; int Hi = FloorHi[x + 17 * z] / 256; for (int y = 0; y < SEGMENT_HEIGHT; y++) { int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; if (Val < Threshold) // Don't calculate if the block should be Netherrack when it's already decided that it's air. { a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_NETHERRACK); } } } // Swap the floors: std::swap(FloorLo, FloorHi); } // Bedrock at the bottom and at the top: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) { a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); int Height = a_ChunkDesc.GetHeight(x, z); a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_BEDROCK); NOISE_DATATYPE CeilingDisguise = (m_Noise1.CubicNoise2D(static_cast<float>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10, static_cast<float>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10)); if (CeilingDisguise < 0) { CeilingDisguise = -CeilingDisguise; } int CeilingDisguiseHeight = Height - 2 - FloorC(CeilingDisguise * 3); for (int y = Height - 1; y > CeilingDisguiseHeight; y--) { a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_NETHERRACK); } } }