feat: add villagerExperienceForLevel and villagerLevelForExperience

This commit is contained in:
dualfroz committed 2026-09-09 23:39:10 +02:00
1 parent 12285e5542
commit 26f75858ef
1 file changed
+154
+154
View File
@@ -155,3 +155,157 @@ bool isWorkstationBlock(Block block) {
return professionForWorkstation(block) != VillagerProfession::Unemployed; return professionForWorkstation(block) != VillagerProfession::Unemployed;
} }
int villagerExperienceForLevel(int level) {
if (level <= 1) return 0;
if (level >= villagerMaxLevel) return levelThresholds[villagerMaxLevel - 1];
return levelThresholds[level - 1];
}
int villagerLevelForExperience(int experience) {
int level = 1;
for (int candidate = 2; candidate <= villagerMaxLevel; ++candidate)
if (experience >= levelThresholds[candidate - 1]) level = candidate;
return level;
}
const char* villagerActivityName(VillagerActivity activity) {
switch (activity) {
case VillagerActivity::Idle: return "idle";
case VillagerActivity::SeekWorkstation: return "seekWorkstation";
case VillagerActivity::Work: return "work";
case VillagerActivity::Meet: return "meet";
case VillagerActivity::Sleep: return "sleep";
case VillagerActivity::Panic: return "panic";
case VillagerActivity::Count: break;
}
return "unknown";
}
void PoiRegistry::scan(const World& world, const Vec3i& center, int radius, int verticalRadius,
const PathConfig& config) {
clear();
for (int x = center.x - radius; x <= center.x + radius; ++x) {
for (int y = center.y - verticalRadius; y <= center.y + verticalRadius; ++y) {
for (int z = center.z - radius; z <= center.z + radius; ++z) {
Block block = world.blockAt(x, y, z);
if (!poiBlock(block)) continue;
WorkstationPoi poi;
poi.pos = {x, y, z};
poi.block = block;
poi.profession = professionForWorkstation(block);
poi.valid = true;
poi.reachable = resolveStandCell(world, poi.pos, block, config, poi.standCell);
if (block == Block::Bell && bell < 0) bell = static_cast<int>(pois.size());
pois.push_back(poi);
}
}
}
}
int PoiRegistry::indexAt(const Vec3i& pos) const {
for (std::size_t i = 0; i < pois.size(); ++i)
if (pois[i].valid && pois[i].pos == pos) return static_cast<int>(i);
return -1;
}
int PoiRegistry::workstationCount() const { return countPois(false, false); }
int PoiRegistry::bedCount() const { return countPois(true, false); }
int PoiRegistry::freeWorkstationCount() const { return countPois(false, true); }
int PoiRegistry::freeBedCount() const { return countPois(true, true); }
int PoiRegistry::countPois(bool beds, bool onlyFree) const {
int count = 0;
for (const WorkstationPoi& poi : pois) {
if (beds ? !poi.isBed() : !poi.isWorkstation()) continue;
if (onlyFree ? !poi.free() : !poi.valid) continue;
++count;
}
return count;
}
int PoiRegistry::nearestFree(const Vec3i& from, bool beds, VillagerProfession wanted) const {
int best = -1;
int bestDistance = 0;
for (std::size_t i = 0; i < pois.size(); ++i) {
const WorkstationPoi& poi = pois[i];
if (!poi.free() || !poi.reachable) continue;
if (beds ? !poi.isBed() : !poi.isWorkstation()) continue;
if (!beds && wanted != VillagerProfession::Unemployed && poi.profession != wanted) continue;
int distance = squaredDistance(poi.standCell, from);
if (best < 0 || distance < bestDistance) {
best = static_cast<int>(i);
bestDistance = distance;
}
}
return best;
}
int PoiRegistry::nearestFreeWorkstation(const Vec3i& from, VillagerProfession wanted) const {
return nearestFree(from, false, wanted);
}
int PoiRegistry::nearestFreeBed(const Vec3i& from) const {
return nearestFree(from, true, VillagerProfession::Unemployed);
}
bool PoiRegistry::claim(int index, int villagerId) {
if (index < 0 || index >= size()) return false;
WorkstationPoi& poi = pois[index];
if (!poi.valid || poi.ownerId >= 0) return false;
poi.ownerId = villagerId;
return true;
}
void PoiRegistry::release(int index) {
if (index < 0 || index >= size()) return;
pois[index].ownerId = -1;
}
void PoiRegistry::releaseOwner(int villagerId) {
for (WorkstationPoi& poi : pois)
if (poi.ownerId == villagerId) poi.ownerId = -1;
}
int PoiRegistry::revalidate(const World& world, const PathConfig& config) {
int lost = 0;
for (std::size_t i = 0; i < pois.size(); ++i) {
WorkstationPoi& poi = pois[i];
if (!poi.valid) continue;
if (world.blockAt(poi.pos.x, poi.pos.y, poi.pos.z) != poi.block) {
poi.valid = false;
poi.ownerId = -1;
if (bell == static_cast<int>(i)) bell = -1;
++lost;
continue;
}
poi.reachable = resolveStandCell(world, poi.pos, poi.block, config, poi.standCell);
}
return lost;
}
void PoiRegistry::clear() {
pois.clear();
bell = -1;
}
Vec3i Villager::cell() const {
return {static_cast<int>(std::floor(body.position.x)),
static_cast<int>(std::floor(body.position.y)),
static_cast<int>(std::floor(body.position.z))};
}
Villager makeVillager(int id, const Vec3& position, uint32_t seed) {
Villager villager;
villager.id = id;
villager.body.position = position;
villager.body.width = 0.6f;
villager.body.height = 1.95f;
villager.rng = seed ^ (static_cast<uint32_t>(id) * 0x27d4eb2du);
uint32_t draw = nextRandom(villager.rng);
if (draw % 8u == 0u) villager.profession = VillagerProfession::Nitwit;
return villager;
}