190 lines
6.5 KiB
Java
190 lines
6.5 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(" ");
|
|
|
|
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().pushPose();
|
|
guiGraphics.pose().translate(guiGraphics.guiWidth() - l * 1.0F - 2.0F, guiGraphics.guiHeight() - 35 - i * (m + 1) * 1.0F, 0.0F);
|
|
guiGraphics.pose().scale(1.0F, 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().popPose();
|
|
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();
|
|
}
|
|
}
|
|
}
|