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 .
×

JRPG Chat System: Sorta working but need help.

First of all I'd like to say, very belatedly, thank you to The Aaron and William C. for comments in my FP Dungeon script. I'm still working on it but put it on the back burner to get this done. Now, I have modified a script I found that was used for creating a waiting screen text and using rollable tables I have been able to create a very simple method of having players and GM use the chat bar but have it pop up like in a JRPG. However, the script isn't elegant enough to deal with multiple people spamming it. I was wondering if the wizards here would know of a good way to solve this problem. I'm thinking I need to have a separate script that will ingest all the chat logs using the "!say" tag and store it into a list (a state) that will be accessed by the script I currently have. The current script would need a for loop to iterate through the list and then once gone through an element, grab an updated version of the list, and then remove the currently held loop element. For this idea, im not sure its the most efficient or if javascript has the means to do this the way I imagine (I think in python). So what do you all think? (this is how it works right now. type !say anything and it will read the ID of the chat, use a rollable table to display your icon, and then generate text on the object I have on the GM layer before removing themselves after some time.) Code: on("ready",function(){     state.charText = undefined;     on("chat:message",function(msg){         //'use strict';         //var players=findObjs({_type:'player'});         //_.each(players,function (obj){         //sendChat(sendingPlayer, 'Player '+obj.get('displayname')+' has id: '+obj.get('id'))         //log('Player '+obj.get('displayname')+' has id: '+obj.get('id'));         //});         //log("start");         //Need to make a buffer for text.         var chatLog = [];         var textBox;         var curToken = findObjs({                type:"graphic",         });         var sendingPlayer = msg.playerid;         if(msg.type=="api" && msg.content.indexOf("!say")==0){             log(sendingPlayer);             pageid:Campaign().get("playerpageid");             var token = findObjs({                 name:"chatBitch",                 type:"graphic",                 pageid:Campaign().get("playerpageid")             });             var textLocation = token[0];             var InputString = msg.content.replace('!say', '');             var time = 6000;             //TRYING TO PUT CHAT TO LIST             var textMSG = sendingPlayer + ":"             var userTEXT  = textMSG.concat(' ', InputString);             chatLog.push(userTEXT) //Add text to list             //NEED TO ADD FOR LOOP TO GO THROUGH THE LIST AND THEN DELETE CONTENT AFTER SHOWING             if (!state.charText){                 log("!state.charText");                 //log(!state.charText);                 var topOffest = textLocation.get("top") - 1;                 sendChat(sendingPlayer, sendingPlayer);                 var textBox = createObj("text",                 {                     text:InputString,                     font_size:36,                     font_family:"Contrail One",                     color:"rgb(0,0,0)",                     layer:"objects",                     pageid:Campaign().get("playerpageid"),                     left:textLocation.get("left"),                     top:topOffest                 });                 log("state.charText iddddd");                 state.charText = {                     module:"Old_School_Dungeon_Test",                     textID:textBox.get("id")                 };                 log(sendingPlayer);                 log('sendingPlayer');                 if (sendingPlayer == "-Mtjru7YtFnNCi18v8Fp"){                     sendChat(sendingPlayer, "George Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|1 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-MteTpkfNZ7TbJwvD0Rt"){                     sendChat(sendingPlayer, "Brian Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|3 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-My00c7Dtfov7FRk-Og0"){                     sendChat(sendingPlayer, "Sean Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|4 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-N-owesO58dLMiomCvih"){                     sendChat(sendingPlayer, "Ricco Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|2 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-MtdanhLJHL8GqdQxG2B"){                     sendChat(sendingPlayer, "GM Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|6 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-N-owexBvjBD-73Fx2p_"){                     sendChat(sendingPlayer, "D Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|7 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 if (sendingPlayer == "-My00i1Dc_awKRg9kcGu"){                     sendChat(sendingPlayer, "EuroTom Talked");                     sendChat(sendingPlayer, "!token-mod --set currentside|5 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 }                 log("TimeOut");                 setTimeout(function() {                     if(textBox!==undefined){                         textBox.remove();                         sendChat(sendingPlayer, "!token-mod --set currentside|8 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ");                     }                 state.charText = undefined;                 log("ChatText");                 }, time);             log("endMain");             }             //else //{                 //log('elsed')                 //textBox = getObj("text",state.charText.textID);             //}     });         on("chat:message",function(msg){             if(msg.type=="api" && msg.content.indexOf("!clearText")==0){                 log("CHAT:MESSAGE");                 textBox = getObj("text",state.charText.textID);                 if(textBox!==undefined){                     textBox.remove();                 }                 state.charText = undefined;             }         });         msg.content = _.chain(msg.inlinerolls)         .reduce(function(m,v,k){             m['$[['+k+']]']=v.results.total || 0;             sendChat('', "return m");             return m;         },{})         .reduce(function(m,v,k){             return m.replace(k,v);         },msg.content)         .value(); });
1656471198

Edited 1656471623
GiGs
Pro
Sheet Author
API Scripter
You might need to look into the timeout function to process messages slowly. That's an interesting looking script, but it looks like you've hardcoded bits (like palyer ids). Can you share a link to the original script? You shouldn't need to hardcode the players - you could automate finding a list of players, then grabbing their bio image or something similar, or in your rollable table method, have the table's Item column be used and add the player name there, then select the correct image by that (and a default placeholder image for player-not-found errors)
1656471300

Edited 1656471426
GiGs
Pro
Sheet Author
API Scripter
The Almighty Tallest said: I was wondering if the wizards here would know of a good way to solve this problem. I'm thinking I need to have a separate script that will ingest all the chat logs using the "!say" tag and store it into a list (a state) that will be accessed by the script I currently have. The current script would need a for loop to iterate through the list and then once gone through an element, grab an updated version of the list, and then remove the currently held loop element. For this idea, im not sure its the most efficient or if javascript has the means to do this the way I imagine (I think in python). So what do you all think? Your plan here should be possible, but you shouldn't need a separate script - just modify the existing script to include that feature. If players are spamming text (multiple people typing at the same time0, you also want to think about how to handle people sending messages close together so that one appears and then is overwritten too quickly to be read. Maybe have more text boxes showing previous lines of text, of decreasing size above the central message?
1656497709

Edited 1656498010
Oosh
Sheet Author
API Scripter
Edit - whoops, I left console logging in. Pretend all those console.log, console.info and console.warn calls are Roll20's log() instead :) This might be above and beyond what you need, but a ring buffer / simple event queue class will give you a self-contained mechanism for controlling the output of your script, as well as handle data coming in too quickly and give you an easy way to adjust things like delay between output messages. It's... possibly a bit overwhelming, and you don't need to encapsulate it in a class, but it helps to segregate all the code since, once it works, you shouldn't really need to touch the code again (beyond maybe tweaking the properties). It also means your actual Roll20 code becomes hugely simplified as it no longer needs to deal with queueing or throttling data. Modern Node/JS allows private methods and properties in Class definitions, but we can't use those on Roll20 so I've used the leading _underscore tradition of marking properties and methods which should not be accessed from outside the class. Either way, pretty much any language is better for writing classes than Python... :) class EventQueue {   _buffer = []; // Array to hold the queued events   _state = '' ; // Internal state of the queue   _writeIndex = 0 ; // Index of the buffer to write   _readIndex = 0 ; // Index of the buffer to read   /* These are the properties to customise depending on your needs */   _bufferLength = 8 ; // Max number of events the queue can hold   _eventDelay = 50 ; // Number of milliseconds to wait between processing events   _externalHandler = () => {}; // The function to pass events out to   constructor ( queueData = {}) {     // Pass in some basic properties for the queue     Object . assign ( this , {       name : queueData . name || 'newEventQueue' ,       _bufferLength : queueData . length || 8 ,       _externalHandler : queueData . handler ,       _eventDelay : queueData . delay > - 1 ? queueData . delay : 50 ,       // Whatever other properties you want to assign to the queue     });     // Ensure we have a valid function to pass events out to, otherwise the queue does nothing.     if ( typeof ( this . _externalHandler ) !== 'function' ) return {};     this . _buffer = Array ( this . _bufferLength ). fill ( null ); // Initialise the buffer with null values     this . state = 'IDLE' ; // Set queue state to ready, unless you have other init() functions   }   // Handle queue state   validStates = [ 'IDLE' , 'BUSY' , 'ERROR' , 'OVERFLOW' ];   get state () { return this . _state }   set state ( newState ) { this . _state = this . validStates . includes ( newState ) ? newState : this . _state }   _writeBuffer ( newEvent ) {     if ( this . state === 'ERROR' || this . state === 'OVERFLOW' ) return ; // No point writing to the buffer if the queue is full or broken.     if ( this . _buffer [ this . _writeIndex ] !== null ) this . _bufferOverflow (); // If the target write index is not null, the buffer is full     // You can do simple validation here, but it's better done elsewhere, like the public methods at the bottom     if ( newEvent . name && newEvent . data ) {       this . _buffer [ this . _writeIndex ] = newEvent ;       // This next line is important - this creates a ring buffer, the write index is incremented by 1, but returns       // to index 0 once it hits the max length due to the modulo operator       this . _writeIndex = ( this . _writeIndex + 1 ) % this . _bufferLength ;       if ( this . state === 'IDLE' ) this . _readBuffer (); // Start the _readBuffer() method if the queue is idle     }   }   _readBuffer () {     if ( this . state === 'BUSY' ) return ; // Already reading from the buffer, you don't want this method running twice     else if ( this . state === 'IDLE' ) {       // Ideally we'll land here - set the queue to BUSY and start reading from the buffer in the READ loop       this . state = 'BUSY' ;     }     else if ( this . state === 'ERROR' ) {       // Bad things have happened, abort       console . warn ( ` ${this . name } : buffer is in ERROR state!` );       return ;     }     else if ( this . state === 'OVERFLOW' ) {       // You probably want to continue reading on an overflow or the buffer will never clear       console . warn ( ` ${this . name } : buffer is full! Continuing read...` );     }     else {       // We shouldn't be able to land here as we've already covered all valid states       console . error ( ` ${this . name } : +++ Out of cheese error +++ Redo from start...` );     }     // The main READ loop:     const readLoop = setInterval (() => {       if ( this . _buffer [ this . _readIndex ] === null ) clearInterval ( readLoop );       else {         const currentEvent = this . _buffer [ this . _readIndex ];         // Consume event - whatever your script does, pass the event to your main function here         this . _externalHandler ( currentEvent );         this . _buffer [ this . _readIndex ] = null ; // Clear this index of the buffer once the event is consumed         this . _readIndex = ( this . _readIndex + 1 ) % this . _bufferLength ; // Same as _writeBuffer(), increment the index       }     }, this . _eventDelay );     // If the loop exits, we're out of events to consume and the queue is now IDLE     // Set the state to IDLE, but only if it's BUSY. We don't want to clear errors/overflows accidentally     if ( this . state === 'BUSY' ) this . state = 'IDLE' ;   }   _bufferOverflow () {     // Your use case dictates how to handle an overflow. This is a simple example that just     // times out to let _readBuffer() catch up     this . state = 'OVERFLOW' ;     console . warn ( `Buffer is full! Disabling write access temporarily` );     setTimeout (() => {       const nonEmptySlots = this . _buffer . filter ( v => v !== null );       if ( nonEmptySlots . length ) {         // The buffer hasn't cleared after 3 seconds. Something bad has happened         // 3 seconds is a looooong time         this . state = 'ERROR' ;         console . error ( ` ${this . name } : buffer is jammed! Recommend immediate panic!` );       } else {         this . state = 'IDLE' ;         console . info ( ` ${this . name } : buffer has recovered, write access re-enabled.` );       }     }, 3000 ); // This timer should be a property of the EventQueue, not hard-coded, so it's easy to change   }   // Then the public methods go down here, the ones to access from outside this Class   // At its simplest, all you need is a public method to add something to the queue   addEvent ( newEvent ) {     // Do some validation - make sure all required data is valid before passing it to the buffer     console . info ( ` ${this . name } : new event received: ` , newEvent )     this . _writeBuffer ( newEvent );   } } That's probably a huge amount to take in, and more than likely it's more than what you need. But decoupling the event queue from your Roll20 functions makes it re-usable (on the off chance you'll need one again) and makes the core of your script super simple. You no longer need to handle timings or throttle input - just throw events at the queue at let it handle itself, and provide a handler function to receive the data back as the queue gives it up. // Your function that receives events from the queue // This just outputs to sendChat const myScriptOutput = ( newEvent ) => {   sendChat ( newEvent . creator , newEvent . data ); } // Create an instance of the EventQueue class defined above const eventQueue = new EventQueue ({   name : 'chatEventQueue' ,   length : 16 ,   delay : 50 ,   handler : myScriptOutput // Register the above function to receive events from the queue }); // A simple Roll20 chat handler on ( 'chat:message' , ( msg ) => {   if ( msg . type === 'api' && / ^ !myscript/ . test ( msg . content )) {     // Create your event object with the data scraped from the chat message     const newChatEvent = { name : 'playerChat' , data : 'Blah blah blah!' , creator : msg . who , created : Date . now () }     eventQueue . addEvent ( newChatEvent );   } });
@Gigs here's the original code that was done by a youtuber named NickOlivo. var isItRunning ; on ( "change:campaign:playerpageid" , function ( ) { var currentPage = getObj ( "page" , Campaign ( ) . get ( "playerpageid" ) ) ; if ( currentPage . get ( "name" ) == "breakScreen" ) { var textBox ; var token = findObjs ( { name : "textLocation" , type : "graphic" , pageid : Campaign ( ) . get ( "playerpageid" ) } ) ; if ( token [ 0 ] === undefined ) { sendChat ( "API" , "Could not locate a token named textLocation" ) ; return ; } var textLocation = token [ 0 ] ; if ( ! state . BreakScreen ) { log ( "increate" ) textBox = createObj ( "text" , { text : "Hello World" , font_size : 36 , font_family : "Contrail One" , color : "rgb(255,255,255)" , layer : "objects" , pageid : Campaign ( ) . get ( "playerpageid" ) , left : textLocation . get ( "left" ) , top : textLocation . get ( "top" ) } ) ; state . BreakScreen = { module : "Break Screen" , textID : textBox . get ( "id" ) } } else { textBox = getObj ( "text" , state . BreakScreen . textID ) ; } var tables = findObjs ( { type : "rollabletable" , name : "loadingMessages" } ) ; if ( tables [ 0 ] === undefined ) { sendChat ( "API" , "Unable to locate a rollable table called loadingMessages" ) ; return ; } var loadingMessagesTable = tables [ 0 ] ; var messageList = findObjs ( { type : "tableitem" , rollabletableid : loadingMessagesTable . get ( "id" ) } ) ; isItRunning = setInterval ( function ( ) { var tableItem = messageList [ ( randomInteger ( messageList . length ) - 1 ) ] ; textBox . set ( { text : tableItem . get ( "name" ) , left : textLocation . get ( "left" ) , top : textLocation . get ( "top" ) } ) ; } , 3000 ) ; } else { clearInterval ( isItRunning ) ; } } ) ; on ( "chat:message" , function ( msg ) { if ( msg . type == "api" && msg . content . indexOf ( "!clearBreakScreenText" ) == 0 ) { textBox = getObj ( "text" , state . BreakScreen . textID ) ; if ( textBox !== undefined ) { textBox . remove ( ) ; } state . BreakScreen = undefined ; } } ) ; } ) ; on ( "ready" , function ( ) { @Oosh... You are a wizard! lol. Wow that a lot to comb through, thank you!
Never mind lol. I did some super hackery stuff and eventually got it to work. Ideally Id like to make it more stream lined but right now it does the job. My issue now is that the first message inherits the delay of all the messages. I suppose I need to make an if statement for the the buffer going from idle to busy and put the changed delay there. Here's the code now: state.textBox = undefined; class EventQueue { _buffer = []; // Array to hold the queued events _state = ''; // Internal state of the queue _writeIndex = 0; // Index of the buffer to write _readIndex = 0; // Index of the buffer to read textBox; // the text created by the user /* These are the properties to customise depending on your needs */ _bufferLength = 8; // Max number of events the queue can hold _eventDelay = 6000; // Number of milliseconds to wait between processing events _externalHandler = () => {}; // The function to pass events out to constructor(queueData = {}) { // Pass in some basic properties for the queue Object.assign(this, { name: queueData.name || 'newEventQueue', _bufferLength: queueData.length || 8, _externalHandler: queueData.handler, _eventDelay: queueData.delay > -1 ? queueData.delay : 50, // Whatever other properties you want to assign to the queue }); // Ensure we have a valid function to pass events out to, otherwise the queue does nothing. if (typeof(this._externalHandler) !== 'function') return {}; this._buffer = Array(this._bufferLength).fill(null); // Initialise the buffer with null values this.state = 'IDLE'; // Set queue state to ready, unless you have other init() functions } // Handle queue state validStates = ['IDLE', 'BUSY', 'ERROR', 'OVERFLOW']; get state() { return this._state } set state(newState) { this._state = this.validStates.includes(newState) ? newState : this._state } _writeBuffer(newEvent) { if (this.state === 'ERROR' || this.state === 'OVERFLOW') return; // No point writing to the buffer if the queue is full or broken. if (this._buffer[this._writeIndex] !== null) this._bufferOverflow(); // If the target write index is not null, the buffer is full // You can do simple validation here, but it's better done elsewhere, like the public methods at the bottom if (newEvent.name && newEvent.data) { this._buffer[this._writeIndex] = newEvent; // This next line is important - this creates a ring buffer, the write index is incremented by 1, but returns // to index 0 once it hits the max length due to the modulo operator this._writeIndex = (this._writeIndex + 1) % this._bufferLength; if (this.state === 'IDLE') this._readBuffer(); // Start the _readBuffer() method if the queue is idle } } _readBuffer() { sendChat('test',"readBuffer"); if (this.state === 'BUSY') { //sendChat('Test', 'already Busy'); return; // Already reading from the buffer, you don't want this method running twice } else if (this.state === 'IDLE') { //sendChat('',"status Idle"); // Ideally we'll land here - set the queue to BUSY and start reading from the buffer in the READ loop this.state = 'BUSY'; //sendChat('', "now busy"); } else if (this.state === 'ERROR') { // Bad things have happened, abort console.warn(`${this.name}: buffer is in ERROR state!`); return; } else if (this.state === 'OVERFLOW') { // You probably want to continue reading on an overflow or the buffer will never clear console.warn(`${this.name}: buffer is full! Continuing read...`); } else { // We shouldn't be able to land here as we've already covered all valid states console.error(`${this.name}: +++ Out of cheese error +++ Redo from start...`); } // The main READ loop: const readLoop = setInterval(() => { if (this._buffer[this._readIndex] === null){ //sendChat('', "loop is empty"), // this is where we reset the chat UI state.textBox.remove(), sendChat('', "!token-mod --set currentside|8 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"), clearInterval(readLoop); } else { const currentEvent = this._buffer[this._readIndex]; // Consume event - whatever your script does, pass the event to your main function here this._externalHandler(currentEvent); this._buffer[this._readIndex] = null; // Clear this index of the buffer once the event is consumed this._readIndex = (this._readIndex + 1) % this._bufferLength; // Same as _writeBuffer(), increment the index } }, this._eventDelay); // If the loop exits, we're out of events to consume and the queue is now IDLE // Set the state to IDLE, but only if it's BUSY. We don't want to clear errors/overflows accidentally if (this.state === 'BUSY') this.state = 'IDLE'; } _bufferOverflow() { // Your use case dictates how to handle an overflow. This is a simple example that just // times out to let _readBuffer() catch up this.state = 'OVERFLOW'; console.warn(`Buffer is full! Disabling write access temporarily`); setTimeout(() => { const nonEmptySlots = this._buffer.filter(v => v !== null); if (nonEmptySlots.length) { // The buffer hasn't cleared after 3 seconds. Something bad has happened // 3 seconds is a looooong time this.state = 'ERROR'; console.error(`${this.name}: buffer is jammed! Recommend immediate panic!`); } else { this.state = 'IDLE'; console.info(`${this.name}: buffer has recovered, write access re-enabled.`); } }, 3000); // This timer should be a property of the EventQueue, not hard-coded, so it's easy to change } // Then the public methods go down here, the ones to access from outside this Class // At its simplest, all you need is a public method to add something to the queue addEvent(newEvent) { // Do some validation - make sure all required data is valid before passing it to the buffer console.info(`${this.name}: new event received: `, newEvent) this._writeBuffer(newEvent); } } // Your function that receives events from the queue // This just outputs to sendChat const myScriptOutput = (newEvent) => { if(state.textBox!==undefined){ state.textBox.remove(); } var token = findObjs({ name:"chatBitch", type:"graphic", pageid:Campaign().get("playerpageid") }); var textLocation = token[0]; sendChat('test', "myScriptOutput"), sendChat(newEvent.creator, newEvent.data); var topOffest = textLocation.get("top") - 1; state.textBox = createObj("text", { text:newEvent.data, font_size:36, font_family:"Contrail One", color:"rgb(0,0,0)", layer:"objects", pageid:Campaign().get("playerpageid"), left:textLocation.get("left"), top:topOffest }); if (newEvent.creator == "-Mtjru7YtFnNCi18v8Fp"){ sendChat(sendingPlayer, "George Talked"); sendChat(sendingPlayer, "!token-mod --set currentside|1 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-MteTpkfNZ7TbJwvD0Rt"){ sendChat(newEvent.creator, "Brian Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|3 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-My00c7Dtfov7FRk-Og0"){ sendChat(newEvent.creator, "Sean Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|4 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-N-owesO58dLMiomCvih"){ sendChat(newEvent.creator, "Ricco Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|2 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-MtdanhLJHL8GqdQxG2B"){ sendChat(newEvent.creator, "GM Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|6 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-N-owexBvjBD-73Fx2p_"){ sendChat(newEvent.creator, "D Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|7 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } if (newEvent.creator == "-My00i1Dc_awKRg9kcGu"){ sendChat(newEvent.creator, "EuroTom Talked"); sendChat(newEvent.creator, "!token-mod --set currentside|5 --api-as -MiHnsx-c60LH76XSwZc --ids -N1xoywZ8bX8AUmjB9SQ"); } //sendChat(newEvent.creator, newEvent.data); } // Create an instance of the EventQueue class defined above const eventQueue = new EventQueue({ name: 'chatEventQueue', length: 16, delay: 6000, handler: myScriptOutput // Register the above function to receive events from the queue }); // A simple Roll20 chat handler on('chat:message', (msg) => { if (msg.type === 'api' && msg.content.indexOf("!say")==0) { //if (msg.type === 'api' && /^!myscript/.test(msg.content)) { // msg.content.indexOf("!say")==0) // Create your event object with the data scraped from the chat message const newChatEvent = { name: 'playerChat', data: msg.content.replace('!say', ''), creator: msg.playerid, created: Date.now() } eventQueue.addEvent(newChatEvent); } });
@Oosh, I noticed that the delay between multiple messages isnt the same between the time it takes to load the first queue and time it takes for the last one to time out.