Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[SCRIPT] mimic.js: Tokens learn new actions on-the-fly. Alpha/beta testers needed.

I did this to reduce/eliminate my GM prep time for roll20 NPCs / Mooks / etc. The intention is to slap down a token and teach it some actions in under 10 seconds; no fiddling with NPC sheets or macro text. Instant (almost) NPC actions. Think of it like telling the token "Did you see that last thing I did? You can do that now." I started to write a simple script to automate copy-paste from the chat command into token actions -which turned out impractical- and that polymorphed into this beast instead. As far as I can see there isn't an existing script to do this? I think it will be useful that each individual token (not just character tokens) can be taught its own unique actions, which kind of just fell out of getting this to work. I didn't even realize that would be a thing at first. I need alpha/beta testers, particularly to see if the script is stable with other scripts, and find bugs in general. Please don't use your running campaigns because if there is a cascading bug failure it may eat up all of the sandbox memory, and you would need a dev guy to figure out how to fix that. Link to the code: <a href="https://github.com/computed/rolledCode.git" rel="nofollow">https://github.com/computed/rolledCode.git</a> For starters, try this : after installing the script, if you chat !mimic -buttons it should set up two token macro buttons, "[Learn]" and "[Perform]". The learn button should teach your most recent chat message to any selected tokens, and the perform button should make the selected tokens mimic the message you taught them. Learn/Perform should be able to mimic most messages such as rolls, text, emotes, and even messages originating from a character sheet action. The mimic/tokens should re-do rolls, but they can't figure out what macros you used, which is OK only if the first results of the macro is still valid. I'd like to hear back what fails to mimic correctly. (Tokens are also prevented from using any api script calls, so I wonder if it can mimic powercards, or other api reaults, for example. Does it work? Any powercard users?) Also testers: I am particularly interested if you make learn/perform visible to players, does the script gets confused between your chats and the other players' chats? Your selected tokens should only learn to mimic the chat messages YOU send. For the next beta version, I want copies of tokens to remember the actions you have taught them.
1431747203
The Aaron
Pro
API Scripter
Cute! Not sure I'd end up using it personally, but I can see how it could be very useful for a particular type of game. =D The only bugs I found were that resetOperation() and cleanOperation() are missing their msg arguments. Adding them in caused them to work fine. One note, the way you are using null , you should really be using undefined . If you're coming from another language, what you are used to using null for, javascript uses undefined for. In javascript, undefined is the nothing state, whereas null is the "this space intentionally left blank" state. The only time you need to use null is in the rare cases where you need to define a set of empty properties in an object, which almost never happens. By default, use undefined . Subbing in undefined didn't change the behavior of the script. I noticed you commented in the script that you can't whisper in the api to characters (true, but read on) and also that you can't whisper to players (not true). Whispering to characters as a human by typing /w CharacterName works by whispering to each of the controlling players. Whispering to players as a human by typing /w Player works. In the API, you can't whisper to a character, but back to that in a sec. In the API you can whisper to a player just fine, with the small caveat that you can only specify the first word in their name. So to whisper me, you'd do: sendChat('EdScript', '/w The some message'); That's highly obnoxious I realize. As a human you can use /w "The Aaron" some message but you can't do that in the API right now. (The change is on Dev and will hit Prod sometime in the near future). So, back to characters. You can't whisper to them, true, but you can whisper to each of the controlling players. Here's an excerpt from a script I'm working on right now that does just that, along with just sending the chat to everyone if all is one of the controllers. if(_.contains(char.get('controlledby').split(/,/),'all')){ sendChat('(To '+char.get('name')+')',pmsg); } else { _.each(char.get('controlledby').split(/,/),function(pid){ var p=getObj('player',pid); if(p) { sendChat('(To '+char.get('name')+')','/w '+ (p.get('_displayname').split(' ')[0])+ ' '+pmsg ); } }); } Cheers!
1431747955
The Aaron
Pro
API Scripter
One other minor note, you could likely ignore messages from playerid API when storing: state.mimicMsgStore { . "mimicMsgStore": { . . "API": { . . . "content": "Tokens now forget what they learned.", . . . "playerid": "API", . . . "type": "general", . . . "who": "Mimic script" . . } . } }
1431749813

Edited 1431750547
@'The Aaron' Thanks for explaining undefined. I was having trouble with a return value for a function used in an expression, and thought it was because of undefined as an operand, but maybe it was something else I was doing. So the places I am converting undefined to null like this are unnecessary?! Or possibly incorrect? var oldval = state.mimicMsgStore[key] || null; I needed to store the API values in a previous version, I was allowing the tokens to mimic messages generated from inside API calls, but that was confusing from the player perspective so I decided to short-circuit them. I forgot they were still in the storage; or maybe I wasn't 100% sure the api calls are working the way I think they work (if that makes sense..) Do you notice my comment about being concerned with synchronization? Are you able to confirm that it is a non-issue? Thanks for code checking me! I certainly wasn't expecting that. Bed time now..
1431750976
The Aaron
Pro
API Scripter
I'd say your use of null is not idiomatic for Javascript. =D It doesn't cause a problem, but it's "not the way we do it round here!" =D Re: Synchronization comments: I did. Javascript is single threaded, but asynchronous. It's pretty nearly cooperative multitasking, if you want to think of it that way. Your code isn't likely to run into any issues from it though. If you start doing things that require callbacks (sendChat() to parse a roll, some of the get/set stuff for notes and bios, setTimeout()/setInterval() for delayed actions), you might want to consider passing context via a closure, or just create a context object to pass to the operations from which they can get special settings. Speaking of code walk throughs, your whisperize() function originally raised a red flag for me with it's destructive changes to the argument, but I saw later you were passing a _.clone()'d copy in. Personally, I'd put the _.clone() in whisperize() so that calls to it won't silently change the input. I can see an argument for the other way, but I prefer my functions to have no side effects unless it's really obvious. One other idiomatic curiosity: 'playerid' in msg I'd just write that as msg.playerid. No player will have a falsey playerid, so it's safe. if a message didn't have a playerid, it would just be undefined, which would be falsey and drop out as expected. I love code, so no worries on the read through. =D It's always interesting to see how other people write things.
1431753733
Lithl
Pro
Sheet Author
API Scripter
Ed B. said: @'The Aaron' Thanks for explaining undefined. I was having trouble with a return value for a function used in an expression, and thought it was because of undefined as an operand, but maybe it was something else I was doing. So the places I am converting undefined to null like this are unnecessary?! Or possibly incorrect? var oldval = state.mimicMsgStore[key] || null; This is definitely an odd construct to see in JavaScript, I'll admit. Using logical-or in this fashion is generally used to specify a default usable value, so that you don't need to do things like perform null-guards. Normally, you see this pattern in a snippet where you're calling some method of oldval or accessing one of its properties, so you supply a default value to ensure that the variable has the expected method/property. Something like this: var myString = myMap[myKey] || ''; for (var i = 0; i &lt; myString.length; i++) { // ... } With a default value of an empty string, myString.length is a valid property (with a value of zero), and the loop iterates zero times, as opposed to: var myString = myMap[myKey] || null; if (myString !== null) { for (var i = 0; i &lt; myString.length; i++) { // ... } } If there isn't really a reasonable default value, in the general case I would just create a guard by checking for falseyness: var myVar = getMyVar(); if (myVar) { // ... } In the specific case of an object's property potentially being undefined, you can check if the property exists as an alternative: var myVar; if (_.has(myMap, myKey)) { myVar = myMap[myKey]; // ... }
So how I am understanding this... This script will copy the last thing ran through in the chat? if I run the Learn command?
1431775348

Edited 1431775616
So how I am understanding this... This script will copy the last thing ran through in the chat? if I run the Learn command? Essentially that's it. It's slightly more subtle than how you phrased it. The "last thing ran through in the chat" will be keyed to the selected token by Learn. Whenever that token is selected, the script's Perform will try to reproduce the chat you keyed to that token. So each token can "remember" a different chat message.
1431776535

Edited 1431777067
The Aaron said: I noticed you commented in the script that you can't whisper in the api to characters (true, but read on) and also that you can't whisper to players (not true). Whispering to characters as a human by typing /w CharacterName works by whispering to each of the controlling players. Whispering to players as a human by typing /w Player works. In the API, you can't whisper to a character, but back to that in a sec. In the API you can whisper to a player just fine, with the small caveat that you can only specify the first word in their name. So to whisper me, you'd do: sendChat('EdScript', '/w The some message'); OK, if this works on the prod server.. then I can add it to !mimic. I don't need to whisper to characters. I'll put it on the todo list. That explains why whispers to GM were working for me. What happens if "the first word in their name" is ambiguous?
Ed B. said: So how I am understanding this... This script will copy the last thing ran through in the chat? if I run the Learn command? Essentially that's it. It's slightly more subtle than how you phrased it. The "last thing ran through in the chat" will be keyed to the selected token by Learn. Whenever that token is selected, the script's Perform will try to reproduce the chat you keyed to that token. So each token can "remember" a different chat message. Curious. Can players access this too? Does it overwrite the last thing the token "learned"? So potentionally you could put the learn/perform commands into the same macro and it will immediately replicate whatever command was used last? No matter who called it?
Saevar L. said: Curious. Can players access this too? Does it overwrite the last thing the token "learned"? So potentionally you could put the learn/perform commands into the same macro and it will immediately replicate whatever command was used last? No matter who called it? I don't have a non-mentor account, so I didn't test it for non-mentors, but it should work if the game creator is a mentor with script priviledges. The GM has to give them control of the token in question so they can select it of course. Right now each token can only learn one thing at a time, so Learn will change the only thing that particular token knows.The token doesn't care who "taught" it, the token can have two masters, so to speak. Of course, the GM can always put another token on the table so you don;t have to share your "learners". "immediately replicate" is not exactly how I would put it. The token waits until you ask to perform with !mimic -perform.
1431784631

Edited 1431790159
Saevar L. said: Curious. Can players access this too? Does it overwrite the last thing the token "learned"? So potentionally you could put the learn/perform commands into the same macro and it will immediately replicate whatever command was used last? No matter who called it? Oh, I think I can see where you might be going. If two players have control of the same token, player A might be able to performs actions learned from player B's macros or character sheet. I didn't think of that! That is a bit weird. And possibly useful.
Its useful for the concept I was pitching towards Aaron earlier. If I could say, pump out a macro which is basically a con saving throw. Could they run your command with the selected token, which will do the same thing. This is useful for playing a game with newbies/asking for checks from players. Other uses might be just everyone needing to do the same check as someone else. Possibly its great for learning statuses or words for checking information. For example if a person wants to update their list of ongoing buffs. Shouldn't be too hard to pass it as a whisper command, Write in it. Learn it again. etc
1431788768
The Aaron
Pro
API Scripter
Ed B. said: OK, if this works on the prod server.. then I can add it to !mimic. I don't need to whisper to characters. I'll put it on the todo list. That explains why whispers to GM were working for me. What happens if "the first word in their name" is ambiguous? It will go to the first one alphabetically. Sad panda. I'm looking forward to the quoted syntax. =D
Saevar L. said: Its useful for the concept I was pitching towards Aaron earlier. If I could say, pump out a macro which is basically a con saving throw. Could they run your command with the selected token, which will do the same thing. This is useful for playing a game with newbies/asking for checks from players. Other uses might be just everyone needing to do the same check as someone else. Possibly its great for learning statuses or words for checking information. For example if a person wants to update their list of ongoing buffs. Shouldn't be too hard to pass it as a whisper command, Write in it. Learn it again. etc That should work, and you could set up a token that is not a character of course. Not my motivation for building !mimic, but it should work. The problem you will have to deal with is the mimic-token cannot figure out what macros/attribs were used to produce the original message. So when character stats change, for example, you will have to "teach/learn" the token again so it has the new stats. Potentially, re-learning could be automatically triggered by ANOTHER macro that depends on / uses !mimic.
ofc thats why i'd use @{selected|con_mod}+ so it shouldn't need to remember the variables. just the macro?
1431795174

Edited 1431876726
Unfortunately the macro is expanded before it gets to the chat window so before the token can learn it. You can try proceeding it with a backquote but I think it then becomes invisible to the mimic script. (Update - This answer was corrected in the following post)
1431876601

Edited 1431916679
Saevar L. said: ofc thats why i'd use @{selected|con_mod}+ so it shouldn't need to remember the variables. just the macro? Ed B. said: Unfortunately the macro is expanded before it gets to the chat window so before the token can learn it. You can try proceeding it with a backquote but I think it then becomes invisible to the mimic script. CORRECTION: I just checked and the backquote trick worked. I thought the backquote-literals are filtered out, but I was wrong. This may do what you want: `@{Character|con_mod} Followed by the appropriate mimic-learn command !mimic -learn Notice the back-quote at the beginning that tells your client not to expand the macro values, but simply text the string into the chat message exactly literally as typed. So when you ask the token to learn the string, it gets exactly what you typed without rolls or expanded macros or attrs. When the token performs and thereby mimics the string, it will be expanded then - at performance time. (Don't use selected in your attr string b/c with this trick the mimic-token will be selected when the string is expanded. You don't want the mimic-token's con_mod, you want Character's con_mod) There doesn't seem to be any way to whisper (/w) the literal string command. The backquote has to be first on the line. Let me know if it works for you.
Ah I see how this works now. The logic of it... Hmmm it works but not in the way i'd like. I understand the principle behind it though. It works though at least.
Script Update version 0.3. Tokens will now whisper to players if taught to do so. Tokens will keep track of a simple ammo count in a token bar if requested.