2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "StructureFeature.h"
# include "StructureStart.h"
# include "StructurePiece.h"
# include "ChunkPos.h"
# include "BoundingBox.h"
# include "net.minecraft.world.level.h"
# include "LevelData.h"
2026-03-03 03:04:10 +08:00
StructureFeature : : StructureFeature ( )
{
# ifdef ENABLE_STRUCTURE_SAVING
savedData = nullptr ;
# endif
}
2026-03-01 12:16:08 +08:00
StructureFeature : : ~ StructureFeature ( )
{
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; it + + )
{
delete it - > second ;
}
}
void StructureFeature : : addFeature ( Level * level , int x , int z , int xOffs , int zOffs , byteArray blocks )
{
2026-03-03 03:04:10 +08:00
// this method is called for each chunk within 8 chunk's distance from
// the chunk being generated, but not all chunks are the sources of
// structures
restoreSavedData ( level ) ;
2026-03-01 12:16:08 +08:00
if ( cachedStructures . find ( ChunkPos : : hashCode ( x , z ) ) ! = cachedStructures . end ( ) )
{
2026-03-03 03:04:10 +08:00
return ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
// clear random key
random - > nextInt ( ) ;
2026-03-01 12:16:08 +08:00
// 4J-PB - want to know if it's a superflat land, so we don't generate so many villages - we've changed the distance required between villages on the xbox
2026-03-03 03:04:10 +08:00
if ( isFeatureChunk ( x , z , level - > getLevelData ( ) - > getGenerator ( ) = = LevelType : : lvl_flat ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
StructureStart * start = createStructureStart ( x , z ) ;
cachedStructures [ ChunkPos : : hashCode ( x , z ) ] = start ;
saveFeature ( x , z , start ) ;
}
2026-03-01 12:16:08 +08:00
}
bool StructureFeature : : postProcess ( Level * level , Random * random , int chunkX , int chunkZ )
{
2026-03-03 03:04:10 +08:00
restoreSavedData ( level ) ;
2026-03-01 12:16:08 +08:00
// 4J Stu - The x and z used to be offset by (+8) here, but that means we can miss out half structures on the edge of the world
// Normal feature generation offsets generation by half a chunk to ensure that it can generate the entire feature in chunks already created
// Structure features don't need this, as the PlaceBlock function only places blocks inside the BoundingBox specified, and parts
// of a struture piece can be added in more than one post-process call
2026-03-03 03:04:10 +08:00
int cx = ( chunkX < < 4 ) ; // + 8;
int cz = ( chunkZ < < 4 ) ; // + 8;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
bool intersection = false ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; it + + )
{
StructureStart * structureStart = it - > second ;
2026-03-03 03:04:10 +08:00
if ( structureStart - > isValid ( ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( structureStart - > getBoundingBox ( ) - > intersects ( cx , cz , cx + 15 , cz + 15 ) )
2026-03-01 12:16:08 +08:00
{
BoundingBox * bb = new BoundingBox ( cx , cz , cx + 15 , cz + 15 ) ;
2026-03-03 03:04:10 +08:00
structureStart - > postProcess ( level , random , bb ) ;
2026-03-01 12:16:08 +08:00
delete bb ;
2026-03-03 03:04:10 +08:00
intersection = true ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
// because some feature pieces are modified in the postProcess step, we need to save them again
saveFeature ( structureStart - > getChunkX ( ) , structureStart - > getChunkZ ( ) , structureStart ) ;
}
}
}
return intersection ;
2026-03-01 12:16:08 +08:00
}
bool StructureFeature : : isIntersection ( int cellX , int cellZ )
{
2026-03-03 03:04:10 +08:00
restoreSavedData ( level ) ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; it + + )
{
StructureStart * structureStart = it - > second ;
2026-03-03 03:04:10 +08:00
if ( structureStart - > isValid ( ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( structureStart - > getBoundingBox ( ) - > intersects ( cellX , cellZ , cellX , cellZ ) )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( it2 , structureStart - > getPieces ( ) - > begin ( ) ) ;
while ( it2 ! = structureStart - > getPieces ( ) - > end ( ) )
{
2026-03-03 03:04:10 +08:00
StructurePiece * next = * it2 + + ;
if ( next - > getBoundingBox ( ) - > intersects ( cellX , cellZ , cellX , cellZ ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return true ;
}
}
}
}
}
return false ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:39:35 +07:00
bool StructureFeature : : isInsideFeature ( int cellX , int cellY , int cellZ )
2026-03-03 03:04:10 +08:00
{
restoreSavedData ( level ) ;
return getStructureAt ( cellX , cellY , cellZ ) ! = NULL ;
}
StructureStart * StructureFeature : : getStructureAt ( int cellX , int cellY , int cellZ )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:39:35 +07:00
//for (StructureStart structureStart : cachedStructures.values())
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; + + it )
{
StructureStart * pStructureStart = it - > second ;
2026-03-02 17:39:35 +07:00
if ( pStructureStart - > isValid ( ) )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:39:35 +07:00
if ( pStructureStart - > getBoundingBox ( ) - > intersects ( cellX , cellZ , cellX , cellZ ) )
2026-03-01 12:16:08 +08:00
{
/*
Iterator < StructurePiece > it = structureStart . getPieces ( ) . iterator ( ) ;
while ( it . hasNext ( ) ) {
StructurePiece next = it . next ( ) ;
if ( next . getBoundingBox ( ) . isInside ( cellX , cellY , cellZ ) ) {
return true ;
}
*/
list < StructurePiece * > * pieces = pStructureStart - > getPieces ( ) ;
for ( AUTO_VAR ( it2 , pieces - > begin ( ) ) ; it2 ! = pieces - > end ( ) ; it2 + + )
{
StructurePiece * piece = * it2 ;
if ( piece - > getBoundingBox ( ) - > isInside ( cellX , cellY , cellZ ) )
{
2026-03-03 03:04:10 +08:00
return pStructureStart ;
2026-03-01 12:16:08 +08:00
}
}
}
}
}
2026-03-03 03:04:10 +08:00
return NULL ;
}
bool StructureFeature : : isInsideBoundingFeature ( int cellX , int cellY , int cellZ )
{
restoreSavedData ( level ) ;
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; + + it )
{
StructureStart * structureStart = it - > second ;
if ( structureStart - > isValid ( ) )
{
return ( structureStart - > getBoundingBox ( ) - > intersects ( cellX , cellZ , cellX , cellZ ) ) ;
}
}
2026-03-01 12:16:08 +08:00
return false ;
}
2026-03-02 17:39:35 +07:00
TilePos * StructureFeature : : getNearestGeneratedFeature ( Level * level , int cellX , int cellY , int cellZ )
2026-03-01 12:16:08 +08:00
{
// this is a hack that will "force" the feature to generate positions
// even if the player hasn't generated new chunks yet
this - > level = level ;
2026-03-03 03:04:10 +08:00
restoreSavedData ( level ) ;
2026-03-01 12:16:08 +08:00
random - > setSeed ( level - > getSeed ( ) ) ;
2026-03-02 17:39:35 +07:00
__int64 xScale = random - > nextLong ( ) ;
__int64 zScale = random - > nextLong ( ) ;
__int64 xx = ( cellX > > 4 ) * xScale ;
__int64 zz = ( cellZ > > 4 ) * zScale ;
2026-03-01 12:16:08 +08:00
random - > setSeed ( xx ^ zz ^ level - > getSeed ( ) ) ;
addFeature ( level , cellX > > 4 , cellZ > > 4 , 0 , 0 , byteArray ( ) ) ;
double minDistance = DBL_MAX ;
TilePos * selected = NULL ;
for ( AUTO_VAR ( it , cachedStructures . begin ( ) ) ; it ! = cachedStructures . end ( ) ; + + it )
{
StructureStart * pStructureStart = it - > second ;
2026-03-02 17:39:35 +07:00
if ( pStructureStart - > isValid ( ) )
2026-03-01 12:16:08 +08:00
{
//StructurePiece *pStructurePiece = pStructureStart->getPieces().get(0);
StructurePiece * pStructurePiece = * pStructureStart - > getPieces ( ) - > begin ( ) ;
TilePos * locatorPosition = pStructurePiece - > getLocatorPosition ( ) ;
int dx = locatorPosition - > x - cellX ;
int dy = locatorPosition - > y - cellY ;
int dz = locatorPosition - > z - cellZ ;
2026-03-03 03:04:10 +08:00
double dist = dx * dx + dy * dy + dz * dz ;
2026-03-01 12:16:08 +08:00
2026-03-02 17:39:35 +07:00
if ( dist < minDistance )
2026-03-01 12:16:08 +08:00
{
minDistance = dist ;
selected = locatorPosition ;
}
}
}
2026-03-02 17:39:35 +07:00
if ( selected ! = NULL )
2026-03-01 12:16:08 +08:00
{
return selected ;
2026-03-02 17:39:35 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
vector < TilePos > * guesstimatedFeaturePositions = getGuesstimatedFeaturePositions ( ) ;
2026-03-02 17:39:35 +07:00
if ( guesstimatedFeaturePositions ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
TilePos * pSelectedPos = new TilePos ( 0 , 0 , 0 ) ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , guesstimatedFeaturePositions - > begin ( ) ) ; it ! = guesstimatedFeaturePositions - > end ( ) ; + + it )
{
int dx = ( * it ) . x - cellX ;
int dy = ( * it ) . y - cellY ;
int dz = ( * it ) . z - cellZ ;
2026-03-03 03:04:10 +08:00
double dist = dx * dx + dy * dy + dz * dz ;
2026-03-01 12:16:08 +08:00
2026-03-02 17:39:35 +07:00
if ( dist < minDistance )
2026-03-01 12:16:08 +08:00
{
minDistance = dist ;
pSelectedPos - > x = ( * it ) . x ;
pSelectedPos - > y = ( * it ) . y ;
pSelectedPos - > z = ( * it ) . z ;
}
}
delete guesstimatedFeaturePositions ;
return pSelectedPos ;
}
}
return NULL ;
}
2026-03-02 17:39:35 +07:00
vector < TilePos > * StructureFeature : : getGuesstimatedFeaturePositions ( )
2026-03-01 12:16:08 +08:00
{
return NULL ;
}
2026-03-03 03:04:10 +08:00
void StructureFeature : : restoreSavedData ( Level * level )
{
# ifdef ENABLE_STRUCTURE_SAVING
if ( savedData = = NULL )
{
savedData = dynamic_pointer_cast < StructureFeatureSavedData > ( level - > getSavedData ( typeid ( StructureFeatureSavedData ) , getFeatureName ( ) ) ) ;
if ( savedData = = NULL )
{
savedData = shared_ptr < StructureFeatureSavedData > ( new StructureFeatureSavedData ( getFeatureName ( ) ) ) ;
level - > setSavedData ( getFeatureName ( ) , savedData ) ;
}
else
{
CompoundTag * fullTag = savedData - > getFullTag ( ) ;
vector < Tag * > * allTags = fullTag - > getAllTags ( ) ;
for ( AUTO_VAR ( it , allTags - > begin ( ) ) ; it ! = allTags - > end ( ) ; + + it )
{
Tag * featureTag = * it ;
if ( featureTag - > getId ( ) = = Tag : : TAG_Compound )
{
CompoundTag * ct = ( CompoundTag * ) featureTag ;
if ( ct - > contains ( L " ChunkX " ) & & ct - > contains ( L " ChunkZ " ) )
{
int cx = ct - > getInt ( L " ChunkX " ) ;
int cz = ct - > getInt ( L " ChunkZ " ) ;
StructureStart * start = StructureFeatureIO : : loadStaticStart ( ct , level ) ;
// System.out.println("Loaded " + start.getClass().getSimpleName() + " from file");
cachedStructures [ ChunkPos : : hashCode ( cx , cz ) ] = start ;
}
}
}
delete allTags ;
}
}
# endif
}
void StructureFeature : : saveFeature ( int chunkX , int chunkZ , StructureStart * feature )
{
# ifdef ENABLE_STRUCTURE_SAVING
savedData - > putFeatureTag ( feature - > createTag ( chunkX , chunkZ ) , chunkX , chunkZ ) ;
savedData - > setDirty ( ) ;
# endif
}