156 lines
4.6 KiB
C++
156 lines
4.6 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "Behavior.h"
|
|
#include "BiomeGen.h"
|
|
#include "Block.h"
|
|
#include "Dimension.h"
|
|
#include "Lighting.h"
|
|
#include "Math.h"
|
|
#include "Mob.h"
|
|
#include "Pathfind.h"
|
|
#include "World.h"
|
|
|
|
namespace vf {
|
|
|
|
// Natural spawning: which mob may appear where, how many of them the loaded
|
|
// world can hold, how they arrive in packs and when they vanish again. Every
|
|
// decision is a pure function of the world, the rule table and a seed - the
|
|
// same inputs always produce the same spawn.
|
|
enum class SpawnCategory : uint8_t {
|
|
Hostile = 0,
|
|
Passive = 1,
|
|
Water = 2,
|
|
Ambient = 3,
|
|
Count,
|
|
};
|
|
|
|
constexpr int spawnCategoryCount = static_cast<int>(SpawnCategory::Count);
|
|
|
|
const char* spawnCategoryName(SpawnCategory category);
|
|
SpawnCategory categoryForSpecies(Species species);
|
|
|
|
// Why a spawn did not happen. A refusal always names its reason instead of
|
|
// collapsing into a bare false.
|
|
enum class SpawnDenial : uint8_t {
|
|
None = 0,
|
|
CategoryCapReached,
|
|
WrongDimension,
|
|
WrongBiome,
|
|
WrongTimeOfDay,
|
|
HeightOutOfRange,
|
|
UnsuitableGround,
|
|
NoHeadroom,
|
|
BlockLightTooHigh,
|
|
SkyLightTooHigh,
|
|
LightTooLow,
|
|
TooCloseToPlayer,
|
|
TooFarFromPlayer,
|
|
NoStandableCell,
|
|
NoRuleMatched,
|
|
Count,
|
|
};
|
|
|
|
constexpr int spawnDenialCount = static_cast<int>(SpawnDenial::Count);
|
|
|
|
const char* spawnDenialName(SpawnDenial denial);
|
|
|
|
// One species' conditions. An empty biome or ground list means "anything";
|
|
// each light bound is checked on its own channel, so a rule can demand open
|
|
// sky and still refuse a torch-lit cell.
|
|
struct SpawnRule {
|
|
Species species = Species::Zombie;
|
|
SpawnCategory category = SpawnCategory::Hostile;
|
|
DimensionId dimension = DimensionId::Overworld;
|
|
|
|
int minBlockLight = 0;
|
|
int maxBlockLight = 15;
|
|
int minSkyLight = 0;
|
|
int maxSkyLight = 15;
|
|
int minY = 0;
|
|
int maxY = 127;
|
|
|
|
std::vector<Block> groundBlocks;
|
|
int requiredHeadroom = 2;
|
|
std::vector<BiomeKind> biomes;
|
|
|
|
bool dayAllowed = true;
|
|
bool nightAllowed = true;
|
|
|
|
int packMin = 1;
|
|
int packMax = 4;
|
|
int packSpread = 3;
|
|
int weight = 10;
|
|
|
|
float minPlayerDistance = 8.0f;
|
|
float maxPlayerDistance = 96.0f;
|
|
|
|
bool allowsBiome(BiomeKind biome) const;
|
|
bool allowsGround(Block block) const;
|
|
};
|
|
|
|
// The stock table covers every species Mob.h defines: three hostile and three
|
|
// passive. Water and Ambient exist as first-class categories with their own
|
|
// caps and are filled by whatever rules a caller adds.
|
|
std::vector<SpawnRule> defaultSpawnRules();
|
|
const SpawnRule* findSpawnRule(const std::vector<SpawnRule>& rules, Species species);
|
|
|
|
// Population ceiling per category, scaled by how much world is actually
|
|
// loaded: the same base numbers give a small cap for a handful of chunks and
|
|
// the full cap for a fully loaded view distance.
|
|
struct MobCap {
|
|
std::array<int, spawnCategoryCount> baseCap{{70, 10, 5, 15}};
|
|
int chunksPerCapUnit = 289;
|
|
|
|
int capFor(SpawnCategory category, int loadedChunks) const;
|
|
int baseCapFor(SpawnCategory category) const;
|
|
};
|
|
|
|
// Everything a spawn check reads besides the rule itself. `light` is the baked
|
|
// field from Lighting.h, so block light and sky light come from the same
|
|
// source the renderer uses.
|
|
struct SpawnEnvironment {
|
|
const World* world = nullptr;
|
|
const LightField* light = nullptr;
|
|
const BiomeGen* biomes = nullptr;
|
|
|
|
DimensionId dimension = DimensionId::Overworld;
|
|
BiomeKind fallbackBiome = BiomeKind::Plains;
|
|
bool daytime = true;
|
|
|
|
Vec3 playerPosition;
|
|
bool playerPresent = true;
|
|
|
|
int loadedChunks = 289;
|
|
int topY = 96;
|
|
int bottomY = 1;
|
|
PathConfig path;
|
|
|
|
std::array<int, spawnCategoryCount> population{{0, 0, 0, 0}};
|
|
|
|
int populationOf(SpawnCategory category) const;
|
|
void addPopulation(SpawnCategory category, int amount);
|
|
BiomeKind biomeAt(int gx, int gz) const;
|
|
int blockLightAt(const Vec3i& cell) const;
|
|
// Sky light as the spawn rules see it: the baked value during the day,
|
|
// dimmed by the night penalty after dusk, which is what lets hostiles
|
|
// appear under an open sky only once it is dark.
|
|
int skyLightAt(const Vec3i& cell) const;
|
|
};
|
|
|
|
// The plan for one spawn attempt: which rule won the weighted draw, which cell
|
|
// the seed picked and how large the pack would be. Nothing here has been
|
|
// validated yet - that is what attemptSpawn does with it.
|
|
struct SpawnTrial {
|
|
Vec3i cell;
|
|
Species species = Species::Zombie;
|
|
SpawnCategory category = SpawnCategory::Hostile;
|
|
int packSize = 0;
|
|
bool ruleFound = false;
|
|
bool cellFound = false;
|
|
};
|
|
|