/******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp The following code is licensed under the BSD 1 clause license. SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "LiquidTileDynamic.hpp" #include "world/level/Level.hpp" constexpr bool g_bDisableSponges = true; // disable sponges for now LiquidTileDynamic::LiquidTileDynamic(int id, Material* pMtl) : LiquidTile(id, pMtl) { } bool LiquidTileDynamic::checkSpongesNearby(Level* level, int x, int y, int z) { // NOTE: I imagine this could get slow. So you can disable it if (g_bDisableSponges) return false; for (int ox = -2; ox <= 2; ox++) { for (int oy = -2; oy <= 2; oy++) { for (int oz = -2; oz <= 2; oz++) { if (level->getTile(x + ox, y + oy, z + oz) == Tile::sponge->m_ID) return true; } } } return false; } bool LiquidTileDynamic::isWaterBlocking(Level* level, int x, int y, int z) { TileID tile = level->getTile(x, y, z); if (tile == Tile::reeds->m_ID) return true; if (!g_bDisableSponges) { if (checkSpongesNearby(level, x, y, z)) return true; } if (!tile) return false; return Tile::tiles[tile]->m_pMaterial->isSolid(); } bool LiquidTileDynamic::canSpreadTo(Level* level, int x, int y, int z) { Material* pMtl = level->getMaterial(x, y, z); if (pMtl == m_pMaterial || pMtl == Material::lava) return false; return !isWaterBlocking(level, x, y, z); } int LiquidTileDynamic::getSlopeDistance(Level* level, int x, int y, int z, int depth, int a7) { int cost = 1000; for (int i = 0; i < 4; i++) { if ((i == 0 && a7 == 1) || (i == 1 && a7 == 0) || (i == 2 && a7 == 3) || (i == 3 && a7 == 2)) continue; int checkX = x, checkY = y, checkZ = z; switch (i) { case 0: checkX--; break; case 1: checkX++; break; case 2: checkZ--; break; case 3: checkZ++; break; } if (isWaterBlocking(level, checkX, checkY, checkZ)) continue; if (level->getMaterial(checkX, checkY, checkZ) == m_pMaterial && level->getData(checkX,checkY,checkZ) == 0) continue; if (!isWaterBlocking(level, checkX, checkY - 1, checkZ)) return depth; if (depth >= 4) continue; int otherCost = getSlopeDistance(level, checkX, checkY, checkZ, depth + 1, i); if (cost > otherCost) cost = otherCost; } return cost; } bool* LiquidTileDynamic::getSpread(Level* level, int x, int y, int z) { for (int i = 0; i < 4; i++) { field_74[i] = 1000; int xChk = x, zChk = z; switch (i) { case 0: xChk--; break; case 1: xChk++; break; case 2: zChk--; break; case 3: zChk++; break; } if (isWaterBlocking(level, xChk, y, zChk)) continue; if (level->getMaterial(xChk, y, zChk) == m_pMaterial && level->getData(xChk, y, zChk) == 0) continue; if (isWaterBlocking(level, xChk, y - 1, zChk)) field_74[i] = getSlopeDistance(level, xChk, y, zChk, 1, i); else field_74[i] = 0; } int min = field_74[0]; // @NOTE: unrolled loop for (int i = 1; i < 4; i++) { if (min >= field_74[i]) min = field_74[i]; } // @NOTE: unrolled loop for (int i = 0; i < 4; i++) field_70[i] = field_74[i] == min; return field_70; } void LiquidTileDynamic::onPlace(Level* level, int x, int y, int z) { updateLiquid(level, x, y, z); if (level->getTile(x, y, z) == m_ID) { level->addToTickNextTick(x, y, z, m_ID, getTickDelay()); } } void LiquidTileDynamic::setStatic(Level* level, int x, int y, int z) { int data = level->getData(x, y, z); level->setTileAndDataNoUpdate(x, y, z, m_ID + 1, data); level->setTilesDirty(x, y, z, x, y, z); level->sendTileUpdated(x, y, z); } void LiquidTileDynamic::trySpreadTo(Level* level, int x, int y, int z, int data) { if (!canSpreadTo(level, x, y, z)) return; TileID tile = level->getTile(x, y, z); if (tile > 0) { if (m_pMaterial == Material::lava) { fizz(level, x, y, z); } else { Tile::tiles[tile]->spawnResources(level, x, y, z, level->getData(x, y, z)); } } level->setTileAndData(x, y, z, m_ID, data); } // @NOTE: This is inlined in PE. int LiquidTileDynamic::getSmallestDepth(Level* level, int x, int y, int z, int oldDepth) { int depth = getDepth(level, x, y, z); if (depth < 0) return oldDepth; if (depth == 0) field_6C++; if (depth >= 8) depth = 0; return oldDepth >= 0 && depth >= oldDepth ? oldDepth : depth; } void LiquidTileDynamic::tick(Level* level, int x, int y, int z, Random* random) { int depth = getDepth(level, x, y, z); int speed; if (m_pMaterial != Material::lava || level->m_pDimension->field_D) speed = 1; else speed = 2; int depthMax, newData; bool flag = true; if (depth > 0) { field_6C = 0; depthMax = -100; depthMax = getSmallestDepth(level, x - 1, y, z, depthMax); depthMax = getSmallestDepth(level, x + 1, y, z, depthMax); depthMax = getSmallestDepth(level, x, y, z - 1, depthMax); depthMax = getSmallestDepth(level, x, y, z + 1, depthMax); newData = speed + depthMax; if (newData > 7 || newData < 0) newData = -1; if (getDepth(level, x, y + 1, z) >= 0) { int depthUp = getDepth(level, x, y + 1, z); if (depthUp >= 8) newData = depthUp; else newData = depthUp + 8; } if (field_6C >= 2 && m_pMaterial == Material::water) { if (level->isSolidTile(x, y - 1, z)) { newData = 0; } else if (m_pMaterial == level->getMaterial(x, y - 1, z)) { if (!level->getData(x, y - 1, z)) newData = 0; } } if (m_pMaterial == Material::lava && newData < 8 && depth < 8 && newData > depth && random->nextInt(4)) { flag = false; newData = depth; } if (newData != depth) { depth = newData; if (depth < 0) { level->setTile(x, y, z, 0); } else { level->setData(x, y, z, depth); level->addToTickNextTick(x, y, z, m_ID, getTickDelay()); } } else if (flag) { setStatic(level, x, y, z); } } else { setStatic(level, x, y, z); } if (canSpreadTo(level, x, y - 1, z)) { if (depth < 8) depth += 8; level->setTileAndData(x, y - 1, z, m_ID, depth); return; } if (depth >= 0 && (depth == 0 || isWaterBlocking(level, x, y - 1, z))) { bool* bSpread = getSpread(level, x, y, z); int data = depth + speed; if (depth >= 8) data = 1; if (data >= 8) return; if (bSpread[0]) trySpreadTo(level, x - 1, y, z, data); if (bSpread[1]) trySpreadTo(level, x + 1, y, z, data); if (bSpread[2]) trySpreadTo(level, x, y, z - 1, data); if (bSpread[3]) trySpreadTo(level, x, y, z + 1, data); } }