Skip to content

Commit

Permalink
fix: Make translations work with NPC dialogues (#2362)
Browse files Browse the repository at this point in the history
* refactor: ChatHandler signals to NpcDialogueModel, merge NpcDialogue and ConfirmationlessDialogue types

* feat: Add the new post-processing pipeline for NPC dialogues

* fix: work on making TranscribeMessagesFeature work with translations correctly

* fix: Fix translation cache not being able to load, add shutdown hook for saving, save on world state change

* chore: update special character handling and add notes about characters

* fix: Clear selection dialogues from chat when they disappear (with overlay on)

* feat: Add hover/clickable event support for translations

* fix: add missing return, fix saving logic

* chore: add coment for confirmationlessDialogues
  • Loading branch information
kristofbolyai committed Mar 12, 2024
1 parent 8e767f7 commit dd0360e
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 234 deletions.
Expand Up @@ -16,11 +16,10 @@
import com.wynntils.core.persisted.config.Config;
import com.wynntils.core.persisted.config.ConfigCategory;
import com.wynntils.core.text.StyledText;
import com.wynntils.handlers.chat.event.NpcDialogEvent;
import com.wynntils.handlers.chat.type.NpcDialogueType;
import com.wynntils.mc.event.RenderEvent;
import com.wynntils.mc.event.TickEvent;
import com.wynntils.models.npcdialogue.type.ConfirmationlessDialogue;
import com.wynntils.models.npcdialogue.event.NpcDialogueProcessingEvent;
import com.wynntils.models.npcdialogue.type.NpcDialogue;
import com.wynntils.models.worlds.event.WorldStateEvent;
import com.wynntils.overlays.NpcDialogueOverlay;
Expand Down Expand Up @@ -82,7 +81,7 @@ public class NpcDialogueFeature extends Feature {
// with all the currently displayed dialogues
private List<Component> currentlyDisplayedDialogue = null;
private NpcDialogue currentDialogue = null;
private List<ConfirmationlessDialogue> confirmationlessDialogues = new ArrayList<>();
private List<NpcDialogue> confirmationlessDialogues = new ArrayList<>();
private MessageContainer autoProgressContainer = null;

// Legacy mode
Expand All @@ -93,20 +92,14 @@ public class NpcDialogueFeature extends Feature {
private StyledText displayedHelperMessage = null;

public NpcDialogueFeature() {
super();

// Add this feature as a dependent of the NpcDialogueModel
Models.NpcDialogue.addNpcDialogExtractionDependent(this);
}

@SubscribeEvent
public void onNpcDialogue(NpcDialogEvent e) {
// If the overlay is not enabled, print the dialogue in chat, like Wynn would
if (!Managers.Overlay.isEnabled(npcDialogueOverlay)) {
printDialogueInChat(e.getChatMessage(), e.getType(), e.isProtected());
}

NpcDialogueType dialogueType = e.getType();
public void onNpcDialogue(NpcDialogueProcessingEvent.Pre event) {
NpcDialogue dialogue = event.getDialogue();
NpcDialogueType dialogueType = dialogue.dialogueType();

if (dialogueType == NpcDialogueType.CONFIRMATIONLESS) return;

Expand All @@ -121,19 +114,30 @@ public void onNpcDialogue(NpcDialogEvent e) {
}

if (autoProgress.get() && dialogueType == NpcDialogueType.NORMAL) {
List<StyledText> msg =
e.getChatMessage().stream().map(StyledText::fromComponent).toList();

// Schedule a new sneak key press if this is not the end of the dialogue
if (!msg.isEmpty()) {
scheduledAutoProgressKeyPress = scheduledSneakPress(msg);
if (!dialogue.isEmpty()) {
scheduledAutoProgressKeyPress = scheduledSneakPress(dialogue.currentDialogue());

// Display the auto progress notification
updateAutoProgressNotification();
}
}
}

@SubscribeEvent
public void onNpcDialoguePost(NpcDialogueProcessingEvent.Post event) {
NpcDialogue dialogue = event.getDialogue();

// If the overlay is not enabled, print the dialogue in chat, like Wynn would
if (!Managers.Overlay.isEnabled(npcDialogueOverlay)
&& chatDisplayType.get() == NpcDialogueChatDisplayType.LEGACY) {
printLegacyDialogueInChat(
event.getPostProcessedDialogueComponent(), dialogue.dialogueType(), dialogue.isProtected());
}

// NpcDialogueChatDisplayType.NORMAL mode updates in the onTick method
}

@SubscribeEvent
public void onTick(TickEvent event) {
if (Managers.Overlay.isEnabled(npcDialogueOverlay)) return;
Expand All @@ -148,6 +152,13 @@ public void onTick(TickEvent event) {

// Legacy mode
if (chatDisplayType.get() == NpcDialogueChatDisplayType.LEGACY) {
if (!Models.NpcDialogue.isInDialogue()) {
lastDialogue = null;
removeHelperMessage();
resetAutoProgressContainer();
return;
}

displayHelperMessage();
}
}
Expand Down Expand Up @@ -179,8 +190,8 @@ public ScheduledFuture<?> getScheduledAutoProgressKeyPress() {
return scheduledAutoProgressKeyPress;
}

private ScheduledFuture<?> scheduledSneakPress(List<StyledText> msg) {
long delay = Models.NpcDialogue.calculateMessageReadTime(msg);
private ScheduledFuture<?> scheduledSneakPress(List<StyledText> dialogue) {
long delay = Models.NpcDialogue.calculateMessageReadTime(dialogue);

return autoProgressExecutor.schedule(
() -> McUtils.sendPacket(new ServerboundPlayerCommandPacket(
Expand All @@ -189,42 +200,31 @@ private ScheduledFuture<?> scheduledSneakPress(List<StyledText> msg) {
TimeUnit.MILLISECONDS);
}

private void printDialogueInChat(List<Component> dialogues, NpcDialogueType type, boolean isProtected) {
if (chatDisplayType.get() == NpcDialogueChatDisplayType.NORMAL) {
updateDialogueScreen();
} else {
if (type == NpcDialogueType.NONE || dialogues.isEmpty()) {
lastDialogue = null;
removeHelperMessage();
resetAutoProgressContainer();
return;
}
private void printLegacyDialogueInChat(List<Component> dialogues, NpcDialogueType type, boolean isProtected) {
// If the dialogues are not the same as the last dialogues, print them in chat
if (!Objects.equals(dialogues, this.lastDialogue)) {
this.lastDialogue = dialogues;

// If the dialogues are not the same as the last dialogues, print them in chat
if (!Objects.equals(dialogues, this.lastDialogue)) {
this.lastDialogue = dialogues;

// In legacy mode, just print the dialogues in chat
dialogues.forEach(McUtils::sendMessageToClient);
}
// In legacy mode, just print the dialogues in chat
dialogues.forEach(McUtils::sendMessageToClient);
}

// Either ways, display the helper message
if (type == NpcDialogueType.SELECTION) {
displayedHelperMessage =
StyledText.fromComponent(Component.translatable("feature.wynntils.npcDialogue.selectAnOption")
.withStyle(ChatFormatting.RED));
displayHelperMessage();
} else if (type == NpcDialogueType.NORMAL) {
displayedHelperMessage =
StyledText.fromComponent(Component.translatable("feature.wynntils.npcDialogue.shiftToProgress")
.withStyle(ChatFormatting.GREEN));
displayHelperMessage();
}
// Either ways, display the helper message
if (type == NpcDialogueType.SELECTION) {
displayedHelperMessage =
StyledText.fromComponent(Component.translatable("feature.wynntils.npcDialogue.selectAnOption")
.withStyle(ChatFormatting.RED));
displayHelperMessage();
} else if (type == NpcDialogueType.NORMAL) {
displayedHelperMessage =
StyledText.fromComponent(Component.translatable("feature.wynntils.npcDialogue.shiftToProgress")
.withStyle(ChatFormatting.GREEN));
displayHelperMessage();
}
}

private void updateDialogueScreen() {
List<ConfirmationlessDialogue> confirmationlessDialogues = Models.NpcDialogue.getConfirmationlessDialogues();
List<NpcDialogue> confirmationlessDialogues = Models.NpcDialogue.getConfirmationlessDialogues();
NpcDialogue currentDialogue = Models.NpcDialogue.getCurrentDialogue();

// If there is no dialogue, clear the last dialogue
Expand All @@ -245,11 +245,9 @@ private void updateDialogueScreen() {
// Construct the dialogue screen
List<Component> screenLines = new ArrayList<>();

for (ConfirmationlessDialogue confirmationlessDialogue : confirmationlessDialogues) {
for (NpcDialogue confirmationlessDialogue : confirmationlessDialogues) {
screenLines.add(Component.empty());
screenLines.addAll(confirmationlessDialogue.text().stream()
.map(StyledText::getComponent)
.toList());
screenLines.addAll(confirmationlessDialogue.dialogueComponent());
}

if (currentDialogue != null && !currentDialogue.isEmpty()) {
Expand Down
Expand Up @@ -4,8 +4,6 @@
*/
package com.wynntils.features.utilities;

import com.wynntils.core.WynntilsMod;
import com.wynntils.core.components.Managers;
import com.wynntils.core.components.Models;
import com.wynntils.core.consumers.features.Feature;
import com.wynntils.core.persisted.Persisted;
Expand All @@ -16,19 +14,16 @@
import com.wynntils.core.text.StyledText;
import com.wynntils.core.text.StyledTextPart;
import com.wynntils.handlers.chat.event.ChatMessageReceivedEvent;
import com.wynntils.handlers.chat.event.NpcDialogEvent;
import com.wynntils.handlers.chat.type.NpcDialogueType;
import com.wynntils.models.npcdialogue.event.NpcDialogueProcessingEvent;
import com.wynntils.models.wynnalphabet.WynnAlphabet;
import com.wynntils.models.wynnalphabet.type.TranscribeCondition;
import com.wynntils.utils.colors.ColorChatFormatting;
import com.wynntils.utils.mc.McUtils;
import com.wynntils.utils.type.IterationDecision;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
Expand Down Expand Up @@ -79,37 +74,32 @@ public void onChat(ChatMessageReceivedEvent event) {
}

@SubscribeEvent(priority = EventPriority.HIGHEST)
public void onNpcDialogue(NpcDialogEvent event) {
public void onNpcDialogue(NpcDialogueProcessingEvent.Pre event) {
if (!transcribeNpcs.get()) return;
if (!Models.WynnAlphabet.hasWynnicOrGavellian(event.getChatMessage().toString())) return;

boolean transcribeWynnic = Models.WynnAlphabet.shouldTranscribe(transcribeCondition.get(), WynnAlphabet.WYNNIC);
boolean transcribeGavellian =
Models.WynnAlphabet.shouldTranscribe(transcribeCondition.get(), WynnAlphabet.GAVELLIAN);

if (!transcribeWynnic && !transcribeGavellian) return;

if (!showTooltip.get()) {
event.setCanceled(true);
event.addProcessingStep(future ->
future.thenApply(styledTexts -> transcribeText(styledTexts, transcribeWynnic, transcribeGavellian)));
}

private List<StyledText> transcribeText(
List<StyledText> styledTexts, boolean transcribeWynnic, boolean transcribeGavellian) {
// If there are no Wynnic or Gavellian characters, return the original text
if (styledTexts.stream()
.noneMatch(text -> Models.WynnAlphabet.hasWynnicOrGavellian(text.getStringWithoutFormatting()))) {
return styledTexts;
}

List<Component> transcriptedComponents = event.getChatMessage().stream()
.map(styledText -> getStyledTextWithTranscription(
StyledText.fromComponent(styledText), transcribeWynnic, transcribeGavellian, true))
.map(s -> ((Component) s.getComponent()))
// Transcribe each styled text
return styledTexts.stream()
.map(styledText ->
getStyledTextWithTranscription(styledText, transcribeWynnic, transcribeGavellian, true))
.toList();

if (showTooltip.get()) {
for (Component transcriptedComponent : transcriptedComponents) {
McUtils.sendMessageToClient(transcriptedComponent);
}
} else {
Managers.TickScheduler.scheduleNextTick(() -> {
NpcDialogEvent transcriptedEvent = new WynnTranscriptedNpcDialogEvent(
transcriptedComponents, event.getType(), event.isProtected());
WynntilsMod.postEvent(transcriptedEvent);
});
}
}

private StyledText getStyledTextWithTranscription(
Expand Down Expand Up @@ -229,10 +219,4 @@ private List<StyledTextPart> translatePartUsingMatcher(

return newParts;
}

private static class WynnTranscriptedNpcDialogEvent extends NpcDialogEvent {
protected WynnTranscriptedNpcDialogEvent(List<Component> chatMsg, NpcDialogueType type, boolean isProtected) {
super(chatMsg, type, isProtected);
}
}
}

0 comments on commit dd0360e

Please sign in to comment.