From f5b4e59834c66c65f5f2c063b2b6000faa1db48f Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 23:51:05 +0200 Subject: [PATCH] feat: add assignVillagerWorkstation and assignVillagerBed to Villager --- core/src/Villager.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/core/src/Villager.cpp b/core/src/Villager.cpp index 078dd77..225b69e 100644 --- a/core/src/Villager.cpp +++ b/core/src/Villager.cpp @@ -309,3 +309,167 @@ Villager makeVillager(int id, const Vec3& position, uint32_t seed) { 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 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; +} +