package com.tiedup.remake.dialogue; import java.util.Set; import java.util.regex.Pattern; /** * Detects and applies emotional context to gagged speech. */ public class EmotionalContext { /** * Types of emotional expression that affect speech transformation. */ public enum EmotionType { NORMAL, SHOUTING, WHISPERING, QUESTIONING, PLEADING, DISTRESSED, LAUGHING, CRYING, } // Pattern for extended vowels (nooooo, heeelp, pleeease) private static final Pattern EXTENDED_VOWELS = Pattern.compile( "([aeiou])\\1{2,}", Pattern.CASE_INSENSITIVE ); // Pattern for laughing (haha, hehe, lol) private static final Pattern LAUGHING_PATTERN = Pattern.compile( "(ha){2,}|(he){2,}|(hi){2,}|lol|lmao|xd", Pattern.CASE_INSENSITIVE ); // Pattern for crying indicators private static final Pattern CRYING_PATTERN = Pattern.compile( "\\*(?:sob|cry|sniff|whimper)s?\\*|;-?;|T[-_]T|:'+\\(", Pattern.CASE_INSENSITIVE ); // Pleading keywords private static final Set PLEADING_WORDS = Set.of( "please", "help", "stop", "no", "don't", "dont", "wait", "s'il vous plait", "svp", "aide", "aidez", "arrete", "arreter", "non", "mercy", "spare", "let me go", "release" ); // Whispering indicators private static final Set WHISPERING_INDICATORS = Set.of( "*whisper*", "*whispers*", "*quietly*", "*softly*", "(whisper)", "(quietly)" ); /** * Detect the emotional context of a message or word. * * @param text The text to analyze * @return The detected emotion type */ public static EmotionType detectEmotion(String text) { if (text == null || text.isEmpty()) { return EmotionType.NORMAL; } String lower = text.toLowerCase(); // Check for explicit emotional markers first if (CRYING_PATTERN.matcher(text).find()) { return EmotionType.CRYING; } if (LAUGHING_PATTERN.matcher(text).find()) { return EmotionType.LAUGHING; } // Check for whispering indicators for (String indicator : WHISPERING_INDICATORS) { if (lower.contains(indicator)) { return EmotionType.WHISPERING; } } // Check for shouting (ALL CAPS with 3+ letters, or multiple !) String letters = text.replaceAll("[^a-zA-Z]", ""); if (letters.length() >= 3 && letters.equals(letters.toUpperCase())) { return EmotionType.SHOUTING; } // Require at least 2 exclamation marks for shouting if (text.contains("!!") || text.contains("!?") || text.contains("?!")) { return EmotionType.SHOUTING; } // Check for questioning if (text.endsWith("?") || text.endsWith("??")) { return EmotionType.QUESTIONING; } // Check for distress (extended vowels: nooooo, heeelp) if (EXTENDED_VOWELS.matcher(text).find()) { return EmotionType.DISTRESSED; } // Check for pleading keywords for (String word : PLEADING_WORDS) { if (lower.contains(word)) { return EmotionType.PLEADING; } } return EmotionType.NORMAL; } /** * Apply emotional modifiers to a muffled message. * * @param muffled The muffled text * @param emotion The detected emotion type * @return Modified text with emotional expression */ public static String applyEmotionalModifiers( String muffled, EmotionType emotion ) { if (muffled == null || muffled.isEmpty()) { return muffled; } switch (emotion) { case SHOUTING: // Convert to uppercase and emphasize return ( muffled.toUpperCase() + (muffled.endsWith("!") ? "!" : "!!") ); case WHISPERING: // Softer, shorter sounds return "*" + muffled.toLowerCase() + "*"; case QUESTIONING: // Rising intonation String question = muffled.endsWith("?") ? muffled : muffled + "?"; return question.replace("mm", "mn").replace("nn", "nh"); case PLEADING: // Add whimpering return muffled + "-mm.."; case DISTRESSED: // Extend the dominant sound return extendDominantSound(muffled); case LAUGHING: // Muffled laughter return muffled.replace("mm", "hm").replace("nn", "hn") + "-hm"; case CRYING: // Add sobbing sounds return "*hic* " + muffled + " *mm*"; case NORMAL: default: return muffled; } } /** * Extend the dominant/repeated sound in a muffled word for distress effect. */ private static String extendDominantSound(String text) { if (text == null || text.length() < 2) { return text; } StringBuilder result = new StringBuilder(); char prev = 0; int repeatCount = 0; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == prev && Character.isLetter(c)) { repeatCount++; // Extend repeated sounds if (repeatCount <= 3) { result.append(c); } } else { result.append(c); repeatCount = 1; } prev = c; } // If no extension happened, extend the last vowel-like sound String str = result.toString(); if (str.equals(text)) { // Find last 'm' or 'n' or vowel and extend it int lastExtendable = -1; for (int i = str.length() - 1; i >= 0; i--) { char c = Character.toLowerCase(str.charAt(i)); if (c == 'm' || c == 'n' || c == 'a' || c == 'o' || c == 'u') { lastExtendable = i; break; } } if (lastExtendable >= 0) { char ext = str.charAt(lastExtendable); return ( str.substring(0, lastExtendable + 1) + ext + ext + str.substring(lastExtendable + 1) ); } } return result.toString(); } /** * Get the intensity multiplier for an emotion. * Higher intensity = more muffling effect. * * @param emotion The emotion type * @return Intensity multiplier (1.0 = normal) */ public static float getIntensityMultiplier(EmotionType emotion) { switch (emotion) { case SHOUTING: return 1.3f; // Harder to understand when shouting case WHISPERING: return 0.7f; // Easier (softer, clearer) case DISTRESSED: return 1.2f; case CRYING: return 1.4f; case LAUGHING: return 1.1f; case PLEADING: case QUESTIONING: case NORMAL: default: return 1.0f; } } /** * Check if the emotion should preserve more of the original text. * Some emotions (like whispering) are clearer. */ public static boolean shouldPreserveMore(EmotionType emotion) { return emotion == EmotionType.WHISPERING; } }