From 5c140a95b7a7ecff49a282c2a2596274e0ace1a8 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Tue, 5 May 2026 00:13:26 +0200 Subject: [PATCH] fix: guardians spawn in structure --- Minecraft.Client/ServerLevel.cpp | 16 ++++ Minecraft.Client/ServerLevel.h | 1 + Minecraft.World/Dimension.h | 3 +- Minecraft.World/Direction.h | 106 ++++++++++++----------- Minecraft.World/Guardian.cpp | 102 +++++++++++++++++----- Minecraft.World/Guardian.h | 8 +- Minecraft.World/Level.cpp | 52 +++++++++++ Minecraft.World/Level.h | 5 ++ Minecraft.World/MobSpawner.cpp | 60 ++++++++----- Minecraft.World/OceanMonumentFeature.cpp | 40 +++++++-- Minecraft.World/OceanMonumentFeature.h | 3 +- Minecraft.World/OceanMonumentPieces.cpp | 8 +- Minecraft.World/OceanMonumentPieces.h | 2 +- Minecraft.World/Random.h | 3 + Minecraft.World/RandomLevelSource.cpp | 34 ++++---- Minecraft.World/RandomLevelSource.h | 4 +- Minecraft.World/StructureFeature.cpp | 11 ++- 17 files changed, 324 insertions(+), 134 deletions(-) diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index 507bfbc7..0d6dd853 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -28,6 +28,9 @@ #include "../Minecraft.World/ItemEntity.h" #include "../Minecraft.World/Arrow.h" #include "../Minecraft.World/PrimedTnt.h" +#include "../Minecraft.World/RandomLevelSource.h" +#include "../Minecraft.World/MobCategory.h" +#include "../Minecraft.World/OceanMonumentFeature.h" #include "../Minecraft.World/FallingTile.h" #include "../Minecraft.World/net.minecraft.network.packet.h" #include "../Minecraft.World/Mth.h" @@ -377,6 +380,19 @@ void ServerLevel::tick() #endif } +bool ServerLevel::canMobSpawnAt(MobCategory *mobCategory, Biome::MobSpawnerData *data, const BlockPos& pos) +{ + RandomLevelSource *rls = dynamic_cast(getChunkSource()); + if (rls) + { + if (mobCategory == MobCategory::monster && rls->oceanMonument->isInsideBoundingFeature(pos.getX(), pos.getY(), pos.getZ())) + { + return true; + } + } + return false; +} + Biome::MobSpawnerData *ServerLevel::getRandomMobSpawnAt(MobCategory *mobCategory, int x, int y, int z) { vector *mobList = getChunkSource()->getMobsAt(mobCategory, x, y, z); diff --git a/Minecraft.Client/ServerLevel.h b/Minecraft.Client/ServerLevel.h index 792132a4..13432927 100644 --- a/Minecraft.Client/ServerLevel.h +++ b/Minecraft.Client/ServerLevel.h @@ -50,6 +50,7 @@ public: ~ServerLevel(); void tick(); Biome::MobSpawnerData *getRandomMobSpawnAt(MobCategory *mobCategory, int x, int y, int z); + bool canMobSpawnAt(MobCategory *mobCategory, Biome::MobSpawnerData *data, const BlockPos& pos); void updateSleepingPlayerList(); protected: void awakenAllPlayers(); diff --git a/Minecraft.World/Dimension.h b/Minecraft.World/Dimension.h index ca040e21..1c8606c6 100644 --- a/Minecraft.World/Dimension.h +++ b/Minecraft.World/Dimension.h @@ -37,7 +37,8 @@ public: virtual ChunkStorage *createStorage(File dir); virtual bool isValidSpawn(int x, int z) const; - + + virtual bool isHasCeiling() const { return hasCeiling; } virtual float getTimeOfDay(int64_t time, float a) const; virtual int getMoonPhase(int64_t time) const; virtual bool isNaturalDimension(); diff --git a/Minecraft.World/Direction.h b/Minecraft.World/Direction.h index 8b8b91bf..0a088ee6 100644 --- a/Minecraft.World/Direction.h +++ b/Minecraft.World/Direction.h @@ -1,5 +1,4 @@ #pragma once - class Direction { public: @@ -8,68 +7,71 @@ public: static const int WEST = 1; static const int NORTH = 2; static const int EAST = 3; - static const int STEP_X[]; static const int STEP_Z[]; - - static const wstring NAMES[];; - - // for [direction] it gives [tile-face] + static const wstring NAMES[]; static int DIRECTION_FACING[]; - - // for [facing] it gives [direction] static int FACING_DIRECTION[]; - - // for [direction] it gives [opposite direction] static int DIRECTION_OPPOSITE[]; - - // for [direction] it gives [90 degrees clockwise direction] static int DIRECTION_CLOCKWISE[]; - - // for [direction] it gives [90 degrees counter-clockwise direction] static int DIRECTION_COUNTER_CLOCKWISE[]; - - // for [direction][world-facing] it gives [tile-facing] static int RELATIVE_DIRECTION_FACING[4][6]; - static int getDirection(double xd, double zd); static int getDirection(int x0, int z0, int x1, int z1); - static int getStepX(int direction) { - switch (direction) { - case WEST: return -1; - case EAST: return 1; - default: return 0; - } - } - - static int getStepZ(int direction) { - switch (direction) { - case NORTH: return -1; - case SOUTH: return 1; - default: return 0; - } - } - static int getStepY(int direction) { - - return 0; - } + static int getStepX(int direction) + { + switch (direction) { + case WEST: return -1; + case EAST: return 1; + default: return 0; + } + } - class Plane - { - public: + static int getStepZ(int direction) + { + switch (direction) { + case NORTH: return -1; + case SOUTH: return 1; + default: return 0; + } + } - static int getRandomFace(Random* random) - { - - static const int horizontal[4] = { - Direction::SOUTH, - Direction::WEST, - Direction::NORTH, - Direction::EAST - }; - return horizontal[random->nextInt(4)]; - } - }; + static int getStepY(int direction) + { + return 0; + } -}; + static int get3DDataValue(int dir6) + { + return dir6; + } + + static int from3DDataValue(int val) + { + return val; + } + + static int getOpposite(int dir6) + { + static const int opp[6] = { 1, 0, 3, 2, 5, 4 }; + if (dir6 >= 0 && dir6 < 6) + return opp[dir6]; + return dir6; + } + + class Plane + { + public: + static int getRandomFace(Random* random) + { + static const int horizontal[4] = { + Direction::SOUTH, + Direction::WEST, + Direction::NORTH, + Direction::EAST + }; + return horizontal[random->nextInt(4)]; + } + }; +}; \ No newline at end of file diff --git a/Minecraft.World/Guardian.cpp b/Minecraft.World/Guardian.cpp index 59cd023a..6bf42cea 100644 --- a/Minecraft.World/Guardian.cpp +++ b/Minecraft.World/Guardian.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "com.mojang.nbt.h" #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.phys.h" @@ -16,6 +16,7 @@ #include "../Minecraft.World/net.minecraft.world.entity.animal.h" #include "MobEffect.h" #include "MobEffectInstance.h" +#include void Guardian::_init() @@ -34,7 +35,7 @@ void Guardian::_init() attackTimer = 0; } -Guardian::Guardian(Level *level) : WaterAnimal(level) +Guardian::Guardian(Level *level) : Monster(level) { this->defineSynchedData(); registerAttributes(); @@ -52,7 +53,7 @@ Guardian::Guardian(Level *level) : WaterAnimal(level) void Guardian::registerAttributes() { - WaterAnimal::registerAttributes(); + Monster::registerAttributes(); getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE); getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(6.0); getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.5); @@ -64,7 +65,7 @@ void Guardian::registerAttributes() void Guardian::defineSynchedData() { - WaterAnimal::defineSynchedData(); + Monster::defineSynchedData(); entityData->define(16, (byte)0); entityData->define(17, 0); } @@ -75,13 +76,13 @@ void Guardian::defineSynchedData() void Guardian::readAdditionalSaveData(CompoundTag *tag) { - WaterAnimal::readAdditionalSaveData(tag); + Monster::readAdditionalSaveData(tag); setElder(tag->getBoolean(L"Elder")); } void Guardian::addAdditonalSaveData(CompoundTag *tag) { - WaterAnimal::addAdditonalSaveData(tag); + Monster::addAdditonalSaveData(tag); tag->putBoolean(L"Elder", isElder()); } @@ -202,13 +203,6 @@ float Guardian::getSpikesAnimation(float partialTicks) - - -void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) -{ - //loot (prismarine, sponge) -} - float Guardian::getEyeHeight() { return bbHeight * 0.5f; @@ -254,7 +248,7 @@ void Guardian::lookAt(shared_ptr e, float yMax, float xMax) void Guardian::serverAiStep() { - WaterAnimal::serverAiStep(); + Monster::serverAiStep(); if (isElder() && !level->isClientSide) { @@ -348,7 +342,16 @@ void Guardian::serverAiStep() { float angle = random->nextFloat() * (float)PI * 2.0f; tx = Mth::cos(angle) * 0.2f; - ty = -0.1f + random->nextFloat() * 0.2f; + + + if (isElder()) { + + ty = -0.15f + random->nextFloat() * 0.15f; + } else { + + ty = -0.1f + random->nextFloat() * 0.2f; + } + tz = Mth::sin(angle) * 0.2f; noActionTime = 0; } @@ -437,7 +440,7 @@ void Guardian::aiStep() xBodyRotO = xBodyRot; zBodyRotO = zBodyRot; - WaterAnimal::aiStep(); + Monster::aiStep(); } void Guardian::updateSize(bool elder) @@ -458,10 +461,11 @@ void Guardian::travel(float xa, float ya) xd *= 0.9f; yd *= 0.9f; zd *= 0.9f; + yd -= 0.02f; } else { - WaterAnimal::travel(xa, ya); + Monster::travel(xa, ya); } } else @@ -473,10 +477,11 @@ void Guardian::travel(float xa, float ya) xd *= 0.9f; yd *= 0.9f; zd *= 0.9f; + yd -= 0.02f; } else { - WaterAnimal::travel(xa, ya); + Monster::travel(xa, ya); } } @@ -484,7 +489,7 @@ void Guardian::travel(float xa, float ya) MobGroupData *Guardian::finalizeMobSpawn(MobGroupData *groupData, int extraData) { - WaterAnimal::finalizeMobSpawn(groupData, extraData); + Monster::finalizeMobSpawn(groupData, extraData); if (extraData == 1) setElder(true); return groupData; @@ -508,7 +513,7 @@ void Guardian::handleEntityEvent(byte eventId) } } - WaterAnimal::handleEntityEvent(eventId); + Monster::handleEntityEvent(eventId); } int Guardian::getAmbientSound() @@ -621,7 +626,60 @@ float Guardian::getSoundVolume() return 1.0f; } -int Guardian::getDeathLoot() +bool Guardian::removeWhenFarAway() { - return 0; + return !isElder(); +} + +void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + + int shards = 2 + random->nextInt(2); + spawnAtLocation(Item::prismarine_shard_Id, shards, 0.5f); + + + int crystals = random->nextInt(2); + if (crystals > 0) + spawnAtLocation(Item::prismarine_cystal_Id, crystals, 0.5f); + + + spawnAtLocation(Item::fish_raw_Id, 1, 0.5f); + + + if (isElder()) + { + spawnAtLocation(Tile::sponge_Id, 1, 0.5f); + } + + + if (wasKilledByPlayer) + { + level->addEntity(std::make_shared( + level, x, y, z, + isElder() ? 10 : 5)); + } +} + +bool Guardian::canSpawn() +{ + if (level->difficulty == Difficulty::PEACEFUL) + return false; + + + bool bypassSky = random->nextInt(20) == 0; + if (!bypassSky) + { + int bx = Mth::floor(x); + int by = Mth::floor(y); + int bz = Mth::floor(z); + if (level->canSeeSkyFromBelowWater(bx, by, bz)) + return false; + } + + + if (!isInWater()) + return false; + + + return level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty(); } \ No newline at end of file diff --git a/Minecraft.World/Guardian.h b/Minecraft.World/Guardian.h index 826ec376..65c838cf 100644 --- a/Minecraft.World/Guardian.h +++ b/Minecraft.World/Guardian.h @@ -1,11 +1,11 @@ #pragma once using namespace std; -#include "WaterAnimal.h" +#include "Monster.h" class Level; class Player; -class Guardian : public WaterAnimal +class Guardian : public Monster { public: eINSTANCEOF GetType() { return eTYPE_GUARDIAN; } @@ -34,12 +34,12 @@ protected: virtual int getHurtSound(); virtual int getDeathSound(); virtual float getSoundVolume(); - virtual int getDeathLoot(); virtual bool makeStepSound(); void playFlopSound(); void playAttackSound(); void playCurseSound(); virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual bool removeWhenFarAway(); virtual void serverAiStep(); public: @@ -57,6 +57,8 @@ public: bool isMoving(); void setMoving(bool moving); + virtual bool canSpawn(); + virtual bool isWaterMob() { return true; } int getTargetedEntityId(); diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 8478535b..d6e1ccdb 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -1168,6 +1168,20 @@ int Level::getDaytimeRawBrightness(int x, int y, int z) return getChunk(x >> 4, z >> 4)->getRawBrightness(x & 15, y, z & 15, 0); } +int Level::getDaytimeRawBrightness(const BlockPos& pos) +{ + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + + if (y < 0) + return 0; + + if (y >= maxBuildHeight) + y = maxBuildHeight - 1; + + return getChunk(x >> 4, z >> 4)->getRawBrightness(x & 15, y, z & 15, 0); +} int Level::getRawBrightness(int x, int y, int z) { @@ -4810,4 +4824,42 @@ BlockPos Level::getHeightmapPos(int x, int z) } return BlockPos(x, 0, z); +} + +bool Level::canSeeSkyFromBelowWater(int x, int y, int z) +{ + if (y < 63) + { + int cx = x; + int cy = 63; + int cz = z; + + + if (dimension->isHasCeiling()) + return false; + + while (true) + { + cy--; + if (cy <= y) + break; + + int tileId = getTile(cx, cy, cz); + + if (Tile::lightBlock[tileId] > 0) + { + Material *mat = nullptr; + if (Tile::tiles[tileId] != nullptr) + mat = Tile::tiles[tileId]->material; + + if (mat == nullptr || !mat->isLiquid()) + return false; + } + } + return true; + } + else + { + return !dimension->isHasCeiling(); + } } \ No newline at end of file diff --git a/Minecraft.World/Level.h b/Minecraft.World/Level.h index 4a6686fd..1f635cbd 100644 --- a/Minecraft.World/Level.h +++ b/Minecraft.World/Level.h @@ -8,6 +8,7 @@ using namespace std; #include "Definitions.h" #include "ParticleTypes.h" #include "Biome.h" +#include "BlockPos.h" #include "C4JThread.h" #ifdef __PSVITA__ @@ -51,6 +52,7 @@ class Minecart; class EntitySelector; class Scoreboard; class GameRules; +class BlockPos; class Level : public LevelSource { @@ -210,6 +212,7 @@ public: bool reallyHasChunksAt(int x, int y, int z, int r); // 4J added bool reallyHasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1); // 4J added BlockPos getHeightmapPos(int x, int z); + bool canSeeSkyFromBelowWater(int x, int y, int z); public: bool hasChunk(int x, int z); bool reallyHasChunk(int x, int z ); // 4J added @@ -237,6 +240,7 @@ public: virtual bool isTileToBeTickedAt(int x, int y, int z, int tileId); bool canSeeSky(int x, int y, int z); int getDaytimeRawBrightness(int x, int y, int z); + int getDaytimeRawBrightness(const BlockPos& pos); int getRawBrightness(int x, int y, int z); int getRawBrightness(int x, int y, int z, bool propagate); bool isSkyLit(int x, int y, int z); @@ -560,4 +564,5 @@ public: }; bool canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType); + }; diff --git a/Minecraft.World/MobSpawner.cpp b/Minecraft.World/MobSpawner.cpp index f43d9532..c444aec3 100644 --- a/Minecraft.World/MobSpawner.cpp +++ b/Minecraft.World/MobSpawner.cpp @@ -14,9 +14,13 @@ #include "Level.h" #include "ChunkPos.h" #include "TilePos.h" +#include "BlockPos.h" +#include "net.minecraft.world.level.chunk.h" #include "../Minecraft.Client/ServerLevel.h" #include "MobSpawner.h" #include "Dimension.h" +#include "OceanMonumentFeature.h" +#include "RandomLevelSource.h" const int MobSpawner::MIN_SPAWN_DISTANCE = 24; @@ -195,6 +199,7 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie for (unsigned int i = 0; i < MobCategory::values.length; i++) { MobCategory *mobCategory = MobCategory::values[i]; + if ((mobCategory->isFriendly() && !spawnFriendlies) || (!mobCategory->isFriendly() && !spawnEnemies) || (mobCategory->isPersistent() && !spawnPersistent)) { continue; @@ -212,7 +217,8 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie // 4J - this is now quite different to the java version. We just have global max counts for the level whereas the original has a max per chunk that // scales with the number of chunks to be polled. int categoryCount = level->countInstanceOf( mobCategory->getEnumBaseClass(), mobCategory->isSingleType()); - if( categoryCount >= mobCategory->getMaxInstancesPerLevel()) + + if( categoryCount >= mobCategory->getMaxInstancesPerLevel() && mobCategory != MobCategory::monster) { continue; } @@ -235,13 +241,18 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread if( !level->hasChunk(cp->x,cp->z) ) continue; - TilePos start = getRandomPosWithin(level, cp->x, cp->z); - int xStart = start.x; - int yStart = start.y; - int zStart = start.z; + + int xStart = cp->x * 16 + level->random->nextInt(16); + int zStart = cp->z * 16 + level->random->nextInt(16); + + int height = level->getChunk(cp->x, cp->z)->getHeightmap(xStart & 15, zStart & 15); + int yMax = (height + 1 + 15) & ~15; + if (yMax <= 0) yMax = 256; + int yStart = level->random->nextInt(yMax); if (level->isSolidBlockingTile(xStart, yStart, zStart)) continue; - if (level->getMaterial(xStart, yStart, zStart) != mobCategory->getSpawnPositionMaterial()) continue; + if (level->getMaterial(xStart, yStart, zStart) != mobCategory->getSpawnPositionMaterial() && + !(mobCategory == MobCategory::monster && level->getMaterial(xStart, yStart, zStart) == Material::water)) continue; int clusterSize = 0; for (int dd = 0; dd < 3; dd++) @@ -264,11 +275,18 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread if( !level->hasChunkAt( x, y, z ) ) continue; - if (isSpawnPositionOk(mobCategory, level, x, y, z)) + BlockPos pos(x, y, z); + if (currentMobType == nullptr) + { + currentMobType = level->getRandomMobSpawnAt(mobCategory, x, y, z); + } + + if (currentMobType != nullptr && (level->canMobSpawnAt(mobCategory, currentMobType, pos) || isSpawnPositionOk(mobCategory, level, x, y, z))) { float xx = x + 0.5f; float yy = static_cast(y); float zz = z + 0.5f; + if (level->getNearestPlayer(xx, yy, zz, MIN_SPAWN_DISTANCE) != nullptr) { continue; @@ -285,15 +303,6 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie } } - if (currentMobType == nullptr) - { - currentMobType = level->getRandomMobSpawnAt(mobCategory, x, y, z); - if (currentMobType == nullptr) - { - break; - } - } - shared_ptr mob; // 4J - removed try/catch // try @@ -316,18 +325,13 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie if( ( mobType & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ) || ( mobType & eTYPE_MONSTER ) ) { - // even more special rule for ghasts, because filling up the nether with 25 of them is a bit unpleasant. In the java version they are - // only limited by the fact that the world fills up with pig zombies (the only other type of enemy mob in the nether) before them - they - // aren't actually even counted properly themselves if( mobType == eTYPE_GHAST ) { if( level->countInstanceOf(mobType, true) >= 4 ) continue; } else if( mobType == eTYPE_ENDERMAN && level->dimension->id == 1 ) { - // Special rule for the end, as we only have Endermen (plus the dragon). Increase the spawnable counts based on level difficulty int maxEndermen = mobCategory->getMaxInstancesPerLevel(); - if( level->difficulty == Difficulty::NORMAL ) { maxEndermen -= mobCategory->getMaxInstancesPerLevel()/4; @@ -336,7 +340,6 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie { maxEndermen -= mobCategory->getMaxInstancesPerLevel()/2; } - if( level->countInstanceOf(mobType, true) >= maxEndermen ) continue; } else if( level->countInstanceOf(mobType, true) >= ( mobCategory->getMaxInstancesPerLevel() / 2 ) ) continue; @@ -391,6 +394,16 @@ bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, i } #endif + if (category == MobCategory::monster && level->getMaterial(x, y, z) == Material::water) + { + + if (level->getMaterial(x, y - 1, z)->isLiquid()) + { + return !level->isSolidBlockingTile(x, y + 1, z); + } + return false; + } + if (category->getSpawnPositionMaterial() == Material::water) { // 4J - changed to spawn water things only in deep water @@ -403,7 +416,6 @@ bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, i yo++; } - // 4J - Sometimes deep water could be just a waterfall, so check that it's wide as well bool inEnoughWater = false; if( liquidCount == 5 ) { @@ -425,7 +437,7 @@ bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, i int tt = level->getTile(x, y - 1, z); return tt != Tile::unbreakable_Id && !level->isSolidBlockingTile(x, y, z) && !level->getMaterial(x, y, z)->isLiquid() && !level->isSolidBlockingTile(x, y + 1, z); } - } +} void MobSpawner::postProcessSpawnMobs(Level *level, Biome *biome, int xo, int zo, int cellWidth, int cellHeight, Random *random) { diff --git a/Minecraft.World/OceanMonumentFeature.cpp b/Minecraft.World/OceanMonumentFeature.cpp index 3d98a84f..caa0d002 100644 --- a/Minecraft.World/OceanMonumentFeature.cpp +++ b/Minecraft.World/OceanMonumentFeature.cpp @@ -32,6 +32,7 @@ void OceanMonumentFeature::prescanNearby(int scanRadiusInGridCells) int validated = 0; int halfSizeChunks = this->level->getLevelData()->getXZSize() / 2; int halfSizeBlocks = (this->level->getLevelData()->getXZSize() * 16) / 2; + app.DebugPrintf("Ocean Monument pre-scan (HalfSize: %d chunks / %d blocks)\n", halfSizeChunks, halfSizeBlocks); for (int gz = -scanRadiusInGridCells; gz <= scanRadiusInGridCells; gz++) @@ -54,18 +55,28 @@ void OceanMonumentFeature::prescanNearby(int scanRadiusInGridCells) int blockZ = cz * 16 + 8; if (Mth::abs(blockX) > halfSizeBlocks || Mth::abs(blockZ) > halfSizeBlocks) - { - continue; - } + continue; if (this->isFeatureChunk(cx, cz)) { validated++; + + + int64_t key = ChunkPos::hashCode(cx, cz); + if (cachedStructures.find(key) == cachedStructures.end()) + { + StructureStart *start = createStructureStart(cx, cz); + if (start != nullptr) + { + cachedStructures[key] = start; + app.DebugPrintf("prescanNearby: cached MonumentStart at chunk (%d,%d)\n", cx, cz); + } + } } } } - app.DebugPrintf(" Pre-scan done: %d grid slots checked, %d validated monuments found. \n", candidates, validated); + app.DebugPrintf(" Pre-scan done: %d grid slots checked, %d validated monuments found.\n", candidates, validated); } bool OceanMonumentFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) @@ -120,7 +131,22 @@ bool OceanMonumentFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) StructureStart* OceanMonumentFeature::createStructureStart(int x, int z) { if (this->level == nullptr) return nullptr; - return new MonumentStart(level, random, x, z); + MonumentStart* start = new MonumentStart(level, random, x, z); + + + BoundingBox* bb = start->getBoundingBox(); + if (bb != nullptr) + { + app.DebugPrintf("MonumentStart BB: x0=%d y0=%d z0=%d x1=%d y1=%d z1=%d\n", + bb->x0, bb->y0, bb->z0, + bb->x1, bb->y1, bb->z1); + } + else + { + app.DebugPrintf("MonumentStart BB: NULL!\n"); + } + + return start; } OceanMonumentFeature::MonumentStart::MonumentStart() {} @@ -137,7 +163,6 @@ OceanMonumentFeature::MonumentStart::MonumentStart(Level* level, Random* random, int startX = chunkX * 16 + 8 - 29; int startZ = chunkZ * 16 + 8 - 29; - int facing = random->nextInt(4) + 2; OceanMonumentPieces::MonumentBuilding* building = @@ -145,4 +170,5 @@ OceanMonumentFeature::MonumentStart::MonumentStart(Level* level, Random* random, pieces.push_back(building); calculateBoundingBox(); -} \ No newline at end of file +} + diff --git a/Minecraft.World/OceanMonumentFeature.h b/Minecraft.World/OceanMonumentFeature.h index d24eebb8..d2b31101 100644 --- a/Minecraft.World/OceanMonumentFeature.h +++ b/Minecraft.World/OceanMonumentFeature.h @@ -33,7 +33,8 @@ public: void prescanNearby(int scanRadiusInGridCells = 8); -protected: + +public: virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat = false) override; virtual StructureStart* createStructureStart(int x, int z) override; diff --git a/Minecraft.World/OceanMonumentPieces.cpp b/Minecraft.World/OceanMonumentPieces.cpp index 4cb9f536..9c4e5f88 100644 --- a/Minecraft.World/OceanMonumentPieces.cpp +++ b/Minecraft.World/OceanMonumentPieces.cpp @@ -326,13 +326,13 @@ OceanMonumentPieces::MonumentBuilding::MonumentBuilding(Random* random, int x, i p->getBoundingBox()->move(baseX, minY, baseZ); - BoundingBox* wing1BB = new BoundingBox( + BoundingBox* wing1BB = BoundingBox::fromCorners( getWorldX(1, 1), getWorldY(1), getWorldZ(1, 1), getWorldX(23, 21), getWorldY(8), getWorldZ(23, 21)); - BoundingBox* wing2BB = new BoundingBox( + BoundingBox* wing2BB = BoundingBox::fromCorners( getWorldX(34, 1), getWorldY(1), getWorldZ(34, 1), getWorldX(56, 21), getWorldY(8), getWorldZ(56, 21)); - BoundingBox* penthouseBB = new BoundingBox( + BoundingBox* penthouseBB = BoundingBox::fromCorners( getWorldX(22, 22), getWorldY(13), getWorldZ(22, 22), getWorldX(35, 35), getWorldY(17), getWorldZ(35, 35)); @@ -1612,7 +1612,7 @@ bool OceanMonumentPieces::WingRoom::postProcess(Level* level, Random* random, Bo generateBox(level, chunkBB, 6, 0, 21, 7, 4, 21, Tile::prismarine_Id, blockPrismarineBricks(),Tile::prismarine_Id, blockPrismarineBricks(), false); generateBox(level, chunkBB, 15, 0, 21, 16, 4, 21, Tile::prismarine_Id, blockPrismarineBricks(),Tile::prismarine_Id, blockPrismarineBricks(), false); - spawnElderGuardian(level, chunkBB, 11, 2, 16); + spawnElderGuardian(level, chunkBB, 11, 5, 16); } else // wingType == 1 { diff --git a/Minecraft.World/OceanMonumentPieces.h b/Minecraft.World/OceanMonumentPieces.h index 9ebe9877..381eb4e0 100644 --- a/Minecraft.World/OceanMonumentPieces.h +++ b/Minecraft.World/OceanMonumentPieces.h @@ -144,7 +144,7 @@ public: class MonumentBuilding : public Piece { - private: + public: static const int ARRAY_SIZE = 75; // 5*5*3 RoomDefinition* entryRoom; // field_175845_o RoomDefinition* coreRoom; // field_175844_p diff --git a/Minecraft.World/Random.h b/Minecraft.World/Random.h index cfb6af26..3e176b27 100644 --- a/Minecraft.World/Random.h +++ b/Minecraft.World/Random.h @@ -20,4 +20,7 @@ public: float nextFloat(); int64_t nextLong(); bool nextBoolean(); + static int nextInt(Random* r, int n) { return r->nextInt(n); } + static bool nextBoolean(Random* r) { return r->nextBoolean(); } + static long long nextLong(Random* r) { return r->nextLong(); } }; \ No newline at end of file diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp index 1a24ad06..7cbf5aff 100644 --- a/Minecraft.World/RandomLevelSource.cpp +++ b/Minecraft.World/RandomLevelSource.cpp @@ -15,6 +15,8 @@ #ifdef __PS3__ #include "../Minecraft.Client/PS3/SPU_Tasks/PerlinNoise/PerlinNoiseJob.h" #include "C4JSpursJob.h" +#include +#include static PerlinNoise_DataIn g_lperlinNoise1_SPU __attribute__((__aligned__(16))); static PerlinNoise_DataIn g_lperlinNoise2_SPU __attribute__((__aligned__(16))); static PerlinNoise_DataIn g_perlinNoise1_SPU __attribute__((__aligned__(16))); @@ -841,23 +843,21 @@ wstring RandomLevelSource::gatherStats() vector *RandomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) { - Biome *biome = level->getBiome(x, z); - if (biome == nullptr) - { - return nullptr; - } - if (mobCategory == MobCategory::monster) - { - if (scatteredFeature->isSwamphut(x, y, z)) - { - return scatteredFeature->getSwamphutEnemies(); - } - if (oceanMonument->isInsideFeature(x, y, z)) - { + Biome *biome = level->getBiome(x, z); + if (biome == nullptr) + return nullptr; + + if (mobCategory == MobCategory::monster) + { + if (scatteredFeature->isSwamphut(x, y, z)) + return scatteredFeature->getSwamphutEnemies(); + + if (oceanMonument->isInsideBoundingFeature(x, y, z)) return oceanMonument->getMonumentEnemies(); - } - } - return biome->getMobs(mobCategory); + } + + + return biome->getMobs(mobCategory); } TilePos *RandomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) @@ -878,4 +878,4 @@ void RandomLevelSource::recreateLogicStructuresForChunk(int chunkX, int chunkZ) strongholdFeature->apply(this, level, chunkX, chunkZ, byteArray()); scatteredFeature->apply(this, level, chunkX, chunkZ, byteArray()); } -} \ No newline at end of file +} diff --git a/Minecraft.World/RandomLevelSource.h b/Minecraft.World/RandomLevelSource.h index 61a3584a..08a8a51b 100644 --- a/Minecraft.World/RandomLevelSource.h +++ b/Minecraft.World/RandomLevelSource.h @@ -2,6 +2,7 @@ using namespace std; #include "ChunkSource.h" +#include "OceanMonumentFeature.h" class ProgressListener; class LargeFeature; @@ -62,7 +63,7 @@ public: public: void buildSurfaces(int xOffs, int zOffs, byteArray blocks, byteArray blockData, BiomeArray biomes); -private: +public: LargeFeature *caveFeature; StrongholdFeature *strongholdFeature; VillageFeature *villageFeature; @@ -97,4 +98,5 @@ public: virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); virtual void recreateLogicStructuresForChunk(int chunkX, int chunkZ); + }; diff --git a/Minecraft.World/StructureFeature.cpp b/Minecraft.World/StructureFeature.cpp index 8de619ca..5f3c4b86 100644 --- a/Minecraft.World/StructureFeature.cpp +++ b/Minecraft.World/StructureFeature.cpp @@ -132,6 +132,9 @@ StructureStart *StructureFeature::getStructureAt(int cellX, int cellY, int cellZ */ list *pieces=pStructureStart->getPieces(); + + + for (auto& piece : *pieces) { if ( piece->getBoundingBox()->isInside(cellX, cellY, cellZ) ) @@ -154,7 +157,13 @@ bool StructureFeature::isInsideBoundingFeature(int cellX, int cellY, int cellZ) StructureStart *structureStart = it.second; if (structureStart->isValid()) { - return (structureStart->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)); + BoundingBox* bb = structureStart->getBoundingBox(); + if (cellX >= bb->x0 && cellX <= bb->x1 && + cellY >= bb->y0 && cellY <= bb->y1 && + cellZ >= bb->z0 && cellZ <= bb->z1) + { + return true; + } } } return false;