feat: add villagerExperienceForLevel and villagerLevelForExperience
This commit is contained in:
1 parent
12285e5542
commit
26f75858ef
1 file changed
+154
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user