4jcraft/Minecraft.World/Blocks/BaseRailTile.cpp
2026-03-21 15:52:50 -05:00

418 lines
13 KiB
C++

#include "../Platform/stdafx.h"
#include "../Headers/net.minecraft.world.phys.h"
#include "../Headers/net.minecraft.world.level.h"
#include "../Headers/net.minecraft.world.h"
#include "BaseRailTile.h"
BaseRailTile::Rail::Rail(Level* level, int x, int y, int z) {
this->level = level;
this->x = x;
this->y = y;
this->z = z;
int id = level->getTile(x, y, z);
// 4J Stu - We saw a random crash near the end of development on XboxOne
// orignal version where the id here isn't a tile any more Adding this check
// in to avoid that crash
m_bValidRail = isRail(id);
if (m_bValidRail) {
int direction = level->getData(x, y, z);
if (((BaseRailTile*)Tile::tiles[id])->usesDataBit) {
usesDataBit = true;
direction = direction & ~RAIL_DATA_BIT;
} else {
usesDataBit = false;
}
updateConnections(direction);
}
}
BaseRailTile::Rail::~Rail() {
for (int i = 0; i < connections.size(); i++) {
delete connections[i];
}
}
void BaseRailTile::Rail::updateConnections(int direction) {
if (m_bValidRail) {
for (int i = 0; i < connections.size(); i++) {
delete connections[i];
}
connections.clear();
MemSect(50);
if (direction == DIR_FLAT_Z) {
connections.push_back(new TilePos(x, y, z - 1));
connections.push_back(new TilePos(x, y, z + 1));
} else if (direction == DIR_FLAT_X) {
connections.push_back(new TilePos(x - 1, y, z));
connections.push_back(new TilePos(x + 1, y, z));
} else if (direction == 2) {
connections.push_back(new TilePos(x - 1, y, z));
connections.push_back(new TilePos(x + 1, y + 1, z));
} else if (direction == 3) {
connections.push_back(new TilePos(x - 1, y + 1, z));
connections.push_back(new TilePos(x + 1, y, z));
} else if (direction == 4) {
connections.push_back(new TilePos(x, y + 1, z - 1));
connections.push_back(new TilePos(x, y, z + 1));
} else if (direction == 5) {
connections.push_back(new TilePos(x, y, z - 1));
connections.push_back(new TilePos(x, y + 1, z + 1));
} else if (direction == 6) {
connections.push_back(new TilePos(x + 1, y, z));
connections.push_back(new TilePos(x, y, z + 1));
} else if (direction == 7) {
connections.push_back(new TilePos(x - 1, y, z));
connections.push_back(new TilePos(x, y, z + 1));
} else if (direction == 8) {
connections.push_back(new TilePos(x - 1, y, z));
connections.push_back(new TilePos(x, y, z - 1));
} else if (direction == 9) {
connections.push_back(new TilePos(x + 1, y, z));
connections.push_back(new TilePos(x, y, z - 1));
}
MemSect(0);
}
}
void BaseRailTile::Rail::removeSoftConnections() {
if (m_bValidRail) {
for (unsigned int i = 0; i < connections.size(); i++) {
Rail* rail = getRail(connections[i]);
if (rail == NULL || !rail->connectsTo(this)) {
delete connections[i];
connections.erase(connections.begin() + i);
i--;
} else {
delete connections[i];
MemSect(50);
connections[i] = new TilePos(rail->x, rail->y, rail->z);
MemSect(0);
}
delete rail;
}
}
}
bool BaseRailTile::Rail::hasRail(int x, int y, int z) {
if (!m_bValidRail) return false;
if (isRail(level, x, y, z)) return true;
if (isRail(level, x, y + 1, z)) return true;
if (isRail(level, x, y - 1, z)) return true;
return false;
}
BaseRailTile::Rail* BaseRailTile::Rail::getRail(TilePos* p) {
if (!m_bValidRail) return NULL;
if (isRail(level, p->x, p->y, p->z))
return new Rail(level, p->x, p->y, p->z);
if (isRail(level, p->x, p->y + 1, p->z))
return new Rail(level, p->x, p->y + 1, p->z);
if (isRail(level, p->x, p->y - 1, p->z))
return new Rail(level, p->x, p->y - 1, p->z);
return NULL;
}
bool BaseRailTile::Rail::connectsTo(Rail* rail) {
if (m_bValidRail) {
AUTO_VAR(itEnd, connections.end());
for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) {
TilePos* p = *it; // connections[i];
if (p->x == rail->x && p->z == rail->z) {
return true;
}
}
}
return false;
}
bool BaseRailTile::Rail::hasConnection(int x, int y, int z) {
if (m_bValidRail) {
AUTO_VAR(itEnd, connections.end());
for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) {
TilePos* p = *it; // connections[i];
if (p->x == x && p->z == z) {
return true;
}
}
}
return false;
}
int BaseRailTile::Rail::countPotentialConnections() {
int count = 0;
if (m_bValidRail) {
if (hasRail(x, y, z - 1)) count++;
if (hasRail(x, y, z + 1)) count++;
if (hasRail(x - 1, y, z)) count++;
if (hasRail(x + 1, y, z)) count++;
}
return count;
}
bool BaseRailTile::Rail::canConnectTo(Rail* rail) {
if (!m_bValidRail) return false;
if (connectsTo(rail)) return true;
if (connections.size() == 2) {
return false;
}
if (connections.empty()) {
return true;
}
return true;
}
void BaseRailTile::Rail::connectTo(Rail* rail) {
if (m_bValidRail) {
MemSect(50);
connections.push_back(new TilePos(rail->x, rail->y, rail->z));
MemSect(0);
bool n = hasConnection(x, y, z - 1);
bool s = hasConnection(x, y, z + 1);
bool w = hasConnection(x - 1, y, z);
bool e = hasConnection(x + 1, y, z);
int dir = -1;
if (n || s) dir = DIR_FLAT_Z;
if (w || e) dir = DIR_FLAT_X;
if (!usesDataBit) {
if (s && e && !n && !w) dir = 6;
if (s && w && !n && !e) dir = 7;
if (n && w && !s && !e) dir = 8;
if (n && e && !s && !w) dir = 9;
}
if (dir == DIR_FLAT_Z) {
if (isRail(level, x, y + 1, z - 1)) dir = 4;
if (isRail(level, x, y + 1, z + 1)) dir = 5;
}
if (dir == DIR_FLAT_X) {
if (isRail(level, x + 1, y + 1, z)) dir = 2;
if (isRail(level, x - 1, y + 1, z)) dir = 3;
}
if (dir < 0) dir = DIR_FLAT_Z;
int data = dir;
if (usesDataBit) {
data = (level->getData(x, y, z) & RAIL_DATA_BIT) | dir;
}
level->setData(x, y, z, data, Tile::UPDATE_ALL);
}
}
bool BaseRailTile::Rail::hasNeighborRail(int x, int y, int z) {
if (!m_bValidRail) return false;
TilePos tp(x, y, z);
Rail* neighbor = getRail(&tp);
if (neighbor == NULL) return false;
neighbor->removeSoftConnections();
bool retval = neighbor->canConnectTo(this);
delete neighbor;
return retval;
}
void BaseRailTile::Rail::place(bool hasSignal, bool first) {
if (m_bValidRail) {
bool n = hasNeighborRail(x, y, z - 1);
bool s = hasNeighborRail(x, y, z + 1);
bool w = hasNeighborRail(x - 1, y, z);
bool e = hasNeighborRail(x + 1, y, z);
int dir = -1;
if ((n || s) && !w && !e) dir = DIR_FLAT_Z;
if ((w || e) && !n && !s) dir = DIR_FLAT_X;
if (!usesDataBit) {
if (s && e && !n && !w) dir = 6;
if (s && w && !n && !e) dir = 7;
if (n && w && !s && !e) dir = 8;
if (n && e && !s && !w) dir = 9;
}
if (dir == -1) {
if (n || s) dir = DIR_FLAT_Z;
if (w || e) dir = DIR_FLAT_X;
if (!usesDataBit) {
if (hasSignal) {
if (s && e) dir = 6;
if (w && s) dir = 7;
if (e && n) dir = 9;
if (n && w) dir = 8;
} else {
if (n && w) dir = 8;
if (e && n) dir = 9;
if (w && s) dir = 7;
if (s && e) dir = 6;
}
}
}
if (dir == DIR_FLAT_Z) {
if (isRail(level, x, y + 1, z - 1)) dir = 4;
if (isRail(level, x, y + 1, z + 1)) dir = 5;
}
if (dir == DIR_FLAT_X) {
if (isRail(level, x + 1, y + 1, z)) dir = 2;
if (isRail(level, x - 1, y + 1, z)) dir = 3;
}
if (dir < 0) dir = DIR_FLAT_Z;
updateConnections(dir);
int data = dir;
if (usesDataBit) {
data = (level->getData(x, y, z) & RAIL_DATA_BIT) | dir;
}
if (first || level->getData(x, y, z) != data) {
level->setData(x, y, z, data, Tile::UPDATE_ALL);
AUTO_VAR(itEnd, connections.end());
for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) {
Rail* neighbor = getRail(*it);
if (neighbor == NULL) continue;
neighbor->removeSoftConnections();
if (neighbor->canConnectTo(this)) {
neighbor->connectTo(this);
}
delete neighbor;
}
}
}
}
bool BaseRailTile::isRail(Level* level, int x, int y, int z) {
return isRail(level->getTile(x, y, z));
}
bool BaseRailTile::isRail(int id) {
return id == Tile::rail_Id || id == Tile::goldenRail_Id ||
id == Tile::detectorRail_Id || id == Tile::activatorRail_Id;
}
BaseRailTile::BaseRailTile(int id, bool usesDataBit)
: Tile(id, Material::decoration, false) {
this->usesDataBit = usesDataBit;
setShape(0, 0, 0, 1, 2 / 16.0f, 1);
iconTurn = NULL;
}
bool BaseRailTile::isUsesDataBit() { return usesDataBit; }
AABB* BaseRailTile::getAABB(Level* level, int x, int y, int z) { return NULL; }
bool BaseRailTile::blocksLight() { return false; }
bool BaseRailTile::isSolidRender(bool isServerLevel) { return false; }
HitResult* BaseRailTile::clip(Level* level, int xt, int yt, int zt, Vec3* a,
Vec3* b) {
updateShape(level, xt, yt, zt);
return Tile::clip(level, xt, yt, zt, a, b);
}
void BaseRailTile::updateShape(
LevelSource* level, int x, int y, int z, int forceData,
std::shared_ptr<TileEntity>
forceEntity) // 4J added forceData, forceEntity param
{
int data = level->getData(x, y, z);
if (data >= 2 && data <= 5) {
setShape(0, 0, 0, 1, 2 / 16.0f + 0.5f, 1);
} else {
setShape(0, 0, 0, 1, 2 / 16.0f, 1);
}
}
bool BaseRailTile::isCubeShaped() { return false; }
int BaseRailTile::getRenderShape() { return Tile::SHAPE_RAIL; }
int BaseRailTile::getResourceCount(Random random) { return 1; }
bool BaseRailTile::mayPlace(Level* level, int x, int y, int z) {
if (level->isTopSolidBlocking(x, y - 1, z)) {
return true;
}
return false;
}
void BaseRailTile::onPlace(Level* level, int x, int y, int z) {
if (!level->isClientSide) {
updateDir(level, x, y, z, true);
if (usesDataBit) {
neighborChanged(level, x, y, z, id);
}
}
}
void BaseRailTile::neighborChanged(Level* level, int x, int y, int z,
int type) {
if (level->isClientSide) return;
int data = level->getData(x, y, z);
int dir = data;
if (usesDataBit) {
dir = dir & RAIL_DIRECTION_MASK;
}
bool remove = false;
if (!level->isTopSolidBlocking(x, y - 1, z)) remove = true;
if (dir == 2 && !level->isTopSolidBlocking(x + 1, y, z)) remove = true;
if (dir == 3 && !level->isTopSolidBlocking(x - 1, y, z)) remove = true;
if (dir == 4 && !level->isTopSolidBlocking(x, y, z - 1)) remove = true;
if (dir == 5 && !level->isTopSolidBlocking(x, y, z + 1)) remove = true;
if (remove) {
spawnResources(level, x, y, z, level->getData(x, y, z), 0);
level->removeTile(x, y, z);
} else {
updateState(level, x, y, z, data, dir, type);
}
}
void BaseRailTile::updateState(Level* level, int x, int y, int z, int data,
int dir, int type) {}
void BaseRailTile::updateDir(Level* level, int x, int y, int z, bool first) {
if (level->isClientSide) return;
Rail* rail = new Rail(level, x, y, z);
rail->place(level->hasNeighborSignal(x, y, z), first);
delete rail;
}
int BaseRailTile::getPistonPushReaction() {
// override the decoration material's reaction
return Material::PUSH_NORMAL;
}
void BaseRailTile::onRemove(Level* level, int x, int y, int z, int id,
int data) {
int dir = data;
if (usesDataBit) {
dir &= RAIL_DIRECTION_MASK;
}
Tile::onRemove(level, x, y, z, id, data);
if (dir == 2 || dir == 3 || dir == 4 || dir == 5) {
level->updateNeighborsAt(x, y + 1, z, id);
}
if (usesDataBit) {
level->updateNeighborsAt(x, y, z, id);
level->updateNeighborsAt(x, y - 1, z, id);
}
}