static bool Particle_CollideHor(Vector3* nextPos, BlockID block) { Vector3 horPos = Vector3_Create3((Real32)Math_Floor(nextPos->X), 0.0f, (Real32)Math_Floor(nextPos->Z)); Vector3 min, max; Vector3_Add(&min, &Block_MinBB[block], &horPos); Vector3_Add(&max, &Block_MaxBB[block], &horPos); return nextPos->X >= min.X && nextPos->Z >= min.Z && nextPos->X < max.X && nextPos->Z < max.Z; }
/*########################################################################################################################* *-------------------------------------------------------Respawning--------------------------------------------------------* *#########################################################################################################################*/ float Respawn_HighestSolidY(struct AABB* bb) { int minX = Math_Floor(bb->Min.X), maxX = Math_Floor(bb->Max.X); int minY = Math_Floor(bb->Min.Y), maxY = Math_Floor(bb->Max.Y); int minZ = Math_Floor(bb->Min.Z), maxZ = Math_Floor(bb->Max.Z); float highestY = RESPAWN_NOT_FOUND; BlockID block; struct AABB blockBB; Vector3 v; int x, y, z; for (y = minY; y <= maxY; y++) { v.Y = (float)y; for (z = minZ; z <= maxZ; z++) { v.Z = (float)z; for (x = minX; x <= maxX; x++) { v.X = (float)x; block = World_GetPhysicsBlock(x, y, z); Vector3_Add(&blockBB.Min, &v, &Blocks.MinBB[block]); Vector3_Add(&blockBB.Max, &v, &Blocks.MaxBB[block]); if (Blocks.Collide[block] != COLLIDE_SOLID) continue; if (!AABB_Intersects(bb, &blockBB)) continue; if (blockBB.Max.Y > highestY) highestY = blockBB.Max.Y; } } } return highestY; }
static bool Particle_PhysicsTick(Particle* p, Real32 gravity, bool throughLiquids, Real64 delta) { p->LastPos = p->NextPos; BlockID cur = Particle_GetBlock((Int32)p->NextPos.X, (Int32)p->NextPos.Y, (Int32)p->NextPos.Z); Real32 minY = Math_Floor(p->NextPos.Y) + Block_MinBB[cur].Y; Real32 maxY = Math_Floor(p->NextPos.Y) + Block_MaxBB[cur].Y; if (!Particle_CanPass(cur, throughLiquids) && p->NextPos.Y >= minY && p->NextPos.Y < maxY && Particle_CollideHor(&p->NextPos, cur)) { return true; } p->Velocity.Y -= gravity * (Real32)delta; Int32 startY = Math_Floor(p->NextPos.Y); Vector3 velocity; Vector3_Mul1(&velocity, &p->Velocity, (Real32)delta * 3.0f); Vector3_Add(&p->NextPos, &p->NextPos, &velocity); Int32 endY = Math_Floor(p->NextPos.Y); Int32 y; if (p->Velocity.Y > 0.0f) { /* don't test block we are already in */ for (y = startY + 1; y <= endY && Particle_TestY(p, y, false, throughLiquids); y++) {} } else { for (y = startY; y >= endY && Particle_TestY(p, y, true, throughLiquids); y--) {} } p->Lifetime -= (Real32)delta; return p->Lifetime < 0.0f; }
static bool InputHandler_IntersectsOthers(Vector3 pos, BlockID block) { struct AABB blockBB, entityBB; struct Entity* entity; int id; Vector3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); Vector3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); for (id = 0; id < ENTITIES_SELF_ID; id++) { entity = Entities.List[id]; if (!entity) continue; Entity_GetBounds(entity, &entityBB); entityBB.Min.Y += 1.0f / 32.0f; /* when player is exactly standing on top of ground */ if (AABB_Intersects(&entityBB, &blockBB)) return true; } return false; }
static bool InputHandler_CheckIsFree(BlockID block) { struct Entity* p = &LocalPlayer_Instance.Base; struct HacksComp* hacks = &LocalPlayer_Instance.Hacks; Vector3 pos, nextPos; struct AABB blockBB, playerBB; struct LocationUpdate update; /* Non solid blocks (e.g. water/flowers) can always be placed on players */ if (Blocks.Collide[block] != COLLIDE_SOLID) return true; Vector3I_ToVector3(&pos, &Game_SelectedPos.TranslatedPos); if (InputHandler_IntersectsOthers(pos, block)) return false; nextPos = LocalPlayer_Instance.Interp.Next.Pos; Vector3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); Vector3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); /* NOTE: Need to also test against next position here, otherwise player can fall through the block at feet as collision is performed against nextPos */ Entity_GetBounds(p, &playerBB); playerBB.Min.Y = min(nextPos.Y, playerBB.Min.Y); if (hacks->Noclip || !AABB_Intersects(&playerBB, &blockBB)) return true; if (hacks->CanPushbackBlocks && hacks->PushbackPlacing && hacks->Enabled) { return InputHandler_PushbackPlace(&blockBB); } playerBB.Min.Y += 0.25f + ENTITY_ADJUSTMENT; if (AABB_Intersects(&playerBB, &blockBB)) return false; /* Push player upwards when they are jumping and trying to place a block underneath them */ nextPos.Y = pos.Y + Blocks.MaxBB[block].Y + ENTITY_ADJUSTMENT; LocationUpdate_MakePos(&update, nextPos, false); p->VTABLE->SetLocation(p, &update, false); return true; }
void Particles_RainSnowEffect(Vector3 pos) { Vector3 startPos = pos; Int32 i; for (i = 0; i < 2; i++) { Real32 velX = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ Real32 velZ = Random_Float(&rnd) * 0.8f - 0.4f; Real32 velY = Random_Float(&rnd) + 0.4f; Vector3 velocity = Vector3_Create3(velX, velY, velZ); Vector3 offset; offset.X = Random_Float(&rnd); /* [0.0, 1.0] */ offset.Y = Random_Float(&rnd) * 0.1f + 0.01f; offset.Z = Random_Float(&rnd); if (Rain_Count == PARTICLES_MAX) Rain_RemoveAt(0); RainParticle* p = &Rain_Particles[Rain_Count++]; Vector3_Add(&pos, &startPos, &offset); Particle_Reset(&p->Base, pos, velocity, 40.0f); Int32 type = Random_Range(&rnd, 0, 30); p->Base.Size = (UInt8)(type >= 28 ? 2 : (type >= 25 ? 4 : 3)); } }
void Particles_BreakBlockEffect(Vector3I coords, BlockID oldBlock, BlockID block) { if (block != BLOCK_AIR || Block_Draw[oldBlock] == DRAW_GAS) return; block = oldBlock; Vector3 worldPos; Vector3I_ToVector3(&worldPos, &coords); TextureLoc texLoc = Block_GetTexLoc(block, FACE_XMIN); Int32 texIndex; TextureRec baseRec = Atlas1D_TexRec(texLoc, 1, &texIndex); Real32 uScale = (1.0f / 16.0f), vScale = (1.0f / 16.0f) * Atlas1D_InvTileSize; Vector3 minBB = Block_MinBB[block]; Vector3 maxBB = Block_MaxBB[block]; Int32 minX = (Int32)(minBB.X * 16), minZ = (Int32)(minBB.Z * 16); Int32 maxX = (Int32)(maxBB.X * 16), maxZ = (Int32)(maxBB.Z * 16); Int32 minU = min(minX, minZ), maxU = min(maxX, maxZ); Int32 minV = (Int32)(16 - maxBB.Y * 16), maxV = (Int32)(16 - minBB.Y * 16); Int32 maxUsedU = maxU, maxUsedV = maxV; /* This way we can avoid creating particles which outside the bounds and need to be clamped */ if (minU < 12 && maxU > 12) maxUsedU = 12; if (minV < 12 && maxV > 12) maxUsedV = 12; #define GRID_SIZE 4 /* gridOffset gives the centre of the cell on a grid */ #define CELL_CENTRE ((1.0f / GRID_SIZE) * 0.5f) Int32 x, y, z; Real32 maxU2 = baseRec.U1 + maxU * uScale; Real32 maxV2 = baseRec.V1 + maxV * vScale; for (x = 0; x < GRID_SIZE; x++) { for (y = 0; y < GRID_SIZE; y++) { for (z = 0; z < GRID_SIZE; z++) { Real32 cellX = (Real32)x / GRID_SIZE, cellY = (Real32)y / GRID_SIZE, cellZ = (Real32)z / GRID_SIZE; Vector3 cell = Vector3_Create3(CELL_CENTRE + cellX, CELL_CENTRE / 2 + cellY, CELL_CENTRE + cellZ); if (cell.X < minBB.X || cell.X > maxBB.X || cell.Y < minBB.Y || cell.Y > maxBB.Y || cell.Z < minBB.Z || cell.Z > maxBB.Z) continue; Vector3 velocity; /* centre random offset around [-0.2, 0.2] */ velocity.X = CELL_CENTRE + (cellX - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); velocity.Y = CELL_CENTRE + (cellY - 0.0f) + (Random_Float(&rnd) * 0.4f - 0.2f); velocity.Z = CELL_CENTRE + (cellZ - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); TextureRec rec = baseRec; rec.U1 = baseRec.U1 + Random_Range(&rnd, minU, maxUsedU) * uScale; rec.V1 = baseRec.V1 + Random_Range(&rnd, minV, maxUsedV) * vScale; rec.U2 = rec.U1 + 4 * uScale; rec.V2 = rec.V1 + 4 * vScale; rec.U2 = min(rec.U2, maxU2) - 0.01f * uScale; rec.V2 = min(rec.V2, maxV2) - 0.01f * vScale; if (Terrain_Count == PARTICLES_MAX) Terrain_RemoveAt(0); TerrainParticle* p = &Terrain_Particles[Terrain_Count++]; Real32 life = 0.3f + Random_Float(&rnd) * 1.2f; Vector3 pos; Vector3_Add(&pos, &worldPos, &cell); Particle_Reset(&p->Base, pos, velocity, life); p->Rec = rec; p->TexLoc = (TextureLoc)texLoc; p->Block = block; Int32 type = Random_Range(&rnd, 0, 30); p->Base.Size = (UInt8)(type >= 28 ? 12 : (type >= 25 ? 10 : 8)); } } } }