While running my games I've run into the situation where multiple PCs are present who do not all speak the same in-game language or who are conversing with a character that only some of them can understand. While the /w Whisper command can be used to be sparing of information it can be tedious to send multiple messages to each individual player, plus it can leave some players twiddling their thumbs wondering if everyone is just being quiet or they're out of the loop. This can be mitigated by the GM sending general chat messages that update everyone on what's happening, but it increases the already overburdened workload from messaging multiple people with the same semi-secret information.
Feature Request
I would love to have a built-in function where Tokens can be assigned Languages they can understand and have it integrated into the chat window where the player/GM can select a language from an impersonated token in which to speak. This message would then be broadcast to the connected players and, depending on their own impersonated tokens' language properties, either transmitted or obfuscated.
Example / Suggested Operation
For example, the GM impersonates a token named Elrond who speaks multiple languages and has in its Token Properties the Languages of Elven, Dwarven, Common, etc. Present are the PC players impersonating tokens for Gandalf, Gimli, and Legolas. Gandalf has multiple Languages, Gimli has Dwarven and Common, and Legolas has Elven and Common.
The GM (as Elrond) says something in Common, and all the players' chat boxes display that information verbatim as all understand that language. GM-as-Elrond then clicks a pull-down at the end of the Chat bar that lists all the Impersonated token's Languages and changes it from Common to Elven. Now when she types something into the box and sends it, only Gandalf and Legolas get the unaltered text while Gimli is left with some other alternate entry. The player Impersonating Legolas switches his Chat window to Elven and responds, meaning the GM and the player Impersonating Gandalf get the clear text while Gimli is once again left in the dark. Finally Gandalf switches to "Black Speech" and utters a phrase. None of the PCs nor the Elrond Token have this in their Languages so they receive gibberish; however the GM still receives the clear message, perhaps in an additional line so that she knows Elrond would not necessarily understand what Gandalf said.
Proposed Additional Features
In the implementation I coded up using macros I had the text either be sent in the clear (prepended with the language spoken) or substituted with a message "[character] speaks in a language you don't understand." However this is suboptimal, as messages varying in length from "No," to a lengthy speech give the same response. This would get repetitive and boring to read quickly and would deprive the player of some degree of context that might still be useful for roleplaying, i.e. quick terse responses vs. longer discussions. While actual translation of the text would be a preposterous ask, I think there's a workable facsimile by using a character shift, i.e. doing a one-for-one character swap of the submitted text with other letters and symbols.
The simplest implementation would be a random character string, but I propose a slightly more complicated scheme. In addition to each token having a property listing known Languages, Campaign Properties could have all available Languages listed and an associated text file that gives an ASCII or Unicode character map. For example, with Elven many letters could map to vowels like a, e, and i or consonants like l, y, t, etc. In contrast, Dwarven could map many letters to k, g, x, r, and u. In this way different languages could be "translated" with unique characteristics. With Unicode support even more elaborate languages could be used, such as mimicking sign language with symbols of hands, computery symbols to represent robots, wingdings or the like to mimic aliens, special characters for regular Earth languages, and more.
Tokens could then inherit from the Campaign Properties and it could be an option to select from a list or a series of check boxes of available languages. This would avoid issues with misspelling the languages in different tokens leading to problems with "translation".
GM's screen wrote:Elrond: Welcome to Rivendell
Elrond: (Elven) Here you will be safe from Mordor's armies.
Legolas: (Elven) But how long can we stay?
Gandalf: (Unknown) Wks Qawn wg wahs kgg, Wks Qawn sf awsm hasw, Wks Qawn wg awois wkk wmk ma mkw kjsfario onjb onwa.
Gandalf: (to GM) One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them.
Gimli: (Dwarven) Would you jerks just speak in something everyone can understand already? It sucks to be left out!
Legolas's screen wrote:Elrond: Welcome to Rivendell
Elrond: (Elven) Here you will be safe from Mordor's armies.
Legolas: (Elven) But how long can we stay?
Gandalf: (Unknown) Wks Qawn wg wahs kgg, Wks Qawn sf awsm hasw, Wks Qawn wg awois wkk wmk ma mkw kjsfario onjb onwa.
Gimli: (Unkown) ᚠᚡᚢᚣᚤ ᚥᚦᚧ ᚨᚩᚪᚫᚬ ᚭᚮᚯᚰ ᚱᚲᚳᚴᚵ ᚶᚷ ᚸᚹᚺᚻᚼᚽᚾᚿᛀ ᛁᛂᛃᛄᛅᛆᛇᛈ ᛉᛊᛋ ᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕ ᛖᛗᛘᛙᛚᛛᛜ? ᛝᛞ ᛟᛠᛡᛢᛣ ᛤᛥ ᚼᚽ ᚡᚢᚣᚤ ᛏᛐᛑ!
(alternately, if unicode characters aren't supported)
Gimli: (Unkown) Kgkxz ghr kurzd grts kqska gt kgshkiskn qpgnaghx gha plksagssza dkawgdk? Wk zkswa hg so kghs wrt!
Macro ImplementationGimli's screen wrote:Elrond: Welcome to Rivendell
Elrond: (Unknown) Aely eal ouse wo elas eosa Asale aelyosa.
Legolas: (Uknown) Ean lae cael esa le yaet?
Gandalf: (Unknown) Wks Qawn wg wahs kgg, Wks Qawn sf awsm hasw, Wks Qawn wg awois wkk wmk ma mkw kjsfario onjb onwa.
Gimli: (Dwarven) Would you jerks just speak in something everyone can understand already? It sucks to be left out!
As mentioned earlier I've already coded up a version in a Framework I was trying to make ages ago for Iron Kingdoms, which I have uploaded for reference. I've at least confirmed it runs in 1.13.2 - the macro itself is on the Lib:token token under "speakEasy", but is called from within character tokens that have a macro consisting of " [r,macro("speakEasy@Lib:token"):"{}"] ".
To use it, select a token that's been initialized (one of the two tokens named Sebastian Cole, or import a new one and add a macro with the above command) and activate the green macro button labeled "Translate and Whisper". The script reads the calling token's Languages and fills out a pull-down for the user to select. The script will also cycle through all tokens looking for those labeled PC and list them as targets for a direct whisper (a feature I included because my players also wanted to whisper to multiple people in one command) or allow you to do a broadcast to the chat window. When run, it first checks each PC token's Owner and what Languages it knows, then sends a whisper of either the message itself or the text "[character] speaks in a language you do not understand" depending on the selected language the message was sent in and the token's knowledge.
If you want to try it out, have two sessions of MapTool running and have the separate windows impersonate different tokens and send text to the chat window using the macro. You can also go into the Token Properties and add more languages (separated by commas) to fool with that.
I've embedded the code below:
Code: Select all
<!--- Get list of connected players --->
[h:players=getAllPlayerNames("json")]
<!--- Get list of tokens that have had F4 macro run on them and languages --->
[h:tokenList=getTokens("json",json.set("{}","pc",1))]
[h: speaks=getProperty("Languages")]
<!--- Construct dialog box input string; labels, list of languages, and list of tokens to talk to --->
<!--- If the token id matches what called the macro, the if statement removes it from list so it can't select itself --->
[h:inputStr=listAppend("langSelected|"+speaks+"|Select language:|LIST|SPAN=FALSE VALUE=STRING","junkVar|value|Select whisper targets:|LABEL|TEXT=FALSE","##")]
[h,foreach(token,tokenList),code: {
[if(token==getSelected()), code: {
};
{
[inputStr=listAppend(inputStr,token+"|"+0+"|"+getName(token)+"|CHECK","##")]
}]
}]
[h:inputStr=listAppend(inputStr,"junkVar|------------------------------OR------------------------------||LABEL|TEXT=TRUE SPAN=TRUE","##")]
[h:inputStr=listAppend(inputStr,"sayAll|1|Say to all players|CHECK","##")]
[h:inputStr=listAppend(inputStr,"message|Enter whispered text here...|Message:|TEXT|WIDTH=30 SPAN=TRUE","##")]
<!--- Open dialog box using constructed string (uses ## as delimiter) --->
[h:status=input(inputStr)]
[h:abort(status)]
<!--- Initiate variables for list that can and can't understand --->
[h:hearableList="{}"]
[h:deafList="{}"]
<!--- Cycle through tokens and add those that speak the selected Language to hearable, others to deaf --->
[r,foreach(token,tokenList,""),code: {
[h:tokenOwners=getOwners(",",token)]
[h:langsSpoken=getProperty("Languages",token)]
[h,if(listContains(langsSpoken,langSelected)): hearableList=json.set(hearableList,token,tokenOwners); deafList=json.set(deafList,token,tokenOwners)]
}]
<!--- If "Say to all players" is not selected, restrict hearableList to those checked --->
<!----- Note that we are already two code levels deep, so we must use multiple separate lines ----->
[if(sayAll), code: {
<!--- If Say All is selected no editing of hearableList required --->
};
{
<!--- Add unselected tokens to deafList and remove them from hearableList --->
[foreach(id, json.fields(hearableList),""), code: {
[h,if(id==getSelected()): temp="true"; temp=id]
[h,if(eval(temp)): "{}"; deafList=json.set(deafList,id,json.get(hearableList,id))]
[h,if(eval(temp)): "{}"; hearableList=json.remove(hearableList,id)]
}]
[h:hearableList=json.set(hearableList,getSelected(),getOwners(",",getSelected()))]
}]
[h:deafList=json.remove(deafList,getOwners())]
<!--- whisperList and silenceList are the names of the players that own the tokens, used for whispering message --->
[h:whisperList="[]"]
[h,foreach(id,json.fields(hearableList)),code: {
[whisperList=json.append(whisperList, json.get(hearableList,id))]
}]
[h:silenceList="[]"]
[h,foreach(id,json.fields(deafList)),code: {
[silenceList=json.append(silenceList, json.get(deafList,id))]
}]
<!--- Output the messages to their respective players --->
[r,g,w(whisperList):"(speaking in "+langSelected+") - <i>"+message+"</i>"]
[r,w(silenceList):"("+getName()+" says something in a language you don't understand.)"]
This scheme only really works when each player is connected to the session with their own instance of MapTool and thus has their own individual chat window. I have no idea what the background coding of the chat window is like, if it supports unicode or anything like that, what degree of sanitation is done on the input, the risks for inadvertently randomly writing a text string that would be parsed as code, and so on.
Group Whisper
I've also included a macro for Group Whisper, as my players found that handy when they wanted to whisper two or more GM/players the same thing.