package net.minecraft.world.entity.monster.warden; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Streams; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraft.core.UUIDUtil; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; public class AngerManagement { @VisibleForTesting protected static final int CONVERSION_DELAY = 2; @VisibleForTesting protected static final int MAX_ANGER = 150; private static final int DEFAULT_ANGER_DECREASE = 1; private int conversionDelay = Mth.randomBetweenInclusive(RandomSource.create(), 0, 2); int highestAnger; private static final Codec> SUSPECT_ANGER_PAIR = RecordCodecBuilder.create( p_253580_ -> p_253580_.group( UUIDUtil.CODEC.fieldOf("uuid").forGetter(Pair::getFirst), ExtraCodecs.NON_NEGATIVE_INT.fieldOf("anger").forGetter(Pair::getSecond) ) .apply(p_253580_, Pair::of) ); private final Predicate filter; @VisibleForTesting protected final ArrayList suspects; private final AngerManagement.Sorter suspectSorter; @VisibleForTesting protected final Object2IntMap angerBySuspect; @VisibleForTesting protected final Object2IntMap angerByUuid; public static Codec codec(Predicate p_219278_) { return RecordCodecBuilder.create( p_219281_ -> p_219281_.group(SUSPECT_ANGER_PAIR.listOf().fieldOf("suspects").orElse(Collections.emptyList()).forGetter(AngerManagement::createUuidAngerPairs)) .apply(p_219281_, p_219284_ -> new AngerManagement(p_219278_, p_219284_)) ); } public AngerManagement(Predicate p_219254_, List> p_219255_) { this.filter = p_219254_; this.suspects = new ArrayList<>(); this.suspectSorter = new AngerManagement.Sorter(this); this.angerBySuspect = new Object2IntOpenHashMap<>(); this.angerByUuid = new Object2IntOpenHashMap<>(p_219255_.size()); p_219255_.forEach(p_219272_ -> this.angerByUuid.put(p_219272_.getFirst(), p_219272_.getSecond())); } private List> createUuidAngerPairs() { return Streams.>concat( this.suspects.stream().map(p_219295_ -> Pair.of(p_219295_.getUUID(), this.angerBySuspect.getInt(p_219295_))), this.angerByUuid.object2IntEntrySet().stream().map(p_219276_ -> Pair.of(p_219276_.getKey(), p_219276_.getIntValue())) ) .collect(Collectors.toList()); } public void tick(ServerLevel p_219264_, Predicate p_219265_) { this.conversionDelay--; if (this.conversionDelay <= 0) { this.convertFromUuids(p_219264_); this.conversionDelay = 2; } ObjectIterator> objectiterator = this.angerByUuid.object2IntEntrySet().iterator(); while (objectiterator.hasNext()) { Entry entry = objectiterator.next(); int i = entry.getIntValue(); if (i <= 1) { objectiterator.remove(); } else { entry.setValue(i - 1); } } ObjectIterator> objectiterator1 = this.angerBySuspect.object2IntEntrySet().iterator(); while (objectiterator1.hasNext()) { Entry entry1 = objectiterator1.next(); int j = entry1.getIntValue(); Entity entity = entry1.getKey(); Entity.RemovalReason entity$removalreason = entity.getRemovalReason(); if (j > 1 && p_219265_.test(entity) && entity$removalreason == null) { entry1.setValue(j - 1); } else { this.suspects.remove(entity); objectiterator1.remove(); if (j > 1 && entity$removalreason != null) { switch (entity$removalreason) { case CHANGED_DIMENSION: case UNLOADED_TO_CHUNK: case UNLOADED_WITH_PLAYER: this.angerByUuid.put(entity.getUUID(), j - 1); } } } } this.sortAndUpdateHighestAnger(); } private void sortAndUpdateHighestAnger() { this.highestAnger = 0; this.suspects.sort(this.suspectSorter); if (this.suspects.size() == 1) { this.highestAnger = this.angerBySuspect.getInt(this.suspects.get(0)); } } private void convertFromUuids(ServerLevel p_219262_) { ObjectIterator> objectiterator = this.angerByUuid.object2IntEntrySet().iterator(); while (objectiterator.hasNext()) { Entry entry = objectiterator.next(); int i = entry.getIntValue(); Entity entity = p_219262_.getEntity(entry.getKey()); if (entity != null) { this.angerBySuspect.put(entity, i); this.suspects.add(entity); objectiterator.remove(); } } } public int increaseAnger(Entity p_219269_, int p_219270_) { boolean flag = !this.angerBySuspect.containsKey(p_219269_); int i = this.angerBySuspect.computeInt(p_219269_, (p_219259_, p_219260_) -> Math.min(150, (p_219260_ == null ? 0 : p_219260_) + p_219270_)); if (flag) { int j = this.angerByUuid.removeInt(p_219269_.getUUID()); i += j; this.angerBySuspect.put(p_219269_, i); this.suspects.add(p_219269_); } this.sortAndUpdateHighestAnger(); return i; } public void clearAnger(Entity p_219267_) { this.angerBySuspect.removeInt(p_219267_); this.suspects.remove(p_219267_); this.sortAndUpdateHighestAnger(); } @Nullable private Entity getTopSuspect() { return this.suspects.stream().filter(this.filter).findFirst().orElse(null); } public int getActiveAnger(@Nullable Entity p_219287_) { return p_219287_ == null ? this.highestAnger : this.angerBySuspect.getInt(p_219287_); } public Optional getActiveEntity() { return Optional.ofNullable(this.getTopSuspect()).filter(p_219293_ -> p_219293_ instanceof LivingEntity).map(p_219290_ -> (LivingEntity)p_219290_); } @VisibleForTesting protected record Sorter(AngerManagement angerManagement) implements Comparator { public int compare(Entity p_219303_, Entity p_219304_) { if (p_219303_.equals(p_219304_)) { return 0; } else { int i = this.angerManagement.angerBySuspect.getOrDefault(p_219303_, 0); int j = this.angerManagement.angerBySuspect.getOrDefault(p_219304_, 0); this.angerManagement.highestAnger = Math.max(this.angerManagement.highestAnger, Math.max(i, j)); boolean flag = AngerLevel.byAnger(i).isAngry(); boolean flag1 = AngerLevel.byAnger(j).isAngry(); if (flag != flag1) { return flag ? -1 : 1; } else { boolean flag2 = p_219303_ instanceof Player; boolean flag3 = p_219304_ instanceof Player; if (flag2 != flag3) { return flag2 ? -1 : 1; } else { return Integer.compare(j, i); } } } } } }