diff --git a/tests/testVillager.cpp b/tests/testVillager.cpp index 35f7d03..820e5ac 100644 --- a/tests/testVillager.cpp +++ b/tests/testVillager.cpp @@ -156,3 +156,162 @@ VF_TEST(villagerWalksTheWholeDayFromBedToWorkToTheBellAndBackToBed) { VillagerSchedule schedule; TimeOfDay clock(0); + std::vector seen; + Vec3i bellStand = pois.at(pois.bellIndex()).standCell; + bool reachedBellAtNoon = false; + + for (WorldTick step = 0; step < 24000; step += 20) { + clock.setTick(step); + runTicks(world, villager, pois, clock, schedule, false, 20); + if (seen.empty() || seen.back() != villager.activity) seen.push_back(villager.activity); + if (villager.activity == VillagerActivity::Meet && + villager.state == VillagerState::AtTarget) { + reachedBellAtNoon = true; + CHECK(horizontalGap(villager.body.position, bellStand) < 0.01f); + } + } + + CHECK(reachedBellAtNoon); + CHECK(seen.size() >= 7); + CHECK(seen[0] == VillagerActivity::Sleep); + CHECK(seen[1] == VillagerActivity::Idle); + CHECK(seen[2] == VillagerActivity::SeekWorkstation); + CHECK(seen[3] == VillagerActivity::Work); + CHECK(seen[4] == VillagerActivity::Meet); + CHECK(seen[5] == VillagerActivity::Work); + CHECK(seen.back() == VillagerActivity::Sleep); + + CHECK(villager.profession == VillagerProfession::Librarian); + CHECK(villager.workTicks > 1000); + CHECK(villager.bedIndex >= 0); + CHECK(villager.state == VillagerState::AtTarget); + CHECK(horizontalGap(villager.body.position, pois.at(villager.bedIndex).standCell) < 0.01f); +} + +VF_TEST(breakingTheWorkstationDemotesAFreshVillagerBackToUnemployed) { + World world = makeVillage(); + PoiRegistry pois; + pois.scan(world, villageCenter, 12, 4); + + Villager villager = makeWorker(1, -1, 0, 21u); + TimeOfDay clock = clockAtFraction(0.35); + VillagerSchedule schedule; + runTicks(world, villager, pois, clock, schedule, false, 400); + CHECK(villager.profession == VillagerProfession::Librarian); + CHECK_EQ(villager.level, 1); + + world.setBlock(-4, walkY, 0, Block::Air); + CHECK_EQ(pois.revalidate(world), 1); + + runTicks(world, villager, pois, clock, schedule, false, 1); + CHECK(villager.profession == VillagerProfession::Unemployed); + CHECK_EQ(villager.workstationIndex, -1); + CHECK_EQ(villager.experience, 0); + + runTicks(world, villager, pois, clock, schedule, false, 800); + CHECK(villager.profession == VillagerProfession::Farmer); + CHECK(pois.at(villager.workstationIndex).block == Block::Composter); +} + +VF_TEST(aVillagerPastLevelOneKeepsItsTradeWhenTheWorkstationIsBroken) { + World world = makeVillage(); + placePoi(world, -6, 0, Block::Lectern); + PoiRegistry pois; + pois.scan(world, villageCenter, 12, 4); + + Villager villager = makeWorker(1, -1, 0, 31u); + TimeOfDay clock = clockAtFraction(0.35); + VillagerSchedule schedule; + runTicks(world, villager, pois, clock, schedule, false, 400); + CHECK(villager.profession == VillagerProfession::Librarian); + + CHECK(grantVillagerExperience(villager, 20)); + CHECK_EQ(villager.level, 2); + + world.setBlock(-4, walkY, 0, Block::Air); + pois.revalidate(world); + runTicks(world, villager, pois, clock, schedule, false, 1); + CHECK(villager.profession == VillagerProfession::Librarian); + CHECK_EQ(villager.workstationIndex, -1); + CHECK_EQ(villager.experience, 20); + + runTicks(world, villager, pois, clock, schedule, false, 400); + CHECK(villager.workstationIndex >= 0); + CHECK(pois.at(villager.workstationIndex).pos == Vec3i(-6, walkY, 0)); + CHECK(villager.profession == VillagerProfession::Librarian); +} + +VF_TEST(experienceThresholdsPromoteTheVillagerThroughAllFiveLevels) { + CHECK_EQ(villagerExperienceForLevel(1), 0); + CHECK_EQ(villagerLevelForExperience(0), 1); + CHECK_EQ(villagerLevelForExperience(villagerExperienceForLevel(5)), villagerMaxLevel); + + Villager villager = makeWorker(1, 0, 0, 4u); + int reachedLevels = 1; + for (int step = 0; step < 60; ++step) { + if (grantVillagerExperience(villager, 5)) ++reachedLevels; + CHECK(villager.level <= villagerMaxLevel); + } + CHECK_EQ(reachedLevels, villagerMaxLevel); + CHECK_EQ(villager.level, villagerMaxLevel); + CHECK(!grantVillagerExperience(villager, 0)); + + for (int level = 2; level <= villagerMaxLevel; ++level) + CHECK(villagerExperienceForLevel(level) > villagerExperienceForLevel(level - 1)); +} + +VF_TEST(panicOverridesTheScheduleAndSendsTheVillagerBackToItsBed) { + World world = makeVillage(); + PoiRegistry pois; + pois.scan(world, villageCenter, 12, 4); + + Villager villager = makeWorker(1, -1, 0, 12u); + TimeOfDay clock = clockAtFraction(0.35); + VillagerSchedule schedule; + runTicks(world, villager, pois, clock, schedule, false, 400); + CHECK(villager.activity == VillagerActivity::Work); + CHECK(villager.profession == VillagerProfession::Librarian); + + runTicks(world, villager, pois, clock, schedule, true, 1); + CHECK(villager.activity == VillagerActivity::Panic); + + runTicks(world, villager, pois, clock, schedule, true, 600); + CHECK(villager.state == VillagerState::AtTarget); + CHECK(horizontalGap(villager.body.position, pois.at(villager.bedIndex).standCell) < 0.01f); + int workTicksUnderThreat = villager.workTicks; + + runTicks(world, villager, pois, clock, schedule, true, 100); + CHECK_EQ(villager.workTicks, workTicksUnderThreat); + + runTicks(world, villager, pois, clock, schedule, false, 1); + CHECK(villager.activity == VillagerActivity::Work); +} + +VF_TEST(deathReleasesTheBedAndWorkstationForSomebodyElse) { + World world = makeVillage(); + PoiRegistry pois; + pois.scan(world, villageCenter, 12, 4); + + Villager villager = makeWorker(1, -1, 0, 55u); + TimeOfDay clock = clockAtFraction(0.35); + VillagerSchedule schedule; + runTicks(world, villager, pois, clock, schedule, false, 400); + CHECK_EQ(pois.freeWorkstationCount(), 1); + CHECK_EQ(pois.freeBedCount(), 1); + + CHECK(!damageVillager(villager, pois, 10.0f)); + CHECK(villager.alive); + CHECK(damageVillager(villager, pois, 10.0f)); + CHECK(!villager.alive); + CHECK(villager.state == VillagerState::Dead); + CHECK_EQ(villager.workstationIndex, -1); + CHECK_EQ(pois.freeWorkstationCount(), 2); + CHECK_EQ(pois.freeBedCount(), 2); + + CHECK(!damageVillager(villager, pois, 10.0f)); + + Villager heir = makeWorker(2, -1, 0, 56u); + runTicks(world, heir, pois, clock, schedule, false, 400); + CHECK(heir.profession == VillagerProfession::Librarian); +} +