minecraft-src/net/minecraft/world/entity/monster/piglin/PiglinAi.java
2025-07-04 02:00:41 +03:00

842 lines
34 KiB
Java

package net.minecraft.world.entity.monster.piglin;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.mojang.datafixers.util.Pair;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.RandomSource;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.behavior.BackUpIfTooClose;
import net.minecraft.world.entity.ai.behavior.BehaviorControl;
import net.minecraft.world.entity.ai.behavior.BehaviorUtils;
import net.minecraft.world.entity.ai.behavior.CopyMemoryWithExpiry;
import net.minecraft.world.entity.ai.behavior.CrossbowAttack;
import net.minecraft.world.entity.ai.behavior.DismountOrSkipMounting;
import net.minecraft.world.entity.ai.behavior.DoNothing;
import net.minecraft.world.entity.ai.behavior.EraseMemoryIf;
import net.minecraft.world.entity.ai.behavior.GoToTargetLocation;
import net.minecraft.world.entity.ai.behavior.GoToWantedItem;
import net.minecraft.world.entity.ai.behavior.InteractWith;
import net.minecraft.world.entity.ai.behavior.InteractWithDoor;
import net.minecraft.world.entity.ai.behavior.LookAtTargetSink;
import net.minecraft.world.entity.ai.behavior.MeleeAttack;
import net.minecraft.world.entity.ai.behavior.Mount;
import net.minecraft.world.entity.ai.behavior.MoveToTargetSink;
import net.minecraft.world.entity.ai.behavior.OneShot;
import net.minecraft.world.entity.ai.behavior.RandomStroll;
import net.minecraft.world.entity.ai.behavior.RunOne;
import net.minecraft.world.entity.ai.behavior.SetEntityLookTarget;
import net.minecraft.world.entity.ai.behavior.SetLookAndInteract;
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom;
import net.minecraft.world.entity.ai.behavior.SetWalkTargetFromAttackTargetIfTargetOutOfReach;
import net.minecraft.world.entity.ai.behavior.SetWalkTargetFromLookTarget;
import net.minecraft.world.entity.ai.behavior.StartAttacking;
import net.minecraft.world.entity.ai.behavior.StartCelebratingIfTargetDead;
import net.minecraft.world.entity.ai.behavior.StopAttackingIfTargetInvalid;
import net.minecraft.world.entity.ai.behavior.StopBeingAngryIfTargetDead;
import net.minecraft.world.entity.ai.behavior.TriggerGate;
import net.minecraft.world.entity.ai.behavior.SetEntityLookTargetSometimes.Ticker;
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder;
import net.minecraft.world.entity.ai.behavior.declarative.Trigger;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.sensing.Sensor;
import net.minecraft.world.entity.ai.util.LandRandomPos;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.monster.hoglin.Hoglin;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.schedule.Activity;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.Vec3;
public class PiglinAi {
public static final int REPELLENT_DETECTION_RANGE_HORIZONTAL = 8;
public static final int REPELLENT_DETECTION_RANGE_VERTICAL = 4;
public static final Item BARTERING_ITEM = Items.GOLD_INGOT;
private static final int PLAYER_ANGER_RANGE = 16;
private static final int ANGER_DURATION = 600;
private static final int ADMIRE_DURATION = 119;
private static final int MAX_DISTANCE_TO_WALK_TO_ITEM = 9;
private static final int MAX_TIME_TO_WALK_TO_ITEM = 200;
private static final int HOW_LONG_TIME_TO_DISABLE_ADMIRE_WALKING_IF_CANT_REACH_ITEM = 200;
private static final int CELEBRATION_TIME = 300;
protected static final UniformInt TIME_BETWEEN_HUNTS = TimeUtil.rangeOfSeconds(30, 120);
private static final int BABY_FLEE_DURATION_AFTER_GETTING_HIT = 100;
private static final int HIT_BY_PLAYER_MEMORY_TIMEOUT = 400;
private static final int MAX_WALK_DISTANCE_TO_START_RIDING = 8;
private static final UniformInt RIDE_START_INTERVAL = TimeUtil.rangeOfSeconds(10, 40);
private static final UniformInt RIDE_DURATION = TimeUtil.rangeOfSeconds(10, 30);
private static final UniformInt RETREAT_DURATION = TimeUtil.rangeOfSeconds(5, 20);
private static final int MELEE_ATTACK_COOLDOWN = 20;
private static final int EAT_COOLDOWN = 200;
private static final int DESIRED_DISTANCE_FROM_ENTITY_WHEN_AVOIDING = 12;
private static final int MAX_LOOK_DIST = 8;
private static final int MAX_LOOK_DIST_FOR_PLAYER_HOLDING_LOVED_ITEM = 14;
private static final int INTERACTION_RANGE = 8;
private static final int MIN_DESIRED_DIST_FROM_TARGET_WHEN_HOLDING_CROSSBOW = 5;
private static final float SPEED_WHEN_STRAFING_BACK_FROM_TARGET = 0.75F;
private static final int DESIRED_DISTANCE_FROM_ZOMBIFIED = 6;
private static final UniformInt AVOID_ZOMBIFIED_DURATION = TimeUtil.rangeOfSeconds(5, 7);
private static final UniformInt BABY_AVOID_NEMESIS_DURATION = TimeUtil.rangeOfSeconds(5, 7);
private static final float PROBABILITY_OF_CELEBRATION_DANCE = 0.1F;
private static final float SPEED_MULTIPLIER_WHEN_AVOIDING = 1.0F;
private static final float SPEED_MULTIPLIER_WHEN_RETREATING = 1.0F;
private static final float SPEED_MULTIPLIER_WHEN_MOUNTING = 0.8F;
private static final float SPEED_MULTIPLIER_WHEN_GOING_TO_WANTED_ITEM = 1.0F;
private static final float SPEED_MULTIPLIER_WHEN_GOING_TO_CELEBRATE_LOCATION = 1.0F;
private static final float SPEED_MULTIPLIER_WHEN_DANCING = 0.6F;
private static final float SPEED_MULTIPLIER_WHEN_IDLING = 0.6F;
protected static Brain<?> makeBrain(Piglin piglin, Brain<Piglin> brain) {
initCoreActivity(brain);
initIdleActivity(brain);
initAdmireItemActivity(brain);
initFightActivity(piglin, brain);
initCelebrateActivity(brain);
initRetreatActivity(brain);
initRideHoglinActivity(brain);
brain.setCoreActivities(ImmutableSet.of(Activity.CORE));
brain.setDefaultActivity(Activity.IDLE);
brain.useDefaultActivity();
return brain;
}
protected static void initMemories(Piglin piglin, RandomSource random) {
int i = TIME_BETWEEN_HUNTS.sample(random);
piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, i);
}
private static void initCoreActivity(Brain<Piglin> brain) {
brain.addActivity(
Activity.CORE,
0,
ImmutableList.of(
new LookAtTargetSink(45, 90),
new MoveToTargetSink(),
InteractWithDoor.create(),
babyAvoidNemesis(),
avoidZombified(),
StopHoldingItemIfNoLongerAdmiring.create(),
StartAdmiringItemIfSeen.create(119),
StartCelebratingIfTargetDead.create(300, PiglinAi::wantsToDance),
StopBeingAngryIfTargetDead.create()
)
);
}
private static void initIdleActivity(Brain<Piglin> brain) {
brain.addActivity(
Activity.IDLE,
10,
ImmutableList.of(
SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F),
StartAttacking.<Piglin>create((serverLevel, piglin) -> piglin.isAdult(), PiglinAi::findNearestValidAttackTarget),
BehaviorBuilder.triggerIf(Piglin::canHunt, StartHuntingHoglin.create()),
avoidRepellent(),
babySometimesRideBabyHoglin(),
createIdleLookBehaviors(),
createIdleMovementBehaviors(),
SetLookAndInteract.create(EntityType.PLAYER, 4)
)
);
}
private static void initFightActivity(Piglin piglin, Brain<Piglin> brain) {
brain.addActivityAndRemoveMemoryWhenStopped(
Activity.FIGHT,
10,
ImmutableList.of(
StopAttackingIfTargetInvalid.create((serverLevel, livingEntity) -> !isNearestValidAttackTarget(serverLevel, piglin, livingEntity)),
BehaviorBuilder.triggerIf(PiglinAi::hasCrossbow, BackUpIfTooClose.create(5, 0.75F)),
SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.0F),
MeleeAttack.create(20),
new CrossbowAttack(),
RememberIfHoglinWasKilled.create(),
EraseMemoryIf.create(PiglinAi::isNearZombified, MemoryModuleType.ATTACK_TARGET)
),
MemoryModuleType.ATTACK_TARGET
);
}
private static void initCelebrateActivity(Brain<Piglin> brain) {
brain.addActivityAndRemoveMemoryWhenStopped(
Activity.CELEBRATE,
10,
ImmutableList.of(
avoidRepellent(),
SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F),
StartAttacking.<Piglin>create((serverLevel, piglin) -> piglin.isAdult(), PiglinAi::findNearestValidAttackTarget),
BehaviorBuilder.triggerIf(piglin -> !piglin.isDancing(), GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 2, 1.0F)),
BehaviorBuilder.triggerIf(Piglin::isDancing, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 4, 0.6F)),
new RunOne<LivingEntity>(
ImmutableList.of(
Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1), Pair.of(RandomStroll.stroll(0.6F, 2, 1), 1), Pair.of(new DoNothing(10, 20), 1)
)
)
),
MemoryModuleType.CELEBRATE_LOCATION
);
}
private static void initAdmireItemActivity(Brain<Piglin> brain) {
brain.addActivityAndRemoveMemoryWhenStopped(
Activity.ADMIRE_ITEM,
10,
ImmutableList.of(
GoToWantedItem.create(PiglinAi::isNotHoldingLovedItemInOffHand, 1.0F, true, 9),
StopAdmiringIfItemTooFarAway.create(9),
StopAdmiringIfTiredOfTryingToReachItem.create(200, 200)
),
MemoryModuleType.ADMIRING_ITEM
);
}
private static void initRetreatActivity(Brain<Piglin> brain) {
brain.addActivityAndRemoveMemoryWhenStopped(
Activity.AVOID,
10,
ImmutableList.of(
SetWalkTargetAwayFrom.entity(MemoryModuleType.AVOID_TARGET, 1.0F, 12, true),
createIdleLookBehaviors(),
createIdleMovementBehaviors(),
EraseMemoryIf.<PathfinderMob>create(PiglinAi::wantsToStopFleeing, MemoryModuleType.AVOID_TARGET)
),
MemoryModuleType.AVOID_TARGET
);
}
private static void initRideHoglinActivity(Brain<Piglin> brain) {
brain.addActivityAndRemoveMemoryWhenStopped(
Activity.RIDE,
10,
ImmutableList.of(
Mount.create(0.8F),
SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 8.0F),
BehaviorBuilder.sequence(
BehaviorBuilder.triggerIf(Entity::isPassenger),
TriggerGate.triggerOneShuffled(
ImmutableList.<Pair<? extends Trigger<? super LivingEntity>, Integer>>builder()
.addAll(createLookBehaviors())
.add(Pair.of(BehaviorBuilder.triggerIf((Predicate<? super LivingEntity>)(piglin -> true)), 1))
.build()
)
),
DismountOrSkipMounting.<LivingEntity>create(8, PiglinAi::wantsToStopRiding)
),
MemoryModuleType.RIDE_TARGET
);
}
private static ImmutableList<Pair<OneShot<LivingEntity>, Integer>> createLookBehaviors() {
return ImmutableList.of(
Pair.of(SetEntityLookTarget.create(EntityType.PLAYER, 8.0F), 1),
Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1),
Pair.of(SetEntityLookTarget.create(8.0F), 1)
);
}
private static RunOne<LivingEntity> createIdleLookBehaviors() {
return new RunOne<>(
ImmutableList.<Pair<? extends BehaviorControl<? super LivingEntity>, Integer>>builder()
.addAll(createLookBehaviors())
.add(Pair.of(new DoNothing(30, 60), 1))
.build()
);
}
private static RunOne<Piglin> createIdleMovementBehaviors() {
return new RunOne<>(
ImmutableList.of(
Pair.of(RandomStroll.stroll(0.6F), 2),
Pair.of(InteractWith.of(EntityType.PIGLIN, 8, MemoryModuleType.INTERACTION_TARGET, 0.6F, 2), 2),
Pair.of(BehaviorBuilder.triggerIf(PiglinAi::doesntSeeAnyPlayerHoldingLovedItem, SetWalkTargetFromLookTarget.create(0.6F, 3)), 2),
Pair.of(new DoNothing(30, 60), 1)
)
);
}
private static BehaviorControl<PathfinderMob> avoidRepellent() {
return SetWalkTargetAwayFrom.pos(MemoryModuleType.NEAREST_REPELLENT, 1.0F, 8, false);
}
private static BehaviorControl<Piglin> babyAvoidNemesis() {
return CopyMemoryWithExpiry.create(Piglin::isBaby, MemoryModuleType.NEAREST_VISIBLE_NEMESIS, MemoryModuleType.AVOID_TARGET, BABY_AVOID_NEMESIS_DURATION);
}
private static BehaviorControl<Piglin> avoidZombified() {
return CopyMemoryWithExpiry.create(
PiglinAi::isNearZombified, MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, MemoryModuleType.AVOID_TARGET, AVOID_ZOMBIFIED_DURATION
);
}
protected static void updateActivity(Piglin piglin) {
Brain<Piglin> brain = piglin.getBrain();
Activity activity = (Activity)brain.getActiveNonCoreActivity().orElse(null);
brain.setActiveActivityToFirstValid(ImmutableList.of(Activity.ADMIRE_ITEM, Activity.FIGHT, Activity.AVOID, Activity.CELEBRATE, Activity.RIDE, Activity.IDLE));
Activity activity2 = (Activity)brain.getActiveNonCoreActivity().orElse(null);
if (activity != activity2) {
getSoundForCurrentActivity(piglin).ifPresent(piglin::makeSound);
}
piglin.setAggressive(brain.hasMemoryValue(MemoryModuleType.ATTACK_TARGET));
if (!brain.hasMemoryValue(MemoryModuleType.RIDE_TARGET) && isBabyRidingBaby(piglin)) {
piglin.stopRiding();
}
if (!brain.hasMemoryValue(MemoryModuleType.CELEBRATE_LOCATION)) {
brain.eraseMemory(MemoryModuleType.DANCING);
}
piglin.setDancing(brain.hasMemoryValue(MemoryModuleType.DANCING));
}
private static boolean isBabyRidingBaby(Piglin passenger) {
if (!passenger.isBaby()) {
return false;
} else {
Entity entity = passenger.getVehicle();
return entity instanceof Piglin && ((Piglin)entity).isBaby() || entity instanceof Hoglin && ((Hoglin)entity).isBaby();
}
}
protected static void pickUpItem(ServerLevel serverLevel, Piglin piglin, ItemEntity itemEntity) {
stopWalking(piglin);
ItemStack itemStack;
if (itemEntity.getItem().is(Items.GOLD_NUGGET)) {
piglin.take(itemEntity, itemEntity.getItem().getCount());
itemStack = itemEntity.getItem();
itemEntity.discard();
} else {
piglin.take(itemEntity, 1);
itemStack = removeOneItemFromItemEntity(itemEntity);
}
if (isLovedItem(itemStack)) {
piglin.getBrain().eraseMemory(MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM);
holdInOffhand(serverLevel, piglin, itemStack);
admireGoldItem(piglin);
} else if (isFood(itemStack) && !hasEatenRecently(piglin)) {
eat(piglin);
} else {
boolean bl = !piglin.equipItemIfPossible(serverLevel, itemStack).equals(ItemStack.EMPTY);
if (!bl) {
putInInventory(piglin, itemStack);
}
}
}
private static void holdInOffhand(ServerLevel serverLevel, Piglin piglin, ItemStack itemStack) {
if (isHoldingItemInOffHand(piglin)) {
piglin.spawnAtLocation(serverLevel, piglin.getItemInHand(InteractionHand.OFF_HAND));
}
piglin.holdInOffHand(itemStack);
}
private static ItemStack removeOneItemFromItemEntity(ItemEntity itemEntity) {
ItemStack itemStack = itemEntity.getItem();
ItemStack itemStack2 = itemStack.split(1);
if (itemStack.isEmpty()) {
itemEntity.discard();
} else {
itemEntity.setItem(itemStack);
}
return itemStack2;
}
protected static void stopHoldingOffHandItem(ServerLevel serverLevel, Piglin piglin, boolean bl) {
ItemStack itemStack = piglin.getItemInHand(InteractionHand.OFF_HAND);
piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
if (piglin.isAdult()) {
boolean bl2 = isBarterCurrency(itemStack);
if (bl && bl2) {
throwItems(piglin, getBarterResponseItems(piglin));
} else if (!bl2) {
boolean bl3 = !piglin.equipItemIfPossible(serverLevel, itemStack).isEmpty();
if (!bl3) {
putInInventory(piglin, itemStack);
}
}
} else {
boolean bl2 = !piglin.equipItemIfPossible(serverLevel, itemStack).isEmpty();
if (!bl2) {
ItemStack itemStack2 = piglin.getMainHandItem();
if (isLovedItem(itemStack2)) {
putInInventory(piglin, itemStack2);
} else {
throwItems(piglin, Collections.singletonList(itemStack2));
}
piglin.holdInMainHand(itemStack);
}
}
}
protected static void cancelAdmiring(ServerLevel serverLevel, Piglin piglin) {
if (isAdmiringItem(piglin) && !piglin.getOffhandItem().isEmpty()) {
piglin.spawnAtLocation(serverLevel, piglin.getOffhandItem());
piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
}
}
private static void putInInventory(Piglin piglin, ItemStack stack) {
ItemStack itemStack = piglin.addToInventory(stack);
throwItemsTowardRandomPos(piglin, Collections.singletonList(itemStack));
}
private static void throwItems(Piglin pilgin, List<ItemStack> stacks) {
Optional<Player> optional = pilgin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER);
if (optional.isPresent()) {
throwItemsTowardPlayer(pilgin, (Player)optional.get(), stacks);
} else {
throwItemsTowardRandomPos(pilgin, stacks);
}
}
private static void throwItemsTowardRandomPos(Piglin piglin, List<ItemStack> stacks) {
throwItemsTowardPos(piglin, stacks, getRandomNearbyPos(piglin));
}
private static void throwItemsTowardPlayer(Piglin piglin, Player player, List<ItemStack> stacks) {
throwItemsTowardPos(piglin, stacks, player.position());
}
private static void throwItemsTowardPos(Piglin piglin, List<ItemStack> stacks, Vec3 pos) {
if (!stacks.isEmpty()) {
piglin.swing(InteractionHand.OFF_HAND);
for (ItemStack itemStack : stacks) {
BehaviorUtils.throwItem(piglin, itemStack, pos.add(0.0, 1.0, 0.0));
}
}
}
private static List<ItemStack> getBarterResponseItems(Piglin piglin) {
LootTable lootTable = piglin.level().getServer().reloadableRegistries().getLootTable(BuiltInLootTables.PIGLIN_BARTERING);
List<ItemStack> list = lootTable.getRandomItems(
new LootParams.Builder((ServerLevel)piglin.level()).withParameter(LootContextParams.THIS_ENTITY, piglin).create(LootContextParamSets.PIGLIN_BARTER)
);
return list;
}
private static boolean wantsToDance(LivingEntity piglin, LivingEntity target) {
return target.getType() != EntityType.HOGLIN ? false : RandomSource.create(piglin.level().getGameTime()).nextFloat() < 0.1F;
}
protected static boolean wantsToPickup(Piglin piglin, ItemStack stack) {
if (piglin.isBaby() && stack.is(ItemTags.IGNORED_BY_PIGLIN_BABIES)) {
return false;
} else if (stack.is(ItemTags.PIGLIN_REPELLENTS)) {
return false;
} else if (isAdmiringDisabled(piglin) && piglin.getBrain().hasMemoryValue(MemoryModuleType.ATTACK_TARGET)) {
return false;
} else if (isBarterCurrency(stack)) {
return isNotHoldingLovedItemInOffHand(piglin);
} else {
boolean bl = piglin.canAddToInventory(stack);
if (stack.is(Items.GOLD_NUGGET)) {
return bl;
} else if (isFood(stack)) {
return !hasEatenRecently(piglin) && bl;
} else {
return !isLovedItem(stack) ? piglin.canReplaceCurrentItem(stack) : isNotHoldingLovedItemInOffHand(piglin) && bl;
}
}
}
protected static boolean isLovedItem(ItemStack item) {
return item.is(ItemTags.PIGLIN_LOVED);
}
private static boolean wantsToStopRiding(Piglin piglin, Entity vehicle) {
return !(vehicle instanceof Mob mob)
? false
: !mob.isBaby() || !mob.isAlive() || wasHurtRecently(piglin) || wasHurtRecently(mob) || mob instanceof Piglin && mob.getVehicle() == null;
}
private static boolean isNearestValidAttackTarget(ServerLevel serverLevel, Piglin piglin, LivingEntity livingEntity) {
return findNearestValidAttackTarget(serverLevel, piglin).filter(livingEntity2 -> livingEntity2 == livingEntity).isPresent();
}
private static boolean isNearZombified(Piglin piglin) {
Brain<Piglin> brain = piglin.getBrain();
if (brain.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED)) {
LivingEntity livingEntity = (LivingEntity)brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED).get();
return piglin.closerThan(livingEntity, 6.0);
} else {
return false;
}
}
private static Optional<? extends LivingEntity> findNearestValidAttackTarget(ServerLevel serverLevel, Piglin piglin) {
Brain<Piglin> brain = piglin.getBrain();
if (isNearZombified(piglin)) {
return Optional.empty();
} else {
Optional<LivingEntity> optional = BehaviorUtils.getLivingEntityFromUUIDMemory(piglin, MemoryModuleType.ANGRY_AT);
if (optional.isPresent() && Sensor.isEntityAttackableIgnoringLineOfSight(serverLevel, piglin, (LivingEntity)optional.get())) {
return optional;
} else {
if (brain.hasMemoryValue(MemoryModuleType.UNIVERSAL_ANGER)) {
Optional<Player> optional2 = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER);
if (optional2.isPresent()) {
return optional2;
}
}
Optional<Mob> optional2 = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_NEMESIS);
if (optional2.isPresent()) {
return optional2;
} else {
Optional<Player> optional3 = brain.getMemory(MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD);
return optional3.isPresent() && Sensor.isEntityAttackable(serverLevel, piglin, (LivingEntity)optional3.get()) ? optional3 : Optional.empty();
}
}
}
}
public static void angerNearbyPiglins(ServerLevel serverLevel, Player player, boolean bl) {
List<Piglin> list = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0));
list.stream().filter(PiglinAi::isIdle).filter(piglin -> !bl || BehaviorUtils.canSee(piglin, player)).forEach(piglin -> {
if (serverLevel.getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
setAngerTargetToNearestTargetablePlayerIfFound(serverLevel, piglin, player);
} else {
setAngerTarget(serverLevel, piglin, player);
}
});
}
public static InteractionResult mobInteract(ServerLevel serverLevel, Piglin piglin, Player player, InteractionHand interactionHand) {
ItemStack itemStack = player.getItemInHand(interactionHand);
if (canAdmire(piglin, itemStack)) {
ItemStack itemStack2 = itemStack.consumeAndReturn(1, player);
holdInOffhand(serverLevel, piglin, itemStack2);
admireGoldItem(piglin);
stopWalking(piglin);
return InteractionResult.SUCCESS;
} else {
return InteractionResult.PASS;
}
}
protected static boolean canAdmire(Piglin piglin, ItemStack stack) {
return !isAdmiringDisabled(piglin) && !isAdmiringItem(piglin) && piglin.isAdult() && isBarterCurrency(stack);
}
protected static void wasHurtBy(ServerLevel serverLevel, Piglin piglin, LivingEntity livingEntity) {
if (!(livingEntity instanceof Piglin)) {
if (isHoldingItemInOffHand(piglin)) {
stopHoldingOffHandItem(serverLevel, piglin, false);
}
Brain<Piglin> brain = piglin.getBrain();
brain.eraseMemory(MemoryModuleType.CELEBRATE_LOCATION);
brain.eraseMemory(MemoryModuleType.DANCING);
brain.eraseMemory(MemoryModuleType.ADMIRING_ITEM);
if (livingEntity instanceof Player) {
brain.setMemoryWithExpiry(MemoryModuleType.ADMIRING_DISABLED, true, 400L);
}
getAvoidTarget(piglin).ifPresent(livingEntity2 -> {
if (livingEntity2.getType() != livingEntity.getType()) {
brain.eraseMemory(MemoryModuleType.AVOID_TARGET);
}
});
if (piglin.isBaby()) {
brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, livingEntity, 100L);
if (Sensor.isEntityAttackableIgnoringLineOfSight(serverLevel, piglin, livingEntity)) {
broadcastAngerTarget(serverLevel, piglin, livingEntity);
}
} else if (livingEntity.getType() == EntityType.HOGLIN && hoglinsOutnumberPiglins(piglin)) {
setAvoidTargetAndDontHuntForAWhile(piglin, livingEntity);
broadcastRetreat(piglin, livingEntity);
} else {
maybeRetaliate(serverLevel, piglin, livingEntity);
}
}
}
protected static void maybeRetaliate(ServerLevel serverLevel, AbstractPiglin abstractPiglin, LivingEntity livingEntity) {
if (!abstractPiglin.getBrain().isActive(Activity.AVOID)) {
if (Sensor.isEntityAttackableIgnoringLineOfSight(serverLevel, abstractPiglin, livingEntity)) {
if (!BehaviorUtils.isOtherTargetMuchFurtherAwayThanCurrentAttackTarget(abstractPiglin, livingEntity, 4.0)) {
if (livingEntity.getType() == EntityType.PLAYER && serverLevel.getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
setAngerTargetToNearestTargetablePlayerIfFound(serverLevel, abstractPiglin, livingEntity);
broadcastUniversalAnger(serverLevel, abstractPiglin);
} else {
setAngerTarget(serverLevel, abstractPiglin, livingEntity);
broadcastAngerTarget(serverLevel, abstractPiglin, livingEntity);
}
}
}
}
}
public static Optional<SoundEvent> getSoundForCurrentActivity(Piglin piglin) {
return piglin.getBrain().getActiveNonCoreActivity().map(activity -> getSoundForActivity(piglin, activity));
}
private static SoundEvent getSoundForActivity(Piglin piglin, Activity activity) {
if (activity == Activity.FIGHT) {
return SoundEvents.PIGLIN_ANGRY;
} else if (piglin.isConverting()) {
return SoundEvents.PIGLIN_RETREAT;
} else if (activity == Activity.AVOID && isNearAvoidTarget(piglin)) {
return SoundEvents.PIGLIN_RETREAT;
} else if (activity == Activity.ADMIRE_ITEM) {
return SoundEvents.PIGLIN_ADMIRING_ITEM;
} else if (activity == Activity.CELEBRATE) {
return SoundEvents.PIGLIN_CELEBRATE;
} else if (seesPlayerHoldingLovedItem(piglin)) {
return SoundEvents.PIGLIN_JEALOUS;
} else {
return isNearRepellent(piglin) ? SoundEvents.PIGLIN_RETREAT : SoundEvents.PIGLIN_AMBIENT;
}
}
private static boolean isNearAvoidTarget(Piglin piglin) {
Brain<Piglin> brain = piglin.getBrain();
return !brain.hasMemoryValue(MemoryModuleType.AVOID_TARGET)
? false
: ((LivingEntity)brain.getMemory(MemoryModuleType.AVOID_TARGET).get()).closerThan(piglin, 12.0);
}
protected static List<AbstractPiglin> getVisibleAdultPiglins(Piglin piglin) {
return (List<AbstractPiglin>)piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS).orElse(ImmutableList.of());
}
private static List<AbstractPiglin> getAdultPiglins(AbstractPiglin piglin) {
return (List<AbstractPiglin>)piglin.getBrain().getMemory(MemoryModuleType.NEARBY_ADULT_PIGLINS).orElse(ImmutableList.of());
}
public static boolean isWearingSafeArmor(LivingEntity livingEntity) {
for (ItemStack itemStack : livingEntity.getArmorAndBodyArmorSlots()) {
if (itemStack.is(ItemTags.PIGLIN_SAFE_ARMOR)) {
return true;
}
}
return false;
}
private static void stopWalking(Piglin piglin) {
piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET);
piglin.getNavigation().stop();
}
private static BehaviorControl<LivingEntity> babySometimesRideBabyHoglin() {
Ticker ticker = new Ticker(RIDE_START_INTERVAL);
return CopyMemoryWithExpiry.create(
livingEntity -> livingEntity.isBaby() && ticker.tickDownAndCheck(livingEntity.level().random),
MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN,
MemoryModuleType.RIDE_TARGET,
RIDE_DURATION
);
}
protected static void broadcastAngerTarget(ServerLevel serverLevel, AbstractPiglin abstractPiglin, LivingEntity livingEntity) {
getAdultPiglins(abstractPiglin).forEach(abstractPiglinx -> {
if (livingEntity.getType() != EntityType.HOGLIN || abstractPiglinx.canHunt() && ((Hoglin)livingEntity).canBeHunted()) {
setAngerTargetIfCloserThanCurrent(serverLevel, abstractPiglinx, livingEntity);
}
});
}
protected static void broadcastUniversalAnger(ServerLevel serverLevel, AbstractPiglin abstractPiglin) {
getAdultPiglins(abstractPiglin)
.forEach(abstractPiglinx -> getNearestVisibleTargetablePlayer(abstractPiglinx).ifPresent(player -> setAngerTarget(serverLevel, abstractPiglinx, player)));
}
protected static void setAngerTarget(ServerLevel serverLevel, AbstractPiglin abstractPiglin, LivingEntity livingEntity) {
if (Sensor.isEntityAttackableIgnoringLineOfSight(serverLevel, abstractPiglin, livingEntity)) {
abstractPiglin.getBrain().eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
abstractPiglin.getBrain().setMemoryWithExpiry(MemoryModuleType.ANGRY_AT, livingEntity.getUUID(), 600L);
if (livingEntity.getType() == EntityType.HOGLIN && abstractPiglin.canHunt()) {
dontKillAnyMoreHoglinsForAWhile(abstractPiglin);
}
if (livingEntity.getType() == EntityType.PLAYER && serverLevel.getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
abstractPiglin.getBrain().setMemoryWithExpiry(MemoryModuleType.UNIVERSAL_ANGER, true, 600L);
}
}
}
private static void setAngerTargetToNearestTargetablePlayerIfFound(ServerLevel serverLevel, AbstractPiglin abstractPiglin, LivingEntity livingEntity) {
Optional<Player> optional = getNearestVisibleTargetablePlayer(abstractPiglin);
if (optional.isPresent()) {
setAngerTarget(serverLevel, abstractPiglin, (LivingEntity)optional.get());
} else {
setAngerTarget(serverLevel, abstractPiglin, livingEntity);
}
}
private static void setAngerTargetIfCloserThanCurrent(ServerLevel serverLevel, AbstractPiglin abstractPiglin, LivingEntity livingEntity) {
Optional<LivingEntity> optional = getAngerTarget(abstractPiglin);
LivingEntity livingEntity2 = BehaviorUtils.getNearestTarget(abstractPiglin, optional, livingEntity);
if (!optional.isPresent() || optional.get() != livingEntity2) {
setAngerTarget(serverLevel, abstractPiglin, livingEntity2);
}
}
private static Optional<LivingEntity> getAngerTarget(AbstractPiglin piglin) {
return BehaviorUtils.getLivingEntityFromUUIDMemory(piglin, MemoryModuleType.ANGRY_AT);
}
public static Optional<LivingEntity> getAvoidTarget(Piglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.AVOID_TARGET) ? piglin.getBrain().getMemory(MemoryModuleType.AVOID_TARGET) : Optional.empty();
}
public static Optional<Player> getNearestVisibleTargetablePlayer(AbstractPiglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER)
? piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER)
: Optional.empty();
}
private static void broadcastRetreat(Piglin piglin, LivingEntity target) {
getVisibleAdultPiglins(piglin)
.stream()
.filter(abstractPiglin -> abstractPiglin instanceof Piglin)
.forEach(abstractPiglin -> retreatFromNearestTarget((Piglin)abstractPiglin, target));
}
private static void retreatFromNearestTarget(Piglin piglin, LivingEntity target) {
Brain<Piglin> brain = piglin.getBrain();
LivingEntity livingEntity = BehaviorUtils.getNearestTarget(piglin, brain.getMemory(MemoryModuleType.AVOID_TARGET), target);
livingEntity = BehaviorUtils.getNearestTarget(piglin, brain.getMemory(MemoryModuleType.ATTACK_TARGET), livingEntity);
setAvoidTargetAndDontHuntForAWhile(piglin, livingEntity);
}
private static boolean wantsToStopFleeing(Piglin piglin) {
Brain<Piglin> brain = piglin.getBrain();
if (!brain.hasMemoryValue(MemoryModuleType.AVOID_TARGET)) {
return true;
} else {
LivingEntity livingEntity = (LivingEntity)brain.getMemory(MemoryModuleType.AVOID_TARGET).get();
EntityType<?> entityType = livingEntity.getType();
if (entityType == EntityType.HOGLIN) {
return piglinsEqualOrOutnumberHoglins(piglin);
} else {
return isZombified(entityType) ? !brain.isMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, livingEntity) : false;
}
}
}
private static boolean piglinsEqualOrOutnumberHoglins(Piglin piglin) {
return !hoglinsOutnumberPiglins(piglin);
}
private static boolean hoglinsOutnumberPiglins(Piglin piglin) {
int i = (Integer)piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT).orElse(0) + 1;
int j = (Integer)piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT).orElse(0);
return j > i;
}
private static void setAvoidTargetAndDontHuntForAWhile(Piglin piglin, LivingEntity target) {
piglin.getBrain().eraseMemory(MemoryModuleType.ANGRY_AT);
piglin.getBrain().eraseMemory(MemoryModuleType.ATTACK_TARGET);
piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET);
piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, target, RETREAT_DURATION.sample(piglin.level().random));
dontKillAnyMoreHoglinsForAWhile(piglin);
}
protected static void dontKillAnyMoreHoglinsForAWhile(AbstractPiglin piglin) {
piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, TIME_BETWEEN_HUNTS.sample(piglin.level().random));
}
private static void eat(Piglin piglin) {
piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.ATE_RECENTLY, true, 200L);
}
private static Vec3 getRandomNearbyPos(Piglin piglin) {
Vec3 vec3 = LandRandomPos.getPos(piglin, 4, 2);
return vec3 == null ? piglin.position() : vec3;
}
private static boolean hasEatenRecently(Piglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.ATE_RECENTLY);
}
protected static boolean isIdle(AbstractPiglin piglin) {
return piglin.getBrain().isActive(Activity.IDLE);
}
private static boolean hasCrossbow(LivingEntity piglin) {
return piglin.isHolding(Items.CROSSBOW);
}
private static void admireGoldItem(LivingEntity piglin) {
piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.ADMIRING_ITEM, true, 119L);
}
private static boolean isAdmiringItem(Piglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.ADMIRING_ITEM);
}
private static boolean isBarterCurrency(ItemStack stack) {
return stack.is(BARTERING_ITEM);
}
private static boolean isFood(ItemStack stack) {
return stack.is(ItemTags.PIGLIN_FOOD);
}
private static boolean isNearRepellent(Piglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_REPELLENT);
}
private static boolean seesPlayerHoldingLovedItem(LivingEntity piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_PLAYER_HOLDING_WANTED_ITEM);
}
private static boolean doesntSeeAnyPlayerHoldingLovedItem(LivingEntity piglin) {
return !seesPlayerHoldingLovedItem(piglin);
}
public static boolean isPlayerHoldingLovedItem(LivingEntity player) {
return player.getType() == EntityType.PLAYER && player.isHolding(PiglinAi::isLovedItem);
}
private static boolean isAdmiringDisabled(Piglin piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.ADMIRING_DISABLED);
}
private static boolean wasHurtRecently(LivingEntity piglin) {
return piglin.getBrain().hasMemoryValue(MemoryModuleType.HURT_BY);
}
private static boolean isHoldingItemInOffHand(Piglin piglin) {
return !piglin.getOffhandItem().isEmpty();
}
private static boolean isNotHoldingLovedItemInOffHand(Piglin piglin) {
return piglin.getOffhandItem().isEmpty() || !isLovedItem(piglin.getOffhandItem());
}
public static boolean isZombified(EntityType<?> entityType) {
return entityType == EntityType.ZOMBIFIED_PIGLIN || entityType == EntityType.ZOGLIN;
}
}