201 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |