minecraft-src/net/minecraft/world/entity/monster/warden/AngerManagement.java
2025-07-04 01:41:11 +03:00

201 lines
7 KiB
Java

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 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 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;
import org.jetbrains.annotations.Nullable;
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<Pair<UUID, Integer>> SUSPECT_ANGER_PAIR = RecordCodecBuilder.create(
instance -> instance.group(UUIDUtil.CODEC.fieldOf("uuid").forGetter(Pair::getFirst), ExtraCodecs.NON_NEGATIVE_INT.fieldOf("anger").forGetter(Pair::getSecond))
.apply(instance, Pair::of)
);
private final Predicate<Entity> filter;
@VisibleForTesting
protected final ArrayList<Entity> suspects;
private final AngerManagement.Sorter suspectSorter;
@VisibleForTesting
protected final Object2IntMap<Entity> angerBySuspect;
@VisibleForTesting
protected final Object2IntMap<UUID> angerByUuid;
public static Codec<AngerManagement> codec(Predicate<Entity> filter) {
return RecordCodecBuilder.create(
instance -> instance.group(SUSPECT_ANGER_PAIR.listOf().fieldOf("suspects").orElse(Collections.emptyList()).forGetter(AngerManagement::createUuidAngerPairs))
.apply(instance, list -> new AngerManagement(filter, list))
);
}
public AngerManagement(Predicate<Entity> filter, List<Pair<UUID, Integer>> angerByUuid) {
this.filter = filter;
this.suspects = new ArrayList();
this.suspectSorter = new AngerManagement.Sorter(this);
this.angerBySuspect = new Object2IntOpenHashMap<>();
this.angerByUuid = new Object2IntOpenHashMap<>(angerByUuid.size());
angerByUuid.forEach(pair -> this.angerByUuid.put((UUID)pair.getFirst(), (Integer)pair.getSecond()));
}
private List<Pair<UUID, Integer>> createUuidAngerPairs() {
return (List<Pair<UUID, Integer>>)Streams.concat(
this.suspects.stream().map(entity -> Pair.of(entity.getUUID(), this.angerBySuspect.getInt(entity))),
this.angerByUuid.object2IntEntrySet().stream().map(entry -> Pair.of((UUID)entry.getKey(), entry.getIntValue()))
)
.collect(Collectors.toList());
}
public void tick(ServerLevel level, Predicate<Entity> predicate) {
this.conversionDelay--;
if (this.conversionDelay <= 0) {
this.convertFromUuids(level);
this.conversionDelay = 2;
}
ObjectIterator<Entry<UUID>> objectIterator = this.angerByUuid.object2IntEntrySet().iterator();
while (objectIterator.hasNext()) {
Entry<UUID> entry = (Entry<UUID>)objectIterator.next();
int i = entry.getIntValue();
if (i <= 1) {
objectIterator.remove();
} else {
entry.setValue(i - 1);
}
}
ObjectIterator<Entry<Entity>> objectIterator2 = this.angerBySuspect.object2IntEntrySet().iterator();
while (objectIterator2.hasNext()) {
Entry<Entity> entry2 = (Entry<Entity>)objectIterator2.next();
int j = entry2.getIntValue();
Entity entity = (Entity)entry2.getKey();
Entity.RemovalReason removalReason = entity.getRemovalReason();
if (j > 1 && predicate.test(entity) && removalReason == null) {
entry2.setValue(j - 1);
} else {
this.suspects.remove(entity);
objectIterator2.remove();
if (j > 1 && removalReason != null) {
switch (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 level) {
ObjectIterator<Entry<UUID>> objectIterator = this.angerByUuid.object2IntEntrySet().iterator();
while (objectIterator.hasNext()) {
Entry<UUID> entry = (Entry<UUID>)objectIterator.next();
int i = entry.getIntValue();
Entity entity = level.getEntity((UUID)entry.getKey());
if (entity != null) {
this.angerBySuspect.put(entity, i);
this.suspects.add(entity);
objectIterator.remove();
}
}
}
public int increaseAnger(Entity entity, int offset) {
boolean bl = !this.angerBySuspect.containsKey(entity);
int i = this.angerBySuspect.computeInt(entity, (entityx, integer) -> Math.min(150, (integer == null ? 0 : integer) + offset));
if (bl) {
int j = this.angerByUuid.removeInt(entity.getUUID());
i += j;
this.angerBySuspect.put(entity, i);
this.suspects.add(entity);
}
this.sortAndUpdateHighestAnger();
return i;
}
public void clearAnger(Entity entity) {
this.angerBySuspect.removeInt(entity);
this.suspects.remove(entity);
this.sortAndUpdateHighestAnger();
}
@Nullable
private Entity getTopSuspect() {
return (Entity)this.suspects.stream().filter(this.filter).findFirst().orElse(null);
}
public int getActiveAnger(@Nullable Entity entity) {
return entity == null ? this.highestAnger : this.angerBySuspect.getInt(entity);
}
public Optional<LivingEntity> getActiveEntity() {
return Optional.ofNullable(this.getTopSuspect()).filter(entity -> entity instanceof LivingEntity).map(entity -> (LivingEntity)entity);
}
@VisibleForTesting
protected record Sorter(AngerManagement angerManagement) implements Comparator<Entity> {
public int compare(Entity first, Entity second) {
if (first.equals(second)) {
return 0;
} else {
int i = this.angerManagement.angerBySuspect.getOrDefault(first, 0);
int j = this.angerManagement.angerBySuspect.getOrDefault(second, 0);
this.angerManagement.highestAnger = Math.max(this.angerManagement.highestAnger, Math.max(i, j));
boolean bl = AngerLevel.byAnger(i).isAngry();
boolean bl2 = AngerLevel.byAnger(j).isAngry();
if (bl != bl2) {
return bl ? -1 : 1;
} else {
boolean bl3 = first instanceof Player;
boolean bl4 = second instanceof Player;
if (bl3 != bl4) {
return bl3 ? -1 : 1;
} else {
return Integer.compare(j, i);
}
}
}
}
}
}