Files
mcpe/source/world/tile/DoorTile.cpp
2023-08-10 13:27:30 +03:00

246 lines
5.6 KiB
C++

/********************************************************************
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 "DoorTile.hpp"
#include "world/level/Level.hpp"
#include "world/item/Item.hpp"
DoorTile::DoorTile(int ID, Material* pMtl) : Tile(ID, pMtl)
{
m_TextureFrame = TEXTURE_DOOR_BOTTOM;
if (pMtl == Material::metal)
m_TextureFrame = TEXTURE_DOOR_IRON_BOTTOM;
Tile::setShape(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
}
int DoorTile::use(Level* level, int x, int y, int z, Player* player)
{
// well, you know, iron doors can't be opened by right clicking
if (m_pMaterial == Material::metal)
return 1;
int data = level->getData(x, y, z);
// if we're the top tile
if (data & 8)
{
if (level->getTile(x, y - 1, z) == m_ID)
use(level, x, y - 1, z, player);
}
else
{
data ^= 4;
if (level->getTile(x, y + 1, z) == m_ID)
level->setData(x, y + 1, z, data + 8);
level->setData(x, y, z, data);
// @BUG: marking the wrong tiles as dirty? No problem because setData sends an update immediately anyways
level->setTilesDirty(x, y - 1, z, x, y, z);
std::string snd;
if (Mth::random() < 0.5f)
snd = "random.door_open";
else
snd = "random.door_close";
level->playSound(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, snd, 1.0f, 0.9f + 0.1f * level->m_random.nextFloat());
}
return 1;
}
void DoorTile::attack(Level* level, int x, int y, int z, Player* player)
{
use(level, x, y, z, player);
}
// @HUH: This function has NO references to itself. Not even in the vtable of the tile.
// This could have been an attempt by Mojang to override a function, but it clearly failed
// From this, I suspect that they never actually used the `override` keyword for their classes.
bool DoorTile::blocksLight()
{
return false;
}
HitResult DoorTile::clip(Level* level, int x, int y, int z, Vec3 v1, Vec3 v2)
{
// @NOTE: Tile::clip calls updateShape too. So this is redundant
updateShape(level, x, y, z);
return Tile::clip(level, x, y, z, v1, v2);
}
AABB* DoorTile::getAABB(Level* level, int x, int y, int z)
{
updateShape(level, x, y, z);
return Tile::getAABB(level, x, y, z);
}
int DoorTile::getDir(int data)
{
if (!isOpen(data))
return (data - 1) & 3;
return data & 3;
}
int DoorTile::getRenderShape()
{
return SHAPE_DOOR;
}
int DoorTile::getResource(int data, Random* random)
{
// breaking the top of the tile doesn't drop anything.
// In JE, it probably fixed a certain dupe glitch with doors
if (isTop(data))
return 0;
if (m_pMaterial == Material::metal)
return Item::door_iron->m_itemID;
return Item::door_wood->m_itemID;
}
int DoorTile::getTexture(int dir, int data)
{
if (dir == 0 || dir == 1)
return m_TextureFrame;
int doorDir = getDir(data);
if ((doorDir == 0 || doorDir == 2) != (dir <= 3))
return m_TextureFrame;
int l = doorDir / 2 + ((dir & 1) ^ doorDir);
l += (data & 4) / 4;
int idx = m_TextureFrame - (data & 8) * 2;
if ((l & 1) != 0)
idx = -idx;
return idx;
}
AABB DoorTile::getTileAABB(Level* level, int x, int y, int z)
{
updateShape(level, x, y, z);
return Tile::getTileAABB(level, x, y, z);
}
bool DoorTile::isCubeShaped()
{
return false;
}
bool DoorTile::isSolidRender()
{
return false;
}
bool DoorTile::mayPlace(Level* level, int x, int y, int z)
{
return y <= 126 && level->isSolidTile(x, y - 1, z) && Tile::mayPlace(level, x, y, z) && Tile::mayPlace(level, x, y + 1, z);
}
void DoorTile::setShape(int dir)
{
// @NOTE: What? Why 2 tiles tall??
Tile::setShape(0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 1.0f);
switch (dir)
{
case 0:
Tile::setShape(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.1875f);
break;
case 1:
Tile::setShape(0.8125f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
break;
case 2:
Tile::setShape(0.0f, 0.0f, 0.8125f, 1.0f, 1.0f, 1.0f);
break;
case 3:
Tile::setShape(0.0f, 0.0f, 0.0f, 0.1875f, 1.0f, 1.0f);
break;
}
}
void DoorTile::updateShape(LevelSource* level, int x, int y, int z)
{
setShape(getDir(level->getData(x, y, z)));
}
void DoorTile::setOpen(Level* level, int x, int y, int z, bool bOpen)
{
int data = level->getData(x, y, z);
if (isTop(data))
{
if (level->getTile(x, y - 1, z) == m_ID)
setOpen(level, x, y - 1, z, bOpen);
return;
}
if (isOpen(level->getData(x, y, z)) != bOpen)
{
if (level->getTile(x, y + 1, z) == m_ID)
level->setData(x, y + 1, z, (data ^ 4) + 8);
level->setData(x, y, z, data ^ 4);
level->setTilesDirty(x, y - 1, z, x, y, z);
std::string snd;
if (Mth::random() < 0.5f)
snd = "random.door_open";
else
snd = "random.door_close";
level->playSound(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, snd, 1.0f, 0.9f + 0.1f * level->m_random.nextFloat());
}
}
void DoorTile::neighborChanged(Level* level, int x, int y, int z, int newTile)
{
int isTop = level->getData(x, y, z) & 8;
if (isTop)
{
if (level->getTile(x, y - 1, z) != m_ID)
level->setTile(x, y, z, TILE_AIR);
if (newTile > 0)
{
if (Tile::tiles[newTile]->isSignalSource())
neighborChanged(level, x, y - 1, z, newTile);
}
return;
}
if (level->getTile(x, y + 1, z) != m_ID)
{
level->setTile(x, y, z, TILE_AIR);
isTop = 1;
}
if (!level->isSolidTile(x, y - 1, z))
{
level->setTile(x, y, z, TILE_AIR);
if (level->getTile(x, y + 1, z) == m_ID)
level->setTile(x, y + 1, z, TILE_AIR);
}
if (!isTop && newTile > 0 && Tile::tiles[newTile]->isSignalSource())
{
bool bOpen = false;
if (level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z))
bOpen = true;
setOpen(level, x, y, z, bOpen);
}
}