diff --git a/core/src/Spawning.cpp b/core/src/Spawning.cpp new file mode 100644 index 0000000..966592a --- /dev/null +++ b/core/src/Spawning.cpp @@ -0,0 +1,160 @@ +#include "vf/Spawning.h" + +#include +#include + +#include "vf/Effect.h" + +namespace vf { + +namespace { + +constexpr int nightSkyLightPenalty = 11; + +float straightDistance(const Vec3& a, const Vec3& b) { + Vec3 delta = a - b; + return length(delta); +} + +bool groundAndRoomFit(const SpawnRule& rule, const SpawnEnvironment& env, const Vec3i& cell) { + Block ground = env.world->blockAt(cell.x, cell.y - 1, cell.z); + if (!isSolid(ground)) return false; + if (!rule.allowsGround(ground)) return false; + return hasSpawnRoom(*env.world, cell, rule.requiredHeadroom); +} + +bool lightFits(const SpawnRule& rule, const SpawnEnvironment& env, const Vec3i& cell) { + int blockLight = env.blockLightAt(cell); + int skyLight = env.skyLightAt(cell); + if (blockLight > rule.maxBlockLight) return false; + if (skyLight > rule.maxSkyLight) return false; + if (blockLight < rule.minBlockLight) return false; + if (skyLight < rule.minSkyLight) return false; + return true; +} + +SpawnRule makeHostileRule(Species species, int packMin, int packMax) { + SpawnRule rule; + rule.species = species; + rule.category = SpawnCategory::Hostile; + rule.dimension = DimensionId::Overworld; + rule.maxBlockLight = 7; + rule.maxSkyLight = 7; + rule.minY = 1; + rule.maxY = 127; + rule.requiredHeadroom = 2; + rule.dayAllowed = false; + rule.nightAllowed = true; + rule.packMin = packMin; + rule.packMax = packMax; + rule.packSpread = 3; + rule.weight = 10; + rule.minPlayerDistance = 24.0f; + rule.maxPlayerDistance = 128.0f; + return rule; +} + +SpawnRule makePassiveRule(Species species, int packMin, int packMax, + std::vector biomeList) { + SpawnRule rule; + rule.species = species; + rule.category = SpawnCategory::Passive; + rule.dimension = DimensionId::Overworld; + rule.minBlockLight = 0; + rule.minSkyLight = 9; + rule.maxBlockLight = 15; + rule.maxSkyLight = 15; + rule.minY = 48; + rule.maxY = 127; + rule.groundBlocks = {Block::Grass}; + rule.requiredHeadroom = 2; + rule.biomes = std::move(biomeList); + rule.dayAllowed = true; + rule.nightAllowed = true; + rule.packMin = packMin; + rule.packMax = packMax; + rule.packSpread = 4; + rule.weight = 12; + rule.minPlayerDistance = 12.0f; + rule.maxPlayerDistance = 96.0f; + return rule; +} + +} // namespace + +const char* spawnCategoryName(SpawnCategory category) { + switch (category) { + case SpawnCategory::Hostile: return "hostile"; + case SpawnCategory::Passive: return "passive"; + case SpawnCategory::Water: return "water"; + case SpawnCategory::Ambient: return "ambient"; + case SpawnCategory::Count: break; + } + return "unknown"; +} + +SpawnCategory categoryForSpecies(Species species) { + return isHostile(species) ? SpawnCategory::Hostile : SpawnCategory::Passive; +} + +const char* spawnDenialName(SpawnDenial denial) { + switch (denial) { + case SpawnDenial::None: return "none"; + case SpawnDenial::CategoryCapReached: return "categoryCapReached"; + case SpawnDenial::WrongDimension: return "wrongDimension"; + case SpawnDenial::WrongBiome: return "wrongBiome"; + case SpawnDenial::WrongTimeOfDay: return "wrongTimeOfDay"; + case SpawnDenial::HeightOutOfRange: return "heightOutOfRange"; + case SpawnDenial::UnsuitableGround: return "unsuitableGround"; + case SpawnDenial::NoHeadroom: return "noHeadroom"; + case SpawnDenial::BlockLightTooHigh: return "blockLightTooHigh"; + case SpawnDenial::SkyLightTooHigh: return "skyLightTooHigh"; + case SpawnDenial::LightTooLow: return "lightTooLow"; + case SpawnDenial::TooCloseToPlayer: return "tooCloseToPlayer"; + case SpawnDenial::TooFarFromPlayer: return "tooFarFromPlayer"; + case SpawnDenial::NoStandableCell: return "noStandableCell"; + case SpawnDenial::NoRuleMatched: return "noRuleMatched"; + case SpawnDenial::Count: break; + } + return "unknown"; +} + +bool SpawnRule::allowsBiome(BiomeKind biome) const { + if (biomes.empty()) return true; + return std::find(biomes.begin(), biomes.end(), biome) != biomes.end(); +} + +bool SpawnRule::allowsGround(Block block) const { + if (groundBlocks.empty()) return isSolid(block); + return std::find(groundBlocks.begin(), groundBlocks.end(), block) != groundBlocks.end(); +} + +std::vector defaultSpawnRules() { + std::vector rules; + rules.push_back(makeHostileRule(Species::Zombie, 2, 4)); + rules.push_back(makeHostileRule(Species::Skeleton, 1, 3)); + + SpawnRule creeper = makeHostileRule(Species::Creeper, 1, 2); + creeper.biomes = {BiomeKind::Plains, BiomeKind::Forest, BiomeKind::Taiga, + BiomeKind::Tundra, BiomeKind::Swamp, BiomeKind::Jungle, + BiomeKind::Savanna, BiomeKind::Mountains, BiomeKind::Desert}; + rules.push_back(creeper); + + rules.push_back(makePassiveRule( + Species::Sheep, 2, 4, + {BiomeKind::Plains, BiomeKind::Forest, BiomeKind::Savanna, BiomeKind::Taiga})); + rules.push_back(makePassiveRule( + Species::Cow, 2, 4, + {BiomeKind::Plains, BiomeKind::Forest, BiomeKind::Savanna, BiomeKind::Taiga})); + rules.push_back(makePassiveRule( + Species::Pig, 1, 3, {BiomeKind::Plains, BiomeKind::Forest, BiomeKind::Swamp})); + return rules; +} + +const SpawnRule* findSpawnRule(const std::vector& rules, Species species) { + for (const SpawnRule& rule : rules) { + if (rule.species == species) return &rule; + } + return nullptr; +} +