#include "stdafx.h" #include "net.minecraft.world.level.levelgen.structure.h" #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.level.material.h" #include "net.minecraft.world.level.tile.entity.h" #include "net.minecraft.world.entity.h" #include "net.minecraft.world.entity.monster.h" #include "net.minecraft.world.entity.npc.h" #include "WeighedTreasure.h" #include "StructurePiece.h" #include "BoundingBox.h" #include "Direction.h" #include "JavaMath.h" #include "Facing.h" #include "DoorItem.h" #include "EnchantedBookItem.h" /** * * A structure piece is a construction or room, located somewhere in the world * with a given orientatino (out of Direction.java). Structure pieces have a * bounding box that says where the piece is located and its bounds, and the * orientation is used to translate local coordinates into world coordinates. *

* The default orientation is Direction.UNDEFINED, in which case no translation * will occur. If the orientation is Direction::NORTH, coordinate (0, 0, 0) will * be at (boundingBox.x0, boundingBox.y0, boundingBox.z1). In other words, (1, * 1, 1) will be translated to (boundingBox.x0 + 1, boundingBox.y0 + 1, * boundingBox.z1 - 1). *

* When using Direction::SOUTH, the x coordinate will be the same, and the z * coordinate will be flipped. In other words, the bounding box is NOT rotated! * It is only flipped along the z axis. Also note that the bounding box is in * world coordinates, so the local drawing must never reach outside of this. *

* When using east and west coordinates, the local z coordinate will be swapped * with the local x coordinate. For example, (0, 0, 0) is (boundingBox.z1, * boundingBox.y0, boundingBox.z0), and (1, 1, 1) becomes (boundingBox.x1 - 1, * boundingBox.y0 + 1, boundingBox.z0 + 1) when using Direction::WEST. *

* When-ever a structure piece is placing blocks, it is VERY IMPORTANT to always * make sure that all getTile and setTile calls are within the chunk's bounding * box. Failing to check this will cause the level generator to create new * chunks, leading to infinite loops and other errors. */ StructurePiece::StructurePiece() { boundingBox = nullptr; orientation = 0; genDepth = 0; dirty = false; // for reflection } StructurePiece::StructurePiece( int genDepth ) { boundingBox = nullptr; this->genDepth = genDepth; orientation = Direction::UNDEFINED; } StructurePiece::~StructurePiece() { if(boundingBox != nullptr) delete boundingBox; } CompoundTag *StructurePiece::createTag() { CompoundTag *tag = new CompoundTag(); return tag; } void StructurePiece::write(DataOutputStream *dos) { wstring encodeId = StructureFeatureIO::getEncodeId(this); dos->writeUTF(encodeId); boundingBox->write(dos); dos->writeInt(orientation); dos->writeInt(genDepth); addAdditionalSaveData(dos); } void StructurePiece::load(Level *level, DataInputStream *dis) { boundingBox = new BoundingBox(); boundingBox->read(dis); orientation = dis->readInt(); genDepth = dis->readInt(); readAdditonalSaveData(dis); } void StructurePiece::addChildren( StructurePiece* startPiece, list< StructurePiece* > *pieces, Random* random ) { } BoundingBox* StructurePiece::getBoundingBox() { return boundingBox; } int StructurePiece::getGenDepth() { return genDepth; } bool StructurePiece::isInChunk( ChunkPos* pos ) { int cx = ( pos->x << 4 ); int cz = ( pos->z << 4 ); return boundingBox->intersects( cx, cz, cx + 15, cz + 15 ); } StructurePiece* StructurePiece::findCollisionPiece( list< StructurePiece* > *pieces, BoundingBox* box ) { for (auto& piece : *pieces) { if ( piece && piece->getBoundingBox() && piece->getBoundingBox()->intersects( box ) ) { return piece; } } return nullptr; } // 4J-PB - Added from 1.2.3 TilePos *StructurePiece::getLocatorPosition() { return new TilePos(boundingBox->getXCenter(), boundingBox->getYCenter(), boundingBox->getZCenter()); } bool StructurePiece::edgesLiquid( Level* level, BoundingBox* chunkBB ) { int x0 = Math::_max( boundingBox->x0 - 1, chunkBB->x0 ); int y0 = Math::_max( boundingBox->y0 - 1, chunkBB->y0 ); int z0 = Math::_max( boundingBox->z0 - 1, chunkBB->z0 ); int x1 = Math::_min( boundingBox->x1 + 1, chunkBB->x1 ); int y1 = Math::_min( boundingBox->y1 + 1, chunkBB->y1 ); int z1 = Math::_min( boundingBox->z1 + 1, chunkBB->z1 ); // roof and floor for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { int tile = level->getTile( x, y0, z ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } tile = level->getTile( x, y1, z ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } } } // north and south for ( int x = x0; x <= x1; x++ ) { for ( int y = y0; y <= y1; y++ ) { int tile = level->getTile( x, y, z0 ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } tile = level->getTile( x, y, z1 ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } } } // east and west for ( int z = z0; z <= z1; z++ ) { for ( int y = y0; y <= y1; y++ ) { int tile = level->getTile( x0, y, z ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } tile = level->getTile( x1, y, z ); if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) { return true; } } } return false; } int StructurePiece::getWorldX( int x, int z ) { switch ( orientation ) { case Direction::NORTH: case Direction::SOUTH: return boundingBox->x0 + x; case Direction::WEST: return boundingBox->x1 - z; case Direction::EAST: return boundingBox->x0 + z; default: return x; } } int StructurePiece::getWorldY( int y ) { if ( orientation == Direction::UNDEFINED ) { return y; } return y + boundingBox->y0; } int StructurePiece::getWorldZ( int x, int z ) { switch ( orientation ) { case Direction::NORTH: return boundingBox->z1 - z; case Direction::SOUTH: return boundingBox->z0 + z; case Direction::WEST: case Direction::EAST: return boundingBox->z0 + x; default: return z; } } int StructurePiece::getOrientationData( int tile, int data ) { if ( tile == Tile::rail->id ) { if ( orientation == Direction::WEST || orientation == Direction::EAST ) { if ( data == BaseRailTile::DIR_FLAT_X ) { return BaseRailTile::DIR_FLAT_Z; } else { return BaseRailTile::DIR_FLAT_X; } } } else if ( tile == Tile::door_wood_Id || tile == Tile::door_iron_Id ) { if ( orientation == Direction::SOUTH ) { if ( data == 0 ) { return 2; } if ( data == 2 ) { return 0; } } else if ( orientation == Direction::WEST ) { // 0 = 1 // 1 = 2 // 2 = 3 // 3 = 0 return ( data + 1 ) & 3; } else if ( orientation == Direction::EAST ) { // 0 = 3 // 1 = 0 // 2 = 1 // 3 = 2 return ( data + 3 ) & 3; } } else if ( tile == Tile::stairs_stone_Id || tile == Tile::stairs_wood_Id || tile == Tile::stairs_netherBricks_Id || tile == Tile::stairs_stoneBrick_Id || tile == Tile::stairs_sandstone_Id) { if ( orientation == Direction::SOUTH ) { if ( data == 2 ) { return 3; } if ( data == 3 ) { return 2; } } else if ( orientation == Direction::WEST ) { if ( data == 0 ) { return 2; } if ( data == 1 ) { return 3; } if ( data == 2 ) { return 0; } if ( data == 3 ) { return 1; } } else if ( orientation == Direction::EAST ) { if ( data == 0 ) { return 2; } if ( data == 1 ) { return 3; } if ( data == 2 ) { return 1; } if ( data == 3 ) { return 0; } } } else if ( tile == Tile::ladder->id ) { if ( orientation == Direction::SOUTH ) { if ( data == Facing::NORTH ) { return Facing::SOUTH; } if ( data == Facing::SOUTH ) { return Facing::NORTH; } } else if ( orientation == Direction::WEST ) { if ( data == Facing::NORTH ) { return Facing::WEST; } if ( data == Facing::SOUTH ) { return Facing::EAST; } if ( data == Facing::WEST ) { return Facing::NORTH; } if ( data == Facing::EAST ) { return Facing::SOUTH; } } else if ( orientation == Direction::EAST ) { if ( data == Facing::NORTH ) { return Facing::EAST; } if ( data == Facing::SOUTH ) { return Facing::WEST; } if ( data == Facing::WEST ) { return Facing::NORTH; } if ( data == Facing::EAST ) { return Facing::SOUTH; } } } else if ( tile == Tile::button->id ) { if ( orientation == Direction::SOUTH ) { if ( data == 3 ) { return 4; } if ( data == 4 ) { return 3; } } else if ( orientation == Direction::WEST ) { if ( data == 3 ) { return 1; } if ( data == 4 ) { return 2; } if ( data == 2 ) { return 3; } if ( data == 1 ) { return 4; } } else if ( orientation == Direction::EAST ) { if ( data == 3 ) { return 2; } if ( data == 4 ) { return 1; } if ( data == 2 ) { return 3; } if ( data == 1 ) { return 4; } } } else if (tile == Tile::tripWireSource_Id || (Tile::tiles[tile] != nullptr && dynamic_cast(Tile::tiles[tile]))) { if (orientation == Direction::SOUTH) { if (data == Direction::SOUTH || data == Direction::NORTH) { return Direction::DIRECTION_OPPOSITE[data]; } } else if (orientation == Direction::WEST) { if (data == Direction::NORTH) { return Direction::WEST; } if (data == Direction::SOUTH) { return Direction::EAST; } if (data == Direction::WEST) { return Direction::NORTH; } if (data == Direction::EAST) { return Direction::SOUTH; } } else if (orientation == Direction::EAST) { if (data == Direction::NORTH) { return Direction::EAST; } if (data == Direction::SOUTH) { return Direction::WEST; } if (data == Direction::WEST) { return Direction::NORTH; } if (data == Direction::EAST) { return Direction::SOUTH; } } } else if (tile == Tile::pistonBase_Id || tile == Tile::pistonStickyBase_Id || tile == Tile::lever_Id || tile == Tile::dispenser_Id) { if (orientation == Direction::SOUTH) { if (data == Facing::NORTH || data == Facing::SOUTH) { return Facing::OPPOSITE_FACING[data]; } } else if (orientation == Direction::WEST) { if (data == Facing::NORTH) { return Facing::WEST; } if (data == Facing::SOUTH) { return Facing::EAST; } if (data == Facing::WEST) { return Facing::NORTH; } if (data == Facing::EAST) { return Facing::SOUTH; } } else if (orientation == Direction::EAST) { if (data == Facing::NORTH) { return Facing::EAST; } if (data == Facing::SOUTH) { return Facing::WEST; } if (data == Facing::WEST) { return Facing::NORTH; } if (data == Facing::EAST) { return Facing::SOUTH; } } } return data; } void StructurePiece::placeBlock( Level* level, int block, int data, int x, int y, int z, BoundingBox* chunkBB ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( y ); int worldZ = getWorldZ( x, z ); if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) { return; } // 4J Stu - We shouldn't be removing bedrock when generating things (eg in SuperFlat) if(worldY == 0) return; level->setTileAndData( worldX, worldY, worldZ, block, data, Tile::UPDATE_CLIENTS); } /** * The purpose of this method is to wrap the getTile call on Level, in order * to prevent the level from generating chunks that shouldn't be loaded yet. * Returns 0 if the call is out of bounds. * * @param level * @param x * @param y * @param z * @param chunkPosition * @return */ int StructurePiece::getBlock( Level* level, int x, int y, int z, BoundingBox* chunkBB ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( y ); int worldZ = getWorldZ( x, z ); if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) { return 0; } return level->getTile( worldX, worldY, worldZ ); } void StructurePiece::generateAirBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1) { for (int y = y0; y <= y1; y++) { for (int x = x0; x <= x1; x++) { for (int z = z0; z <= z1; z++) { placeBlock(level, 0, 0, x, y, z, chunkBB); } } } } void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir ) { for ( int y = y0; y <= y1; y++ ) { for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) { continue; } if ( y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ) { placeBlock( level, edgeTile, 0, x, y, z, chunkBB ); } else { placeBlock( level, fillTile, 0, x, y, z, chunkBB ); } } } } } void StructurePiece::generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int edgeData, int fillTile, int fillData, bool skipAir) { for (int y = y0; y <= y1; y++) { for (int x = x0; x <= x1; x++) { for (int z = z0; z <= z1; z++) { if (skipAir && getBlock(level, x, y, z, chunkBB) == 0) { continue; } if (y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1) { placeBlock(level, edgeTile, edgeData, x, y, z, chunkBB); } else { placeBlock(level, fillTile, fillData, x, y, z, chunkBB); } } } } } void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, BoundingBox* boxBB, int edgeTile, int fillTile, bool skipAir ) { generateBox( level, chunkBB, boxBB->x0, boxBB->y0, boxBB->z0, boxBB->x1, boxBB->y1, boxBB->z1, edgeTile, fillTile, skipAir ); } void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, bool skipAir, Random* random, StructurePiece::BlockSelector* selector ) { for ( int y = y0; y <= y1; y++ ) { for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) { continue; } selector->next( random, x, y, z, y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ); placeBlock( level, selector->getNextId(), selector->getNextData(), x, y, z, chunkBB ); } } } } void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, BoundingBox* boxBB, bool skipAir, Random* random, StructurePiece::BlockSelector* selector ) { generateBox( level, chunkBB, boxBB->x0, boxBB->y0, boxBB->z0, boxBB->x1, boxBB->y1, boxBB->z1, skipAir, random, selector ); } void StructurePiece::generateMaybeBox( Level* level, BoundingBox* chunkBB, Random *random, float probability, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir ) { for ( int y = y0; y <= y1; y++ ) { for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { if ( random->nextFloat() > probability ) { continue; } if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) { continue; } if ( y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ) { placeBlock( level, edgeTile, 0, x, y, z, chunkBB ); } else { placeBlock( level, fillTile, 0, x, y, z, chunkBB ); } } } } } void StructurePiece::maybeGenerateBlock( Level* level, BoundingBox* chunkBB, Random *random, float probability, int x, int y, int z, int tile, int data ) { if ( random->nextFloat() < probability ) { placeBlock( level, tile, data, x, y, z, chunkBB ); } } void StructurePiece::generateUpperHalfSphere( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int fillTile, bool skipAir ) { float diagX = static_cast(x1 - x0 + 1); float diagY = static_cast(y1 - y0 + 1); float diagZ = static_cast(z1 - z0 + 1); float cx = x0 + diagX / 2; float cz = z0 + diagZ / 2; for ( int y = y0; y <= y1; y++ ) { float normalizedYDistance = static_cast(y - y0) / diagY; for ( int x = x0; x <= x1; x++ ) { float normalizedXDistance = ( float )( x - cx ) / ( diagX * 0.5f ); for ( int z = z0; z <= z1; z++ ) { float normalizedZDistance = ( float )( z - cz ) / ( diagZ * 0.5f ); if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) { continue; } float dist = ( normalizedXDistance * normalizedXDistance ) + ( normalizedYDistance * normalizedYDistance ) + ( normalizedZDistance * normalizedZDistance ); if ( dist <= 1.05f ) { placeBlock( level, fillTile, 0, x, y, z, chunkBB ); } } } } } void StructurePiece::generateFullSphere( Level* level, BoundingBox* chunkBB, int cx, int cy, int cz, int radius, int edgeTile, int edgeData, int fillTile, int fillData, bool skipAir ) { for ( int y = cy - radius; y <= cy + radius; y++ ) { for ( int x = cx - radius; x <= cx + radius; x++ ) { for ( int z = cz - radius; z <= cz + radius; z++ ) { float dx = ( float )( abs( x - cx ) ) + 0.5f; float dy = ( float )( abs( y - cy ) ) + 0.5f; float dz = ( float )( abs( z - cz ) ) + 0.5f; float dist = sqrtf( dx * dx + dy * dy + dz * dz ); if ( !skipAir || getBlock( level, x, y, z, chunkBB ) != 0 ) { if ( fabsf( dist - ( float )radius ) <= 0.5f ) placeBlock( level, edgeTile, edgeData, x, y, z, chunkBB ); } if ( ( float ) radius - dist > 0.5f ) { placeBlock( level, fillTile, fillData, x, y, z, chunkBB ); } } } } } void StructurePiece::generateAirColumnUp( Level* level, int x, int startY, int z, BoundingBox* chunkBB ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( startY ); int worldZ = getWorldZ( x, z ); if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) { return; } while ( !level->isEmptyTile( worldX, worldY, worldZ ) && worldY < Level::maxBuildHeight - 1 ) { level->setTileAndData( worldX, worldY, worldZ, 0, 0, Tile::UPDATE_CLIENTS); worldY++; } } void StructurePiece::generateAirBoxUp( Level* level, int x0, int z0, int x1, int z1, int y, BoundingBox* chunkBB ) { for ( int x = x0; x < x1; ++x ) { for ( int z = z0; z < z1; ++z ) { placeBlock( level, 0, 0, x, y, z, chunkBB ); } } } void StructurePiece::fillColumnDown( Level* level, int tile, int tileData, int x, int startY, int z, BoundingBox* chunkBB ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( startY ); int worldZ = getWorldZ( x, z ); if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) { return; } while ( ( level->isEmptyTile( worldX, worldY, worldZ ) || level->getMaterial( worldX, worldY, worldZ )->isLiquid() ) && worldY > 1 ) { level->setTileAndData( worldX, worldY, worldZ, tile, tileData, Tile::UPDATE_CLIENTS ); worldY--; } } void StructurePiece::fillBoxDown( Level* level, int tileId, int tileData, int x0, int y0, int z0, int x1, int z1, BoundingBox* chunkBB ) { for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { fillColumnDown( level, tileId, tileData, x, y0, z, chunkBB ); } } } bool StructurePiece::createChest( Level* level, BoundingBox* chunkBB, Random* random, int x, int y, int z, WeighedTreasureArray treasure, int numRolls ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( y ); int worldZ = getWorldZ( x, z ); if ( chunkBB->isInside( worldX, worldY, worldZ ) ) { if ( level->getTile( worldX, worldY, worldZ ) != Tile::chest->id ) { level->setTileAndData( worldX, worldY, worldZ, Tile::chest->id, 0, Tile::UPDATE_CLIENTS ); shared_ptr chest = dynamic_pointer_cast(level->getTileEntity( worldX, worldY, worldZ )); if ( chest != nullptr ) WeighedTreasure::addChestItems( random, treasure, chest, numRolls ); return true; } } return false; } bool StructurePiece::createDispenser(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int facing, WeighedTreasureArray items, int numRolls) { int worldX = getWorldX(x, z); int worldY = getWorldY(y); int worldZ = getWorldZ(x, z); if (chunkBB->isInside(worldX, worldY, worldZ)) { if (level->getTile(worldX, worldY, worldZ) != Tile::dispenser_Id) { level->setTileAndData(worldX, worldY, worldZ, Tile::dispenser_Id, getOrientationData(Tile::dispenser_Id, facing), Tile::UPDATE_CLIENTS); shared_ptr dispenser = dynamic_pointer_cast(level->getTileEntity(worldX, worldY, worldZ)); if (dispenser != nullptr) WeighedTreasure::addDispenserItems(random, items, dispenser, numRolls); return true; } } return false; } int StructurePiece::getBlockDataValue( StructureTable::eBlockDataType type, int tile, int data ) { if ( type == StructureTable::eBlockDataType_Orientation ) return getOrientationData( tile, data ); if ( type == StructureTable::eBlockDataType_Inverted ) return ~data & 0xF; return data; } void StructurePiece::createCrops( Level *level, BoundingBox *chunkBB, Random *random, int x0, int z0, int x1, int z1, int y, int tileId, int minData, int maxData ) { for ( int x = x0; x <= x1; x++ ) { for ( int z = z0; z <= z1; z++ ) { placeBlock( level, tileId, Mth::nextInt( random, minData, maxData ), x, y, z, chunkBB ); } } } void StructurePiece::generateStructureFromData( Level *level, BoundingBox *chunkBB, Random *random, StructureTable::StructurePiece *piece, vector &objectPlacedFlags, vector featureConditions, vector tileOptionRemap, vector &entitySpawnCounts ) { unordered_map failures; int treasureCount = piece->treasure.size(); WeighedTreasureArray treasureItems; if ( treasureCount > 0 ) { treasureItems = WeighedTreasureArray( treasureCount ); for ( unsigned int i = 0; i < treasureCount; ++i ) { const StructureTable::TreasureData &t = piece->treasure[i]; treasureItems[i] = new WeighedTreasure( t.itemId, t.auxValue, t.minCount, t.maxCount, t.weight ); } } for ( unsigned int i = 0; i < piece->structures.size(); ++i ) { StructureTable::StructureData &s = piece->structures[i]; bool shouldGenerate = true; if ( s.v26.size() == 2 ) { int bitIndex = s.v26[0]; bool invert = s.v26[1] != 0; if ( bitIndex >= (int)featureConditions.size() ) { shouldGenerate = false; } else { bool bit = featureConditions[bitIndex]; if ( invert ) { if ( bit ) { shouldGenerate = false; } } else { if ( !bit ) { shouldGenerate = false; } } } } if (!s.v27.empty()) { for (unsigned int depIdx = 0; depIdx < s.v27.size(); ++depIdx) { if (s.id != eGenerateStructure_CheckBlock) { auto it = failures.find(s.v27[depIdx]); if (it != failures.end() && it->second) { shouldGenerate = false; break; } } } } if (shouldGenerate && !s.cropAges.empty()) { for (unsigned int exclIdx = 0; exclIdx < s.cropAges.size(); ++exclIdx) { for (unsigned int remapIdx = 0; remapIdx < tileOptionRemap.size(); ++remapIdx) { if (s.cropAges[exclIdx] == tileOptionRemap[remapIdx]) { shouldGenerate = false; break; } } if (!shouldGenerate) break; } } if (!shouldGenerate) continue; // @Patoke todo: this is broken, please fix for (unsigned int remapIdx = 0; remapIdx < tileOptionRemap.size(); ++remapIdx) { int offsetValue = tileOptionRemap[remapIdx]; // start if (s.startRemap0 == remapIdx) s.x0 += offsetValue; if (s.startRemap1 == remapIdx) s.x0 += offsetValue; if (s.startRemap2 == remapIdx) s.y0 += offsetValue; // end if (s.endRemap0 == remapIdx) s.x1 += offsetValue; if (s.endRemap1 == remapIdx) s.x1 += offsetValue; if (s.endRemap2 == remapIdx) s.y1 += offsetValue; } switch( s.id ) { case eGenerateStructure_Block: placeBlock( level, s.tileId, getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ), s.x0, s.y0, s.z0, chunkBB ); break; /* case eGenerateStructure_maybeGenerateBlock: maybeGenerateBlock( level, chunkBB, random, s.probability, s.x0, s.y0, s.z0, s.tileId, s.data ); break; case eGenerateStructure_AirBox: generateAirBox( level, chunkBB, s.x0, s.y0, s.z0, s.x1, s.y1, s.z1 ); break; */ case eGenerateStructure_Box: { int data0 = getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ); int data1 = getBlockDataValue( (StructureTable::eBlockDataType)s.secondDataType, s.secondTileId, s.secondData ); generateBox( level, chunkBB, s.x0, s.y0, s.z0, s.x1, s.y1, s.z1, s.tileId, data0, s.secondTileId, data1, false ); break; } /* case eGenerateStructure_MaybeBox: generateMaybeBox( level, chunkBB, random, s.probability, s.x0, s.y0, s.z0, s.x1, s.y1, s.z1, s.tileId, s.data, false ); break; case eGenerateStructure_UpperHalfSphere: generateUpperHalfSphere( level, chunkBB, s.x0, s.y0, s.z0, s.x1, s.y1, s.z1, s.tileId, false ); break; */ case eGenerateStructure_FullSphere: { int data0 = getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ); int data1 = getBlockDataValue( (StructureTable::eBlockDataType)s.secondDataType, s.secondTileId, s.secondData ); generateFullSphere( level, chunkBB, s.x0, s.y0, s.z0, s.v25[0], s.tileId, data0, s.secondTileId, data1, false ); break; } /* case eGenerateStructure_AirColumnUp: generateAirColumnUp( level, s.x0, s.y0, s.z0, chunkBB ); break; */ case eGenerateStructure_fillColumnDown: { int data0 = getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ); fillColumnDown( level, s.tileId, data0, s.x0, s.y0, s.z0, chunkBB ); break; } case eGenerateStructure_fillBoxDown: { int data0 = getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ); fillBoxDown( level, s.tileId, data0, s.x0, s.y0, s.z0, s.x1, s.z1, chunkBB ); break; } case eGenerateStructure_generateAirBoxUp: generateAirBoxUp( level, s.x0, s.z0, s.x1, s.z1, s.y0, chunkBB ); break; case eGenerateStructure_Crops: { if( tileOptionRemap.empty() ) break; if( s.cropAges.size() != 2 ) break; if( piece->cropTiles.empty() ) break; if( s.tileId >= (int)tileOptionRemap.size() ) break; if( s.x0 > s.x1 ) break; int minCropAge = s.cropAges[0]; int maxCropAge = s.cropAges[1]; int remapIdx = tileOptionRemap[s.tileId]; int cropTileId = ( remapIdx >= (int)piece->cropTiles.size() ) ? piece->cropTiles.back() : piece->cropTiles[remapIdx]; createCrops( level, chunkBB, random, s.x0, s.z0, s.x1, s.z1, s.y0, cropTileId, minCropAge, maxCropAge); break; } case eGenerateStructure_CreateDoor: { int worldX = getWorldX( s.x0, s.z0 ); int worldY = getWorldY( s.y0 ); int worldZ = getWorldZ( s.x0, s.z0 ); if( chunkBB->isInside( worldX, worldY, worldZ ) ) { int doorData = getBlockDataValue( (StructureTable::eBlockDataType)s.dataType, s.tileId, s.data ); if( s.tileId == Tile::door_wood_Id ) { DoorItem::place( level, worldX, worldY, worldZ, doorData, Tile::door_wood ); } else { DoorItem::place( level, worldX, worldY, worldZ, doorData, Tile::door_iron ); } } break; } case eGenerateStructure_CheckBlock: { int block = getBlock(level, s.x0, s.y0, s.z0, chunkBB); bool match = (block != 0); if (!s.v25.empty() && s.v25[0] == 0) { match = (block == 0); } if (!match) { if (!s.v27.empty()) { failures[s.v27[0]] = true; } } break; } default: break; } } vector newObjectPlacedFlags; for ( unsigned int i = 0; i < piece->objects.size(); ++i ) { const StructureTable::ObjectData &o = piece->objects[i]; bool process = true; if ( (int)objectPlacedFlags.size() == (int)piece->objects.size() ) { process = objectPlacedFlags[i]; } bool placed = false; if ( process ) { if ( o.id == eGenerateObject_Chest || o.id == eGenerateObject_Dispenser ) { if ( o.canGenEnchantedBooks ) { WeighedTreasure* book = Item::enchantedBook->createForRandomTreasure( random, o.minEnchantedBooks, o.maxEnchantedBooks, o.enchantedBookWeight ); WeighedTreasureArray treasure = WeighedTreasureArray( treasureCount + 1 ); for ( int k = 0; k < treasureCount; k++ ) { treasure[k] = treasureItems[k]; treasure[treasureCount] = book; } int rollOffset = random->nextInt( o.rollWeight ); if ( o.id == eGenerateObject_Chest ) { placed = createChest( level, chunkBB, random, o.x, o.y, o.z, treasure, o.rollCount + rollOffset ); } else { placed = createDispenser( level, chunkBB, random, o.x, o.y, o.z, o.dispenserDir, treasure, o.rollCount + rollOffset ); } delete book; } else { int rollOffset = random->nextInt( o.rollWeight ); if ( o.id == eGenerateObject_Chest ) { placed = createChest( level, chunkBB, random, o.x, o.y, o.z, treasureItems, o.rollCount + rollOffset ); } else { placed = createDispenser( level, chunkBB, random, o.x, o.y, o.z, o.dispenserDir, treasureItems, o.rollCount + rollOffset ); } } } } newObjectPlacedFlags.push_back( placed ); } objectPlacedFlags = newObjectPlacedFlags; for ( unsigned int j = 0; j < piece->entities.size(); ++j ) { const StructureTable::EntityData &e = piece->entities[j]; if ( j >= entitySpawnCounts.size() ) { entitySpawnCounts.push_back( 0 ); } spawnEntity( level, chunkBB, e.x, e.y, e.z, (eGenerateEntity)e.id, e.count, const_cast&>( e.professions ), entitySpawnCounts[j] ); } for (int i = 0; i < treasureItems.length; i++) { delete treasureItems[i]; } } void StructurePiece::setDirty() { dirty = true; } void StructurePiece::spawnEntity( Level *level, BoundingBox *chunkBB, int x, int y, int z, eGenerateEntity entityType, int count, vector &professions, int &entitySpawnCounts ) { while ( entitySpawnCounts < count ) { int worldX = getWorldX( x, z ); int worldY = getWorldY( y ); int worldZ = getWorldZ( x, z ); worldX += entitySpawnCounts; if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) { return; } if ( entityType == eGenerateEntity_Witch ) { setDirty(); shared_ptr witch = shared_ptr( new Witch( level ) ); witch->moveTo( worldX + 0.5, worldY, worldZ + 0.5, 0, 0 ); level->addEntity( witch ); } else if ( entityType == eGenerateEntity_Villager ) { int profession = 0; if ( !professions.empty() && entitySpawnCounts < professions.size() ) profession = professions[entitySpawnCounts]; setDirty(); shared_ptr villager = shared_ptr( new Villager( level, profession ) ); villager->moveTo( worldX + 0.5, worldY, worldZ + 0.5, 0, 0 ); level->addEntity( villager ); } entitySpawnCounts++; } } void StructurePiece::getRandomValuesFromDataSet( StructureTable::StructurePiece *piece, Random *random, vector &stateConditions, vector &placedTiles ) { for ( int i = 0; i < piece->v8; i++ ) { bool value = random->nextBoolean(); stateConditions.push_back( value ); } for ( unsigned int i = 0; i < piece->v11.size(); i++ ) { int value = random->nextInt( piece->v11[i] ); placedTiles.push_back( value ); } } void StructurePiece::addStructurePieceSaveData(DataOutputStream *dos, vector &objectState, vector &featureConditions, vector &tileOptionRemap, vector &entitySpawnCounts) { addBoolSaveData( dos, objectState ); addBoolSaveData( dos, featureConditions ); addIntSaveData( dos, tileOptionRemap ); addIntSaveData( dos, entitySpawnCounts ); } void StructurePiece::readStructurePieceSaveData(DataInputStream *dis, vector &objectState, vector &featureConditions, vector &tileOptionRemap, vector &entitySpawnCounts) { objectState = readBoolSaveData( dis ); featureConditions = readBoolSaveData( dis ); tileOptionRemap = readIntSaveData( dis ); entitySpawnCounts = readIntSaveData( dis ); } void StructurePiece::clearDirty() { dirty = false; } bool StructurePiece::isDirty() { return dirty; } void StructurePiece::addBoolSaveData(DataOutputStream *dos, vector &data) { wstring outData; outData.assign( L"\0" ); for ( size_t i = 0; i < data.size(); ++i ) { wostringstream woss; woss << boolalpha << data[i]; outData.append( woss.str() ); if ( i < data.size() - 1 ) { outData.append( L"," ); } } dos->writeUTF( outData ); } vector StructurePiece::readBoolSaveData( DataInputStream *dis ) { wstring data = dis->readUTF(); vector tokens = stringSplit( data, L',' ); vector outData; for ( size_t i = 0; i < tokens.size(); ++i ) { wistringstream wiss( tokens[i] ); bool outVal = false; wiss >> boolalpha >> outVal; outData.push_back( outVal ); } return outData; } void StructurePiece::addIntSaveData(DataOutputStream *dos, vector &data) { wstring outData; outData.assign( L"\0" ); for ( size_t i = 0; i < data.size(); ++i ) { wostringstream woss; woss << dec << data[i]; outData.append( woss.str() ); if ( i < data.size() - 1 ) { outData.append( L"," ); } } dos->writeUTF( outData ); } vector StructurePiece::readIntSaveData(DataInputStream *dis) { wstring data = dis->readUTF(); vector tokens = stringSplit( data, L',' ); vector outData; for ( size_t i = 0; i < tokens.size(); ++i ) { wistringstream wiss( tokens[i] ); int outVal = 0; wiss >> dec >> outVal; outData.push_back( outVal ); } return outData; }