From 08ada9458701092f3367eb18c8a3e186d7917869 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 7 Apr 2024 21:15:01 +0300 Subject: [PATCH] * Add working lever block. Currently it renders like a torch. --- .../windows/projects/World/World.vcxproj | 2 + .../projects/World/World.vcxproj.filters | 6 + source/CMakeLists.txt | 1 + source/world/item/Inventory.cpp | 1 + source/world/tile/LeverTile.cpp | 245 ++++++++++++++++++ source/world/tile/LeverTile.hpp | 34 +++ source/world/tile/Tile.cpp | 9 +- source/world/tile/WireTile.cpp | 6 +- 8 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 source/world/tile/LeverTile.cpp create mode 100644 source/world/tile/LeverTile.hpp diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj index 17da8b55..a92a37d9 100644 --- a/platforms/windows/projects/World/World.vcxproj +++ b/platforms/windows/projects/World/World.vcxproj @@ -348,6 +348,7 @@ + @@ -459,6 +460,7 @@ + diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters index 0dbbb7ab..f1befe91 100644 --- a/platforms/windows/projects/World/World.vcxproj.filters +++ b/platforms/windows/projects/World/World.vcxproj.filters @@ -473,6 +473,9 @@ Source Files\Tile + + Source Files\Tile + @@ -802,5 +805,8 @@ Header Files\Tile + + Header Files\Tile + \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index dcb31eed..37ce7bfa 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -273,6 +273,7 @@ add_library(reminecraftpe-core STATIC world/tile/RocketLauncherTile.cpp world/tile/WireTile.cpp world/tile/RedStoneTorchTile.cpp + world/tile/LeverTile.cpp renderer/GL/GL.cpp ) target_include_directories(reminecraftpe-core PUBLIC . ..) diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 9d6e2e0d..93012309 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -84,6 +84,7 @@ void Inventory::prepareCreativeInventory() addCreativeItem(Tile::wire->m_ID); addCreativeItem(Tile::notGate_off->m_ID); addCreativeItem(Tile::notGate->m_ID); + addCreativeItem(Tile::lever->m_ID); for (int i = 0; i < C_MAX_HOTBAR_ITEMS; i++) m_hotbar[i] = i; diff --git a/source/world/tile/LeverTile.cpp b/source/world/tile/LeverTile.cpp new file mode 100644 index 00000000..9fccc195 --- /dev/null +++ b/source/world/tile/LeverTile.cpp @@ -0,0 +1,245 @@ +/******************************************************************** + 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 "LeverTile.hpp" +#include "world/level/Level.hpp" + +LeverTile::LeverTile(int ID, int texture, Material* pMtl) : Tile(ID, texture, pMtl) +{ +} + +AABB* LeverTile::getAABB(Level*, int x, int y, int z) +{ + return nullptr; +} + +int LeverTile::getRenderShape() +{ + return SHAPE_TORCH; // SHAPE_LEVER +} + +bool LeverTile::isCubeShaped() +{ + return false; +} + +bool LeverTile::isSolidRender() +{ + return false; +} + +bool LeverTile::checkCanSurvive(Level* level, int x, int y, int z) +{ + if (mayPlace(level, x, y, z)) + return true; + + spawnResources(level, x, y, z, level->getData(x, y, z)); + level->setTile(x, y, z, TILE_AIR); + + return false; +} + +HitResult LeverTile::clip(Level* level, int x, int y, int z, Vec3 a, Vec3 b) +{ + constexpr float f = 0.1875f; + constexpr float f1 = 0.25f; + + switch (level->getData(x, y, z) & 0x7) + { + case 1: + setShape(0.0f, 0.2f, 0.5f - f, f * 2.0f, 0.8f, 0.5f + f); + break; + case 2: + setShape(1.0f - f * 2.0f, 0.2f, 0.5f - f, 1.0f, 0.8f, 0.5f + f); + break; + case 3: + setShape(0.5f - f, 0.2f, 0.0f, 0.5f + f, 0.8f, f * 2.0f); + break; + case 4: + setShape(0.5f - f, 0.2f, 1.0f - f * 2.0f, 0.5f + f, 0.8f, 1.0f); + break; + default: + setShape(0.5f - f1, 0.0f, 0.5f - f1, 0.5f + f1, 0.6f, 0.5f + f1); + break; + } + + return Tile::clip(level, x, y, z, a, b); +} + +bool LeverTile::mayPlace(Level* level, int x, int y, int z) +{ + if (level->isSolidTile(x, y - 1, z)) return true; + if (level->isSolidTile(x - 1, y, z)) return true; + if (level->isSolidTile(x + 1, y, z)) return true; + if (level->isSolidTile(x, y, z - 1)) return true; + if (level->isSolidTile(x, y, z + 1)) return true; + + return false; +} + +void LeverTile::neighborChanged(Level* level, int x, int y, int z, int dir) +{ + if (!checkCanSurvive(level, x, y, z)) + return; + + int data = level->getData(x, y, z) & 0x7; + + bool flag = false; + if (!level->isSolidTile(x - 1, y, z) && data == 1) flag = true; + if (!level->isSolidTile(x + 1, y, z) && data == 2) flag = true; + if (!level->isSolidTile(x, y, z - 1) && data == 3) flag = true; + if (!level->isSolidTile(x, y, z + 1) && data == 4) flag = true; + if (!level->isSolidTile(x, y - 1, z) && data == 5) flag = true; + if (!level->isSolidTile(x, y - 1, z) && data == 6) flag = true; + + if (!flag) + return; // all good + + spawnResources(level, x, y, z, level->getData(x, y, z)); + level->setTile(x, y, z, TILE_AIR); +} + +void LeverTile::onPlace(Level* level, int x, int y, int z) +{ + if (level->isSolidTile(x - 1, y, z)) + level->setData(x, y, z, 1); + else if (level->isSolidTile(x + 1, y, z)) + level->setData(x, y, z, 2); + else if (level->isSolidTile(x, y, z - 1)) + level->setData(x, y, z, 3); + else if (level->isSolidTile(x, y, z + 1)) + level->setData(x, y, z, 4); + else if (level->isSolidTile(x, y - 1, z)) + level->setData(x, y, z, 5); + + checkCanSurvive(level, x, y, z); +} + +void LeverTile::setPlacedOnFace(Level* level, int x, int y, int z, int dir) +{ + int data = level->getData(x, y, z); + int bit3 = data & 0x8; + data = -1; + + switch (dir) + { + case DIR_YPOS: + if (level->isSolidTile(x, y - 1, z)) + data = 5; // TODO: +level->m_random.nextInt(2) + break; + case DIR_ZNEG: + if (level->isSolidTile(x, y, z + 1)) + data = 4; + break; + case DIR_ZPOS: + if (level->isSolidTile(x, y, z - 1)) + data = 3; + break; + case DIR_XNEG: + if (level->isSolidTile(x + 1, y, z)) + data = 2; + break; + case DIR_XPOS: + if (level->isSolidTile(x - 1, y, z)) + data = 1; + break; + } + + if (data == -1) + { + spawnResources(level, x, y, z, level->getData(x, y, z)); + level->setTile(x, y, z, TILE_AIR); + return; + } + + level->setData(x, y, z, data | bit3); +} + +int LeverTile::use(Level* pLevel, int x, int y, int z, Player* player) +{ + if (pLevel->m_bIsMultiplayer) + return true; + + int data = pLevel->getData(x, y, z); + data ^= 0x8; + pLevel->setData(x, y, z, data); + pLevel->setTilesDirty(x, y, z, x, y, z); + pLevel->playSound(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, "random.click", 0.3f, (data & 0x8) ? 0.6f : 0.5f); + pLevel->updateNeighborsAt(x, y, z, m_ID); + + switch (data & 0x7) + { + case 1: pLevel->updateNeighborsAt(x - 1, y, z, m_ID); break; + case 2: pLevel->updateNeighborsAt(x + 1, y, z, m_ID); break; + case 3: pLevel->updateNeighborsAt(x, y, z - 1, m_ID); break; + case 4: pLevel->updateNeighborsAt(x, y, z + 1, m_ID); break; + default: pLevel->updateNeighborsAt(x, y - 1, z, m_ID); break; + } + + return true; +} + +void LeverTile::destroy(Level* pLevel, int x, int y, int z, int dir) +{ + int data = pLevel->getData(x, y, z); + if (~data & 0x8) + { + Tile::destroy(pLevel, x, y, z, dir); + return; + } + + pLevel->updateNeighborsAt(x, y, z, m_ID); + + switch (data & 0x7) + { + case 1: pLevel->updateNeighborsAt(x - 1, y, z, m_ID); break; + case 2: pLevel->updateNeighborsAt(x + 1, y, z, m_ID); break; + case 3: pLevel->updateNeighborsAt(x, y, z - 1, m_ID); break; + case 4: pLevel->updateNeighborsAt(x, y, z + 1, m_ID); break; + default: pLevel->updateNeighborsAt(x, y - 1, z, m_ID); break; + } + + Tile::destroy(pLevel, x, y, z, dir); +} + +// TODO FIXME: +// In Minecraft JE Beta 1.6.4, WireTile::getDirectSignal is `isPoweringTo` and +// WireTile::getSignal is `isIndirectlyPoweringTo`. +// +// Meanwhile I had to reverse them, so LeverTile::getDirectSignal is actually +// `isIndirectlyPoweringTo` and vice versa... + +int LeverTile::getDirectSignal(LevelSource* pLevel, int x, int y, int z, int dir) +{ + int data = pLevel->getData(x, y, z); + if (~data & 0x8) + return false; + + switch (data & 0x7) + { + case 1: return dir == DIR_XPOS; + case 2: return dir == DIR_XNEG; + case 3: return dir == DIR_ZPOS; + case 4: return dir == DIR_ZNEG; + case 5: + case 6: return dir == DIR_YPOS; + } + + return false; +} + +int LeverTile::getSignal(LevelSource* pLevel, int x, int y, int z, int dir) +{ + int data = pLevel->getData(x, y, z); + return (data & 0x8) != 0; +} + +bool LeverTile::isSignalSource() +{ + return true; +} diff --git a/source/world/tile/LeverTile.hpp b/source/world/tile/LeverTile.hpp new file mode 100644 index 00000000..f89e3b44 --- /dev/null +++ b/source/world/tile/LeverTile.hpp @@ -0,0 +1,34 @@ +/******************************************************************** + 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 + ********************************************************************/ + +#pragma once + +#include "Tile.hpp" + +class LeverTile : public Tile +{ +public: + LeverTile(int ID, int texture, Material* pMtl); + + AABB* getAABB(Level*, int x, int y, int z) override; + bool isSolidRender() override; + bool isCubeShaped() override; + int getRenderShape() override; + HitResult clip(Level*, int x, int y, int z, Vec3 a, Vec3 b) override; + bool mayPlace(Level*, int, int, int) override; + void neighborChanged(Level*, int, int, int, int) override; + void onPlace(Level*, int, int, int) override; + void setPlacedOnFace(Level*, int, int, int, int) override; + int use(Level* pLevel, int x, int y, int z, Player* player) override; + void destroy(Level*, int, int, int, int dir) override; + int getSignal(LevelSource*, int x, int y, int z, int dir) override; + int getDirectSignal(LevelSource*, int x, int y, int z, int dir) override; + bool isSignalSource() override; + + bool checkCanSurvive(Level*, int x, int y, int z); +}; diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index ae5c718d..61ae5fe7 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -50,6 +50,7 @@ #include "RocketLauncherTile.hpp" #include "WireTile.hpp" #include "RedStoneTorchTile.hpp" +#include "LeverTile.hpp" std::string Tile::TILE_DESCRIPTION_PREFIX = "tile."; @@ -708,7 +709,7 @@ void Tile::initTiles() Tile::wire = (new WireTile(TILE_WIRE)) ->init() ->setDestroyTime(0.0f) - ->setSoundType(Tile::SOUND_STONE) + ->setSoundType(Tile::SOUND_NORMAL) ->setDescriptionId("wire"); Tile::notGate_off = (new RedStoneTorchTile(TILE_NOT_GATE_OFF, TEXTURE_TORCH_RED_STONE_OFF, Material::decoration)) @@ -723,6 +724,12 @@ void Tile::initTiles() ->setSoundType(Tile::SOUND_WOOD) ->setDescriptionId("notGateLit"); + Tile::lever = (new LeverTile(TILE_LEVER, TEXTURE_LEVER, Material::decoration)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("lever"); + for (int i = 0; i < C_MAX_TILES; i++) { if (Tile::tiles[i]) diff --git a/source/world/tile/WireTile.cpp b/source/world/tile/WireTile.cpp index 5dfa9d9d..f419396a 100644 --- a/source/world/tile/WireTile.cpp +++ b/source/world/tile/WireTile.cpp @@ -13,13 +13,14 @@ // HACK HACK HACK: This is crap -// Include the C++ source directly, because Apple made it purposely +// Include other C++ sources directly, because Apple made it purposely // difficult to add more source files by modifying the PBXPROJ. I // have to allocate GUIDs and .. ugh, it's a pain. Apple can suck // a big, fat one. // // Brent, please help. I don't want to merge it into master like this. #include "RedStoneTorchTile.cpp" +#include "LeverTile.cpp" #endif @@ -354,6 +355,9 @@ int WireTile::getDirectSignal(LevelSource* level, int x, int y, int z, int dir) if (level->getData(x, y, z) == 0) return 0; + if (dir == DIR_YPOS) + return true; + bool flag0 = isSignalSource(level, x - 1, y, z) || !level->isSolidTile(x - 1, y, z) && isSignalSource(level, x - 1, y - 1, z); bool flag1 = isSignalSource(level, x + 1, y, z) || !level->isSolidTile(x + 1, y, z) && isSignalSource(level, x + 1, y - 1, z); bool flag2 = isSignalSource(level, x, y, z - 1) || !level->isSolidTile(x, y, z - 1) && isSignalSource(level, x, y - 1, z - 1);