diff --git a/.gitignore b/.gitignore index b28b405..f53a044 100644 --- a/.gitignore +++ b/.gitignore @@ -123,7 +123,7 @@ xcuserdata/ /sound_data # Ignore linux build artifacts. -/build +/build* # Ignore wasm build artifacts. /wasm @@ -161,6 +161,7 @@ xcuserdata/ /game/assets/misc/foliagecolor.png /game/assets/misc/grasscolor.png /game/assets/mob/char.png +/game/assets/mob/*.png /game/assets/particles.png /game/assets/terrain.png /game/assets/gui/buynow.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f1400..736b20a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") endif() # Use actual name as some IDEs can show them -project(ReMinecraftPE LANGUAGES CXX) +project(ReMinecraftPE LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 11) set(PLATFORMS android sdl windows server) @@ -41,6 +41,12 @@ elseif (EMSCRIPTEN) # Code should be built with -pthread to use pthread add_compile_options(-pthread) add_link_options(-pthread) +endif() +if (platform STREQUAL android) + # set -fPIC + set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + set(USE_NATIVE_ANDROID 1) + add_compile_definitions(USE_NATIVE_ANDROID) elseif (platform STREQUAL macos) message(FATAL_ERROR "${PLATFORM} currently can't be built using cmake. Use XCode instead") elseif (platform STREQUAL sdl) @@ -51,14 +57,14 @@ elseif (platform STREQUAL sdl) # add_compile_definitions(SERVER) endif() -if(WIN32) - set(USE_GLES1_COMPATIBILITY_LAYER FALSE CACHE BOOL "" FORCE) -elseif(NOT EMSCRIPTEN AND NOT ANDROID) +if (WIN32 OR ANDROID OR EMSCRIPTEN) + set(USE_GLES1_COMPATIBILITY_LAYER FALSE CACHE BOOL "" FORCE) +elseif (NOT EMSCRIPTEN AND NOT ANDROID) option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" TRUE) else() set(USE_GLES1_COMPATIBILITY_LAYER TRUE CACHE BOOL "" FORCE) endif() -if(USE_GLES1_COMPATIBILITY_LAYER OR platform STREQUAL sdl) +if (USE_GLES1_COMPATIBILITY_LAYER OR platform STREQUAL sdl) # Prepare SDL library add_library(SDL INTERFACE) if (NOT platform STREQUAL sdl) diff --git a/platforms/android/CMakeLists.txt b/platforms/android/CMakeLists.txt index 4120307..9c00513 100644 --- a/platforms/android/CMakeLists.txt +++ b/platforms/android/CMakeLists.txt @@ -1,9 +1,5 @@ SET(MC_ROOT ../..) -# Native Android Build -add_compile_definitions(USE_NATIVE_ANDROID) -set(USE_NATIVE_ANDROID TRUE) - add_library(reminecraftpe SHARED AppPlatform_android.cpp SoundSystemSL.cpp diff --git a/platforms/android/SoundSystemSL.hpp b/platforms/android/SoundSystemSL.hpp index 121625b..b757072 100644 --- a/platforms/android/SoundSystemSL.hpp +++ b/platforms/android/SoundSystemSL.hpp @@ -21,7 +21,7 @@ #endif #include -#ifdef ANDROID +#ifdef __ANDROID__ #include #endif @@ -62,4 +62,4 @@ private: static std::vector toRemove; static SLObjectItf objEngine; static pthread_mutex_t toRemoveMutex; -}; \ No newline at end of file +}; diff --git a/platforms/android/main.cpp b/platforms/android/main.cpp index 4f40469..8073197 100644 --- a/platforms/android/main.cpp +++ b/platforms/android/main.cpp @@ -314,7 +314,7 @@ static void initWindow(struct engine* engine, struct android_app* app) ANativeWindow_setBuffersGeometry(app->window, 0, 0, format); - engine->surface = eglCreateWindowSurface(engine->display, config, app->window, NULL); + engine->surface = eglCreateWindowSurface(engine->display, config, (EGLNativeWindowType)app->window, NULL); engine->context = eglCreateContext(engine->display, config, NULL, NULL); if (!engine->context) @@ -471,4 +471,4 @@ void android_main(struct android_app* state) { exit: engine.ninecraftApp->saveOptions(); delete engine.ninecraftApp; -} \ No newline at end of file +} diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 74e02c8..d789316 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -509,6 +509,11 @@ void Minecraft::tickInput() m_pLevelRenderer->allChanged(); } #endif + else if (getOptions()->isKey(KM_FOG, keyCode)) + { + // actually pause the game + field_288 = !field_288; + } } if (getOptions()->field_19) diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp index ee3d6f1..b67dc8f 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -311,6 +311,7 @@ void OptionList::initDefaultMenu() OPTION(Boolean, m_bInvertMouse, "Invert Y-axis"); OPTION(Boolean, m_bSplitControls, "Split controls"); idxSplit = currentIndex; OPTION(Boolean, m_bFlyCheat, "Flight hax"); + OPTION(Boolean, m_bFlyNoclip, "Flight noclip"); } HEADER("Multiplayer"); diff --git a/source/client/gui/components/TextInputBox.cpp b/source/client/gui/components/TextInputBox.cpp index 4d6f8fc..be60550 100644 --- a/source/client/gui/components/TextInputBox.cpp +++ b/source/client/gui/components/TextInputBox.cpp @@ -132,6 +132,8 @@ void TextInputBox::keyPressed(Minecraft* minecraft, int key) #define AKEYCODE_ARROW_LEFT VK_LEFT #define AKEYCODE_ARROW_RIGHT VK_RIGHT #define AKEYCODE_DEL VK_BACK +#elif defined(__ANDROID__) +// everything is ok #else #error #endif diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index 77e1e78..5be6dd1 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -74,18 +74,12 @@ void LocalPlayer::animateRespawn() void LocalPlayer::calculateFlight(float x, float y, float z) { - float f1 = m_pMinecraft->getOptions()->field_244; + float f1 = m_pMinecraft->getOptions()->field_8; + float f2 = f1 * 0.35f; float x1 = f1 * x; float z1 = f1 * z; - float y1 = 0.0f; - if (Keyboard::isKeyDown(m_pMinecraft->getOptions()->getKey(KM_FLY_UP))) - y1 = f1 * 0.2f; - if (Keyboard::isKeyDown(m_pMinecraft->getOptions()->getKey(KM_FLY_DOWN))) - y1 = f1 * -0.2f; - field_BFC += x1; - float f2 = m_pMinecraft->getOptions()->field_8 * 0.35f; float f3 = f2 * (field_BFC - field_C00); float f4 = field_C04 + 0.5f * (f3 - field_C04); field_C04 = f4; @@ -94,7 +88,7 @@ void LocalPlayer::calculateFlight(float x, float y, float z) field_C00 += f4; field_BF0 = f4 * 10.0f; - field_C08 += y1; + field_C08 += m_pMoveInput->m_flyInput * f2; float f5 = f2 * (field_C08 - field_C0C); float f6 = field_C10 + 0.5f * (f5 - field_C10); field_C10 = f6; @@ -137,7 +131,7 @@ int LocalPlayer::move(float x, float y, float z) if (Minecraft::DEADMAU5_CAMERA_CHEATS && pLP == this && m_pMinecraft->getOptions()->m_bFlyCheat) { //@HUH: Using m_pMinecraft->m_pLocalPlayer instead of this, even though they're the same - pLP->m_bNoCollision = true; + pLP->m_bNoCollision = m_pMinecraft->getOptions()->m_bFlyNoclip; float field_94_old = field_94; diff --git a/source/client/player/input/ControllerTurnInput.cpp b/source/client/player/input/ControllerTurnInput.cpp index 8e0ea8b..178f13a 100644 --- a/source/client/player/input/ControllerTurnInput.cpp +++ b/source/client/player/input/ControllerTurnInput.cpp @@ -75,7 +75,7 @@ TurnDelta ControllerTurnInput::getTurnDelta() deltaX = deltaTime * xt; deltaY = deltaTime * -yt; } - else if (field_8 != 2 || (!field_18 && !isTouched)) + else if (field_8 != 2 || (!field_18 && !isTouched)) { deltaX = 0.0f; deltaY = -0.0f; diff --git a/source/client/player/input/IMoveInput.cpp b/source/client/player/input/IMoveInput.cpp index 3ad063a..03f047c 100644 --- a/source/client/player/input/IMoveInput.cpp +++ b/source/client/player/input/IMoveInput.cpp @@ -11,6 +11,7 @@ IMoveInput::IMoveInput() : m_horzInput(0.0f), m_vertInput(0.0f), + m_flyInput(0.0f), field_C(false), m_bJumpButton(false), m_bSneakButton(false) diff --git a/source/client/player/input/IMoveInput.hpp b/source/client/player/input/IMoveInput.hpp index c507fc9..f9b5baf 100644 --- a/source/client/player/input/IMoveInput.hpp +++ b/source/client/player/input/IMoveInput.hpp @@ -21,6 +21,8 @@ enum INPUT_JUMP, INPUT_SNEAK, INPUT_CHAT, + INPUT_FLY_UP, + INPUT_FLY_DOWN, }; class IMoveInput @@ -38,6 +40,7 @@ public: public: float m_horzInput; float m_vertInput; + float m_flyInput; bool field_C; bool m_bJumpButton; bool m_bSneakButton; diff --git a/source/client/player/input/KeyboardInput.cpp b/source/client/player/input/KeyboardInput.cpp index 107bba8..7ddbc7e 100644 --- a/source/client/player/input/KeyboardInput.cpp +++ b/source/client/player/input/KeyboardInput.cpp @@ -7,6 +7,7 @@ ********************************************************************/ #include "KeyboardInput.hpp" +#include "common/Utils.hpp" KeyboardInput::KeyboardInput(Options* pOpts) { @@ -15,6 +16,8 @@ KeyboardInput::KeyboardInput(Options* pOpts) field_C = false; m_bJumpButton = false; m_bSneakButton = false; + m_jumpTick = 0; + m_bJumped = false; for (int i = 0; i < 10; i++) m_keys[i] = false; @@ -38,6 +41,8 @@ void KeyboardInput::setKey(int keyCode, bool b) if (m_pOptions->getKey(KM_RIGHT) == keyCode) index = INPUT_RIGHT; if (m_pOptions->getKey(KM_JUMP) == keyCode) index = INPUT_JUMP; if (m_pOptions->getKey(KM_SNEAK) == keyCode) index = INPUT_SNEAK; + if (m_pOptions->getKey(KM_FLY_UP) == keyCode) index = INPUT_FLY_UP; + if (m_pOptions->getKey(KM_FLY_DOWN) == keyCode) index = INPUT_FLY_DOWN; if (index == -1) return; @@ -49,16 +54,39 @@ void KeyboardInput::tick(Player* pPlayer) { m_horzInput = 0.0f; m_vertInput = 0.0f; + m_flyInput = 0.0f; if (m_keys[INPUT_FORWARD]) m_vertInput += 1.0f; if (m_keys[INPUT_BACKWARD]) m_vertInput -= 1.0f; if (m_keys[INPUT_LEFT]) m_horzInput += 1.0f; if (m_keys[INPUT_RIGHT]) m_horzInput -= 1.0f; + if (m_keys[INPUT_FLY_UP]) m_flyInput += 1.0f; + if (m_keys[INPUT_FLY_DOWN]) m_flyInput -= 1.0f; - m_bJumpButton = m_keys[INPUT_JUMP]; + m_bJumped = m_bJumpButton; + m_bJumpButton = m_keys[INPUT_JUMP]; + if (m_bJumpButton && !m_bJumped) + { + int tick = getTimeMs(); + if (tick - m_jumpTick < 300) + { + m_pOptions->m_bFlyCheat = !m_pOptions->m_bFlyCheat; + m_jumpTick = 0; + m_bJumpButton = false; + } + else + m_jumpTick = tick; + } m_bSneakButton = m_keys[INPUT_SNEAK]; - - if (m_keys[INPUT_SNEAK]) + + if (m_pOptions->m_bFlyCheat) + { + if (m_bJumpButton) + m_flyInput += 1.0f; + if (m_bSneakButton) + m_flyInput -= 1.0f; + } + else if (m_keys[INPUT_SNEAK]) { m_horzInput = m_horzInput * 0.3f; m_vertInput = m_vertInput * 0.3f; diff --git a/source/client/player/input/KeyboardInput.hpp b/source/client/player/input/KeyboardInput.hpp index bd18985..f1c2d3a 100644 --- a/source/client/player/input/KeyboardInput.hpp +++ b/source/client/player/input/KeyboardInput.hpp @@ -26,5 +26,7 @@ public: public: bool m_keys[10]; Options* m_pOptions; + bool m_bJumped; + int m_jumpTick; }; diff --git a/source/client/player/input/TouchscreenInput_TestFps.cpp b/source/client/player/input/TouchscreenInput_TestFps.cpp index e27e9a7..50a002c 100644 --- a/source/client/player/input/TouchscreenInput_TestFps.cpp +++ b/source/client/player/input/TouchscreenInput_TestFps.cpp @@ -16,8 +16,10 @@ TouchscreenInput_TestFps::TouchscreenInput_TestFps(Minecraft* pMinecraft, Options* pOptions) : m_rectArea(0.0f, 0.0f, 1.0f, 1.0f), m_pOptions(pOptions), - field_40(false), - m_bJumpBeingHeld(false), + m_bForwardHeld(false), + m_bJumpingHeld(false), + m_bFlyActive(false), + m_jumpTick(0), m_pMinecraft(pMinecraft), m_pAreaLeft(nullptr), m_pAreaRight(nullptr), @@ -40,6 +42,7 @@ void TouchscreenInput_TestFps::releaseAllKeys() { m_horzInput = 0.0f; m_vertInput = 0.0f; + m_flyInput = 0.0f; for (int i = 0; i < 7; i++) field_6C[i] = false; } @@ -135,6 +138,7 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) { m_horzInput = 0.0f; m_vertInput = 0.0f; + m_flyInput = 0.0f; m_bJumpButton = false; for (int i = 0; i < 7; i++) @@ -143,7 +147,7 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) const int* activePointers; int activePointerCount = Multitouch::getActivePointerIds(&activePointers); - bool bJumpPressed = false, bForwardPressed = false; + bool bJumpPressed = false, bForwardPressed = false, bBackwardPressed = false; for (int i = 0; i < activePointerCount; i++) { @@ -167,13 +171,26 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) if (pointerId == 100 + INPUT_JUMP) // jump { - if (pPlayer->isInWater()) - m_bJumpButton = true; - else if (Multitouch::isPressed(finger)) - m_bJumpButton = true; - else if (field_40) + if (Multitouch::isPressed(finger)) { - pointerId = 100; // forward + m_bJumpButton = true; + int tick = getTimeMs(); + if (tick - m_jumpTick < 300) + { + m_jumpTick = 0; + m_bFlyActive = !m_bFlyActive; + m_pMinecraft->getOptions()->m_bFlyCheat = m_bFlyActive; + m_bJumpButton = false; + } + else m_jumpTick = tick; + } + else if (m_bFlyActive) + bJumpPressed = true; + else if (pPlayer->isInWater()) + m_bJumpButton = true; + else if (m_bForwardHeld) + { + pointerId = 100 + INPUT_FORWARD; bJumpPressed = true; m_vertInput += 1.0f; } @@ -182,7 +199,7 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) switch (pointerId) { case 100 + INPUT_FORWARD: - if (pPlayer->isInWater()) + if (pPlayer->isInWater() && !m_bFlyActive) m_bJumpButton = true; else bForwardPressed = true; @@ -191,6 +208,7 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) break; case 100 + INPUT_BACKWARD: + bBackwardPressed = true; m_vertInput -= 1.0f; break; @@ -209,20 +227,29 @@ void TouchscreenInput_TestFps::tick(Player* pPlayer) } } - field_40 = bForwardPressed; + m_bForwardHeld = bForwardPressed; if (bJumpPressed) { // Don't allow the player to hold jump to repeatedly jump. // Only let them jump once - have them jump again - if (!m_bJumpBeingHeld) + if (!m_bJumpingHeld && !m_bFlyActive) m_bJumpButton = true; - m_bJumpBeingHeld = true; + m_bJumpingHeld = true; + } + else if (m_bFlyActive && m_bJumpingHeld && (bForwardPressed || bBackwardPressed)) + { + if (bForwardPressed) + m_flyInput += 1.0f; + if (bBackwardPressed) + m_flyInput -= 1.0f; + + m_vertInput = 0.0f; } else { - m_bJumpBeingHeld = false; + m_bJumpingHeld = false; } } diff --git a/source/client/player/input/TouchscreenInput_TestFps.hpp b/source/client/player/input/TouchscreenInput_TestFps.hpp index 0f993bb..238978b 100644 --- a/source/client/player/input/TouchscreenInput_TestFps.hpp +++ b/source/client/player/input/TouchscreenInput_TestFps.hpp @@ -36,8 +36,10 @@ private: RectangleArea m_rectArea; bool field_30[10]; Options* m_pOptions; - bool field_40; - bool m_bJumpBeingHeld; + bool m_bForwardHeld; + bool m_bJumpingHeld; + bool m_bFlyActive; + int m_jumpTick; TouchAreaModel m_touchAreaModel; Minecraft* m_pMinecraft; PolygonArea* m_pAreaLeft; diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp index 7fa78d8..6edcfb1 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -335,7 +335,7 @@ void GameRenderer::setupFog(int i) if (m_pMinecraft->m_pMobPersp->isUnderLiquid(Material::water)) { - #if defined(ORIGINAL_CODE) || defined(ANDROID) + #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_EXP); #else glFogi(GL_FOG_MODE, GL_EXP); @@ -345,7 +345,7 @@ void GameRenderer::setupFog(int i) } else if (m_pMinecraft->m_pMobPersp->isUnderLiquid(Material::lava)) { - #if defined(ORIGINAL_CODE) || defined(ANDROID) + #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_EXP); #else glFogi(GL_FOG_MODE, GL_EXP); @@ -355,7 +355,7 @@ void GameRenderer::setupFog(int i) } else { - #if defined(ORIGINAL_CODE) || defined(ANDROID) + #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_LINEAR); #else glFogi(GL_FOG_MODE, GL_LINEAR); @@ -703,6 +703,7 @@ void GameRenderer::render(float f) if (tile > 0) { pTile = Tile::tiles[tile]; debugTextRight << "\nTile: " << std::to_string(tile) <<" "<< pTile->m_descriptionID; + debugTextRight << "\nData: " << std::to_string(m_pMinecraft->m_pLevel->getData(hr.m_tileX, hr.m_tileY, hr.m_tileZ)); } } } diff --git a/source/client/renderer/Tesselator.cpp b/source/client/renderer/Tesselator.cpp index 53f186b..67c8f13 100644 --- a/source/client/renderer/Tesselator.cpp +++ b/source/client/renderer/Tesselator.cpp @@ -12,7 +12,9 @@ #include -int dword_2514A4 = 0; +int g_nNormals = 0; +int g_nVertices = 0; +int g_nTriangles = 0; Tesselator Tesselator::instance; @@ -33,7 +35,7 @@ Tesselator::Tesselator(int allotedSize) field_26 = false; m_bBlockColor = false; field_28 = false; - field_2C = 0; + m_nVertices = 0; field_30 = 0; field_34 = false; @@ -73,7 +75,7 @@ void Tesselator::clear() m_accessMode = 2; field_4 = 0; field_30 = 0; - field_2C = 0; + m_nVertices = 0; field_28 = 0; } @@ -158,7 +160,7 @@ void Tesselator::draw() field_3C = 0; xglBindBuffer(GL_ARRAY_BUFFER, m_pVBOs[field_3C]); - xglBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * field_2C, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + xglBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * m_nVertices, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); if (m_bHaveTex) { @@ -211,9 +213,9 @@ RenderChunk Tesselator::end(int vboIdx) vboIdx = m_pVBOs[field_3C]; xglBindBuffer(GL_ARRAY_BUFFER, vboIdx); - xglBufferData(GL_ARRAY_BUFFER, sizeof (Vertex) * field_2C, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + xglBufferData(GL_ARRAY_BUFFER, sizeof (Vertex) * m_nVertices, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); - field_48 += sizeof (Vertex) * field_2C; + field_48 += sizeof (Vertex) * m_nVertices; } clear(); @@ -243,7 +245,7 @@ void Tesselator::noColor() void Tesselator::normal(float x, float y, float z) { // don't get the point of this - dword_2514A4++; + g_nNormals++; } void Tesselator::offset(float x, float y, float z) @@ -273,13 +275,19 @@ void Tesselator::vertexUV(float x, float y, float z, float u, float v) void Tesselator::vertex(float x, float y, float z) { - field_30++; - if (m_drawArraysMode == GL_QUADS && !(field_30 << 30)) + if (m_nVertices >= m_maxVertices) { - for (int v18 = 3; v18 != 1; v18--) + LOG_W("Overwriting the vertex buffer! This chunk/entity won't show up"); + clear(); + } + + field_30++; + if (m_drawArraysMode == GL_QUADS && (field_30 & 3) == 0) + { + for (int idx = 3; idx != 1; idx--) { - // why the hell is it doing this - Vertex *pVert1 = &m_pVertices[field_2C - v18], *pVert2 = &m_pVertices[field_2C]; + // duplicate the last 2 added vertices in quad mode + Vertex *pVert1 = &m_pVertices[m_nVertices - idx], *pVert2 = &m_pVertices[m_nVertices]; if (m_bHaveTex) { pVert2->m_u = pVert1->m_u; @@ -296,11 +304,16 @@ void Tesselator::vertex(float x, float y, float z) pVert2->m_z = pVert1->m_z; field_4++; - field_2C++; + m_nVertices++; +#ifndef NDEBUG + g_nVertices++; +#endif + if (m_nVertices >= m_maxVertices) + return; } } - Vertex* pVert = &m_pVertices[field_2C]; + Vertex* pVert = &m_pVertices[m_nVertices]; if (m_bHaveTex) { pVert->m_u = m_nextVtxU; @@ -317,15 +330,7 @@ void Tesselator::vertex(float x, float y, float z) pVert->m_z = m_offsetZ + z; field_4++; - field_2C++; - - if (!(field_4 & 3) && field_2C >= m_maxVertices - 1) - { - for (int i = 0; i < 3; i++) - LOG_W("Overwriting the vertex buffer! This chunk/entity won't show up"); - - clear(); - } + m_nVertices++; } void Tesselator::voidBeginAndEndCalls(bool b) diff --git a/source/client/renderer/Tesselator.hpp b/source/client/renderer/Tesselator.hpp index 92fcb47..210888c 100644 --- a/source/client/renderer/Tesselator.hpp +++ b/source/client/renderer/Tesselator.hpp @@ -82,7 +82,7 @@ public: bool field_26; bool m_bBlockColor; bool field_28; - int field_2C; + int m_nVertices; int field_30; bool field_34; diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index 8328cfa..2b91393 100644 --- a/source/client/renderer/TileRenderer.cpp +++ b/source/client/renderer/TileRenderer.cpp @@ -11,6 +11,7 @@ #include "client/renderer/PatchManager.hpp" #include "world/tile/FireTile.hpp" #include "world/tile/LiquidTile.hpp" +#include "world/tile/WireTile.hpp" #include "client/renderer/GrassColor.hpp" #include "client/renderer/FoliageColor.hpp" @@ -996,7 +997,7 @@ bool TileRenderer::tesselateDoorInWorld(Tile* tile, int x, int y, int z) return true; } -void TileRenderer::tesselateTorch(Tile* tile, float x, float y, float z, float a, float b) +void TileRenderer::tesselateTorch(Tile* tile, float x, float y, float z, float a, float b, float height) { constexpr float C_RATIO = 1.0f / 256.0f; constexpr float C_ONE_PIXEL = 1.0f / 16.0f; @@ -1034,8 +1035,8 @@ void TileRenderer::tesselateTorch(Tile* tile, float x, float y, float z, float a float x_9 = x_4 + a; float x_0 = x_5 + a; - float y_1 = y + C_ONE_PIXEL * 10.0f; - float y_2 = y + 1.0f; + float y_1 = y + C_ONE_PIXEL * 10.0f * height; + float y_2 = y + height; float y_3 = y + 0.0f; float z_1 = z2 - C_ONE_PIXEL; @@ -1313,6 +1314,244 @@ bool TileRenderer::tesselateFireInWorld(Tile* tile, int x, int y, int z) return true; } +bool TileRenderer::tesselateWireInWorld(Tile* tile, int x, int y, int z) +{ + int data = m_pLevelSource->getData(x, y, z); + int texture = tile->getTexture(DIR_YPOS, data); + + if (m_textureOverride >= 0) + texture = m_textureOverride; + + WireTile* pWireTile = (WireTile*)tile; + int connFlags = pWireTile->getConnections(m_pLevelSource, x, y, z); + + bool bUsingStraightTexture = false; + + // If we are only connected on 2 parallel sides, use the straight wire texture. + const int CF1 = (1 << WireTile::CONN_XN) | (1 << WireTile::CONN_XP); + const int CF2 = (1 << WireTile::CONN_ZN) | (1 << WireTile::CONN_ZP); + + if ((connFlags & WireTile::CONN_MASK) == CF1 || (connFlags & WireTile::CONN_MASK) == CF2) + { + texture++; + bUsingStraightTexture = true; + } + + float texX = 16.0f * float((texture % 16)); + float texY = 16.0f * float((texture / 16)); + const float C_RATIO = 1.0f / 256.0f; + const float C_RATIO2 = 1.0f / 16.0f; + + float texU_1, texU_2, texV_1, texV_2; + bool bRotateWire = bUsingStraightTexture && (connFlags & (1 << WireTile::CONN_ZP)); + + Tesselator& t = Tesselator::instance; + + AABB aabb = tile->m_aabb; + // HACK for bigger hitbox + if (!t.m_bBlockColor && connFlags != WireTile::CONN_MASK) + aabb.grow(-0.1f); + + if (bRotateWire) + { + // this is kind of hacky. + texU_1 = C_RATIO2 * aabb.min.z + C_RATIO * (texX); + texU_2 = C_RATIO2 * aabb.max.z + C_RATIO * (texX); + texV_1 = C_RATIO2 * aabb.min.x + C_RATIO * (texY); + texV_2 = C_RATIO2 * aabb.max.x + C_RATIO * (texY); + } + else + { + texU_1 = C_RATIO2 * aabb.min.x + C_RATIO * (texX); + texU_2 = C_RATIO2 * aabb.max.x + C_RATIO * (texX); + texV_1 = C_RATIO2 * aabb.min.z + C_RATIO * (texY); + texV_2 = C_RATIO2 * aabb.max.z + C_RATIO * (texY); + } + + // calculate the color based on the wire's current power + float bright = tile->getBrightness(m_pLevelSource, x, y, z); + float power = float(data) / 15.0f; + float rt = power * 0.6f + 0.4f; + if (data == 0) + rt = 0.3F; + float gt = power * power * 0.7f - 0.5f; + float bt = power * power * 0.6f - 0.7f; + if (gt < 0.0f) + gt = 0.0f; + if (bt < 0.0f) + bt = 0.0f; + + t.color(bright * rt, bright * gt, bright * bt); + + if (bRotateWire) + { + t.vertexUV(float(x) + aabb.max.x, float(y) + 0.01f, float(z) + aabb.max.z, texU_1, texV_2); + t.vertexUV(float(x) + aabb.max.x, float(y) + 0.01f, float(z) + aabb.min.z, texU_2, texV_2); + t.vertexUV(float(x) + aabb.min.x, float(y) + 0.01f, float(z) + aabb.min.z, texU_2, texV_1); + t.vertexUV(float(x) + aabb.min.x, float(y) + 0.01f, float(z) + aabb.max.z, texU_1, texV_1); + } + else + { + t.vertexUV(float(x) + aabb.max.x, float(y) + 0.01f, float(z) + aabb.max.z, texU_2, texV_2); + t.vertexUV(float(x) + aabb.max.x, float(y) + 0.01f, float(z) + aabb.min.z, texU_2, texV_1); + t.vertexUV(float(x) + aabb.min.x, float(y) + 0.01f, float(z) + aabb.min.z, texU_1, texV_1); + t.vertexUV(float(x) + aabb.min.x, float(y) + 0.01f, float(z) + aabb.max.z, texU_1, texV_2); + } + + if ((connFlags & WireTile::CONN_ABOVE_MASK) == 0) + return true; + + if (!bUsingStraightTexture) + { + texture++; + texX = 16.0f * float((texture % 16)); + texY = 16.0f * float((texture / 16)); + } + + texU_1 = C_RATIO * (texX); + texU_2 = C_RATIO * (texX + 15.99f); + texV_1 = C_RATIO * (texY); + texV_2 = C_RATIO * (texY + 15.99f); + + if (connFlags & (1 << WireTile::CONN_ABOVE_ZP)) + { + t.vertexUV(0.0f + x, 1.0f + y, 0.99f + z, texU_1, texV_1); + t.vertexUV(1.0f + x, 1.0f + y, 0.99f + z, texU_1, texV_2); + t.vertexUV(1.0f + x, 0.0f + y, 0.99f + z, texU_2, texV_2); + t.vertexUV(0.0f + x, 0.0f + y, 0.99f + z, texU_2, texV_1); + } + + if (connFlags & (1 << WireTile::CONN_ABOVE_ZN)) + { + t.vertexUV(1.0f + x, 1.0f + y, 0.01f + z, texU_1, texV_1); + t.vertexUV(0.0f + x, 1.0f + y, 0.01f + z, texU_1, texV_2); + t.vertexUV(0.0f + x, 0.0f + y, 0.01f + z, texU_2, texV_2); + t.vertexUV(1.0f + x, 0.0f + y, 0.01f + z, texU_2, texV_1); + } + + if (connFlags & (1 << WireTile::CONN_ABOVE_XN)) + { + t.vertexUV(0.01f + x, 1.0f + y, 0.0f + z, texU_1, texV_1); + t.vertexUV(0.01f + x, 1.0f + y, 1.0f + z, texU_1, texV_2); + t.vertexUV(0.01f + x, 0.0f + y, 1.0f + z, texU_2, texV_2); + t.vertexUV(0.01f + x, 0.0f + y, 0.0f + z, texU_2, texV_1); + } + + if (connFlags & (1 << WireTile::CONN_ABOVE_XP)) + { + t.vertexUV(0.99f + x, 1.0f + y, 1.0f + z, texU_1, texV_1); + t.vertexUV(0.99f + x, 1.0f + y, 0.0f + z, texU_1, texV_2); + t.vertexUV(0.99f + x, 0.0f + y, 0.0f + z, texU_2, texV_2); + t.vertexUV(0.99f + x, 0.0f + y, 1.0f + z, texU_2, texV_1); + } + + return true; +} + +bool TileRenderer::tesselateLeverInWorld(Tile* tile, int x, int y, int z) +{ + int data = m_pLevelSource->getData(x, y, z); + bool act = data & 16; + float bright = tile->getBrightness(m_pLevelSource, x, y, z); + + Tesselator& t = Tesselator::instance; + t.color(bright, bright, bright); + + switch (data & 7) + { + case 1: + tesselateTorch(tile, float(x) - 0.1f, float(y) + 0.2f, float(z), -0.4f, 0.0f); + break; + case 2: + tesselateTorch(tile, float(x) + 0.1f, float(y) + 0.2f, float(z), 0.4f, 0.0f); + break; + case 3: + tesselateTorch(tile, float(x), float(y) + 0.2f, float(z) - 0.1f, 0.0f, -0.4f); + break; + case 4: + tesselateTorch(tile, float(x), float(y) + 0.2f, float(z) + 0.1f, 0.0f, act ? -0.4f : 0.4f); + break; + default: + tesselateTorch(tile, float(x), float(y), float(z), 0.0f, 0.0f); + break; + } + + return true; +} + +bool TileRenderer::tesselateRepeaterInWorld(Tile* tile, int x, int y, int z) +{ + constexpr float C_RATIO = 1.0f / 256.0f; + + Tesselator& t = Tesselator::instance; + + float bright = m_pLevelSource->getBrightness(x, y, z); + t.color(bright, bright, bright); + + int data = m_pLevelSource->getData(x, y, z); + int texture = tile->getTexture(DIR_YPOS, data); + + float texX = float(16 * (texture % 16)); + float texY = float(16 * (texture / 16)); + + float texU_1 = C_RATIO * texX; + float texU_2 = C_RATIO * (texX + 15.99f); + float texV_1 = C_RATIO * texY; + float texV_2 = C_RATIO * (texY + 15.99f); + + // for torches + float ox1 = 0.0f, ox2 = 0.0f; + float oz1 = 0.0f, oz2 = 0.0f; + + int delay = data >> 2; + + switch (data & 3) + { + case 0: + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 0), texU_1, texV_2); + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 1), texU_2, texV_2); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 1), texU_2, texV_1); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 0), texU_1, texV_1); + ox1 = 0.3f; + ox2 = 0.1f - 0.2f * delay; + break; + case 1: + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 0), texU_2, texV_2); + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 1), texU_2, texV_1); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 1), texU_1, texV_1); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 0), texU_1, texV_2); + oz1 = 0.3f; + oz2 = 0.1f - 0.2f * delay; + break; + case 2: + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 0), texU_1, texV_1); + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 1), texU_2, texV_1); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 1), texU_2, texV_2); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 0), texU_1, texV_2); + ox1 = -0.3f; + ox2 = -0.1f + 0.2f * delay; + break; + case 3: + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 0), texU_1, texV_1); + t.vertexUV(float(x + 0), float(y) + 0.125f, float(z + 1), texU_1, texV_2); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 1), texU_2, texV_2); + t.vertexUV(float(x + 1), float(y) + 0.125f, float(z + 0), texU_2, texV_1); + oz1 = -0.3f; + oz2 = -0.1f + 0.2f * delay; + break; + } + renderFaceUp(tile, x, y, z, tile->getTexture(DIR_YNEG, data)); + renderWest(tile, x, y, z, tile->getTexture(DIR_XNEG, data)); + renderEast(tile, x, y, z, tile->getTexture(DIR_XPOS, data)); + renderNorth(tile, x, y, z, tile->getTexture(DIR_ZNEG, data)); + renderSouth(tile, x, y, z, tile->getTexture(DIR_ZPOS, data)); + + tesselateTorch(Tile::notGate, float(x + ox1), float(y), float(z + oz1), 0.0f, 0.0f, 0.75f); + tesselateTorch(Tile::notGate, float(x + ox2), float(y), float(z + oz2), 0.0f, 0.0f, 0.75f); + + return true; +} + bool TileRenderer::tesselateInWorld(Tile* tile, int x, int y, int z) { int shape = tile->getRenderShape(); @@ -1343,6 +1582,12 @@ bool TileRenderer::tesselateInWorld(Tile* tile, int x, int y, int z) return tesselateDoorInWorld(tile, x, y, z); case SHAPE_STAIRS: return tesselateStairsInWorld(tile, x, y, z); + case SHAPE_WIRE: + return tesselateWireInWorld(tile, x, y, z); + case SHAPE_LEVER: + return tesselateLeverInWorld(tile, x, y, z); + case SHAPE_REPEATER: + return tesselateRepeaterInWorld(tile, x, y, z); } return false; diff --git a/source/client/renderer/TileRenderer.hpp b/source/client/renderer/TileRenderer.hpp index 4757723..3920588 100644 --- a/source/client/renderer/TileRenderer.hpp +++ b/source/client/renderer/TileRenderer.hpp @@ -41,7 +41,7 @@ public: void renderFaceDown(Tile*, float x, float y, float z, int texture); void renderFaceUp(Tile*, float x, float y, float z, int texture); void tesselateCrossTexture(Tile* tile, int data, float x, float y, float z); - void tesselateTorch(Tile*, float x, float y, float z, float a, float b); + void tesselateTorch(Tile*, float x, float y, float z, float a, float b, float height = 1.0f); bool tesselateBlockInWorldWithAmbienceOcclusion(Tile*, int x, int y, int z, float r, float g, float b); bool tesselateBlockInWorld(Tile*, int x, int y, int z, float r, float g, float b); @@ -52,9 +52,11 @@ public: bool tesselateLadderInWorld(Tile*, int x, int y, int z); bool tesselateTorchInWorld(Tile*, int x, int y, int z); bool tesselateDoorInWorld(Tile*, int x, int y, int z); -#ifndef ORIGINAL_CODE bool tesselateFireInWorld(Tile*, int x, int y, int z); -#endif + bool tesselateWireInWorld(Tile*, int x, int y, int z); + bool tesselateLeverInWorld(Tile*, int x, int y, int z); + bool tesselateRepeaterInWorld(Tile*, int x, int y, int z); + #ifdef ENH_USE_OWN_AO bool tesselateBlockInWorldWithAmbienceOcclusionV2(Tile*, int x, int y, int z, float r, float g, float b); #endif diff --git a/source/common/CMakeLists.txt b/source/common/CMakeLists.txt index fd0550e..56a9437 100644 --- a/source/common/CMakeLists.txt +++ b/source/common/CMakeLists.txt @@ -31,3 +31,6 @@ else() find_package(ZLIB REQUIRED) target_link_libraries(Common PRIVATE ZLIB::ZLIB) endif() +if (ANDROID) + target_link_libraries(Common PRIVATE log) +endif() diff --git a/source/common/Logger.hpp b/source/common/Logger.hpp index 3d1f4a5..fd5b090 100644 --- a/source/common/Logger.hpp +++ b/source/common/Logger.hpp @@ -2,7 +2,7 @@ #include -#ifdef ANDROID +#ifdef __ANDROID__ #include enum eLogLevel @@ -39,7 +39,7 @@ public: #ifndef NDEBUG -#ifdef ANDROID +#ifdef __ANDROID__ // TODO: Add a LoggerAndroid #define LOG(level, ...) __android_log_print(level, "ReMinecraftPE", __VA_ARGS__) #else diff --git a/source/common/Matrix.hpp b/source/common/Matrix.hpp index 277c006..1d45b7c 100644 --- a/source/common/Matrix.hpp +++ b/source/common/Matrix.hpp @@ -11,8 +11,6 @@ #include #include "Mth.hpp" -typedef unsigned int GLenum; - class Matrix { public: diff --git a/source/common/Utils.hpp b/source/common/Utils.hpp index c8bc6eb..0806983 100644 --- a/source/common/Utils.hpp +++ b/source/common/Utils.hpp @@ -270,6 +270,8 @@ enum eTileID TILE_OBSIDIAN_CRYING = 200, // custom stuff - ID of 200 TILE_ROCKET_LAUNCHER, + TILE_REDSTONE_TORCH_OFF, + TILE_REDSTONE_TORCH_ON, ITEM_SHOVEL_IRON = 256, ITEM_PICKAXE_IRON, @@ -512,7 +514,9 @@ enum // Textures TEXTURE_NONE126, TEXTURE_NONE127, + TEXTURE_REPEATER_OFF = 131, TEXTURE_LAPIS = 144, + TEXTURE_REPEATER_ON = 147, TEXTURE_ORE_LAPIS = 160, TEXTURE_SANDSTONE_TOP = 176, @@ -536,12 +540,17 @@ enum eRenderShape SHAPE_TORCH, SHAPE_FIRE, SHAPE_WATER, - SHAPE_UNK5, - SHAPE_UNK6, + SHAPE_WIRE, + SHAPE_CROPS, SHAPE_DOOR, SHAPE_LADDER, - SHAPE_UNK9, + SHAPE_RAIL, SHAPE_STAIRS, + SHAPE_FENCE, + SHAPE_LEVER, + SHAPE_CACTUS, + SHAPE_BED, + SHAPE_REPEATER, }; enum eRenderLayer @@ -605,6 +614,20 @@ struct TilePos : Pos return z < b.z; } + + bool operator==(const TilePos& b) const + { + return x == b.x && y == b.y && z == b.z; + } +}; + +template<> +struct std::hash +{ + std::size_t operator()(const TilePos& t) const noexcept + { + return t.x * 0x88f9fa + t.y * 0xef88b + t.z; + } }; #define SAFE_DELETE(ptr) do { if (ptr) delete ptr; } while (0) diff --git a/source/renderer/CMakeLists.txt b/source/renderer/CMakeLists.txt index e9a7c20..6caf5af 100644 --- a/source/renderer/CMakeLists.txt +++ b/source/renderer/CMakeLists.txt @@ -16,7 +16,7 @@ endif() target_include_directories(Renderer PRIVATE ../..) # Find OpenGL library -if(USE_GLES1_COMPATIBILITY_LAYER) +if (USE_GLES1_COMPATIBILITY_LAYER) message(STATUS "GLESv1 compatibility layer") set(GLES_COMPATIBILITY_LAYER_USE_SDL TRUE CACHE BOOL "" FORCE) set(GLES_COMPATIBILITY_LAYER_DEPENDENCY SDL CACHE STRING "" FORCE) @@ -28,10 +28,13 @@ if(USE_GLES1_COMPATIBILITY_LAYER) # Use WebGL 2 target_link_options(Renderer PUBLIC -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2) endif() -elseif(WIN32) +elseif (WIN32) message(STATUS "Windows's opengl32") target_link_libraries(Renderer PUBLIC opengl32 glu32) target_sources(Renderer PRIVATE ../../thirdparty/GL/GLExt.cpp) +elseif (ANDROID) + message(STATUS "Android's opengl32") + target_link_libraries(Renderer PUBLIC EGL GLESv1_CM) else() message(STATUS "System OpenGL") find_package(OpenGL REQUIRED) diff --git a/source/server/Options.cpp b/source/server/Options.cpp index 446d0da..a283042 100644 --- a/source/server/Options.cpp +++ b/source/server/Options.cpp @@ -43,6 +43,7 @@ void Options::_initDefaultValues() field_23E = 0; m_fMasterVolume = 1.0f; m_bFlyCheat = false; + m_bFlyNoclip = false; field_241 = 0; field_8 = 0.5f; field_24C = 0; diff --git a/source/server/Options.hpp b/source/server/Options.hpp index 2ccf1a7..bda0fb2 100644 --- a/source/server/Options.hpp +++ b/source/server/Options.hpp @@ -116,6 +116,7 @@ public: bool m_bThirdPerson; uint8_t field_23E; bool m_bFlyCheat; + bool m_bFlyNoclip; uint8_t field_240; uint8_t field_241; float field_244; diff --git a/source/world/CMakeLists.txt b/source/world/CMakeLists.txt index cfb6b70..c521c40 100644 --- a/source/world/CMakeLists.txt +++ b/source/world/CMakeLists.txt @@ -84,6 +84,7 @@ add_library(World STATIC tile/InvisibleTile.cpp tile/InvisibleTile.hpp tile/LadderTile.cpp tile/LadderTile.hpp tile/LeafTile.cpp tile/LeafTile.hpp + tile/LeverTile.cpp tile/LeverTile.hpp tile/LiquidTile.cpp tile/LiquidTile.hpp tile/LiquidTileDynamic.cpp tile/LiquidTileDynamic.hpp tile/LiquidTileStatic.cpp tile/LiquidTileStatic.hpp @@ -91,6 +92,8 @@ add_library(World STATIC tile/ObsidianTile.cpp tile/ObsidianTile.hpp tile/OreTile.cpp tile/OreTile.hpp tile/RedStoneOreTile.cpp tile/RedStoneOreTile.hpp + tile/RedStoneRepeaterTile.cpp tile/RedStoneRepeaterTile.hpp + tile/RedStoneTorchTile.cpp tile/RedStoneTorchTile.hpp tile/ReedTile.cpp tile/ReedTile.hpp tile/RocketLauncherTile.cpp tile/RocketLauncherTile.hpp tile/SandStoneTile.cpp tile/SandStoneTile.hpp @@ -104,8 +107,10 @@ add_library(World STATIC tile/TntTile.cpp tile/TntTile.hpp tile/TopSnowTile.cpp tile/TopSnowTile.hpp tile/TorchTile.cpp tile/TorchTile.hpp + tile/TorchUpdateEvent.hpp tile/TransparentTile.cpp tile/TransparentTile.hpp tile/TreeTile.cpp tile/TreeTile.hpp + tile/WireTile.cpp tile/WireTile.hpp ) if (NOT BUILD_SERVER) target_sources(World PRIVATE diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index 16c7be4..5586c6f 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -189,6 +189,11 @@ public: (m_pos.z - z) * (m_pos.z - z); } + int getDirection() + { + return Mth::floor((((m_yaw + 180.0f) * 4.0f) / 360.0f) - 0.5f) & 3; + } + public: static int entityCounter; static Random sharedRandom; diff --git a/source/world/item/DoorItem.cpp b/source/world/item/DoorItem.cpp index 9105595..56f1e16 100644 --- a/source/world/item/DoorItem.cpp +++ b/source/world/item/DoorItem.cpp @@ -27,7 +27,7 @@ bool DoorItem::useOn(ItemInstance* inst, Player* player, Level* level, int x, in if (!pTile->mayPlace(level, x, y + 1, z)) return false; - int faceDir = Mth::floor((((player->m_yaw + 180.0f) * 4.0f) / 360.0f) - 0.5f) & 3; + int faceDir = player->getDirection(); int offsetX, offsetZ; switch (faceDir) { diff --git a/source/world/item/DoorItem.hpp b/source/world/item/DoorItem.hpp index 7e4a96f..8938a90 100644 --- a/source/world/item/DoorItem.hpp +++ b/source/world/item/DoorItem.hpp @@ -14,7 +14,7 @@ class DoorItem : public Item public: DoorItem(int id, Material* pMtl); - virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int); + virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int) override; public: Material* m_pMaterial; diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 630bc48..64f695a 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -79,6 +79,13 @@ void Inventory::prepareCreativeInventory() addCreativeItem(Item::door_wood->m_itemID); addCreativeItem(Item::door_iron->m_itemID); addCreativeItem(Item::rocket->m_itemID); + addCreativeItem(Tile::wire->m_ID); + addCreativeItem(Tile::notGate->m_ID); + addCreativeItem(Tile::lever->m_ID); + //addCreativeItem(Tile::button->m_ID); + //addCreativeItem(Tile::plate_stone->m_ID); + //addCreativeItem(Tile::plate_wood->m_ID); + addCreativeItem(Tile::repeater_off->m_ID); for (int i = 0; i < C_MAX_HOTBAR_ITEMS; i++) m_hotbar[i] = i; @@ -312,4 +319,4 @@ int Inventory::getAttackDamage(Entity* pEnt) return 1; return pInst->getAttackDamage(pEnt); -} \ No newline at end of file +} diff --git a/source/world/item/TileItem.hpp b/source/world/item/TileItem.hpp index 9efd050..443a420 100644 --- a/source/world/item/TileItem.hpp +++ b/source/world/item/TileItem.hpp @@ -14,9 +14,9 @@ class TileItem : public Item public: TileItem(int id); - virtual std::string getDescriptionId(); - virtual std::string getDescriptionId(ItemInstance*); - virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int); + virtual std::string getDescriptionId() override; + virtual std::string getDescriptionId(ItemInstance*) override; + virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int) override; public: int m_tile; diff --git a/source/world/item/TilePlanterItem.hpp b/source/world/item/TilePlanterItem.hpp index a42e923..f303a62 100644 --- a/source/world/item/TilePlanterItem.hpp +++ b/source/world/item/TilePlanterItem.hpp @@ -14,7 +14,7 @@ class TilePlanterItem : public Item public: TilePlanterItem(int id, int place); - virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int); + virtual bool useOn(ItemInstance*, Player*, Level*, int, int, int, int) override; public: int m_tile; diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 7ec5473..af95b6f 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -171,7 +171,7 @@ public: public: AABBVector m_aabbs; bool m_bInstantTicking; - bool m_bIsMultiplayer; // if the level is controlled externally by a server + bool m_bIsMultiplayer; // m_bIsClientSide TODO bool m_bPostProcessing; EntityVector m_entities; std::vector m_players; diff --git a/source/world/tile/LeverTile.cpp b/source/world/tile/LeverTile.cpp new file mode 100644 index 0000000..062936e --- /dev/null +++ b/source/world/tile/LeverTile.cpp @@ -0,0 +1,187 @@ +/******************************************************************** + 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; +} + +bool LeverTile::isSolidRender() +{ + return false; +} + +bool LeverTile::isCubeShaped() +{ + return false; +} + +int LeverTile::getRenderShape() +{ + return SHAPE_LEVER; +} + +HitResult LeverTile::clip(Level* level, int x, int y, int z, Vec3 a, Vec3 b) +{ + switch (level->getData(x, y, z) & 7) + { + case 1: + setShape(0.0f, 0.2f, 0.35f, 0.3f, 0.8f, 0.65f); + break; + case 2: + setShape(0.7f, 0.2f, 0.35f, 1.0f, 0.8f, 0.65f); + break; + case 3: + setShape(0.35f, 0.2f, 0.0f, 0.65f, 0.8f, 0.3f); + break; + case 4: + setShape(0.35f, 0.2f, 0.7f, 0.65f, 0.8f, 1.0f); + break; + default: + setShape(0.4f, 0.0f, 0.4f, 0.6f, 0.6f, 0.6f); + 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; +} + +bool LeverTile::checkCanSurvive(Level* level, int x, int y, int z) +{ + if (mayPlace(level, x, y, z)) + return true; + + spawnResources(level, x, y, z, 0); + level->setTile(x, y, z, TILE_AIR); + + 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); + data &= 7; + + 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 (!flag) + return; // all good + + spawnResources(level, x, y, z, 0); + 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); + updateNeighbors(level, x, y, z, m_ID); +} + +void LeverTile::onRemove(Level* level, int x, int y, int z) +{ + updateNeighbors(level, x, y, z, m_ID); +} + +int LeverTile::use(Level* level, int x, int y, int z, Player* player) +{ + int data = level->getData(x, y, z); + // toggling + data ^= 8; + level->setData(x, y, z, data); + level->playSound(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, "random.explode", 1.0f, 0.9f + 0.1f * level->m_random.nextFloat()); + updateNeighbors(level, x, y, z, m_ID); + + return 1; +} + +bool LeverTile::isSignalSource() +{ + return true; +} + +int LeverTile::getTickDelay() +{ + return 2; // 2 in game ticks per redstone tick +} + +int LeverTile::getSignal(LevelSource* level, int x, int y, int z, int dir) +{ + //if (dir == DIR_YNEG) + return getDirectSignal(level, x, y, z, dir); + + return 0; +} + +int LeverTile::getDirectSignal(LevelSource* level, int x, int y, int z, int dir) +{ + int data = level->getData(x, y, z); + if ((data & 8) == 0) + return 0; + data &= 7; + + // check for the sides _behind_ the torch + //if (data == 5 && dir == DIR_YPOS) return 0; + //if (data == 3 && dir == DIR_ZPOS) return 0; + //if (data == 4 && dir == DIR_ZNEG) return 0; + //if (data == 1 && dir == DIR_XPOS) return 0; + //if (data == 2 && dir == DIR_XNEG) return 0; + + return 1; +} + +void LeverTile::updateNeighbors(Level* level, int x, int y, int z, int id) +{ + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); + + level->updateNeighborsAt(x - 1, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z, id); + level->updateNeighborsAt(x + 1, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z, id); + level->updateNeighborsAt(x, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z - 1, id); + level->updateNeighborsAt(x, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z + 1, id); +} + diff --git a/source/world/tile/LeverTile.hpp b/source/world/tile/LeverTile.hpp new file mode 100644 index 0000000..52442b0 --- /dev/null +++ b/source/world/tile/LeverTile.hpp @@ -0,0 +1,40 @@ +/******************************************************************** + 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); + + virtual AABB* getAABB(Level*, int x, int y, int z) override; + virtual bool isSolidRender() override; + virtual bool isCubeShaped() override; + virtual int getRenderShape() override; + virtual HitResult clip(Level*, int x, int y, int z, Vec3 a, Vec3 b) override; + virtual bool mayPlace(Level*, int, int, int) override; + virtual void neighborChanged(Level*, int, int, int, int) override; + int use(Level*, int x, int y, int z, Player*) override; + //virtual void setPlacedOnFace(Level*, int, int, int, int) override; + //virtual void tick(Level*, int, int, int, Random*) override; + + bool checkCanSurvive(Level*, int, int, int); + + bool isSignalSource() override; + int getTickDelay() override; + void onPlace(Level*, int x, int y, int z) override; + void onRemove(Level*, int x, int y, int z) override; + //int getResource(int data, Random* random) 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; + + void updateNeighbors(Level*, int x, int y, int z, int id); +}; diff --git a/source/world/tile/RedStoneRepeaterTile.cpp b/source/world/tile/RedStoneRepeaterTile.cpp new file mode 100644 index 0000000..4b8aee5 --- /dev/null +++ b/source/world/tile/RedStoneRepeaterTile.cpp @@ -0,0 +1,180 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2024 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ +#include "RedStoneRepeaterTile.hpp" +#include "world/level/Level.hpp" + +RedStoneRepeaterTile::RedStoneRepeaterTile(int id, int texture, Material* pMtl) : Tile(id, texture, pMtl) +{ + m_bActive = id == TILE_REPEATER_ON; + m_delay = 0; + + setShape(0, 0, 0, 1, 0.125f, 1); +} + +bool RedStoneRepeaterTile::isCubeShaped() +{ + return false; +} + +bool RedStoneRepeaterTile::isSolidRender() +{ + return false; +} + +bool RedStoneRepeaterTile::isSignalSource() +{ + return true; +} + +int RedStoneRepeaterTile::getTickDelay() +{ + return 2; // 2 in game ticks per redstone tick +} + +int RedStoneRepeaterTile::getRenderShape() +{ + return SHAPE_REPEATER; +} + +int RedStoneRepeaterTile::getTexture(int dir, int data) +{ + switch (dir) + { + case DIR_YPOS: + return m_TextureFrame; + case DIR_YNEG: + return TEXTURE_STONE_SLAB_TOP; + } + return m_TextureFrame; +} + +void RedStoneRepeaterTile::onPlace(Level* level, int x, int y, int z) +{ + if (isActive()) + updateNeighbors(level, x, y, z, m_ID); + m_delay = level->getData(x, y, z) >> 2; +} + +void RedStoneRepeaterTile::onRemove(Level* level, int x, int y, int z) +{ + if (isActive()) + updateNeighbors(level, x, y, z, m_ID); +} + +void RedStoneRepeaterTile::neighborChanged(Level* level, int x, int y, int z, int dir) +{ + Tile::neighborChanged(level, x, y, z, dir); + level->addToTickNextTick(x, y, z, m_ID, getTickDelay() * (m_delay + 1)); +} + +int RedStoneRepeaterTile::getResource(int data, Random* random) +{ + return Tile::repeater_off->m_ID; +} + +void RedStoneRepeaterTile::tick(Level* level, int x, int y, int z, Random* random) +{ + //if (level->m_bIsMultiplayer) + // return; + + if (hasSignalFromBehind(level, x, y, z)) + level->setTileAndData(x, y, z, Tile::repeater_on->m_ID, level->getData(x, y, z)); + else + level->setTileAndData(x, y, z, Tile::repeater_off->m_ID, level->getData(x, y, z)); +} + +int RedStoneRepeaterTile::getDirectSignal(LevelSource* level, int x, int y, int z, int dir) +{ + return getSignal(level, x, y, z, dir); +} + +int RedStoneRepeaterTile::getSignal(LevelSource* level, int x, int y, int z, int dir) +{ + if (!isActive()) return 0; + int face = level->getData(x, y, z) & 3; + switch (dir) + { + case DIR_ZNEG: + return face == 1; + case DIR_ZPOS: + return face == 3; + case DIR_XNEG: + return face == 0; + case DIR_XPOS: + return face == 2; + } + return 0; +} + +void RedStoneRepeaterTile::setPlacedBy(Level* level, int x, int y, int z, Mob* mob) +{ + int data = level->getData(x, y, z); + + data &= 0xC; // 0b1100 + if (data == 0) + data |= m_delay << 2; + + data |= mob->getDirection(); + + level->setData(x, y, z, data); +} + +int RedStoneRepeaterTile::use(Level* level, int x, int y, int z, Player* player) +{ + m_delay = (m_delay + 1) % 3; + level->setData(x, y, z, (level->getData(x, y, z) & 3) | (m_delay << 2)); + return 1; +} + +void RedStoneRepeaterTile::updateNeighbors(Level* level, int x, int y, int z, int id) +{ + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); +} + +bool RedStoneRepeaterTile::hasSignalFromBehind(Level* level, int x, int y, int z) +{ + int data = level->getData(x, y, z) & 3; + if (data == 0 && level->getSignal(x - 1, y, z, DIR_XPOS)) return true; + if (data == 2 && level->getSignal(x + 1, y, z, DIR_XNEG)) return true; + if (data == 1 && level->getSignal(x, y, z - 1, DIR_ZPOS)) return true; + if (data == 3 && level->getSignal(x, y, z + 1, DIR_ZNEG)) return true; + return false; +} + +void RedStoneRepeaterTile::animateTick(Level* level, int x, int y, int z, Random* random) +{ + float partX = float(x) + 0.5f, partZ = float(z) + 0.5f, partY = float(y) + 0.7f; + + // @NOTE: Need to use addParticle("reddust") 5 times. Invalid data values don't actually generate a smoke + switch (level->getData(x, y, z) & 3) + { + case 0: + partX -= 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 1: + partX += 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 2: + partZ -= 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 3: + partZ += 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + } +} diff --git a/source/world/tile/RedStoneRepeaterTile.hpp b/source/world/tile/RedStoneRepeaterTile.hpp new file mode 100644 index 0000000..0b3de1b --- /dev/null +++ b/source/world/tile/RedStoneRepeaterTile.hpp @@ -0,0 +1,45 @@ +/******************************************************************** + Minecraft: Pocket Edition - Decompilation Project + Copyright (C) 2024 iProgramInCpp + + The following code is licensed under the BSD 1 clause license. + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + +#pragma once + +#include "Tile.hpp" + +class RedStoneRepeaterTile : public Tile +{ +public: + RedStoneRepeaterTile(int id, int texture, Material* pMtl); + + bool isCubeShaped() override; + bool isSolidRender() override; + bool isSignalSource() override; + int getTickDelay() override; + int getRenderShape() override; + int getTexture(int dir, int data) override; + void onPlace(Level*, int x, int y, int z) override; + void onRemove(Level*, int x, int y, int z) override; + void neighborChanged(Level*, int x, int y, int z, int dir) override; + int getResource(int data, Random* random) override; + void tick(Level*, int, int, int, Random*) 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; + void animateTick(Level* level, int x, int y, int z, Random* random) override; + void setPlacedBy(Level* level, int x, int y, int z, Mob* mob) override; + int use(Level* level, int x, int y, int z, Player* player) override; + + void updateNeighbors(Level*, int x, int y, int z, int id); + bool hasSignalFromBehind(Level*, int x, int y, int z); + + bool isActive() { + return m_bActive; + } + +private: + bool m_bActive; + int m_delay; +}; diff --git a/source/world/tile/RedStoneTorchTile.cpp b/source/world/tile/RedStoneTorchTile.cpp new file mode 100644 index 0000000..1df9deb --- /dev/null +++ b/source/world/tile/RedStoneTorchTile.cpp @@ -0,0 +1,191 @@ +/******************************************************************** + 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 "RedStoneTorchTile.hpp" +#include "world/level/Level.hpp" + +TorchUpdateEvents RedStoneTorchTile::m_recentUpdates; + +RedStoneTorchTile::RedStoneTorchTile(int id, int texture, Material* pMtl) : TorchTile(id, texture, pMtl) +{ + m_bActive = id == TILE_REDSTONE_TORCH_ON; + + if (m_bActive) + setLightEmission(0.5f); + setTicking(true); +} + +bool RedStoneTorchTile::isSignalSource() +{ + return true; +} + +int RedStoneTorchTile::getTickDelay() +{ + return 2; // 2 in game ticks per redstone tick +} + +void RedStoneTorchTile::onPlace(Level* level, int x, int y, int z) +{ + //LOG_I("RedStoneTorchTile place %d,%d,%d", x, y, z); + + if (level->getData(x, y, z) == 0) + TorchTile::onPlace(level, x, y, z); + + if (isActive()) + updateNeighbors(level, x, y, z, m_ID); +} + +void RedStoneTorchTile::onRemove(Level* level, int x, int y, int z) +{ + if (isActive()) + updateNeighbors(level, x, y, z, m_ID); +} + +void RedStoneTorchTile::neighborChanged(Level* level, int x, int y, int z, int dir) +{ + TorchTile::neighborChanged(level, x, y, z, dir); + level->addToTickNextTick(x, y, z, m_ID, getTickDelay()); +} + +int RedStoneTorchTile::getResource(int data, Random* random) +{ + return Tile::notGate->m_ID; +} + +void RedStoneTorchTile::tick(Level* level, int x, int y, int z, Random* random) +{ + //if (level->m_bIsMultiplayer) + // return; + + bool flag = hasSignalFromBehind(level, x, y, z); + + // remove updates that are too old + while (!m_recentUpdates.empty()) + { + if (level->getTime() - m_recentUpdates.front().time < 100) + break; + + m_recentUpdates.pop_front(); + } + + if (isActive()) + { + if (flag) + { + level->setTileAndData(x, y, z, Tile::notGate_off->m_ID, level->getData(x, y, z)); + + if (checkBurnOut(level, x, y, z, true)) + { + // TODO - Particle related + } + } + } + else if (!flag && !checkBurnOut(level, x, y, z, false)) + { + level->setTileAndData(x, y, z, Tile::notGate->m_ID, level->getData(x, y, z)); + } +} + +int RedStoneTorchTile::getDirectSignal(LevelSource* level, int x, int y, int z, int dir) +{ + if (dir == DIR_YNEG) + return getSignal(level, x, y, z, dir); + + return 0; +} + +int RedStoneTorchTile::getSignal(LevelSource* level, int x, int y, int z, int dir) +{ + if (!isActive()) return 0; + int data = level->getData(x, y, z); + + // check for the sides _behind_ the torch + if (data == 5 && dir == DIR_YPOS) return 0; + if (data == 3 && dir == DIR_ZPOS) return 0; + if (data == 4 && dir == DIR_ZNEG) return 0; + if (data == 1 && dir == DIR_XPOS) return 0; + if (data == 2 && dir == DIR_XNEG) return 0; + + return 1; +} + +void RedStoneTorchTile::updateNeighbors(Level* level, int x, int y, int z, int id) +{ + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); +} + +bool RedStoneTorchTile::hasSignalFromBehind(Level* level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + if (data == 5 && level->getSignal(x, y - 1, z, DIR_YNEG)) return true; + if (data == 3 && level->getSignal(x, y, z - 1, DIR_ZNEG)) return true; + if (data == 4 && level->getSignal(x, y, z + 1, DIR_ZPOS)) return true; + if (data == 1 && level->getSignal(x - 1, y, z, DIR_XNEG)) return true; + if (data == 2 && level->getSignal(x + 1, y, z, DIR_XPOS)) return true; + return false; +} + +bool RedStoneTorchTile::checkBurnOut(Level* level, int x, int y, int z, bool b) +{ + if (b) + { + m_recentUpdates.push_back(TorchUpdateEvent(x, y, z, level->getTime())); + } + + TorchUpdateEvents::iterator iter = m_recentUpdates.begin(); + + int count = 0; + + for (; iter != m_recentUpdates.end(); ++iter) + { + // TODO: make the current level pointer also a filter + TorchUpdateEvent& bot = *iter; + if (bot.x == x && bot.y == y && bot.z == z && ++count == 8) + return true; + } + + return false; +} + +void RedStoneTorchTile::animateTick(Level* level, int x, int y, int z, Random* random) +{ + float partX = float(x) + 0.5f, partZ = float(z) + 0.5f, partY = float(y) + 0.7f; + + // @NOTE: Need to use addParticle("reddust") 5 times. Invalid data values don't actually generate a smoke + switch (level->getData(x, y, z)) + { + case 1: + partX -= 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 2: + partX += 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 3: + partZ -= 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 4: + partZ += 0.27f; + partY += 0.22f; + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + case 5: + level->addParticle("reddust", partX, partY, partZ, 0.0f, 0.0f, 0.0f); + break; + } +} diff --git a/source/world/tile/RedStoneTorchTile.hpp b/source/world/tile/RedStoneTorchTile.hpp new file mode 100644 index 0000000..616f14b --- /dev/null +++ b/source/world/tile/RedStoneTorchTile.hpp @@ -0,0 +1,42 @@ +/******************************************************************** + 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 +#include "TorchTile.hpp" +#include "TorchUpdateEvent.hpp" + +class RedStoneTorchTile : public TorchTile +{ +public: + RedStoneTorchTile(int id, int texture, Material* pMtl); + + bool isSignalSource() override; + int getTickDelay() override; + void onPlace(Level*, int x, int y, int z) override; + void onRemove(Level*, int x, int y, int z) override; + void neighborChanged(Level*, int x, int y, int z, int dir) override; + int getResource(int data, Random* random) override; + void tick(Level*, int, int, int, Random*) 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; + void animateTick(Level* level, int x, int y, int z, Random* random) override; + + void updateNeighbors(Level*, int x, int y, int z, int id); + bool checkBurnOut(Level*, int x, int y, int z, bool b); + bool hasSignalFromBehind(Level*, int x, int y, int z); + + bool isActive() { + return m_bActive; + } +private: + bool m_bActive; + static TorchUpdateEvents m_recentUpdates; +}; + diff --git a/source/world/tile/StoneSlabTile.cpp b/source/world/tile/StoneSlabTile.cpp index 0c0c0d0..975a29d 100644 --- a/source/world/tile/StoneSlabTile.cpp +++ b/source/world/tile/StoneSlabTile.cpp @@ -13,8 +13,9 @@ StoneSlabTile::StoneSlabTile(int id, bool full) : Tile(id, TEXTURE_STONE_SLAB_TO { m_bFull = full; + // TODO(Er2): Re-add bottom slab later if (!full) - setShape(0, 0, 0, 1, 0.5f, 1); + setShape(0, 0.5f, 0, 1, 1, 1); setLightBlock(255); } diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index 9b77abe..2539d16 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -49,6 +49,9 @@ #include "BookshelfTile.hpp" #include "WireTile.hpp" #include "RocketLauncherTile.hpp" +#include "RedStoneTorchTile.hpp" +#include "RedStoneRepeaterTile.hpp" +#include "LeverTile.hpp" std::string Tile::TILE_DESCRIPTION_PREFIX = "tile."; @@ -704,6 +707,52 @@ void Tile::initTiles() ->setSoundType(Tile::SOUND_STONE) ->setDescriptionId("rocketLauncher"); + Tile::wire = (new WireTile(TILE_WIRE)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_STONE) + ->setDescriptionId("wire"); + + Tile::notGate_off = (new RedStoneTorchTile(TILE_REDSTONE_TORCH_OFF, TEXTURE_TORCH_RED_STONE_OFF, Material::decoration)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("notGate"); + + Tile::notGate = (new RedStoneTorchTile(TILE_REDSTONE_TORCH_ON, TEXTURE_TORCH_RED_STONE, Material::decoration)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("notGateLit"); + + Tile::repeater_off = (new RedStoneRepeaterTile(TILE_REPEATER_OFF, TEXTURE_REPEATER_OFF, Material::decoration)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("repeater"); + + Tile::repeater_on = (new RedStoneRepeaterTile(TILE_REPEATER_ON, TEXTURE_REPEATER_ON, Material::decoration)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("repeaterLit"); + + Tile::lever = (new LeverTile(TILE_LEVER, TEXTURE_LEVER, Material::stone)) + ->init() + ->setDestroyTime(0.0f) + ->setSoundType(Tile::SOUND_STONE) + ->setDescriptionId("lever"); + + //Tile::button = (new ButtonTile(TILE_BUTTON, TEXTURE_BUTTON, Material::stone)) + // ->init() + // ->setDestroyTime(0.0f) + // ->setSoundType(Tile::SOUND_STONE) + // ->setDescriptionId("button"); + + //addCreativeItem(Tile::button->m_ID); + //addCreativeItem(Tile::plate_stone->m_ID); + //addCreativeItem(Tile::plate_wood->m_ID); + for (int i = 0; i < C_MAX_TILES; i++) { if (Tile::tiles[i]) @@ -941,17 +990,17 @@ HitResult Tile::clip(Level* level, int x, int y, int z, Vec3 vec1, Vec3 vec2) return HitResult(x, y, z, collType, *pVec + Vec3(float(x), float(y), float(z))); } -int Tile::getSignal(LevelSource* pLevel, int x, int y, int z) +/*int Tile::getSignal(LevelSource* pLevel, int x, int y, int z) { return 0; -} +}*/ int Tile::getSignal(LevelSource* pLevel, int x, int y, int z, int dir) { return 0; } -int Tile::getDirectSignal(Level* pLevel, int x, int y, int z, int dir) +int Tile::getDirectSignal(LevelSource* pLevel, int x, int y, int z, int dir) { return 0; } @@ -1152,4 +1201,13 @@ Tile *Tile::bookshelf, *Tile::mossStone, *Tile::cryingObsidian, - *Tile::rocketLauncher; + *Tile::rocketLauncher, + *Tile::wire, + *Tile::notGate_off, + *Tile::notGate, + *Tile::lever, + *Tile::button, + *Tile::plate_stone, + *Tile::plate_wood, + *Tile::repeater_off, + *Tile::repeater_on; diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp index cba655d..2cd900d 100644 --- a/source/world/tile/Tile.hpp +++ b/source/world/tile/Tile.hpp @@ -82,9 +82,9 @@ public: // virtual functions virtual void handleEntityInside(Level*, int, int, int, Entity*, Vec3&); virtual int getColor(LevelSource*, int, int, int); virtual bool isSignalSource(); - virtual int getSignal(LevelSource*, int, int, int); + //virtual int getSignal(LevelSource*, int, int, int); virtual int getSignal(LevelSource*, int, int, int, int); - virtual int getDirectSignal(Level*, int, int, int, int); + virtual int getDirectSignal(LevelSource*, int, int, int, int); virtual void entityInside(Level*, int, int, int, Entity*); virtual void playerDestroy(Level*, Player*, int, int, int, int); virtual bool canSurvive(Level*, int, int, int); @@ -214,7 +214,16 @@ public: // static variables * bookshelf, * mossStone, * cryingObsidian, - * rocketLauncher; + * rocketLauncher, + * wire, + * notGate_off, + * notGate, + * lever, + * button, + * plate_stone, + * plate_wood, + * repeater_off, + * repeater_on; public: int m_TextureFrame; diff --git a/source/world/tile/TorchTile.cpp b/source/world/tile/TorchTile.cpp index 2cf9d17..d5e5f13 100644 --- a/source/world/tile/TorchTile.cpp +++ b/source/world/tile/TorchTile.cpp @@ -106,6 +106,7 @@ HitResult TorchTile::clip(Level* level, int x, int y, int z, Vec3 a, Vec3 b) bool TorchTile::mayPlace(Level* level, int x, int y, int z) { + if (level->getTile(x, y - 1, z) == Tile::stoneSlabHalf->m_ID) return true; 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; diff --git a/source/world/tile/TorchTile.hpp b/source/world/tile/TorchTile.hpp index e31f2ae..b5cf4d8 100644 --- a/source/world/tile/TorchTile.hpp +++ b/source/world/tile/TorchTile.hpp @@ -15,17 +15,17 @@ class TorchTile : public Tile public: TorchTile(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; - void animateTick(Level*, int x, int y, int z, Random*) 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; - void tick(Level*, int, int, int, Random*) override; + virtual AABB* getAABB(Level*, int x, int y, int z) override; + virtual bool isSolidRender() override; + virtual bool isCubeShaped() override; + virtual int getRenderShape() override; + virtual void animateTick(Level*, int x, int y, int z, Random*) override; + virtual HitResult clip(Level*, int x, int y, int z, Vec3 a, Vec3 b) override; + virtual bool mayPlace(Level*, int, int, int) override; + virtual void neighborChanged(Level*, int, int, int, int) override; + virtual void onPlace(Level*, int, int, int) override; + virtual void setPlacedOnFace(Level*, int, int, int, int) override; + virtual void tick(Level*, int, int, int, Random*) override; bool checkCanSurvive(Level*, int x, int y, int z); }; diff --git a/source/world/tile/TorchUpdateEvent.hpp b/source/world/tile/TorchUpdateEvent.hpp new file mode 100644 index 0000000..3061b17 --- /dev/null +++ b/source/world/tile/TorchUpdateEvent.hpp @@ -0,0 +1,31 @@ +/******************************************************************** + 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 + +struct TorchUpdateEvent +{ + int x, y, z; + int time; + + TorchUpdateEvent() + { + x = y = z = time = 0; + } + + TorchUpdateEvent(int _x, int _y, int _z, int _time) + { + x = _x; + y = _y; + z = _z; + time = _time; + } +}; + +typedef std::list TorchUpdateEvents; diff --git a/source/world/tile/WireTile.cpp b/source/world/tile/WireTile.cpp index c4963d1..4b68b59 100644 --- a/source/world/tile/WireTile.cpp +++ b/source/world/tile/WireTile.cpp @@ -11,5 +11,377 @@ WireTile::WireTile(int id) : Tile(id, Material::decoration) { - m_TextureFrame = 0; + m_TextureFrame = 164; + m_bIsPowerSource = true; +} + +bool WireTile::isSolidRender() +{ + return false; +} + +bool WireTile::isCubeShaped() +{ + return false; +} + +bool WireTile::isSignalSource() +{ + return m_bIsPowerSource; +} + +bool WireTile::canSurvive(Level* level, int x, int y, int z) +{ + TileID tile = level->getTile(x, y - 1, z); + if (tile == Tile::stoneSlabHalf->m_ID || tile == Tile::glass->m_ID) + return true; + return level->isSolidTile(x, y - 1, z); +} + +bool WireTile::mayPlace(Level* level, int x, int y, int z) +{ + return canSurvive(level, x, y, z); +} + +bool WireTile::isSignalSource(LevelSource* level, int x, int y, int z) +{ + int tile = level->getTile(x, y, z); + if (tile == m_ID) return true; + if (!tile) return false; + return Tile::tiles[tile]->isSignalSource(); +} + +int WireTile::getConnections(LevelSource* level, int x, int y, int z) +{ + // Determine connection between wires. + int connFlags = 0; + const int checkXD[] = { -1, +1, 0, 0 }; + const int checkZD[] = { 0, 0, -1, +1 }; + + bool bIsSolidTileAbove = level->isSolidTile(x, y + 1, z); + + for (int i = 0; i < CONN_COUNT; i++) + { + Tile* tile; + + tile = Tile::tiles[level->getTile(x + checkXD[i], y, z + checkZD[i])]; + if (tile && tile->isSignalSource()) + { + connFlags |= (1 << i); + continue; + } + + // check above + if (!bIsSolidTileAbove) + { + bool bHaveRedstone = level->getTile(x + checkXD[i], y + 1, z + checkZD[i]) == m_ID; + if (bHaveRedstone && level->getTile(x + checkXD[i], y, z + checkZD[i]) == Tile::stoneSlabHalf->m_ID) + { + connFlags |= (1 << i); + continue; + } + if (bHaveRedstone) + { + connFlags |= (1 << i) | (1 << (i + 4)); + continue; + } + } + + // check below: + if (level->isSolidTile(x + checkXD[i], y, z + checkZD[i])) + continue; + + if (level->getTile(x + checkXD[i], y - 1, z + checkZD[i]) == m_ID) + { + connFlags |= (1 << i); + continue; + } + } + + // If we have only one flag, set the opposite too. + if ((connFlags & CONN_MASK) == (1 << CONN_XN)) connFlags |= (1 << CONN_XP); + if ((connFlags & CONN_MASK) == (1 << CONN_XP)) connFlags |= (1 << CONN_XN); + if ((connFlags & CONN_MASK) == (1 << CONN_ZN)) connFlags |= (1 << CONN_ZP); + if ((connFlags & CONN_MASK) == (1 << CONN_ZP)) connFlags |= (1 << CONN_ZN); + if (connFlags == 0) connFlags = CONN_MASK; + + return connFlags; +} + +void WireTile::recalculate(Level* level, int x, int y, int z) +{ + calculateChanges(level, x, y, z, x, y, z); + + std::vector tpv(m_positionsToUpdate.begin(), m_positionsToUpdate.end()); + m_positionsToUpdate.clear(); + + std::vector::iterator it; + for (it = tpv.begin(); + it != tpv.end(); + ++it) + { + level->updateNeighborsAt(it->x, it->y, it->z, m_ID); + } +} + +void WireTile::calculateChanges(Level* level, int x, int y, int z, int x2, int y2, int z2) +{ + int oldPower = level->getData(x, y, z); + int newPower = 0; + + m_bIsPowerSource = false; + bool flag = level->hasNeighborSignal(x, y, z); + m_bIsPowerSource = true; + + if (flag) + { + newPower = 15; + } + else + { + for (int i = 0; i < 4; i++) + { + int checkX = x, checkZ = z; + if (i == 0) checkX--; + if (i == 1) checkX++; + if (i == 2) checkZ--; + if (i == 3) checkZ++; + + if (checkX != x2 || y != y2 || checkZ != z2) + { + newPower = getStrongerSignal(level, checkX, y, checkZ, newPower); + } + + if (level->isSolidTile(checkX, y, checkZ) && !level->isSolidTile(checkX, y + 1, checkZ)) + { + if (checkX != x2 || y + 1 != y2 || checkZ != z2) + { + newPower = getStrongerSignal(level, checkX, y + 1, checkZ, newPower); + } + } + + if (!level->isSolidTile(checkX, y, checkZ)) + { + if (checkX != x2 || y - 1 != y2 || checkZ != z2) + { + newPower = getStrongerSignal(level, checkX, y - 1, checkZ, newPower); + } + } + } + + if (newPower > 0) + newPower--; + else + newPower = 0; + } + + if (oldPower != newPower) + { + level->field_30 = true; + level->setData(x, y, z, newPower); + level->setTilesDirty(x, y, z, x, y, z); + level->field_30 = false; + + for (int i = 0; i < 4; i++) + { + int checkX = x; + int checkZ = z; + int checkY = y - 1; + if (i == 0) checkX--; + if (i == 1) checkX++; + if (i == 2) checkZ--; + if (i == 3) checkZ++; + + if (level->isSolidTile(checkX, y, checkZ)) + checkY += 2; + + int power = 0; + power = getStrongerSignal(level, checkX, y, checkZ, -1); + newPower = level->getData(x, y, z); + if (newPower > 0) + newPower--; + + if (power >= 0 && power != newPower) + calculateChanges(level, checkX, y, checkZ, x, y, z); + + power = getStrongerSignal(level, checkX, checkY, checkZ, -1); + newPower = level->getData(x, y, z); + if (newPower > 0) + newPower--; + if (power >= 0 && power != newPower) + calculateChanges(level, checkX, checkY, checkZ, x, y, z); + } + + if (oldPower == 0 || newPower == 0) + { + m_positionsToUpdate.insert(TilePos(x, y, z)); + m_positionsToUpdate.insert(TilePos(x - 1, y, z)); + m_positionsToUpdate.insert(TilePos(x + 1, y, z)); + m_positionsToUpdate.insert(TilePos(x, y - 1, z)); + m_positionsToUpdate.insert(TilePos(x, y + 1, z)); + m_positionsToUpdate.insert(TilePos(x, y, z - 1)); + m_positionsToUpdate.insert(TilePos(x, y, z + 1)); + } + } +} + +int WireTile::getStrongerSignal(Level* level, int x, int y, int z, int prevSignal) +{ + if (level->getTile(x, y, z) != m_ID) + return prevSignal; + + int newSignal = level->getData(x, y, z); + return Mth::Max(newSignal, prevSignal); +} + +static void updateWire(Level* level, int id, int x, int y, int z) +{ + if (level->getTile(x, y, z) != id) return; + + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); +} + +void WireTile::updateWires(Level* level, int x, int y, int z) +{ + updateWire(level, m_ID, x - 1, y, z); + updateWire(level, m_ID, x + 1, y, z); + updateWire(level, m_ID, x, y, z - 1); + updateWire(level, m_ID, x, y, z + 1); + + updateWire(level, m_ID, x - 1, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z); + updateWire(level, m_ID, x + 1, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z); + updateWire(level, m_ID, x, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z - 1); + updateWire(level, m_ID, x, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z + 1); +} + +void WireTile::updateShape(LevelSource* level, int x, int y, int z) +{ + int connFlags = getConnections(level, x, y, z); + + // cut off parts of the texture if needed + float cxn = 0.0f, cxp = 1.0f, czn = 0.0f, czp = 1.0f; + if (~connFlags & (1 << CONN_XN)) cxn += 5.0f / 16.0f; + if (~connFlags & (1 << CONN_XP)) cxp -= 5.0f / 16.0f; + if (~connFlags & (1 << CONN_ZN)) czn += 5.0f / 16.0f; + if (~connFlags & (1 << CONN_ZP)) czp -= 5.0f / 16.0f; + + m_aabb = AABB(cxn, 0.0f, czn, cxp, 0.1f, czp); + if (connFlags != CONN_MASK) + m_aabb.grow(0.1f, 0.0f, 0.1f); +} + +AABB* WireTile::getAABB(Level* level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); +} + +AABB WireTile::getTileAABB(Level* level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); +} + +void WireTile::addAABBs(Level*, int x, int y, int z, const AABB* aabb, std::vector& out) +{ + // there is no collision with redstone!! +} + +void WireTile::onPlace(Level* level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + //if (level->m_bIsMultiplayer) + // return; + + recalculate(level, x, y, z); + level->updateNeighborsAt(x, y + 1, z, m_ID); + level->updateNeighborsAt(x, y - 1, z, m_ID); + updateWires(level, x, y, z); +} + +void WireTile::onRemove(Level* level, int x, int y, int z) +{ + //if (level->m_bIsMultiplayer) + //{ + // Tile::onRemove(level, x, y, z); + // return; + //} + + recalculate(level, x, y, z); + level->updateNeighborsAt(x, y + 1, z, m_ID); + level->updateNeighborsAt(x, y - 1, z, m_ID); + updateWires(level, x, y, z); + + Tile::onRemove(level, x, y, z); +} + +void WireTile::neighborChanged(Level* level, int x, int y, int z, int id) +{ + //if (level->m_bIsMultiplayer) + // return; + + //LOG_I("WireTile neighborChanged %d,%d,%d", x, y, z); + + if (!canSurvive(level, x, y, z)) + { + spawnResources(level, x, y, z, level->getData(x, y, z)); + level->setTile(x, y, z, TILE_AIR); + } + + recalculate(level, x, y, z); + Tile::neighborChanged(level, x, y, z, id); +} + +int WireTile::getSignal(LevelSource* level, int x, int y, int z, int dir) +{ + if (!m_bIsPowerSource) + return 0; + + return getDirectSignal(level, x, y, z, dir); +} + +int WireTile::getDirectSignal(LevelSource* level, int x, int y, int z, int dir) +{ + if (!m_bIsPowerSource) + return 0; + + if (level->getData(x, y, z) == 0) + return 0; + + if (dir == 1) + return 1; + + 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); + bool flag3 = isSignalSource(level, x, y, z + 1) || !level->isSolidTile(x, y, z + 1) && isSignalSource(level, x, y - 1, z + 1); + if (!level->isSolidTile(x, y + 1, z)) + { + if (level->isSolidTile(x - 1, y, z) && isSignalSource(level, x - 1, y + 1, z)) flag0 = true; + if (level->isSolidTile(x + 1, y, z) && isSignalSource(level, x + 1, y + 1, z)) flag1 = true; + if (level->isSolidTile(x, y, z - 1) && isSignalSource(level, x, y + 1, z - 1)) flag2 = true; + if (level->isSolidTile(x, y, z + 1) && isSignalSource(level, x, y + 1, z + 1)) flag3 = true; + } + if (!flag2 && !flag1 && !flag0 && !flag3 && dir >= 2 && dir <= 5) return 1; + if (dir == 2 && flag2 && !flag0 && !flag1) return 1; + if (dir == 3 && flag3 && !flag0 && !flag1) return 1; + if (dir == 4 && flag0 && !flag2 && !flag3) return 1; + if (dir == 5 && flag1 && !flag2 && !flag3) return 1; + return 0; +} + +int WireTile::getRenderShape() +{ + return SHAPE_WIRE; +} + +int WireTile::getTickDelay() +{ + return 2; } diff --git a/source/world/tile/WireTile.hpp b/source/world/tile/WireTile.hpp index d9dc2c5..a31e2f7 100644 --- a/source/world/tile/WireTile.hpp +++ b/source/world/tile/WireTile.hpp @@ -8,10 +8,59 @@ #pragma once +#include #include "Tile.hpp" +// not an actual name. class WireTile : public Tile { public: + // Use as (1 << CONN_??). + enum + { + CONN_XN, + CONN_XP, + CONN_ZN, + CONN_ZP, + CONN_COUNT, + + CONN_ABOVE_XN = 4, + CONN_ABOVE_XP, + CONN_ABOVE_ZN, + CONN_ABOVE_ZP, + + CONN_MASK = 0x0F, // mask in only the connection directions, not if any of them are going above. + CONN_ABOVE_MASK = 0xF0, // mask in only whether any of the directions go up + }; + WireTile(int ID); + + bool isSolidRender() override; + bool isCubeShaped() override; + int getRenderShape() override; + int getTickDelay() override; + bool isSignalSource() override; + bool canSurvive(Level*, int x, int y, int z) override; + bool mayPlace(Level*, int, int, int) override; + void updateShape(LevelSource*, int x, int y, int z) override; + AABB* getAABB(Level*, int x, int y, int z) override; + AABB getTileAABB(Level*, int x, int y, int z) override; + void addAABBs(Level*, int x, int y, int z, const AABB* aabb, std::vector& out) override; + void onPlace(Level*, int x, int y, int z) override; + void onRemove(Level*, int x, int y, int z) override; + void neighborChanged(Level*, int x, int y, int z, int id) 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(LevelSource*, int x, int y, int z); + int getConnections(LevelSource*, int x, int y, int z); + void recalculate(Level* level, int x, int y, int z); + void calculateChanges(Level* level, int x, int y, int z, int x2, int y2, int z2); + int getStrongerSignal(Level* level, int x, int y, int z, int prevSignal); + void updateWires(Level* level, int x, int y, int z); + +private: + bool m_bIsPowerSource; + + std::unordered_set m_positionsToUpdate; };