From 12285e5542e2bd6ffa8a1f88553ef1705108d9cb Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 23:21:35 +0200 Subject: [PATCH] feat: introduce nextRandom and cellCentre --- core/src/Villager.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 core/src/Villager.cpp diff --git a/core/src/Villager.cpp b/core/src/Villager.cpp new file mode 100644 index 0000000..d9bbef1 --- /dev/null +++ b/core/src/Villager.cpp @@ -0,0 +1,157 @@ +#include "vf/Villager.h" + +#include + +namespace vf { + +namespace { + +uint32_t nextRandom(uint32_t& state) { + state += 0x9e3779b9u; + uint32_t z = state; + z = (z ^ (z >> 16)) * 0x85ebca6bu; + z = (z ^ (z >> 13)) * 0xc2b2ae35u; + return z ^ (z >> 16); +} + +Vec3 cellCentre(const Vec3i& cell) { + return {static_cast(cell.x) + 0.5f, static_cast(cell.y), + static_cast(cell.z) + 0.5f}; +} + +int squaredDistance(const Vec3i& a, const Vec3i& b) { + int dx = a.x - b.x; + int dy = a.y - b.y; + int dz = a.z - b.z; + return dx * dx + dy * dy + dz * dz; +} + +const int levelThresholds[villagerMaxLevel] = {0, 10, 70, 150, 250}; + +const int horizontalOffsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + +// A point of interest is worked from the cell on top of it when it is solid, +// from its own cell when it is not, or from any walkable neighbour otherwise. +bool resolveStandCell(const World& world, const Vec3i& pos, Block block, const PathConfig& config, + Vec3i& outStand) { + if (!isSolid(block) && isStandable(world, pos, config)) { + outStand = pos; + return true; + } + Vec3i above{pos.x, pos.y + 1, pos.z}; + if (isStandable(world, above, config)) { + outStand = above; + return true; + } + for (int level = 0; level <= 1; ++level) { + for (const auto& offset : horizontalOffsets) { + Vec3i candidate{pos.x + offset[0], pos.y + level, pos.z + offset[1]}; + if (isStandable(world, candidate, config)) { + outStand = candidate; + return true; + } + } + } + outStand = above; + return false; +} + +bool poiBlock(Block block) { + return block == Block::Bed || block == Block::Bell || isWorkstationBlock(block); +} + +// Nothing under the feet any more, usually because the workstation the villager +// was standing on got mined out: drop it onto the first solid ground below and +// throw away the route it was walking. +bool settleVillager(const World& world, Villager& villager, const PathConfig& config) { + Vec3i cell = villager.cell(); + if (isStandable(world, cell, config)) return false; + for (int drop = 1; drop <= 8; ++drop) { + Vec3i below{cell.x, cell.y - drop, cell.z}; + if (!isStandable(world, below, config)) continue; + villager.body.position = cellCentre(below); + villager.path.clear(); + villager.pathCursor = 0; + villager.hasGoal = false; + return true; + } + return false; +} + +bool withinVillage(const Vec3i& cell, const Vec3i& center, int radius) { + return std::abs(cell.x - center.x) <= radius && std::abs(cell.z - center.z) <= radius && + std::abs(cell.y - center.y) <= radius; +} + +} // namespace + +const char* villagerProfessionName(VillagerProfession profession) { + switch (profession) { + case VillagerProfession::Unemployed: return "unemployed"; + case VillagerProfession::Nitwit: return "nitwit"; + case VillagerProfession::Farmer: return "farmer"; + case VillagerProfession::Fisherman: return "fisherman"; + case VillagerProfession::Shepherd: return "shepherd"; + case VillagerProfession::Fletcher: return "fletcher"; + case VillagerProfession::Librarian: return "librarian"; + case VillagerProfession::Cartographer: return "cartographer"; + case VillagerProfession::Cleric: return "cleric"; + case VillagerProfession::Armorer: return "armorer"; + case VillagerProfession::Weaponsmith: return "weaponsmith"; + case VillagerProfession::Toolsmith: return "toolsmith"; + case VillagerProfession::Butcher: return "butcher"; + case VillagerProfession::Leatherworker: return "leatherworker"; + case VillagerProfession::Mason: return "mason"; + case VillagerProfession::Count: break; + } + return "unknown"; +} + +bool professionEmployable(VillagerProfession profession) { + return profession != VillagerProfession::Unemployed && profession != VillagerProfession::Nitwit; +} + +Block workstationForProfession(VillagerProfession profession) { + switch (profession) { + case VillagerProfession::Farmer: return Block::Composter; + case VillagerProfession::Fisherman: return Block::Barrel; + case VillagerProfession::Shepherd: return Block::Loom; + case VillagerProfession::Fletcher: return Block::CraftingTable; + case VillagerProfession::Librarian: return Block::Lectern; + case VillagerProfession::Cartographer: return Block::CartographyTable; + case VillagerProfession::Cleric: return Block::BrewingStand; + case VillagerProfession::Armorer: return Block::Furnace; + case VillagerProfession::Weaponsmith: return Block::Grindstone; + case VillagerProfession::Toolsmith: return Block::SmithingTable; + case VillagerProfession::Butcher: return Block::FurnaceLit; + case VillagerProfession::Leatherworker: return Block::Cauldron; + case VillagerProfession::Mason: return Block::Stonecutter; + default: break; + } + return Block::Air; +} + +VillagerProfession professionForWorkstation(Block block) { + switch (block) { + case Block::Composter: return VillagerProfession::Farmer; + case Block::Barrel: return VillagerProfession::Fisherman; + case Block::Loom: return VillagerProfession::Shepherd; + case Block::CraftingTable: return VillagerProfession::Fletcher; + case Block::Lectern: return VillagerProfession::Librarian; + case Block::CartographyTable: return VillagerProfession::Cartographer; + case Block::BrewingStand: return VillagerProfession::Cleric; + case Block::Furnace: return VillagerProfession::Armorer; + case Block::Grindstone: return VillagerProfession::Weaponsmith; + case Block::SmithingTable: return VillagerProfession::Toolsmith; + case Block::FurnaceLit: return VillagerProfession::Butcher; + case Block::Cauldron: return VillagerProfession::Leatherworker; + case Block::Stonecutter: return VillagerProfession::Mason; + default: break; + } + return VillagerProfession::Unemployed; +} + +bool isWorkstationBlock(Block block) { + return professionForWorkstation(block) != VillagerProfession::Unemployed; +} +