feat(vf): add SpawnResult and DespawnPolicy to Spawning
This commit is contained in:
1 parent
ad742d3423
commit
66cd109fe1
1 file changed
+141
@@ -153,3 +153,144 @@ struct SpawnTrial {
|
|||||||
bool cellFound = false;
|
bool cellFound = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SpawnResult {
|
||||||
|
bool spawned = false;
|
||||||
|
SpawnDenial denial = SpawnDenial::NoRuleMatched;
|
||||||
|
Species species = Species::Zombie;
|
||||||
|
SpawnCategory category = SpawnCategory::Hostile;
|
||||||
|
BehaviorRole role = BehaviorRole::HostileHunter;
|
||||||
|
Vec3i origin;
|
||||||
|
std::vector<Vec3> positions;
|
||||||
|
|
||||||
|
int packSize() const { return static_cast<int>(positions.size()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deterministic 64-bit mix. Same seed, same cell, same salt, same number.
|
||||||
|
uint64_t spawnHash(uint64_t seed, int x, int y, int z, int salt);
|
||||||
|
|
||||||
|
// Standable, has the headroom the rule wants and nothing solid in the way.
|
||||||
|
bool hasSpawnRoom(const World& world, const Vec3i& cell, int headroom);
|
||||||
|
|
||||||
|
// The full per-cell verdict, checked in a fixed order so a caller can tell
|
||||||
|
// exactly which condition failed.
|
||||||
|
SpawnDenial checkSpawnRule(const SpawnRule& rule, const SpawnEnvironment& env, const Vec3i& cell,
|
||||||
|
const MobCap& cap);
|
||||||
|
|
||||||
|
// Picks the highest standable cell in one column of the chunk, chosen from the
|
||||||
|
// seed rather than scanned in order.
|
||||||
|
bool pickSpawnCell(const SpawnEnvironment& env, const Vec3i& chunkColumn, uint64_t seed,
|
||||||
|
int attemptIndex, Vec3i& outCell);
|
||||||
|
|
||||||
|
// Spreads a pack around an accepted origin. Members that land somewhere the
|
||||||
|
// rule rejects are simply left out; the origin is always member zero.
|
||||||
|
std::vector<Vec3> spreadPack(const SpawnRule& rule, const SpawnEnvironment& env,
|
||||||
|
const Vec3i& origin, int packSize, uint32_t& rng);
|
||||||
|
|
||||||
|
enum class DespawnBand : uint8_t {
|
||||||
|
Protected = 0,
|
||||||
|
Random = 1,
|
||||||
|
Immediate = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Distance bands around the player. Beyond `instantDistance` a mob is gone the
|
||||||
|
// moment it is checked; between the two distances it rolls every tick; closer
|
||||||
|
// than `randomDistance` it never despawns on its own.
|
||||||
|
struct DespawnPolicy {
|
||||||
|
float instantDistance = 128.0f;
|
||||||
|
float randomDistance = 32.0f;
|
||||||
|
int randomChanceDenominator = 800;
|
||||||
|
bool despawnNamed = false;
|
||||||
|
bool despawnLeashed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DespawnCandidate {
|
||||||
|
Vec3 position;
|
||||||
|
bool named = false;
|
||||||
|
bool leashed = false;
|
||||||
|
bool persistent = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
DespawnBand despawnBandFor(const DespawnPolicy& policy, const Vec3& position,
|
||||||
|
const Vec3& playerPosition);
|
||||||
|
bool despawnCandidateIsProtected(const DespawnPolicy& policy, const DespawnCandidate& candidate);
|
||||||
|
bool shouldDespawn(const DespawnPolicy& policy, const DespawnCandidate& candidate,
|
||||||
|
const Vec3& playerPosition, uint32_t& rng);
|
||||||
|
|
||||||
|
// A monster spawner block: counts down, refuses to fire without a player in
|
||||||
|
// range or with too many of its own kind already nearby, then picks a fresh
|
||||||
|
// delay from its range.
|
||||||
|
struct SpawnerBlockState {
|
||||||
|
Species species = Species::Zombie;
|
||||||
|
Vec3i position;
|
||||||
|
int spawnCount = 4;
|
||||||
|
int spawnRange = 4;
|
||||||
|
int nearbyLimit = 6;
|
||||||
|
int requiredHeadroom = 2;
|
||||||
|
float activationRange = 16.0f;
|
||||||
|
int minDelay = 200;
|
||||||
|
int maxDelay = 800;
|
||||||
|
int delay = 20;
|
||||||
|
int totalSpawned = 0;
|
||||||
|
int cyclesFired = 0;
|
||||||
|
uint32_t rng = 0x1f123bb5u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpawnerTickResult {
|
||||||
|
bool playerInRange = false;
|
||||||
|
bool fired = false;
|
||||||
|
SpawnDenial denial = SpawnDenial::None;
|
||||||
|
std::vector<Vec3> positions;
|
||||||
|
int delayAfter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpawnerTickResult tickSpawnerBlock(SpawnerBlockState& spawner, const SpawnEnvironment& env,
|
||||||
|
int nearbyOfKind);
|
||||||
|
|
||||||
|
// Runs spawn cycles over loaded chunk columns and keeps a per-reason tally of
|
||||||
|
// everything it refused, so a world that never spawns anything can be
|
||||||
|
// diagnosed instead of guessed at.
|
||||||
|
class Spawner {
|
||||||
|
public:
|
||||||
|
Spawner();
|
||||||
|
explicit Spawner(std::vector<SpawnRule> ruleTable, const MobCap& caps = {});
|
||||||
|
|
||||||
|
const std::vector<SpawnRule>& rules() const { return ruleTable; }
|
||||||
|
const MobCap& caps() const { return capTable; }
|
||||||
|
|
||||||
|
void setAttemptsPerColumn(int attempts) { attemptsPerColumnCount = attempts; }
|
||||||
|
int attemptsPerColumn() const { return attemptsPerColumnCount; }
|
||||||
|
|
||||||
|
// Weighted pick among the rules of one category that fit the dimension.
|
||||||
|
const SpawnRule* pickRule(const SpawnEnvironment& env, SpawnCategory category,
|
||||||
|
uint64_t seed) const;
|
||||||
|
|
||||||
|
// Draws a rule and a cell without touching the world state.
|
||||||
|
SpawnTrial planSpawn(const SpawnEnvironment& env, SpawnCategory category,
|
||||||
|
const Vec3i& chunkColumn, uint64_t seed, int attemptIndex) const;
|
||||||
|
|
||||||
|
SpawnResult attemptSpawn(const SpawnEnvironment& env, const SpawnRule& rule,
|
||||||
|
const SpawnTrial& trial, uint64_t seed);
|
||||||
|
|
||||||
|
SpawnResult runColumn(SpawnEnvironment& env, SpawnCategory category, const Vec3i& chunkColumn,
|
||||||
|
uint64_t seed, int attemptIndex);
|
||||||
|
|
||||||
|
std::vector<SpawnResult> runCycle(SpawnEnvironment& env, SpawnCategory category,
|
||||||
|
const std::vector<Vec3i>& chunkColumns, uint64_t seed);
|
||||||
|
|
||||||
|
int denialCount(SpawnDenial denial) const;
|
||||||
|
int spawnedCount() const { return spawned; }
|
||||||
|
void resetStats();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<SpawnRule> ruleTable;
|
||||||
|
MobCap capTable;
|
||||||
|
int attemptsPerColumnCount = 3;
|
||||||
|
int spawned = 0;
|
||||||
|
std::array<int, spawnDenialCount> denials{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hands a finished spawn straight to the behaviour layer: every member of the
|
||||||
|
// pack becomes an agent already carrying the tree role for its species.
|
||||||
|
std::vector<BehaviorAgent> agentsFromSpawn(const SpawnResult& result, uint32_t seed);
|
||||||
|
|
||||||
|
} // namespace vf
|
||||||
Reference in new issue
Block a user