User Tools

Site Tools


tutorial:villager_activities

Activities

This is summary of how to add stuff for villagers to do. Here is the repo with the full code. The accessors are not explained here, they are at Mixin Accessors. An activity is a set of tasks. First register the activity:

  1. public static final Activity PRAY = Activity.register("pray");

Then create a task:

  1. public class FindTempleTask extends Task<VillagerEntity> {
  2. public FindTempleTask() {
  3. super(ImmutableMap.of(ReligiousVillagersMod.MOSQUE_POINT, MemoryModuleState.VALUE_ABSENT));//Conditions to be met to start the task
  4. }
  5.  
  6. protected boolean shouldRun(ServerWorld world, VillagerEntity entity) {
  7. return world.getPointOfInterestStorage()
  8. .getNearestPosition(
  9. ReligiousVillagersMod.BELIEVER.getCompletionCondition(),
  10. entity.getBlockPos(),
  11. 48,
  12. PointOfInterestStorage.OccupationStatus.ANY
  13. ).isPresent();
  14. }
  15.  
  16. protected void run(ServerWorld world, VillagerEntity entity, long time) {
  17. ReligiousVillagersMod.LOGGER.info("FindTemple:run!");
  18.  
  19. world.getPointOfInterestStorage()
  20. .getPositions(
  21. ReligiousVillagersMod.BELIEVER.getCompletionCondition(),
  22. (blockPos) -> {
  23. Path path = entity
  24. .getNavigation()
  25. .findPathTo(blockPos, ReligiousVillagersMod.BELIEVER.getSearchDistance());
  26. return (path != null && path.reachesTarget());
  27. },
  28. entity.getBlockPos(),
  29. 48,
  30. PointOfInterestStorage.OccupationStatus.ANY
  31. )
  32. .findAny()
  33. .ifPresent(blockPos -> {
  34. GlobalPos globalPos = GlobalPos.create(world.getRegistryKey(), blockPos);
  35. entity.getBrain().remember(ReligiousVillagersMod.MOSQUE_POINT, globalPos);
  36. });
  37. }
  38. }

This task just finds a ReligiousVillagersMod.MOSQUE_POINT:

  1. public static final MemoryModuleType<GlobalPos> MOSQUE_POINT = MemoryModuleType.register(
  2. "mosque_point",
  3. GlobalPos.CODEC
  4. );

The mosque point is a point of interest (like work benches, or a home):

  1. static {
  2. public static final PointOfInterestType BELIEVER = PointOfInterestType.register(
  3. "believer", PointOfInterestType.getAllStatesOf(Blocks.EMERALD_BLOCK), 32, 100
  4. );
  5.  
  6. addPointsOfInterest();
  7. }
  8.  
  9. private static void addPointsOfInterest() {
  10. VillagerEntityAccessor.setPointsOfInterest(
  11. new ImmutableMap.Builder<MemoryModuleType<GlobalPos>, BiPredicate<VillagerEntity, PointOfInterestType>>()
  12. .putAll(VillagerEntity.POINTS_OF_INTEREST)
  13. .put(MOSQUE_POINT, (villagerEntity, pointOfInterestType) -> pointOfInterestType == BELIEVER)
  14. .build());
  15. }

The position is saved in a memory module:

  1. VillagerEntityAccessor.setMemoryModules(new ImmutableList.Builder<MemoryModuleType<?>>()
  2. .addAll(VillagerEntityAccessor.getMemoryModules()).add(MOSQUE_POINT).build());

Once you have a task, you can create a set of tasks (aka Activity):

  1. public static ImmutableList<Pair<Integer, ? extends Task<? super VillagerEntity>>> createPrayTasks(float speed) {
  2. return ImmutableList.of(
  3. Pair.of(1, new FindTempleTask()),
  4. Pair.of(2, new VillagerWalkTowardsTask(
  5. MOSQUE_POINT, speed, 1, 150, 1200//info about how to do the task
  6. )),
  7. Pair.of(3, new ForgetCompletedPointOfInterestTask(BELIEVER, MOSQUE_POINT)),
  8. Pair.of(3, new PrayVillagerTask()),//Another task, remove it if you dont want it.
  9. Pair.of(99, new ScheduleActivityTask())
  10. );
  11. }

Then, you need to add the activity to their brain:

  1. @Mixin(VillagerEntity.class)
  2. public class VillagerEntityMixin {
  3. @Inject(at = @At("HEAD"), method = "initBrain(Lnet/minecraft/entity/ai/brain/Brain;)V", locals = LocalCapture.CAPTURE_FAILEXCEPTION)
  4. private void initBrain(Brain<VillagerEntity> brain, CallbackInfo info) {
  5. VillagerEntity $this = (VillagerEntity) (Object) this;
  6.  
  7. if (!$this.isBaby()) {
  8. brain.setTaskList(
  9. ReligiousVillagersMod.PRAY,
  10. createPrayTasks(0.75F)
  11. );
  12. }
  13. }
  14. }

And to their schedule:

  1. static {
  2. addScheduled();
  3. }
  4.  
  5. private static void addScheduled() {
  6. ScheduleAccessor.setVillagerDefault(
  7. new ScheduleBuilder(Schedule.VILLAGER_DEFAULT).withActivity(10, ReligiousVillagersMod.PRAY).build());//10 is the start time
  8. }

If all went well and you have all the accessors, you should be able to put down an emerald block inside a village and villagers will pray before work:

tutorial/villager_activities.txt · Last modified: 2023/09/13 20:30 by nebelnidas