feat: add spawnHash and hasSpawnRoom to Spawning
This commit is contained in:
1 parent
ebcace7837
commit
be90af709b
1 file changed
+154
@@ -158,3 +158,157 @@ const SpawnRule* findSpawnRule(const std::vector<SpawnRule>& rules, Species spec
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int MobCap::baseCapFor(SpawnCategory category) const {
|
||||
return baseCap[static_cast<std::size_t>(category)];
|
||||
}
|
||||
|
||||
int MobCap::capFor(SpawnCategory category, int loadedChunks) const {
|
||||
if (loadedChunks <= 0) return 0;
|
||||
long long scaled = static_cast<long long>(baseCapFor(category)) * loadedChunks;
|
||||
return static_cast<int>(scaled / chunksPerCapUnit);
|
||||
}
|
||||
|
||||
int SpawnEnvironment::populationOf(SpawnCategory category) const {
|
||||
return population[static_cast<std::size_t>(category)];
|
||||
}
|
||||
|
||||
void SpawnEnvironment::addPopulation(SpawnCategory category, int amount) {
|
||||
population[static_cast<std::size_t>(category)] += amount;
|
||||
}
|
||||
|
||||
BiomeKind SpawnEnvironment::biomeAt(int gx, int gz) const {
|
||||
return biomes ? biomes->biomeAt(gx, gz) : fallbackBiome;
|
||||
}
|
||||
|
||||
int SpawnEnvironment::blockLightAt(const Vec3i& cell) const {
|
||||
return static_cast<int>(light->blockAtWorld(cell.x, cell.y, cell.z));
|
||||
}
|
||||
|
||||
int SpawnEnvironment::skyLightAt(const Vec3i& cell) const {
|
||||
int raw = static_cast<int>(light->sunAtWorld(cell.x, cell.y, cell.z));
|
||||
if (daytime) return raw;
|
||||
return std::max(0, raw - nightSkyLightPenalty);
|
||||
}
|
||||
|
||||
uint64_t spawnHash(uint64_t seed, int x, int y, int z, int salt) {
|
||||
uint64_t mixed = seed;
|
||||
mixed ^= static_cast<uint64_t>(static_cast<uint32_t>(x)) * 0x9e3779b97f4a7c15ull;
|
||||
mixed ^= static_cast<uint64_t>(static_cast<uint32_t>(y)) * 0xc2b2ae3d27d4eb4full;
|
||||
mixed ^= static_cast<uint64_t>(static_cast<uint32_t>(z)) * 0x165667b19e3779f9ull;
|
||||
mixed ^= static_cast<uint64_t>(static_cast<uint32_t>(salt)) * 0x27d4eb2f165667c5ull;
|
||||
mixed ^= mixed >> 33;
|
||||
mixed *= 0xff51afd7ed558ccdull;
|
||||
mixed ^= mixed >> 33;
|
||||
mixed *= 0xc4ceb9fe1a85ec53ull;
|
||||
mixed ^= mixed >> 33;
|
||||
return mixed;
|
||||
}
|
||||
|
||||
bool hasSpawnRoom(const World& world, const Vec3i& cell, int headroom) {
|
||||
for (int step = 0; step < headroom; ++step) {
|
||||
if (world.solidAt(cell.x, cell.y + step, cell.z)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SpawnDenial checkSpawnRule(const SpawnRule& rule, const SpawnEnvironment& env, const Vec3i& cell,
|
||||
const MobCap& cap) {
|
||||
if (env.populationOf(rule.category) >= cap.capFor(rule.category, env.loadedChunks))
|
||||
return SpawnDenial::CategoryCapReached;
|
||||
if (rule.dimension != env.dimension) return SpawnDenial::WrongDimension;
|
||||
if (cell.y < rule.minY || cell.y > rule.maxY) return SpawnDenial::HeightOutOfRange;
|
||||
if (!rule.allowsBiome(env.biomeAt(cell.x, cell.z))) return SpawnDenial::WrongBiome;
|
||||
if (env.daytime && !rule.dayAllowed) return SpawnDenial::WrongTimeOfDay;
|
||||
if (!env.daytime && !rule.nightAllowed) return SpawnDenial::WrongTimeOfDay;
|
||||
|
||||
Block ground = env.world->blockAt(cell.x, cell.y - 1, cell.z);
|
||||
if (!isSolid(ground) || !rule.allowsGround(ground)) return SpawnDenial::UnsuitableGround;
|
||||
if (!hasSpawnRoom(*env.world, cell, rule.requiredHeadroom)) return SpawnDenial::NoHeadroom;
|
||||
|
||||
int blockLight = env.blockLightAt(cell);
|
||||
int skyLight = env.skyLightAt(cell);
|
||||
if (blockLight > rule.maxBlockLight) return SpawnDenial::BlockLightTooHigh;
|
||||
if (skyLight > rule.maxSkyLight) return SpawnDenial::SkyLightTooHigh;
|
||||
if (blockLight < rule.minBlockLight) return SpawnDenial::LightTooLow;
|
||||
if (skyLight < rule.minSkyLight) return SpawnDenial::LightTooLow;
|
||||
|
||||
if (env.playerPresent) {
|
||||
Vec3 spot = behaviorCellCenter(cell);
|
||||
float distance = straightDistance(spot, env.playerPosition);
|
||||
if (distance < rule.minPlayerDistance) return SpawnDenial::TooCloseToPlayer;
|
||||
if (distance > rule.maxPlayerDistance) return SpawnDenial::TooFarFromPlayer;
|
||||
}
|
||||
return SpawnDenial::None;
|
||||
}
|
||||
|
||||
bool pickSpawnCell(const SpawnEnvironment& env, const Vec3i& chunkColumn, uint64_t seed,
|
||||
int attemptIndex, Vec3i& outCell) {
|
||||
uint64_t roll = spawnHash(seed, chunkColumn.x, attemptIndex, chunkColumn.z, 0x5a);
|
||||
int localX = static_cast<int>(roll % static_cast<uint64_t>(chunkSize));
|
||||
int localZ = static_cast<int>((roll >> 8) % static_cast<uint64_t>(chunkSize));
|
||||
int gx = chunkColumn.x * chunkSize + localX;
|
||||
int gz = chunkColumn.z * chunkSize + localZ;
|
||||
|
||||
for (int y = env.topY; y >= env.bottomY; --y) {
|
||||
Vec3i probe{gx, y, gz};
|
||||
if (!isStandable(*env.world, probe, env.path)) continue;
|
||||
outCell = probe;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Vec3> spreadPack(const SpawnRule& rule, const SpawnEnvironment& env,
|
||||
const Vec3i& origin, int packSize, uint32_t& rng) {
|
||||
std::vector<Vec3> members;
|
||||
members.push_back(behaviorCellCenter(origin));
|
||||
|
||||
for (int index = 1; index < packSize; ++index) {
|
||||
int dx = behaviorRandomRange(rng, -rule.packSpread, rule.packSpread);
|
||||
int dz = behaviorRandomRange(rng, -rule.packSpread, rule.packSpread);
|
||||
if (dx == 0 && dz == 0) continue;
|
||||
|
||||
bool placed = false;
|
||||
for (int dy = 0; dy <= 2 && !placed; ++dy) {
|
||||
for (int sign = 1; sign >= -1 && !placed; sign -= 2) {
|
||||
Vec3i probe{origin.x + dx, origin.y + dy * sign, origin.z + dz};
|
||||
if (!groundAndRoomFit(rule, env, probe)) continue;
|
||||
if (!lightFits(rule, env, probe)) continue;
|
||||
members.push_back(behaviorCellCenter(probe));
|
||||
placed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
DespawnBand despawnBandFor(const DespawnPolicy& policy, const Vec3& position,
|
||||
const Vec3& playerPosition) {
|
||||
float distance = straightDistance(position, playerPosition);
|
||||
if (distance > policy.instantDistance) return DespawnBand::Immediate;
|
||||
if (distance > policy.randomDistance) return DespawnBand::Random;
|
||||
return DespawnBand::Protected;
|
||||
}
|
||||
|
||||
bool despawnCandidateIsProtected(const DespawnPolicy& policy, const DespawnCandidate& candidate) {
|
||||
if (candidate.persistent) return true;
|
||||
if (candidate.named && !policy.despawnNamed) return true;
|
||||
if (candidate.leashed && !policy.despawnLeashed) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool shouldDespawn(const DespawnPolicy& policy, const DespawnCandidate& candidate,
|
||||
const Vec3& playerPosition, uint32_t& rng) {
|
||||
if (despawnCandidateIsProtected(policy, candidate)) return false;
|
||||
DespawnBand band = despawnBandFor(policy, candidate.position, playerPosition);
|
||||
if (band == DespawnBand::Immediate) return true;
|
||||
if (band == DespawnBand::Protected) return false;
|
||||
uint32_t denominator = static_cast<uint32_t>(std::max(1, policy.randomChanceDenominator));
|
||||
return behaviorNextRandom(rng) % denominator == 0u;
|
||||
}
|
||||
|
||||
SpawnerTickResult tickSpawnerBlock(SpawnerBlockState& spawner, const SpawnEnvironment& env,
|
||||
int nearbyOfKind) {
|
||||
SpawnerTickResult outcome;
|
||||
outcome.delayAfter = spawner.delay;
|
||||
|
||||
Reference in new issue
Block a user