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; };