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

Help review formatting and streamline of CustomSpellbook code,

1596688857

Edited 1596689063
Hello all,&nbsp; I posted this question in my other post on help learning to code, but the post has not gained any more view thus, I seek help from my original post of making my code is found&nbsp; here . Tye &nbsp;said: Hello Everyone, the spells that I have coded in are Scorching Ray, Eldritch Blast, and Magic Missile.&nbsp; All results appear in chat in the correct damage or atkdmg roll templates for 5e OGL. I have it even looking to see if they are able to cast that level of spells and if they have any available. Which is only needed for Magic Missile and Scorching Ray at the moment. Adds Attack and Damage mods as needed even subtracts the spell when used and displays a message in chat of how many remaining. Here is the link to my code. <a href="https://gist.github.com/TooTallTye/52b32e0095a9562741ffd9fc6be0f7e1" rel="nofollow">https://gist.github.com/TooTallTye/52b32e0095a9562741ffd9fc6be0f7e1</a> What I want to know is how can I streamline the code,&nbsp; not sure how to convey it but to remove redundant bits of code as well as clean up the formatting, make if more readable to the coders who would read this.&nbsp; Everything works.&nbsp; I want to add more spells but keep it simple. Please advise. Thanks Tye&nbsp; I also would like to somehow tie in a code to make for AOE spells templates appear on token layer. To be able to have this&nbsp; code (found here) &nbsp;fire as well if the spell is an AOE type spell The next spells I would like to add is Fireball,&nbsp;Flame Strike,&nbsp;Cone of Cold,&nbsp;Faerie Fire
1596724108

Edited 1596726165
timmaugh
Pro
API Scripter
Hey, Tye... if this is you learning to code and script, well done! It's a daunting thing, sometimes, with there always being something more to learn. My preliminary thoughts looking at your code... first, let's police your variables Var vs Let vs Const Use let. Use const. Use 'em, and let the Aenglish see ya use 'em. Variables declared with var are not limited to a block scope, which can make them a bit more confusing already, since other parts of your code can access and change them. But a bigger deal is hoisting , which is where the javascript engine "moves" all of your var-declarations to the top of your script. This can lead to your ability (perhaps inadvertently accessed) to use or change a variable BEFORE you've even declared it. var = hoisted, function scoped, and can be reassigned to another value let = not hoisted, block scoped, and can be reassigned to another value const = not hoisted, block scoped, and canNOT be reassigned to another value You might think that this makes const less useful, but that really isn't the case. When const is used to establish an object, for instance, what is retained is the constant POINTER to the object. That is, it will ALWAYS refer to that object. But that doesn't stop the object, itself, from changing. Consider: const hooch = { &nbsp; &nbsp; krunk: "It's just the krunk." }; hooch.funk = "factory"; The completely works because hooch never has its reference (to the initialized object) changed. Const declarations are also helpful to help you catch a problem sooner, especially if you have something that should not be changing.&nbsp; const bestSong = "Ghost Love Score" // ... do stuff ... lose track of the bestSong bestSong = "Ya Ya Ding Dong"; That will immediately let you know the next time you start your sandbox, because I don't care how much you like "Ya Ya Ding Dong," bestSong was fixed as "Ghost Love Score." Play "Ya Ya Ding Dong"! Organize The Data Right now, everything is declared in parallel and there doesn't seem to be any visible structure to where you would find a piece of information. It is all declared at the root (and your root is exposed to other scripts... more on that in a minute). For instance, you have five spells... and you write their descriptions like this: //Description Text for EB traits to add to var desc. var agonizingBlast = "**Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;" ; var eldritchSpear= "**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;"; var graspOfHadar = "**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;"; var lanceOfLethargy = "**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt;"; var repellingBlast = "**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;"; You could have a single object to collect all of this data, and then have the same access point (and anonymized code) in the future. const spellBook = { &nbsp; &nbsp; agonizingblast: "your descriptive text", eldritchspear: "your descriptive text", graspofhadar: "your descriptive text", lanceoflethargy: "your descriptive text", &nbsp;&nbsp;&nbsp;&nbsp;repellingblast: "your descriptive text" }; Now, if you have a new spell in the future, either during code development time or at run time, you can just add it to the object. You no longer have to specifically refer to the variable "agonizingBlast", you can refer to it from the object. Let's say your user supplies an argument that you want map to a variable "spellname", and that value they supplied is "agonizingblast". You could reference the appropriate description like this: ======== user input ============== !yourscript spell#agonizingblast ================================== // code happens... receiving the message and split spell#agonizingblast into an argument array of ['spell','agonizingblast'] let spellname; if (arg[0] === 'spell') { &nbsp; &nbsp; spellname = arg[1]; }; console.log(spellBook[spellname.toLowerCase()]); That's a simplified version of the effect... I would strongly encourage sanitizing the user supplied data and testing whether or not it can be found in the spellBook object, but you can see the effect. After this first test of the argument, we have anonymous code. If the user had supplied a different spell name, we could test/trap for that (1 time), and then reuse the rest of the downstream code. Wrap Them Up EDIT: Aaron's reply lends a little more clarity, here, so this line doesn't apply to you, but the rest of the concept is good to be aware of: Right now you have no encapsulation on your script, so all of your functions and variables exist in the same global space shared by all the scripts in a game where this would be installed. That means that you can run into name collisions and break your script functionality. Since all the enabled scripts added to a game are compiled into a long text file prior to the "ready" event from your sandbox, everything is going to read top-down in historical order, and the LAST declaration of a name is going to win... so if we both declare a function "volcanoMan" and I expect it to return an object but you expect it to return an ID of a game object, one of us will break. (Of course, if we both declared those functions as 'const', your sandbox would break as soon as you restarted it, telling you that you can't re-assign a const declaration.) We want all of YOUR variables name s to have their own space to play... a namespace ... separate from where my variables will play. We can create a namespace using the revealing module pattern .&nbsp;You can find a number of discussions of the idea on this site, but here is one that gives a good template starting point to build in. Use Strict or Fat Arrow Syntax If 'use strict' is the first line of your code, it will require every variable in that scope to be explicitly declared, which is just good practice for catching minor mis-typed lines that will haunt you later. Strict is automatically enforced in fat-arrow structures, so you don't have to specify it: const myfunctionname = () =&gt; {//....code...}; This thread , from another scripter looking for a review of their code, has a lot of good information on all of these subjects. More Agnosticism/Anonymization I kept this for the end because it would be a more advanced tweak to your code (and because I would have to read your code a bit more closely than I have time at the moment in order to give you a good example of it), but the idea would be in the same vein as grouping up your spellBook data. If all of the spells you could cast would use the same general code structures and pull the same levers behind the scenes, you could catalog them the same way we did with spellBook, above. You could have a library object that -- instead of just storing descriptions -- stored spell objects that had a common interface. For instance, one element of the interface could be a "description" (either a string property or a method that returned a string, if you didn't want it to be writable), so that once you identified a spell, you could access the description in the same place... at spell.description. Of course, a library object is just a gateway to full class structures, so... ...remember what I said about always seems to be more to learn? =D You're doing great. Keep it up. Post back if you have trouble integrating these concepts into your code. Play 'Ya Ya Ding Dong'!
1596725572

Edited 1596725606
The Aaron
Roll20 Production Team
API Scripter
That discussion on Revealing Module Pattern has what I would consider an older template.&nbsp; Here's the one I'm currently using: // Github: &lt;add your link&gt; // By: &lt;add your name&gt; // Contact: &lt;add a link to your profile on Roll20&gt; const NAME = (() =&gt; { // eslint-disable-line no-unused-vars const scriptName = 'NAME'; const version = '0.1.0'; const lastUpdate = 1588433968; const schemaVersion = 0.1; const checkInstall = () =&gt; { log(`-=&gt; ${scriptName} v${version} &lt;=- [${new Date(lastUpdate*1000)}]`); if( ! state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion) { log(` &gt; Updating Schema to v${schemaVersion} &lt;`); switch(state[scriptName] &amp;&amp; state[scriptName].version) { case 0.1: /* break; // intentional dropthrough */ /* falls through */ case 'UpdateSchemaVersion': state[scriptName].version = schemaVersion; break; default: state[scriptName] = { version: schemaVersion }; break; } } }; const handleInput = (msg) =&gt; { if (msg.type !== "api") { return; } let args = msg.content.split(/\s+/); switch(args[0]) { case '!NAME': break; } }; const registerEventHandlers = () =&gt; { on('chat:message', handleInput); }; on('ready', () =&gt; { checkInstall(); registerEventHandlers(); }); return { // Public interface here }; })(); This is set up for full-blown multi-part initialization, but you get the idea.&nbsp; The salient parts that make it a Revealing Module Pattern are: const NAME = (() =&gt; { // eslint-disable-line no-unused-vars // magic happens here return { // Public interface here }; })(); An alternative to going full RMP is to just wrap your code in the on('ready',...) event: on('ready',()=&gt;{ // magic happens here }); That will scope it to the anonymous function passed to the ready event, and prevent it from leaking into the global namespace.
1596726804
David M.
Pro
API Scripter
Leaked webcam footage of Aaron and timmaugh checking in on the forums...
1596727399
The Aaron
Roll20 Production Team
API Scripter
hahahahaah
1596731740
timmaugh
Pro
API Scripter
ha! Meanwhile, this is my brain after reading Aaron's code:
Wow, lots to take in, I will definitely read, re-read and try to follow what you all have contributed. &nbsp;Thank you!!
const lastUpdate = 1588433968; = Sat May 02 2020 15:39:28 GMT+0000 (Coordinated Universal Time) How is that number calculated to get today's date
1596835687
timmaugh
Pro
API Scripter
Dates are the number of milliseconds since some particular point in the past (that I can't remember off the top of my head). If you drop the following into your browser console, you'll get the current "date" in milliseconds: console.log(Date.now()); or, in a script in your API sandbox: log(Date.now());
Thanks
1596836658
The Aaron
Roll20 Production Team
API Scripter
The epoch. January 1, 1970 00:00
can you pass multiple values in a const like this? const spellBook = { //spellBook command: Spell Name, Base Damage, damage type magicmissile:"Magic Missile","[[1d4+1]]", "Force", eb:"Eldritch Blast","[[1d10]]", "Force", scorchingRay:"Scorching Ray", "[[2d6]]", "Fire", };
1596844813

Edited 1596845661
GiGs
Pro
Sheet Author
API Scripter
You can in several ways, but not the way you've shown. Essentially, you create an object name, then a colon, and then after that colon you can add any type of variable (or even function, but thats not relevant here). So here are three different ways to do it (the last one is not recommended): const spellBook = { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// a nested object; with properties defined by key and value. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// you access them with spellbook[spellname].name, spellbook[spellname].damage, etc magicmissile: {name: "Magic Missile", damage: "[[1d4+1]]", type: "Force"}, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// an array, where things are in a specific order, and you access the first &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// with spellbook[spellname][0], the second with spellbook[spellname][1], and so on eb: ["Eldritch Blast","[[1d10]]", "Force"], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// a string, where you access it, then split on comma to create an array &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// e.g. spell = spellbook[spellname].split(',') scorchingRay: "Scorching Ray,[[2d6]],Fire", }; Hope this helps! PS: the first approach is the best one for your project, but there are times the array version is best.
1596851518
timmaugh
Pro
API Scripter
One thing to realize is that there are very, very few things in javascript that are NOT objects. That gives a lot of freedom in the way you organize a hierarchy of things. So, at the basic level, you have a spell: const spell = { &nbsp; &nbsp; name: 'Magic Missile, &nbsp; &nbsp; damage: '[[1d4+1]]`, &nbsp; &nbsp; type: 'Force' } Then you could have a set of such spells (a spellbook). Array Here is a set that is organized as an array: const spellBook = [ &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: 'Magic Missle', &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; damage: '[[1d4+1]]', &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; type: 'force' &nbsp; &nbsp; }, &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; name: 'Eldritch Blast', &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; damage: '[[1d10]]', &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; type: 'force' &nbsp; &nbsp; } ] And you would access a given spell by position: log(spellBook[0].name);&nbsp; // outputs "Magic Missile ...or by filtering: // match a variable let n = 'magic missile'; let spell = spellBook.filter(s =&gt; s.name.toLowerCase() === n)[0]; log(spell.damage);&nbsp; &nbsp; // outputs [[1d4+1]]; // match a spell type let t = 'force'; spellBook.filter(s =&gt; s.type.toLowerCase() === t).forEach(s =&gt; log(s.damage)); // outputs the damage value for each force spell And you can assign a variable to any of that to now hold the spell as an object. let n = spellBook[0]; log(n.damage);&nbsp;&nbsp; &nbsp; // outputs [[1d4+1]] ...or by destructuring assignment let [mm, eb] = spellBook; log(mm.name);&nbsp; &nbsp; // outputs "Magic Missile" log(eb.name);&nbsp; &nbsp; // outputs "Eldritch Blast" Object Here is the same spellbook organized as an object. This time, the key:value pair is actually a key:object&nbsp;pair. const spellBook = { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; mm: { name: 'Magic Missle', damage: '[[1d4+1]]', type: 'force' }, &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;eb: { name: 'Eldritch Blast', damage: '[[1d10]]', type: 'force' } }; Then, to access it, you have a number of ways: // access a specific spell by key log(spellBook.mm.name);&nbsp; // outputs "Magic Missile" // by a key stored in a variable let n = 'eb'; log(spellBook[n].name);&nbsp; // outputs "Eldritch Blast" // assign a variable to the spell object (in the&nbsp;key:object pair) to make later references easier to type let n = 'eb'; let s = spellBook[n]; log(s.name);&nbsp; // outputs "Eldritch Blast" // or assign by destructuring let { mm: spell }&nbsp; = spellBook; log(spell.name);&nbsp; &nbsp; // outputs "Magic Missile" Adding New Spells Both spellBook structures (array and object) allow you to add new elements to them, you just have to add the right kind of data (a spell object for an array or a key:spell object for an object. With an array, you can use push() (among other functions) to get the spell object added: spellBook.push({ name: 'The Kronk', damage: '[[2d20+12]], type: 'kronktacity' }); With an object you just have to reference the key; if it doesn't exist, it will add it... spellBook.kr = { name: 'The Kronk', damage: '[[2d20+12]], type: 'kronktacity' };
1596853916

Edited 1596858352
Am i doing this right? EDIT: what I mean are the const groups in the right location? // Github: <a href="https://gist.github.com/TooTallTye/52b32e0095a9562741ffd9fc6be0f7e1" rel="nofollow">https://gist.github.com/TooTallTye/52b32e0095a9562741ffd9fc6be0f7e1</a> // By: TooTallTye // Contact: <a href="https://app.roll20.net/users/4065483/tye" rel="nofollow">https://app.roll20.net/users/4065483/tye</a> const NAME = (() =&gt; { // eslint-disable-line no-unused-vars const scriptName = 'Custom Spellbook'; const version = '0.0.11'; const lastUpdate = 1596836760; const schemaVersion = 0.1; const checkInstall = () =&gt; { log(`-=&gt; ${scriptName} v${version} &lt;=- [${new Date(lastUpdate*1000)}]`); //log(Date.now()); if( ! state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion) { log(` &gt; Updating Schema to v${schemaVersion} &lt;`); switch(state[scriptName] &amp;&amp; state[scriptName].version) { case 0.1: /* break; // intentional dropthrough */ /* falls through */ case 'UpdateSchemaVersion': state[scriptName].version = schemaVersion; break; default: state[scriptName] = { version: schemaVersion }; break; } } }; const handleInput = (msg) =&gt; { if (msg.type !== "api") { return; } let args = msg.content.split(/\s+/); switch(args[0]) { case '!customSpellbook': //log(args); //return; customSpellbook(msg, args); break; } }; const registerEventHandlers = () =&gt; { on('chat:message', handleInput); }; function customSpellbook(msg, args){ var selected = msg.selected; if (selected===undefined) { sendChat("Custom Spellbook", "/w gm no token selected"); return; } var tokenid = selected[0]._id; var token = getObj("graphic",tokenid); var charID = token.get("represents"); var CharacterLevel = Number(getAttrByName(charID, 'level')); let spellLevel = Number(args[2]); if(spellLevel === NaN){ sendChat("CSB", "/w gm Spell Level not properly defined"); return; }; const spellDesc = { agonizingBlast: "**Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;", eldritchSpear: "**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;", graspOfHadar: "**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;", lanceOfLethargy: "**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt;", repellingBlast: "**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;", }; const spellBook = { //spellBook command: Spell Name, Base Damage, damage type mm: {name: "Magic Missile", damage: "[[1d4+1]]", type: "Force", numberOfBlasts: spellLevel + 2, hldmg:"", minLvl: 1, damage2:"", type2:""}, eb:{name: "Eldritch Blast", damage: "[[1d10]]", type: "Force", numberOfBlasts: (Math.floor((CharacterLevel + 1)/6)+1), hldmg:"", minLvl: 0, damage2:"", type2:""}, sr:{name: "Scorching Ray", damage: "[[2d6]]", type: "Fire", numberOfBlasts: spellLevel + 1, hldmg:"", minLvl: 2, damage2:"", type2:""}, fb:{name: "Fireball", damage: "[[8d6]]", type: "Fire", numberOfBlasts: 1, hldmg:"**HLDMG:** [["+(spellLevel-3)+"d6]]", minLvl: 3, damage2:"", type2:""}, fs:{name: "Flame Strike", damage: "[[4d6]]", type: "Fire",damage2:"[[4d6]]", type2:"Radiant", numberOfBlasts: 1, hldmg:"**HLDMG:** [["+(spellLevel-5)+"d6]] choice of Fire or Radiant", minLvl: 5}, }; const rollType = { N:"Normal", A:"Advantage", D:"Disadvantage", }; let spellCmd = args[1]; if(spellBook[spellCmd]===undefined){ sendChat(``,`No spell found using ${spellCmd}`) return; } let s= spellBook[spellCmd]; let spellName = s.name; let rType = rollType[args[3]]; if(spellLevel&lt;s.minLvl){ sendChat("CSB",`${spellName} **CANNOT** be used at ${spellLevel} &lt;br/&gt; It must be used starting at Level ${s.minLvl}`); return; } sendChat("CSB",`${spellName} ${spellLevel} ${rType} # of Blasts: ${s.numberOfBlasts} Damage: ${s.damage} ${s.type} ${s.damage2} ${s.type2} ${s.hldmg}`); }; on('ready', () =&gt; { checkInstall(); registerEventHandlers(); }); return { // Public interface here }; })();
timmaugh said: Adding New Spells Both spellBook structures (array and object) allow you to add new elements to them, you just have to add the right kind of data (a spell object for an array or a key:spell object for an object. With an array, you can use push() (among other functions) to get the spell object added: spellBook.push({ name: 'The Kronk', damage: '[[2d20+12]], type: 'kronktacity' }); With an object you just have to reference the key; if it doesn't exist, it will add it... spellBook.kr = { name: 'The Kronk', damage: '[[2d20+12]], type: 'kronktacity' }; If I send in chat&nbsp; !customSpellbook spellBook.kr = { name: 'The Kronk', damage: '[[2d20+12]], type: 'kronktacity' }; would that now be added to the list?
Added this: let charID = token.get("represents"); log(charID); let characterInfo = { ID: charID, STR: Number(getAttrByName(charID, 'strength_mod')), DEX: Number(getAttrByName(charID, 'dexterity_mod')), CON: Number(getAttrByName(charID, 'constitution_mod')), INT: Number(getAttrByName(charID, 'intelligence_mod')), WIS: Number(getAttrByName(charID, 'wisdom_mod')), CHR: Number(getAttrByName(charID, 'charisma_mod')), CharacterLevel: Number(getAttrByName(charID, 'level')), }; let c = "characterInfo"; log(c[ID]); return; why is this error happening? "-=&gt; Custom Spellbook v0.0.11 &lt;=- [Fri Aug 07 2020 21:46:00 GMT+0000 (Coordinated Universal Time)]" "-Ly2-LwB7XAjc7-qstEi" ReferenceError: ID is not defined ReferenceError: ID is not defined at customSpellbook (apiscript.js:6625:7) at handleInput (apiscript.js:6586:17) at eval (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:154:1), &lt;anonymous&gt;:65:16) at Object.publish (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:154:1), &lt;anonymous&gt;:70:8) at /home/node/d20-api-server/api.js:1661:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Zd.Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) The ID is&nbsp;-Ly2-LwB7XAjc7-qstEi; passed before the let characterInfo = { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ID: charID,
1596867985

Edited 1623253948
timmaugh
Pro
API Scripter
No, not quite, Tye. That line would be one way to add a spell from the code, itself. You can't send javascript through the chat. If you want to do that, what you do is create a way for your script to "catch" a particular sort of user input, and use it to feed the javascript line the right variables. The Flow (thinking through the development) When a chat message is sent, a chat event is created, and with it comes a message object. A script can listen for the chat event by having a function set to run on('chat:message'). Your script establishes that here: const registerEventHandlers = () =&gt; { on('chat:message', handleInput); }; ...and says that when there is a chat event, the function to run is handleInput(). handleInput() first needs to make sure this is the right kind of message. Since it is running for EVERY chat event, it has to first make sure that the chat message is one it should worry about. It does that by testing if the message type is 'api' and then by somehow testing that the opening few characters in the message match the particular invocation pattern for this script. const handleInput = (msg) =&gt; { if (msg.type !== "api") {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// &lt;== tests the msg type to be 'api' return; } let args = msg.content.split(/\s+/);&nbsp;&nbsp;&nbsp;&nbsp; // &lt;== splits the msg content into groups delimited by 1 or more white space characters switch(args[0]) { case '!customSpellbook':&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // &lt;== looks for your particular invocation Once you make it that far, you know you're handling a message intended for your particular script. This is where it gets interesting. You have the rest of the user input in an array (args), broken on white space characters... hang on to that a minute while we figure out if, at this point, you have what you need. Parsing Arguments to Fit Script Functionalities If you want to give your user (or just you) the ability to add a spell from the chat input (or another source -- we'll get to that in a minute), you need to make sure you have certain pieces of information. Let's assume those are name, damage, and type (I know you have a few more properties than that, but we'll keep it simple for the sake of example). If you're going to get all of those in a single chat message, we need a way to differentiate one piece of information from another. !customspellbook data data data data Really quickly we realize we can't split just on white space characters, because the names of these spells, themselves, contain white space characters. So we need a different way to differentiate the beginning of a single argument, one that is unique enough to not occur naturally in the text you want to preserve and parse, but which is easy enough to type for the user. A common one is a double-hyphen. !customspellbook --data --data --data --data By incorporating the double-hyphen into the regex, we can split on "1-or-more whitespace characters followed by double-hyphen"... let args = msg.content.split(/\s+--/); That would give us an array of your api handle followed by each piece of data as discrete elements, provided your user prefaced each argument with the double-hyphens. Stop there just a minute. Parsing the input string is a matter of balancing coding superstructure (work for you) against simplicity and ease-of-use for your user. We are trying to make sure that you have the data you need and that the user can easily understand and use what you give them. Can you make this format work? Probably. Your data is differentiated, but your only way to identify each piece is the order that it comes in. You would have to mandate that the user enters the spell name first, followed by the damage, followed by the type. That doesn't sound too hard for the user to remember the order for three pieces of data, but what if there are 5 pieces? Or 6? Your user is going to start to not you very much. And what if your user has a spell that requires data points 1, 2, 4, and 6, but not 3 and 5? Do they have to put in empty arguments? !customspellbook --data --data -- --data -- --data Tagged Arguments If you give the user a way to tag each piece of data as it comes to you, you could take care of both problems: they could enter the data in any order and you would still be able to recognize it.... AND they would be able to omit any data they didn't need. Common intra-arg delimiters are # (hash), | (pipe), and : (colon), but you could use anything -- again, looking for something easily typed, but not commonly included in your input. From personal experience, a colon can interfere with creating an api button, and a pipe can be a problem down the line if you want to include queries in your input. Let's go with the hash for now. !customspellbook --name#data --damage#data --type#data Now we're getting somewhere. Using our existing parsing (splitting on /\s+--/), we get an args array of: ['!customspellbook', 'name#data', 'damage#data', 'type#data'] Let's go ahead and get rid of the first element, since we don't need it anymore: args.shift(); In fact, since shift() not only removes the first item from an array it also RETURNS that item, we could have used this in our handleInput during the test of whether this script should handle the message... let args = msg.content.split(/s+--/); if(args.shift() === '!customspellbook') customspellbook(msg, args);&nbsp; &nbsp; // the args passed to csb would have the api handle already removed In any case, we now have the discrete data points, and only the discrete data points. Now we want to split each of them into their key#value parts. args.map(m =&gt; m.split("#")); ...would give us, for each element, an array of [key, value]. Except if the value happened to contain another hash... so we need to fix that. Adding another map operation can join up elements after the first (all of the parts that should be together as the value). args.map(m =&gt; m.split("#")).map(m =&gt; [m[0], m.slice(1).join("#")]); Note, you could also accomplish the same splitting on the hash with an indexOf() function, if you wanted to: args.map(m =&gt; { &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;let h = m.indexOf("#"); &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;return [m.slice(0, h), m.slice(h)]; &nbsp;&nbsp;&nbsp;&nbsp;}); A Word on Lifespan, Order, and Sanitization The map operations are not permanent changes to your data; they last only as long as the line unless you assign the newly formatted data to a variable. Map returns an array, so you could feed it to a function that is looking for an array (we're about to), but at some point you have to assign that mapped, formatted, and massaged data to a variable for safe-keeping while you do other stuff... like SANITIZING user input. Whatever that looks like to your script, you have to make sure that the user data is appropriate and formatted properly. Maybe there are only three available types of spells, and the user has to enter one or else the spell is invalid and you don't want to load it. Because these parsing operations (splitting on white space double hyphen, splitting each on the first hash) can be done back-to-back, and because they have to be written to a variable at some point, people will often do it as part of the declaration of the args variable in the first place...&nbsp; args = msg.content.split(/\s+--/) &nbsp; &nbsp; .slice(1) &nbsp; &nbsp; .map(m =&gt; { &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;let h = m.indexOf("#"); &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;return [m.slice(0, h), m.slice(h)]; &nbsp;&nbsp;&nbsp;&nbsp;}); You saw the shift() in there getting rid of the api handle (the '!customspellbook)? That means that this would have to happen *after* you check for whether this script should handle the message. Without the split to isolate the api handle, that means that you'd be using a startsWith() test, or maybe a regex test, and if that passed you could move on to the above parsing. So you can go back and alter your handleInput function to make sure the args are parsed correctly, or you can leave the map/parsing operation of the # exactly where we left it (downstream, in the customspellbook function), and just assign the results to args again. args = args.map(m =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; let h = m.indexOf("#"); &nbsp; &nbsp; &nbsp; &nbsp; return [m.slice(0, h), m.slice(h)]; &nbsp; &nbsp; }); Either way, you now have an array of key/value arrays. Which you will sanitize. Right? Object Magic Now that you have an array of key/value arrays (sanitized, both key and value), you can use the fromEntries() function to create an object: let spell = Object.fromEntries(args); That takes you from this: [['name','data'], ['damage', 'data'], ['type', 'data']] ...to... { name: 'data', damage: 'data', type: 'data' } NOW you're ready to add it to your spellBook. ... ...if your script knew to do that. Branching the Input See, we're still working in the main branch of your script. EVERY time it gets a chat event that it needs to respond to, it will do the process we just outlined. That would be fine if EVERY time we used the !customspellbook handle we wanted to invoke this spell-add process... but we don't. Somehow, then, we have to trip a mechanism to have the script know when it needs to add a spell versus other activities. We could do that with a named argument: !customspellbook --action#addspell --name#data ... ...or just knowing that the first argument following the api handle is the "action" argument that drives what process the script will follow: !customspellbook --addspell --name#data ... Whatever the case is, we build our parsing engine to look for it, and if we find it, we know to add the spell object we just built to our spellbook. Other Considerations This works for basic input, but you also need to make sure that what the user inputs reaches your script without the Roll20 parser eating it, first. For instance, your damage values will get turned into inline rolls if you try to type them into an argument in a call to your function. You would need a way to escape this process (character replacement, space insertions, etc.). Also, what you add at "game" time only lasts for as long as your sandbox/state is around. If you restart the sandbox, your spellbook resets to only the spells coded in during dev time... not the ones that were entered from chat. You would have to re-enter them. This would make me think about a way to have them picked up automatically... probably from a library handout that I could then parse into game-time spells. Every time the sandbox restarts, it throws a "ready" event, so I could have a listener in an on('ready) event go to see if there is a spell library handout and, if it finds one, to load it up. ...but that is development for another day...
1596868535
timmaugh
Pro
API Scripter
why is this error happening? "-=&gt; Custom Spellbook v0.0.11 &lt;=- [Fri Aug 07 2020 21:46:00 GMT+0000 (Coordinated Universal Time)]" "-Ly2-LwB7XAjc7-qstEi" ReferenceError: ID is not defined ReferenceError: ID is not defined at customSpellbook (apiscript.js:6625:7) at handleInput (apiscript.js:6586:17) at eval (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:154:1), &lt;anonymous&gt;:65:16) at Object.publish (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:154:1), &lt;anonymous&gt;:70:8) at /home/node/d20-api-server/api.js:1661:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Zd.Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) The ID is&nbsp;-Ly2-LwB7XAjc7-qstEi; passed before the let characterInfo = { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ID: charID, It's because this line: log(c[ID]); Referencing an object property that way is asking javascript to look for a property name held in the variable named "ID"... which you haven't defined. Change that line to be: log(c.ID); ...and you should be fine. Just to illustrate the point... this can get confusing, but you can house the name of the key in a variable... and that variable could be named after a DIFFERENT key in the same object. let characterInfo = { &nbsp; &nbsp; ID: 10 &nbsp; &nbsp; STR: 'strong' }; let STR = 'ID'; log(characterInfo.ID);&nbsp;&nbsp; &nbsp; //outputs 10 log(characterInfo.STR);&nbsp; &nbsp; // outputs 'strong' log(characterInfo[STR]);&nbsp; &nbsp; // outputs 10 The last one is using the variable STR, not the property , and the content of the variable is a string. That string matches the name of a key for the object ('ID').
Thanks&nbsp;timmaugh, lot to process but definitely tools to learn and add to my toolbox&nbsp;
Not sure why I am not sending the spellName to the SendBlast function?? log(spellName); SendBlast(msg, characterName, character, player,spellAtkBonus, spellName, GDM, spellLevel, rtype, rType, blasts, dmg, dmgType, dmg2, GAM, dmgType2, hldmg, msgDesc, spellRange, emasText, CharacterID, player,template); log("Spell Name after SendBlast Function: "+spellName) }; function SendBlast(msg, characterName, character, player,spellAtkBonus, GDM, spellName, spellLevel, rtype, rType, blasts, dmg, GAM, dmgType, dmg2, dmgType2, hldmg, msgDesc, spellRange, emasText, CharacterID, player,template){ // bshields.sendChat(msg,`!emas ${emasText}`); var beamarray = []; log("Spell Name in SendBlast Function: "+spellName+"!"); here is what I see in the log "Eldritch Blast" "Spell Name in SendBlast Function: !" "&amp;{template:atkdmg} {{mod=+15}} {{rname= 1}} {{r1=[[1d20+15[Spell]]]Force}} {{normal=1}} {{r2=[[0d20+15 [Spell]]]Eldritch Blast}} {{attack=1}} **Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;&lt;br/&gt;**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;&lt;br/&gt;**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;&lt;br/&gt;**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;&lt;br/&gt;**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt; + [[1d4[BLESS]]] {{range=300 ft}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[1d10+6[Chr]]]}} {{dmg1type=}} {{crit1=[[10]]}} {{crit2=[[10]]}}{{spelllevel= 0}} {{charname=Alton Ashfall}} &amp;{template:atkdmg} {{mod=+15}} {{rname= 2}} {{r1=[[1d20+15[Spell]]]Force}} {{normal=1}} {{r2=[[0d20+15 [Spell]]]Eldritch Blast}} {{attack=1}} **Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;&lt;br/&gt;**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;&lt;br/&gt;**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;&lt;br/&gt;**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;&lt;br/&gt;**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt; + [[1d4[BLESS]]] {{range=300 ft}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[1d10+6[Chr]]]}} {{dmg1type=}} {{crit1=[[10]]}} {{crit2=[[10]]}}{{spelllevel= 0}} {{charname=Alton Ashfall}} &amp;{template:atkdmg} {{mod=+15}} {{rname= 3}} {{r1=[[1d20+15[Spell]]]Force}} {{normal=1}} {{r2=[[0d20+15 [Spell]]]Eldritch Blast}} {{attack=1}} **Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;&lt;br/&gt;**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;&lt;br/&gt;**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;&lt;br/&gt;**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;&lt;br/&gt;**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt; + [[1d4[BLESS]]] {{range=300 ft}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[1d10+6[Chr]]]}} {{dmg1type=}} {{crit1=[[10]]}} {{crit2=[[10]]}}{{spelllevel= 0}} {{charname=Alton Ashfall}} &amp;{template:atkdmg} {{mod=+15}} {{rname= 4}} {{r1=[[1d20+15[Spell]]]Force}} {{normal=1}} {{r2=[[0d20+15 [Spell]]]Eldritch Blast}} {{attack=1}} **Agonizing Blast:**&lt;br&gt;When you cast Eldritch Blast, add your Charisma modifier to the damage it deals on a hit.&lt;br&gt;&lt;br/&gt;**Repelling Blast:**&lt;br&gt;When you hit a creature with Eldritch Blast, you can push the creature up to 10 feet away from you in a straight line.&lt;br&gt;&lt;br/&gt;**Eldritch Spear:**&lt;br&gt;When you cast Eldritch Blast, its range is 300 feet.&lt;br&gt;&lt;br/&gt;**Grasp of Hadar:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can move that creature in a straight line 10 feet closer to yourself.&lt;br&gt;&lt;br/&gt;**Lance of Lethargy:**&lt;br&gt;Once on each of your turns when you hit a creature with your Eldritch Blast, you can reduce that creature’s speed by 10 feet until the end of your next turn.&lt;br&gt; + [[1d4[BLESS]]] {{range=300 ft}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[1d10+6[Chr]]]}} {{dmg1type=}} {{crit1=[[10]]}} {{crit2=[[10]]}}{{spelllevel= 0}} {{charname=Alton Ashfall}}" "Eldritch Blast" "Spell Name after SendBlast Function: Eldritch Blast" Here is link to the edited version <a href="https://gist.github.com/TooTallTye/89935c53f47d151a8d78359b491d4ad8" rel="nofollow">https://gist.github.com/TooTallTye/89935c53f47d151a8d78359b491d4ad8</a>
1597458124

Edited 1597458237
Nevermind just realized that the send to fields were not is the same order as the function&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SendBlast(msg, characterName, character, player,spellAtkBonus, spellName, GDM, spellLevel, rtype, rType, blasts, dmg, dmgType, dmg2, GAM, dmgType2, hldmg, msgDesc, spellRange, emasText, CharacterID, player,template); function SendBlast(msg, characterName, character, player,spellAtkBonus, GDM, spellName, spellLevel, rtype, rType, blasts, dmg, GAM, dmgType, dmg2, dmgType2, hldmg, msgDesc, spellRange, emasText, CharacterID, player,template){
1597470107

Edited 1597470148
timmaugh
Pro
API Scripter
That can definitely be a pain. I would suggest for functions that have a lot of variables to pass that you pass them as an object. Your receiving function can either have a single object to receive them, and all references to values would come as property references of this arg object (not recommended, but it works): const weStartHere = (msg) =&gt; { let a = 1, b = 2, c = true, d = 'jazz hands', e = 'volcano man', f = { a: 'moo' }; weCallThis ({ jambone: a, cake: b, or_death: c}); }; const weCallThis = (obj) =&gt; { log(obj.jambone); log(obj.cake + obj.or_death); }; ...or you can use destructuring assignment on the receiving end to refer to the variables independent of the object they were delivered in... you can even rename them and default them if you wanted to: const weStartHere = (msg) =&gt; { let a = 1, b = 2, c = true, d = 'jazz hands', e = 'volcano man', f = { a: 'moo' }; weCallThis ({ jambone: a, cake: b, or_death: c}); }; const weCallThis = ({ jambone: jambone, cake: c = 'cake please', or_death: d = "so my choices are 'or death'?"}) =&gt; { log(obj.jambone); log(obj.c + obj.d); }; You can see that nothing changed about the calling function; it still lists the passed arguments as properties of an object. You can also see that the jambone property does no re-labeling of the argument (left side of the colon is the name of the property passed in the argument object, right side is what you will call it locally), while cake and or_death do. The latter 2 variables also establish default values for the properties. Doing it this way has a couple of benefits. First, you can pass the arguments in any order. That's handy when you don't want to have to look up a long order of many variables, or remember how many you have to skip before you get to the slot where you do need to pass something. Second, every call to the function lists exactly what parameter of the receiving function the passed argument will map to. No more wondering, a month down the line, what a particular argument is referring to. In this way, your passing AND receiving function are self-documenting, to a certain extent.
1597548230

Edited 1597612048
EDIT: Added a count Switch Statement with a set time out to the SpellUsed function I am not sure how to make this happen or why its not in order but how can I make the 'Spells Remaining' come after the actual casting of the spell here is the last lines of code that make the two sendchat commands (also using bsshields&nbsp;Interpreted sendChat (one-click) api to send as the original sender of my API.&nbsp; Using the image below I want them in this order: EMAS&nbsp;&nbsp; the Spell &nbsp;Alton Ashfall Spell Slot Level 3 0 of 2 remaining ALL LEVEL 3 SLOTS EXPENDED 1 &amp; 2 have ALWAYS managed to come correctly however I cannot get 3 to not be 1st &nbsp; switch(updatedSpellsExpended){ case 0: msgText = `**${c.CharacterName}**&lt;br/&gt;&lt;br/&gt;Spell Slot Level ${spellLevel} &lt;br&gt; ${updatedSpellsExpended} of ${c.spellSlotsTotals} remaining &lt;br&gt; &lt;div style="color:red"&gt; **ALL LEVEL ${spellLevel} SLOTS EXPENDED** `; break; default : msgText = `**${c.CharacterName}**&lt;br/&gt;&lt;br/&gt;Spell Slot Level ${spellLevel} &lt;br&gt; ${updatedSpellsExpended} of ${c.spellSlotsTotals} remaining`; break; }; let bshieldsText = `!setattr --modb --silent --charid ${charID} --lvl${spellLevel}_slots_expended|-1 `; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SendBlast(msg, outputMessage, emasText, blasts, msgboxOpen,msgText, bshieldsText); }; function SendBlast(msg, outputMessage, emasText, blasts, msgboxOpen,msgText, bshieldsText){ bshields.sendChat(msg,`!emas ${emasText}`); var beamarray = []; //this makes an array with as many copies of the outputmeassage as needed for(let counter=1;counter&lt;=blasts;counter++){ beamarray.push(outputMessage(counter)); }; // this converts the array into a string, with newlines between each on, so they each go on a new line. var beamOutput = beamarray.join(`\n`); bshields.sendChat(msg,beamOutput); spellUsed(msg,msgboxOpen,msgText, bshieldsText); //log(beamOutput); }; function spellUsed(msg,msgboxOpen,msgText, bshieldsText){ bshields.sendChat(msg,bshieldsText); bshields.sendChat(msg,msgboxOpen+msgText); }; EDITED CODE: function spellUsed(msg,msgboxOpen,msgText, bshieldsText){ for(let counter=1;counter&lt;=2;counter++){ setTimeout(function(){ switch (counter){ case 1: log(counter); bshields.sendChat(msg,bshieldsText); //sends out spell blasts break; case 2: bshields.sendChat(msg,msgboxOpen+msgText); log(counter); break; default: break; }; },2000); }; };
1597621640
timmaugh
Pro
API Scripter
I'm not familiar with that script (bsshields), but it sounds like you might be dealing with asynchronous execution. If the other script runs asynchronously, it may always appear to finish after your synchronous code (it will wait for a window when your code isn't running to take over the single thread of js processing). You can check out the other script to see if something like that is happening. There are workarounds if it is... But their juice might be worth the squeeze. Might be easier to not use the other script, change the other script, code your own version of the other script, or... live with it.
1597623657
GiGs
Pro
Sheet Author
API Scripter
Generally speaking, in roll20 it's best to combine all your print outs and send just one sendChat, otherwise asynchronous issues are bound to bite you in the butt.
Ty, will look into updating that
When you use sendChat would I make 1 msgText to send or can you pass multiple msg in a single sendChat
1597678112
timmaugh
Pro
API Scripter
You only have the one argument for passing message content, so you would have to aggregate your output first, then send it.
1597686569
GiGs
Pro
Sheet Author
API Scripter
You can include linebreaks in the output to separate each message, for the simplest approach. Bu you can also go further, adding full html blocks - using divs with style commands for instance - to make the chat output appear exactly how you want.