diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj
index 7965738d..17da8b55 100644
--- a/platforms/windows/projects/World/World.vcxproj
+++ b/platforms/windows/projects/World/World.vcxproj
@@ -348,6 +348,7 @@
+
@@ -458,6 +459,7 @@
+
diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters
index 8e0eec82..0dbbb7ab 100644
--- a/platforms/windows/projects/World/World.vcxproj.filters
+++ b/platforms/windows/projects/World/World.vcxproj.filters
@@ -470,6 +470,9 @@
Source Files\Particle
+
+ Source Files\Tile
+
@@ -796,5 +799,8 @@
Header Files\Entity
+
+ Header Files\Tile
+
\ No newline at end of file
diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp
index c2cc7766..f9873176 100644
--- a/source/client/renderer/TileRenderer.cpp
+++ b/source/client/renderer/TileRenderer.cpp
@@ -9,8 +9,9 @@
#include "TileRenderer.hpp"
#include "client/app/Minecraft.hpp"
#include "client/renderer/PatchManager.hpp"
-#include "world/tile/FireTile.hpp"
#include "world/tile/LiquidTile.hpp"
+#include "world/tile/FireTile.hpp"
+#include "world/tile/WireTile.hpp"
#include "client/renderer/GrassColor.hpp"
#include "client/renderer/FoliageColor.hpp"
@@ -1312,6 +1313,139 @@ 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);
+
+ tile->updateShape(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));
+
+ AABB aabb = tile->m_aabb;
+
+ 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);
+ }
+
+ Tesselator& t = Tesselator::instance;
+
+ // 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::tesselateInWorld(Tile* tile, int x, int y, int z)
{
int shape = tile->getRenderShape();
@@ -1342,6 +1476,8 @@ 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);
}
return false;
diff --git a/source/client/renderer/TileRenderer.hpp b/source/client/renderer/TileRenderer.hpp
index a92d2296..e3699df5 100644
--- a/source/client/renderer/TileRenderer.hpp
+++ b/source/client/renderer/TileRenderer.hpp
@@ -52,9 +52,9 @@ class TileRenderer
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);
+
#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/Utils.hpp b/source/common/Utils.hpp
index f41ce84c..c3b0c755 100644
--- a/source/common/Utils.hpp
+++ b/source/common/Utils.hpp
@@ -537,12 +537,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
diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp
index 630bc48c..9d6e2e0d 100644
--- a/source/world/item/Inventory.cpp
+++ b/source/world/item/Inventory.cpp
@@ -80,6 +80,11 @@ void Inventory::prepareCreativeInventory()
addCreativeItem(Item::door_iron->m_itemID);
addCreativeItem(Item::rocket->m_itemID);
+ // redstone stuff
+ addCreativeItem(Tile::wire->m_ID);
+ addCreativeItem(Tile::notGate_off->m_ID);
+ addCreativeItem(Tile::notGate->m_ID);
+
for (int i = 0; i < C_MAX_HOTBAR_ITEMS; i++)
m_hotbar[i] = i;
}
diff --git a/source/world/tile/RedStoneTorchTile.cpp b/source/world/tile/RedStoneTorchTile.cpp
new file mode 100644
index 00000000..16a7cd9b
--- /dev/null
+++ b/source/world/tile/RedStoneTorchTile.cpp
@@ -0,0 +1,190 @@
+/********************************************************************
+ 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_NOT_GATE_ON;
+
+ setLightEmission(0.5f);
+ setTicking(true);
+}
+
+bool RedStoneTorchTile::isSignalSource()
+{
+ return m_bActive;
+}
+
+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::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 RedStoneTorchTile::getDirectSignal(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 00000000..616f14b2
--- /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/Tile.cpp b/source/world/tile/Tile.cpp
index 75c6604e..ae5c718d 100644
--- a/source/world/tile/Tile.cpp
+++ b/source/world/tile/Tile.cpp
@@ -47,8 +47,9 @@
#include "DoorTile.hpp"
#include "SpongeTile.hpp"
#include "BookshelfTile.hpp"
-#include "WireTile.hpp"
#include "RocketLauncherTile.hpp"
+#include "WireTile.hpp"
+#include "RedStoneTorchTile.hpp"
std::string Tile::TILE_DESCRIPTION_PREFIX = "tile.";
@@ -704,6 +705,24 @@ 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_NOT_GATE_OFF, TEXTURE_TORCH_RED_STONE_OFF, Material::decoration))
+ ->init()
+ ->setDestroyTime(0.0f)
+ ->setSoundType(Tile::SOUND_WOOD)
+ ->setDescriptionId("notGate");
+
+ Tile::notGate = (new RedStoneTorchTile(TILE_NOT_GATE_ON, TEXTURE_TORCH_RED_STONE, Material::decoration))
+ ->init()
+ ->setDestroyTime(0.0f)
+ ->setSoundType(Tile::SOUND_WOOD)
+ ->setDescriptionId("notGateLit");
+
for (int i = 0; i < C_MAX_TILES; i++)
{
if (Tile::tiles[i])
@@ -951,7 +970,7 @@ 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 +1171,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 d896c88f..4839a8c1 100644
--- a/source/world/tile/Tile.hpp
+++ b/source/world/tile/Tile.hpp
@@ -84,7 +84,7 @@ class Tile
virtual bool isSignalSource();
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 @@ class Tile
* 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.hpp b/source/world/tile/TorchTile.hpp
index e31f2aec..b5cf4d84 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 00000000..32c96ecf
--- /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 c4963d1f..90f5bbd2 100644
--- a/source/world/tile/WireTile.cpp
+++ b/source/world/tile/WireTile.cpp
@@ -11,5 +11,360 @@
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)
+{
+ return level->isSolidTile(x, y - 1, 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)
+ {
+ if (level->getTile(x + checkXD[i], y + 1, z + checkZD[i]) == m_ID)
+ {
+ connFlags |= (1 << i) | (1 << (i + 4));
+ continue;
+ }
+ }
+
+ // check below:
+ if (level->isSolidTile(x + checkXD[i], y, z + checkXD[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);
+
+ 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 std::max(newSignal, prevSignal);
+}
+
+void WireTile::updateWires(Level* level, int x, int y, int z)
+{
+ if (level->getTile(x, y, z) != m_ID) return;
+
+ level->updateNeighborsAt(x, y, z, m_ID);
+ level->updateNeighborsAt(x - 1, y, z, m_ID);
+ level->updateNeighborsAt(x + 1, y, z, m_ID);
+ level->updateNeighborsAt(x, y, z - 1, m_ID);
+ level->updateNeighborsAt(x, y, z + 1, m_ID);
+ level->updateNeighborsAt(x, y - 1, z, m_ID);
+ level->updateNeighborsAt(x, y + 1, z, m_ID);
+}
+
+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);
+}
+
+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 - 1, y, z);
+ updateWires(level, x + 1, y, z);
+ updateWires(level, x, y, z - 1);
+ updateWires(level, x, y, z + 1);
+
+ updateWires(level, x - 1, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z);
+ updateWires(level, x + 1, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z);
+ updateWires(level, x, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z - 1);
+ updateWires(level, x, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z + 1);
+}
+
+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 - 1, y, z);
+ updateWires(level, x + 1, y, z);
+ updateWires(level, x, y, z - 1);
+ updateWires(level, x, y, z + 1);
+
+ updateWires(level, x - 1, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z);
+ updateWires(level, x + 1, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z);
+ updateWires(level, x, level->isSolidTile(x - 1, y, z) ? y + 1 : y - 1, z - 1);
+ updateWires(level, x, level->isSolidTile(x + 1, y, z) ? y + 1 : y - 1, z + 1);
+
+ 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;
+
+ 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 true;
+ if (dir == 2 && flag2 && !flag0 && !flag1) return true;
+ if (dir == 3 && flag3 && !flag0 && !flag1) return true;
+ if (dir == 4 && flag0 && !flag2 && !flag3) return true;
+ if (dir == 5 && flag1 && !flag2 && !flag3) return true;
+ return false;
+}
+
+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 d9dc2c56..4b72c11e 100644
--- a/source/world/tile/WireTile.hpp
+++ b/source/world/tile/WireTile.hpp
@@ -8,10 +8,58 @@
#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;
+ 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::set m_positionsToUpdate;
};