feat: add assignVillagerWorkstation and assignVillagerBed to Villager

This commit is contained in:
dualfroz committed 2026-09-09 23:51:05 +02:00
1 parent 26f75858ef
commit f5b4e59834
1 file changed
+164
+164
View File
@@ -309,3 +309,167 @@ Villager makeVillager(int id, const Vec3& position, uint32_t seed) {
return villager; return villager;
} }
bool assignVillagerWorkstation(Villager& villager, PoiRegistry& pois) {
if (!villager.alive || villager.profession == VillagerProfession::Nitwit) return false;
if (villager.workstationIndex >= 0) return false;
int index = pois.nearestFreeWorkstation(villager.cell(), villager.profession);
if (index < 0) return false;
if (!pois.claim(index, villager.id)) return false;
villager.workstationIndex = index;
if (villager.profession == VillagerProfession::Unemployed)
villager.profession = pois.at(index).profession;
return true;
}
bool assignVillagerBed(Villager& villager, PoiRegistry& pois) {
if (!villager.alive || villager.bedIndex >= 0) return false;
int index = pois.nearestFreeBed(villager.cell());
if (index < 0) return false;
if (!pois.claim(index, villager.id)) return false;
villager.bedIndex = index;
return true;
}
void releaseVillagerPois(Villager& villager, PoiRegistry& pois) {
pois.release(villager.workstationIndex);
pois.release(villager.bedIndex);
villager.workstationIndex = -1;
villager.bedIndex = -1;
villager.targetPoiIndex = -1;
}
bool syncVillagerPois(Villager& villager, PoiRegistry& pois) {
bool professionChanged = false;
if (villager.workstationIndex >= 0) {
const WorkstationPoi& poi = pois.at(villager.workstationIndex);
if (!poi.valid || poi.ownerId != villager.id) {
villager.workstationIndex = -1;
if (villager.level <= 1) {
villager.profession = VillagerProfession::Unemployed;
villager.experience = 0;
professionChanged = true;
}
}
}
if (villager.bedIndex >= 0) {
const WorkstationPoi& bed = pois.at(villager.bedIndex);
if (!bed.valid || bed.ownerId != villager.id) villager.bedIndex = -1;
}
return professionChanged;
}
bool grantVillagerExperience(Villager& villager, int amount) {
if (amount <= 0) return false;
int before = villager.level;
villager.experience += amount;
villager.level = villagerLevelForExperience(villager.experience);
return villager.level > before;
}
void killVillager(Villager& villager, PoiRegistry& pois) {
villager.alive = false;
villager.health = 0.0f;
villager.state = VillagerState::Dead;
villager.activity = VillagerActivity::Idle;
villager.path.clear();
villager.pathCursor = 0;
villager.hasGoal = false;
releaseVillagerPois(villager, pois);
}
bool damageVillager(Villager& villager, PoiRegistry& pois, float amount) {
if (!villager.alive) return false;
villager.health -= amount;
if (villager.health > 0.0f) return false;
killVillager(villager, pois);
return true;
}
VillagerActivity VillagerSchedule::activityAt(double dayFraction) const {
if (dayFraction < wakeFraction) return VillagerActivity::Sleep;
if (dayFraction < workStart) return VillagerActivity::Idle;
if (dayFraction < meetStart) return VillagerActivity::Work;
if (dayFraction < meetEnd) return VillagerActivity::Meet;
if (dayFraction < workEnd) return VillagerActivity::Work;
if (dayFraction < sleepStart) return VillagerActivity::Idle;
return VillagerActivity::Sleep;
}
VillagerActivity chooseVillagerActivity(const Villager& villager, const VillagerSchedule& schedule,
const TimeOfDay& clock, bool villageThreatened) {
if (!villager.alive) return VillagerActivity::Idle;
if (villageThreatened) return VillagerActivity::Panic;
VillagerActivity planned = schedule.activityAt(clock.dayFraction());
switch (planned) {
case VillagerActivity::Work:
if (villager.profession == VillagerProfession::Nitwit) return VillagerActivity::Idle;
if (villager.workstationIndex < 0) return VillagerActivity::SeekWorkstation;
return VillagerActivity::Work;
case VillagerActivity::Sleep:
if (villager.bedIndex < 0) return VillagerActivity::Idle;
return VillagerActivity::Sleep;
default:
return planned;
}
}
bool villagerGoalFor(const Villager& villager, const PoiRegistry& pois, const Vec3i& villageCenter,
VillagerActivity activity, Vec3i& outGoal, int& outPoiIndex) {
outPoiIndex = -1;
switch (activity) {
case VillagerActivity::Work:
if (villager.workstationIndex < 0) return false;
outPoiIndex = villager.workstationIndex;
outGoal = pois.at(outPoiIndex).standCell;
return true;
case VillagerActivity::SeekWorkstation: {
int index = pois.nearestFreeWorkstation(villager.cell(), villager.profession);
if (index < 0) return false;
outPoiIndex = index;
outGoal = pois.at(index).standCell;
return true;
}
case VillagerActivity::Meet: {
int index = pois.bellIndex();
if (index >= 0 && pois.at(index).valid) {
outPoiIndex = index;
outGoal = pois.at(index).standCell;
} else {
outGoal = villageCenter;
}
return true;
}
case VillagerActivity::Sleep:
if (villager.bedIndex < 0) return false;
outPoiIndex = villager.bedIndex;
outGoal = pois.at(outPoiIndex).standCell;
return true;
case VillagerActivity::Panic:
if (villager.bedIndex >= 0 && pois.at(villager.bedIndex).valid) {
outPoiIndex = villager.bedIndex;
outGoal = pois.at(outPoiIndex).standCell;
} else {
outGoal = villageCenter;
}
return true;
default:
return false;
}
}
bool repathVillager(const World& world, Villager& villager, const Vec3i& goal,
const PathConfig& config) {
std::vector<Vec3i> route = findPath(world, villager.cell(), goal, config);
if (route.empty()) {
villager.hasGoal = false;
return false;
}
villager.path = std::move(route);
villager.pathCursor = 0;
villager.goalCell = goal;
villager.hasGoal = true;
villager.state = villager.path.size() == 1 ? VillagerState::AtTarget : VillagerState::Travelling;
return true;
}