193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.client.gui.components;
 | |
| 
 | |
| import com.google.common.collect.Lists;
 | |
| import com.mojang.blaze3d.audio.ListenerTransform;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Comparator;
 | |
| import java.util.Iterator;
 | |
| import java.util.List;
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.client.Minecraft;
 | |
| import net.minecraft.client.gui.GuiGraphics;
 | |
| import net.minecraft.client.resources.sounds.SoundInstance;
 | |
| import net.minecraft.client.sounds.SoundEventListener;
 | |
| import net.minecraft.client.sounds.SoundManager;
 | |
| import net.minecraft.client.sounds.WeighedSoundEvents;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.util.ARGB;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class SubtitleOverlay implements SoundEventListener {
 | |
| 	private static final long DISPLAY_TIME = 3000L;
 | |
| 	private final Minecraft minecraft;
 | |
| 	private final List<SubtitleOverlay.Subtitle> subtitles = Lists.<SubtitleOverlay.Subtitle>newArrayList();
 | |
| 	private boolean isListening;
 | |
| 	private final List<SubtitleOverlay.Subtitle> audibleSubtitles = new ArrayList();
 | |
| 
 | |
| 	public SubtitleOverlay(Minecraft minecraft) {
 | |
| 		this.minecraft = minecraft;
 | |
| 	}
 | |
| 
 | |
| 	public void render(GuiGraphics guiGraphics) {
 | |
| 		SoundManager soundManager = this.minecraft.getSoundManager();
 | |
| 		if (!this.isListening && this.minecraft.options.showSubtitles().get()) {
 | |
| 			soundManager.addListener(this);
 | |
| 			this.isListening = true;
 | |
| 		} else if (this.isListening && !this.minecraft.options.showSubtitles().get()) {
 | |
| 			soundManager.removeListener(this);
 | |
| 			this.isListening = false;
 | |
| 		}
 | |
| 
 | |
| 		if (this.isListening) {
 | |
| 			ListenerTransform listenerTransform = soundManager.getListenerTransform();
 | |
| 			Vec3 vec3 = listenerTransform.position();
 | |
| 			Vec3 vec32 = listenerTransform.forward();
 | |
| 			Vec3 vec33 = listenerTransform.right();
 | |
| 			this.audibleSubtitles.clear();
 | |
| 
 | |
| 			for (SubtitleOverlay.Subtitle subtitle : this.subtitles) {
 | |
| 				if (subtitle.isAudibleFrom(vec3)) {
 | |
| 					this.audibleSubtitles.add(subtitle);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!this.audibleSubtitles.isEmpty()) {
 | |
| 				int i = 0;
 | |
| 				int j = 0;
 | |
| 				double d = this.minecraft.options.notificationDisplayTime().get();
 | |
| 				Iterator<SubtitleOverlay.Subtitle> iterator = this.audibleSubtitles.iterator();
 | |
| 
 | |
| 				while (iterator.hasNext()) {
 | |
| 					SubtitleOverlay.Subtitle subtitle2 = (SubtitleOverlay.Subtitle)iterator.next();
 | |
| 					subtitle2.purgeOldInstances(3000.0 * d);
 | |
| 					if (!subtitle2.isStillActive()) {
 | |
| 						iterator.remove();
 | |
| 					} else {
 | |
| 						j = Math.max(j, this.minecraft.font.width(subtitle2.getText()));
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				j += this.minecraft.font.width("<") + this.minecraft.font.width(" ") + this.minecraft.font.width(">") + this.minecraft.font.width(" ");
 | |
| 				if (!this.audibleSubtitles.isEmpty()) {
 | |
| 					guiGraphics.nextStratum();
 | |
| 				}
 | |
| 
 | |
| 				for (SubtitleOverlay.Subtitle subtitle2 : this.audibleSubtitles) {
 | |
| 					int k = 255;
 | |
| 					Component component = subtitle2.getText();
 | |
| 					SubtitleOverlay.SoundPlayedAt soundPlayedAt = subtitle2.getClosest(vec3);
 | |
| 					if (soundPlayedAt != null) {
 | |
| 						Vec3 vec34 = soundPlayedAt.location.subtract(vec3).normalize();
 | |
| 						double e = vec33.dot(vec34);
 | |
| 						double f = vec32.dot(vec34);
 | |
| 						boolean bl = f > 0.5;
 | |
| 						int l = j / 2;
 | |
| 						int m = 9;
 | |
| 						int n = m / 2;
 | |
| 						float g = 1.0F;
 | |
| 						int o = this.minecraft.font.width(component);
 | |
| 						int p = Mth.floor(Mth.clampedLerp(255.0F, 75.0F, (float)(Util.getMillis() - soundPlayedAt.time) / (float)(3000.0 * d)));
 | |
| 						guiGraphics.pose().pushMatrix();
 | |
| 						guiGraphics.pose().translate(guiGraphics.guiWidth() - l * 1.0F - 2.0F, guiGraphics.guiHeight() - 35 - i * (m + 1) * 1.0F);
 | |
| 						guiGraphics.pose().scale(1.0F, 1.0F);
 | |
| 						guiGraphics.fill(-l - 1, -n - 1, l + 1, n + 1, this.minecraft.options.getBackgroundColor(0.8F));
 | |
| 						int q = ARGB.color(255, p, p, p);
 | |
| 						if (!bl) {
 | |
| 							if (e > 0.0) {
 | |
| 								guiGraphics.drawString(this.minecraft.font, ">", l - this.minecraft.font.width(">"), -n, q);
 | |
| 							} else if (e < 0.0) {
 | |
| 								guiGraphics.drawString(this.minecraft.font, "<", -l, -n, q);
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						guiGraphics.drawString(this.minecraft.font, component, -o / 2, -n, q);
 | |
| 						guiGraphics.pose().popMatrix();
 | |
| 						i++;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onPlaySound(SoundInstance sound, WeighedSoundEvents accessor, float range) {
 | |
| 		if (accessor.getSubtitle() != null) {
 | |
| 			Component component = accessor.getSubtitle();
 | |
| 			if (!this.subtitles.isEmpty()) {
 | |
| 				for (SubtitleOverlay.Subtitle subtitle : this.subtitles) {
 | |
| 					if (subtitle.getText().equals(component)) {
 | |
| 						subtitle.refresh(new Vec3(sound.getX(), sound.getY(), sound.getZ()));
 | |
| 						return;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			this.subtitles.add(new SubtitleOverlay.Subtitle(component, range, new Vec3(sound.getX(), sound.getY(), sound.getZ())));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	record SoundPlayedAt(Vec3 location, long time) {
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	static class Subtitle {
 | |
| 		private final Component text;
 | |
| 		private final float range;
 | |
| 		private final List<SubtitleOverlay.SoundPlayedAt> playedAt = new ArrayList();
 | |
| 
 | |
| 		public Subtitle(Component text, float range, Vec3 location) {
 | |
| 			this.text = text;
 | |
| 			this.range = range;
 | |
| 			this.playedAt.add(new SubtitleOverlay.SoundPlayedAt(location, Util.getMillis()));
 | |
| 		}
 | |
| 
 | |
| 		public Component getText() {
 | |
| 			return this.text;
 | |
| 		}
 | |
| 
 | |
| 		@Nullable
 | |
| 		public SubtitleOverlay.SoundPlayedAt getClosest(Vec3 location) {
 | |
| 			if (this.playedAt.isEmpty()) {
 | |
| 				return null;
 | |
| 			} else {
 | |
| 				return this.playedAt.size() == 1
 | |
| 					? (SubtitleOverlay.SoundPlayedAt)this.playedAt.getFirst()
 | |
| 					: (SubtitleOverlay.SoundPlayedAt)this.playedAt
 | |
| 						.stream()
 | |
| 						.min(Comparator.comparingDouble(soundPlayedAt -> soundPlayedAt.location().distanceTo(location)))
 | |
| 						.orElse(null);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public void refresh(Vec3 location) {
 | |
| 			this.playedAt.removeIf(soundPlayedAt -> location.equals(soundPlayedAt.location()));
 | |
| 			this.playedAt.add(new SubtitleOverlay.SoundPlayedAt(location, Util.getMillis()));
 | |
| 		}
 | |
| 
 | |
| 		public boolean isAudibleFrom(Vec3 location) {
 | |
| 			if (Float.isInfinite(this.range)) {
 | |
| 				return true;
 | |
| 			} else if (this.playedAt.isEmpty()) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				SubtitleOverlay.SoundPlayedAt soundPlayedAt = this.getClosest(location);
 | |
| 				return soundPlayedAt == null ? false : location.closerThan(soundPlayedAt.location, this.range);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public void purgeOldInstances(double displayTime) {
 | |
| 			long l = Util.getMillis();
 | |
| 			this.playedAt.removeIf(soundPlayedAt -> l - soundPlayedAt.time() > displayTime);
 | |
| 		}
 | |
| 
 | |
| 		public boolean isStillActive() {
 | |
| 			return !this.playedAt.isEmpty();
 | |
| 		}
 | |
| 	}
 | |
| }
 |