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

Priming the Pump

1663990895
Joab
Pro
I wrote this script to build characters for my World War Cthulhu game. The original version goes all the way through without user involvement, generates all the elements and puts them into a character sheet and a handout with further instructions. The idea for this version is to allow the player to roll the various sections and write the results to the chat. They can then reroll until they are satisfied and the final selection will be saved to a character sheet. The results are written to the screen. The saving part isn't done yet. The problem I'm having now, is other than generating the stats, all the other options, which are table rolls, always come up as undefined the first time. If you do it again, you get a result, but not the first time. Suggestions? /** * Generates World War Cthulhu Characters * * Syntax: !WWCGen option * */ var WWCGen = WWCGen || { version: "0.0.4", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } switch (msg.content) { case '!GenStats': WWCGen.GenStats(msg); break; case '!GenNationality': WWCGen.GenNationality(msg); break; case '!GenUpbringing': WWCGen.GenUpbringing(msg); break; case '!GenPersonality': WWCGen.GenPersonality(msg); break; case '!GenPreWar': WWCGen.GenPreWar(msg); break; case '!GenMilServ': WWCGen.GenMilServ(msg); break; case '!GenEncounter': WWCGen.GenEncounter(msg); break; case '!GenRecruitment': WWCGen.GenRecruitment(msg); break; case '!GenIncome': WWCGen.GenIncome(msg); break; default: WWCGen.showHelp(); } } ); }, showHelp: function () { sendChat("API", "/direct &lt;table style='background: #DCD9D5; border-radius: 20px; font-size: 10px;'&gt;" + "&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Help&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;" + "&lt;tbody&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;!fodder help&lt;/strong&gt;&lt;br&gt;Show this help screen.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen core&lt;/strong&gt;&lt;br&gt;Runs the script.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;/tr&gt;" + "&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;"); }, GenStats: function (msg) { WWCGen.STR_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.CON_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.POW_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.DEX_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.APP_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.SIZ_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.INT_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.EDU_roll = randomInteger(6) + randomInteger(6) + randomInteger(6) + 6; WWCGen.STR = WWCGen.STR_roll * 5; WWCGen.CON = WWCGen.CON_roll * 5; WWCGen.POW = WWCGen.POW_roll * 5; WWCGen.DEX = WWCGen.DEX_roll * 5; WWCGen.APP = WWCGen.APP_roll * 5; WWCGen.SIZ = WWCGen.SIZ_roll * 5; WWCGen.INT = WWCGen.INT_roll * 5; WWCGen.EDU = WWCGen.EDU_roll * 5; WWCGen.SAN = WWCGen.POW_roll * 5; x = (WWCGen.CON_roll + WWCGen.SIZ_roll) / 2; WWCGen.HP = parseInt(x); WWCGen.MP = WWCGen.POW_roll; WWCGen.Age = WWCGen.EDU_roll + 6; WWCGen.MOV = 8; WWCGen.PER = WWCGen.INT_roll * 10; WWCGen.OCC = WWCGen.EDU_roll * 20; WWCGen.DODGE = WWCGen.DEX_roll * 2; WWCGen.RADIO = WWCGen.INT_roll * 2; WWCGen.LO = WWCGen.EDU_roll * 5; WWCGen.CR = WWCGen.APP_roll; d = (WWCGen.STR_roll + WWCGen.SIZ_roll) / 2; if (d &lt;= 13) { WWCGen.DB = "-2"; } else if (d &lt;= 17) { WWCGen.DB = "-1"; } else if (d &lt;= 25) { WWCGen.DB = "0"; } else if (d &lt;= 33) { WWCGen.DB = "1d4"; } else { WWCGen.DB = "1d6"; } WWCGen.output['Attributes'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Are these values acceptable?" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['STR'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;STR&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.STR + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['CON'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;CON&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.CON + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SIZ'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SIZ&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SIZ + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DEX'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DEX&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.DEX + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['APP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;APP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.APP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['INT'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;INT&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.INT + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['POW'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;POW&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.POW + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['EDU'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;EDU&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.EDU + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['HP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;HP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.HP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;MP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DB'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DB&lt;/td&gt;&lt;td style='padding: 5px;'&gt;STR+SIZ/10" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['Age'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Age&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Age + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SAN'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SAN&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SAN + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MOV'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Movement&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MOV + "&lt;/td&gt;&lt;/tr&gt;"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText1); }, GenNationality: function (msg) { sendChat("API", "/roll 1t[Nationality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Birthplace = values[0]; WWCGen.Nationality = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Birthplace'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Nationality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Birthplace + "&lt;/td&gt;&lt;/tr&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Birthplace'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText3); }, GenUpbringing: function (msg) { sendChat("API", "/roll 1t[Upbringing]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Childhood = values[0]; WWCGen.Upbringing = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Childhood'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Upbringing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Childhood + "&lt;/td&gt;&lt;/tr&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Childhood'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText4); }, GenPersonality: function (msg) { sendChat("API", "/roll 1t[Personality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Personality = values[0]; WWCGen.History = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Personality'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Personality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Personality + "&lt;/td&gt;&lt;/tr&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText6); }, GenPreWar: function (msg) { sendChat("API", "/roll 1t[Pre-War_Occupation]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.PreWar = values[0]; WWCGen.Job = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['PreWar'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Pre-War Occupation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.PreWar + "&lt;/td&gt;&lt;/tr&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText7); }, GenMilServ: function (msg) { sendChat("API", "/roll 1t[Military_Service]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Military_Service = values[0]; WWCGen.Service = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Military_Service'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Military Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Military_Service + "&lt;/td&gt;&lt;/tr&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Military_Service'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText8); }, GenEncounter: function (msg) { sendChat("API", "/roll 1t[Mythos_Encounter]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Mythos_Encounter = values[0]; WWCGen.Encounter = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Mythos_Encounter'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Initial Mythos Encounter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Mythos_Encounter + "&lt;/td&gt;&lt;/tr&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Mythos_Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText9); }, GenRecruitment: function (msg) { sendChat("API", "/roll 1t[Reason_for_Joining]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Recruitment = values[0]; WWCGen.Reason_for_Joining = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Recruitment'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Reason for Joining&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Recruitment + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextA); }, GenIncome: function (msg) { sendChat("API", "/roll 1t[Income]", function (result) { var content = JSON.parse(result[0].content); WWCGen.Income = content.rolls[0].results[0].tableItem.name; }); WWCGen.output['Income'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Income&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Income + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Income'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextB); }, save: function (msg, saveCallback) { var handout = createObj('handout', { name: WWCGen.name + ' Instructions', inplayerjournals: "all", archived: false }); handout.set('notes', WWCPrint); // }, // save: function () { var character = createObj("character", { name: WWCGen.name, archived: false, inplayerjournals: "all", controlledby: "all" }); character.set('bio', WWCText); createObj('attribute', { name: 'player_name', current: WWCGen.player, _characterid: character.id }); createObj('attribute', { name: 'recruitment', current: WWCGen.Recruitment, _characterid: character.id }); createObj('attribute', { name: 'STR', current: WWCGen.STR, _characterid: character.id }); createObj('attribute', { name: 'CON', current: WWCGen.CON, _characterid: character.id }); createObj('attribute', { name: 'SIZ', current: WWCGen.SIZ, _characterid: character.id }); createObj('attribute', { name: 'DEX', current: WWCGen.DEX, _characterid: character.id }); createObj('attribute', { name: 'APP', current: WWCGen.APP, _characterid: character.id }); createObj('attribute', { name: 'INT', current: WWCGen.INT, _characterid: character.id }); createObj('attribute', { name: 'POW', current: WWCGen.POW, _characterid: character.id }); createObj('attribute', { name: 'EDU', current: WWCGen.EDU, _characterid: character.id }); createObj('attribute', { name: 'san', current: WWCGen.SAN, _characterid: character.id }); createObj('attribute', { name: 'hp', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'hp_max', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'mp', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'mp_Max', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'MOV', current: '8', _characterid: character.id }); createObj('attribute', { name: 'Age', current: WWCGen.Age, _characterid: character.id }); createObj('attribute', { name: 'birthplace', current: WWCGen.Birthplace, _characterid: character.id }); createObj('attribute', { name: 'dodge', current: WWCGen.DODGE, _characterid: character.id }); createObj('attribute', { name: 'language_own', current: WWCGen.LO, _characterid: character.id }); createObj('attribute', { name: 'operate_radio', current: WWCGen.RADIO, _characterid: character.id }); createObj('attribute', { name: 'credit_rating', current: WWCGen.CR, _characterid: character.id }); createObj('attribute', { name: 'Nationality', current: WWCGen.Nationality, _characterid: character.id }); createObj('attribute', { name: 'Upbringing', current: WWCGen.Upbringing, _characterid: character.id }); createObj('attribute', { name: 'Personality', current: WWCGen.Personality, _characterid: character.id }); createObj('attribute', { name: 'occupation', current: WWCGen.PreWar, _characterid: character.id }); createObj('attribute', { name: 'milserv', current: WWCGen.Military_Service, _characterid: character.id }); createObj('attribute', { name: 'mythos_encounter', current: WWCGen.Mythos_Encounter, _characterid: character.id }); createObj('attribute', { name: 'reason_for_joining', current: WWCGen.Reason_for_Joining, _characterid: character.id }); createObj('attribute', { name: 'wealth', current: WWCGen.Income, _characterid: character.id }); sendChat('', 'Created &lt;a href="<a href="http://journal.roll20.net/character/" rel="nofollow">http://journal.roll20.net/character/</a>' + character.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); sendChat('', 'Created instructions for completing &lt;a href="<a href="http://journal.roll20.net/handout/" rel="nofollow">http://journal.roll20.net/handout/</a>' + handout.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); } }; on("ready", function () { WWCGen.listen(); });
I'm no longer Pro so take this with a grain of salt. But I think when you use `sendChat` with a callback it is asynchronous. So the sendChats you are using immediately return before the callback is fired, and before the WCGen.* variables are set.&nbsp;
1664028369

Edited 1664028393
Oosh
Sheet Author
Jim has the right of it there. You've got two options: 1 - Move your whole function inside the sendChat callback where each of those table rolls happens- everything that needs to run after the table roll must be inside the callback. This is reasonably simple, but limits your ability to chain the different commands together without running into callback hell. 2 - Use asynchronous functions so you can control the timing yourself. Here's a tiny example of how to wrap sendChat in an async wrapper so you can await it to stop your function from continuing until you have the result: (() =&gt; { // Wrap the sendChat in an async function, returning the callback result as the resolved promise const asyncChatRoll = async function(roll) { return new Promise(function(res) { sendChat('testroll', `${roll}`, function(rollResult) { res(rollResult) }); }); } const handleInput = async function(msg) { if (/^!testroll/.test(msg.content)) { // The async wrapper function needs to be awaited, and must be called from another async function const result = await asyncChatRoll(`[[1d4]]`); sendChat('testroll', `You rolled a ${result[0] ? result[0].inlinerolls[0].results.total : '???'}`); } } on('ready', function() { on('chat:message', handleInput); }); })(); You can take the word 'await' out of line 15 and see what happens - the sendChat will return `You rolled a ???` as the wrapped sendChat with the die roll in it has not had time to resolve. What's happening with your script at the moment, is it fails every time, but since you're storing all of the values in your WWCGen object, you're actually getting the previously rolled (stale) values. In the case of the first time you roll, that's going to be undefined since there is no stale value to grab. Using async is definitely the better solution as it allows much cleaner code and allows you to control the flow of your program, but pushing more code into the callbacks is definitely the faster solution if you just need it to work. Don't go chaining another function (like your save() function) onto the end of any of the table rolls though - since they're not in the sendChat callback, they'll likely run before the table roll is done, and without asynchronous code you have no way to control that properly. Also be wary of setting notes/gmnotes on handouts and characters - those are also async operations. It might not matter in the context of this script, but that handout.set() call is likely to be the last thing to resolve in your save() function. Factoring in human reaction timescales, that's very unlikely to be an issue here - but if you were relying on it elsewhere in the script that would be another potential place for running into async issues.
1664143395
Joab
Pro
OK, I modified the various functions and now they are working properly. Now I need to get the Save function working. In the code below, I get the message that "saveCallback is not defined. /** * Generates World War Cthulhu Characters * * Syntax: !WWCGen option * */ var WWCGen = WWCGen || { version: "0.0.4", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } switch (msg.content) { case '!GenStats': WWCGen.GenStats(msg, saveCallback); break; case '!GenNationality': WWCGen.GenNationality(msg, saveCallback); break; case '!GenUpbringing': WWCGen.GenUpbringing(msg, saveCallback); break; case '!GenPersonality': WWCGen.GenPersonality(msg, saveCallback); break; case '!GenPreWar': WWCGen.GenPreWar(msg, saveCallback); break; case '!GenMilServ': WWCGen.GenMilServ(msg, saveCallback); break; case '!GenEncounter': WWCGen.GenEncounter(msg, saveCallback); break; case '!GenRecruitment': WWCGen.GenRecruitment(msg, saveCallback); break; case '!GenIncome': WWCGen.GenIncome(msg, saveCallback); break; case '!CharView': setTimeout(msg, saveCallback); break; case '!SaveChar': setTimeout(msg, saveCallback); break; default: WWCGen.showHelp(); } }); }, showHelp: function () { sendChat("API", "/direct &lt;table style='background: #DCD9D5; border-radius: 20px; font-size: 10px;'&gt;" + "&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Help&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;" + "&lt;tbody&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;!fodder help&lt;/strong&gt;&lt;br&gt;Show this help screen.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen core&lt;/strong&gt;&lt;br&gt;Runs the script.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;/tr&gt;" + "&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;"); }, GenStats: function (msg, saveCallback) { WWCGen.id = msg.playerid; WWCGen.name = msg.who + " #" + (findObjs({ _type: "character", controlledby: "all" }).length + 1); WWCGen.player = msg.who; WWCGen.STR_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.CON_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.POW_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.DEX_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.APP_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.SIZ_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.INT_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.EDU_roll = randomInteger(6) + randomInteger(6) + randomInteger(6) + 6; WWCGen.STR = WWCGen.STR_roll * 5; WWCGen.CON = WWCGen.CON_roll * 5; WWCGen.POW = WWCGen.POW_roll * 5; WWCGen.DEX = WWCGen.DEX_roll * 5; WWCGen.APP = WWCGen.APP_roll * 5; WWCGen.SIZ = WWCGen.SIZ_roll * 5; WWCGen.INT = WWCGen.INT_roll * 5; WWCGen.EDU = WWCGen.EDU_roll * 5; WWCGen.SAN = WWCGen.POW_roll * 5; x = (WWCGen.CON_roll + WWCGen.SIZ_roll) / 2; WWCGen.HP = parseInt(x); WWCGen.MP = WWCGen.POW_roll; WWCGen.Age = WWCGen.EDU_roll + 6; WWCGen.MOV = 8; WWCGen.PER = WWCGen.INT_roll * 10; WWCGen.OCC = WWCGen.EDU_roll * 20; WWCGen.DODGE = WWCGen.DEX_roll * 2; WWCGen.RADIO = WWCGen.INT_roll * 2; WWCGen.LO = WWCGen.EDU_roll * 5; WWCGen.CR = WWCGen.APP_roll; d = (WWCGen.STR_roll + WWCGen.SIZ_roll) / 2; if (d &lt;= 13) { WWCGen.DB = "-2"; } else if (d &lt;= 17) { WWCGen.DB = "-1"; } else if (d &lt;= 25) { WWCGen.DB = "0"; } else if (d &lt;= 33) { WWCGen.DB = "1d4"; } else { WWCGen.DB = "1d6"; } WWCGen.output['Attributes'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Are these values acceptable?" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['STR'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;STR&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.STR + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['CON'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;CON&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.CON + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SIZ'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SIZ&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SIZ + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DEX'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DEX&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.DEX + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['APP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;APP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.APP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['INT'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;INT&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.INT + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['POW'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;POW&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.POW + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['EDU'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;EDU&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.EDU + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['HP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;HP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.HP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;MP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DB'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DB&lt;/td&gt;&lt;td style='padding: 5px;'&gt;STR+SIZ/10" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['Age'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Age&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Age + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SAN'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SAN&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SAN + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MOV'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Movement&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MOV + "&lt;/td&gt;&lt;/tr&gt;"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText1); }, GenNationality: function (msg, saveCallback) { sendChat("API", "/roll 1t[Nationality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Birthplace = values[0]; WWCGen.Nationality = content.rolls[0].results[0].tableItem.name; WWCGen.output['Birthplace'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Nationality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Birthplace + "&lt;/td&gt;&lt;/tr&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Birthplace'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText3); }); }, GenUpbringing: function (msg,saveCallback) { sendChat("API", "/roll 1t[Upbringing]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Childhood = values[0]; WWCGen.Upbringing = content.rolls[0].results[0].tableItem.name; WWCGen.output['Childhood'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Upbringing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Childhood + "&lt;/td&gt;&lt;/tr&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Childhood'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText4); }); }, GenPersonality: function (msg, saveCallback) { sendChat("API", "/roll 1t[Personality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Personality = values[0]; WWCGen.History = content.rolls[0].results[0].tableItem.name; WWCGen.output['Personality'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Personality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Personality + "&lt;/td&gt;&lt;/tr&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText6); }); }, GenPreWar: function (msg, saveCallback) { sendChat("API", "/roll 1t[Pre-War_Occupation]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.PreWar = values[0]; WWCGen.Job = content.rolls[0].results[0].tableItem.name; WWCGen.output['PreWar'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Pre-War Occupation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.PreWar + "&lt;/td&gt;&lt;/tr&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText7); }); }, GenMilServ: function (msg, saveCallback) { sendChat("API", "/roll 1t[Military_Service]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Military_Service = values[0]; WWCGen.Service = content.rolls[0].results[0].tableItem.name; WWCGen.output['Military_Service'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Military Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Military_Service + "&lt;/td&gt;&lt;/tr&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Military_Service'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText8); }); }, GenEncounter: function (msg, saveCallback) { sendChat("API", "/roll 1t[Mythos_Encounter]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Mythos_Encounter = values[0]; WWCGen.Encounter = content.rolls[0].results[0].tableItem.name; WWCGen.output['Mythos_Encounter'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Initial Mythos Encounter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Mythos_Encounter + "&lt;/td&gt;&lt;/tr&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Mythos_Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText9); }); }, GenRecruitment: function (msg, saveCallback) { sendChat("API", "/roll 1t[Reason_for_Joining]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Recruitment = values[0]; WWCGen.Reason_for_Joining = content.rolls[0].results[0].tableItem.name; WWCGen.output['Recruitment'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Reason for Joining&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Recruitment + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextA); }); }, GenIncome: function (msg, saveCallback) { sendChat("API", "/roll 1t[Income]", function (result) { var content = JSON.parse(result[0].content); WWCGen.Income = content.rolls[0].results[0].tableItem.name; WWCGen.output['Income'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Income&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Income + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Income'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextB); }); }, printSheet: function (msg, saveCallback) { var styleLabel = "style='font-weight: bold; padding: 5px;'"; var styleVal = "style='padding: 5px;'"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCGen.output['Instructions'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Follow these instructions to complete the character according to the options selected." + "&lt;/td&gt;&lt;/tr&gt;"; WWCText2 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Instructions'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Nationality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['NationalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Upbringing'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['UpbringingA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PersonalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['SkillPoints'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWarA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Milserv'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['MilservA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextAA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['RecruitmentA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Additional'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText = + WWCText3 + "&lt;p&gt;&lt;p&gt;" + WWCText4 + "&lt;p&gt;&lt;p&gt;" + WWCText5 + "&lt;p&gt;&lt;p&gt;" + WWCText7 + "&lt;p&gt;&lt;p&gt;" + WWCText8 + "&lt;p&gt;&lt;p&gt;" + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextA; sendChat(msg.who, "/direct " + WWCText); WWCPrint = WWCText2 + "&lt;p&gt;&lt;p&gt;" + WWCText3A + "&lt;p&gt;&lt;p&gt;" + WWCText4A + "&lt;p&gt;&lt;p&gt;" + WWCText5A + "&lt;p&gt;&lt;p&gt;" + WWCText6 + "&lt;p&gt;&lt;p&gt;" + WWCText7A + "&lt;p&gt;&lt;p&gt;" + WWCText8A + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextAA + "&lt;p&gt;&lt;p&gt;" + WWCTextB; if (typeof saveCallback === "function") { saveCallback(); } }, save: function (msg, saveCallback) { sendChat(msg.who, "/direct saving character"), function (character) { var result = createObj('handout', { name: WWCGen.name + ' Instructions', inplayerjournals: "all", archived: false }); var character = createObj("character", { name: WWCGen.name, archived: false, inplayerjournals: "all", controlledby: "all" }); handout.set('notes', WWCPrint); character.set('bio', WWCText); createObj('attribute', { name: 'player_name', current: WWCGen.player, _characterid: character.id }); createObj('attribute', { name: 'recruitment', current: WWCGen.Recruitment, _characterid: character.id }); createObj('attribute', { name: 'STR', current: WWCGen.STR, _characterid: character.id }); createObj('attribute', { name: 'CON', current: WWCGen.CON, _characterid: character.id }); createObj('attribute', { name: 'SIZ', current: WWCGen.SIZ, _characterid: character.id }); createObj('attribute', { name: 'DEX', current: WWCGen.DEX, _characterid: character.id }); createObj('attribute', { name: 'APP', current: WWCGen.APP, _characterid: character.id }); createObj('attribute', { name: 'INT', current: WWCGen.INT, _characterid: character.id }); createObj('attribute', { name: 'POW', current: WWCGen.POW, _characterid: character.id }); createObj('attribute', { name: 'EDU', current: WWCGen.EDU, _characterid: character.id }); createObj('attribute', { name: 'san', current: WWCGen.SAN, _characterid: character.id }); createObj('attribute', { name: 'hp', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'hp_max', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'mp', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'mp_Max', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'MOV', current: '8', _characterid: character.id }); createObj('attribute', { name: 'Age', current: WWCGen.Age, _characterid: character.id }); createObj('attribute', { name: 'birthplace', current: WWCGen.Birthplace, _characterid: character.id }); createObj('attribute', { name: 'dodge', current: WWCGen.DODGE, _characterid: character.id }); createObj('attribute', { name: 'language_own', current: WWCGen.LO, _characterid: character.id }); createObj('attribute', { name: 'operate_radio', current: WWCGen.RADIO, _characterid: character.id }); createObj('attribute', { name: 'credit_rating', current: WWCGen.CR, _characterid: character.id }); createObj('attribute', { name: 'Nationality', current: WWCGen.Nationality, _characterid: character.id }); createObj('attribute', { name: 'Upbringing', current: WWCGen.Upbringing, _characterid: character.id }); createObj('attribute', { name: 'Personality', current: WWCGen.Personality, _characterid: character.id }); createObj('attribute', { name: 'occupation', current: WWCGen.PreWar, _characterid: character.id }); createObj('attribute', { name: 'milserv', current: WWCGen.Military_Service, _characterid: character.id }); createObj('attribute', { name: 'mythos_encounter', current: WWCGen.Mythos_Encounter, _characterid: character.id }); createObj('attribute', { name: 'reason_for_joining', current: WWCGen.Reason_for_Joining, _characterid: character.id }); createObj('attribute', { name: 'wealth', current: WWCGen.Income, _characterid: character.id }); sendChat('', 'Created &lt;a href="<a href="http://journal.roll20.net/character/" rel="nofollow">http://journal.roll20.net/character/</a>' + character.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); sendChat('', 'Created instructions for completing &lt;a href="<a href="http://journal.roll20.net/handout/" rel="nofollow">http://journal.roll20.net/handout/</a>' + handout.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); } } } on("ready", function () { WWCGen.listen(); });
1664145471
GiGs
Pro
Sheet Author
API Scripter
You have this: var WWCGen = WWCGen || { version: "0.0.4", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } switch (msg.content) { case '!GenStats': WWCGen.GenStats(msg, saveCallback); Notice that messages a parameter saveCallback, but that point (and probably not later, I havent looked closely) nothing called saveCallback is ever created. Just based on a quick review, you can probably get rid of savecallback in every function call, like WWCGen.GenStats(msg);
1664147450

Edited 1664147842
Joab
Pro
OK, I removed&nbsp; savecallback &nbsp;everywhere, and now &nbsp;all the steps are performed twice. Not put on the screen twice, but actually rerolled with different results. It gets as far as "saving character" and seems to freeze there. /** * Generates World War Cthulhu Characters * * Syntax: !WWCGen option * */ var WWCGen = WWCGen || { version: "0.0.4", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } switch (msg.content) { case '!GenStats': WWCGen.GenStats(msg); break; case '!GenNationality': WWCGen.GenNationality(msg); break; case '!GenUpbringing': WWCGen.GenUpbringing(msg); break; case '!GenPersonality': WWCGen.GenPersonality(msg); break; case '!GenPreWar': WWCGen.GenPreWar(msg); break; case '!GenMilServ': WWCGen.GenMilServ(msg); break; case '!GenEncounter': WWCGen.GenEncounter(msg); break; case '!GenRecruitment': WWCGen.GenRecruitment(msg); break; case '!GenIncome': WWCGen.GenIncome(msg); break; case '!CharView': WWCGen.printSheet(msg); break; case '!SaveChar': WWCGen.save(msg); break; default: WWCGen.showHelp(); } }); }, showHelp: function () { sendChat("API", "/direct &lt;table style='background: #DCD9D5; border-radius: 20px; font-size: 10px;'&gt;" + "&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Help&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;" + "&lt;tbody&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;!fodder help&lt;/strong&gt;&lt;br&gt;Show this help screen.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen core&lt;/strong&gt;&lt;br&gt;Runs the script.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;/tr&gt;" + "&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;"); }, GenStats: function (msg) { WWCGen.id = msg.playerid; WWCGen.name = msg.who + " #" + (findObjs({ _type: "character", controlledby: "all" }).length + 1); WWCGen.player = msg.who; WWCGen.STR_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.CON_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.POW_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.DEX_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.APP_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.SIZ_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.INT_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.EDU_roll = randomInteger(6) + randomInteger(6) + randomInteger(6) + 6; WWCGen.STR = WWCGen.STR_roll * 5; WWCGen.CON = WWCGen.CON_roll * 5; WWCGen.POW = WWCGen.POW_roll * 5; WWCGen.DEX = WWCGen.DEX_roll * 5; WWCGen.APP = WWCGen.APP_roll * 5; WWCGen.SIZ = WWCGen.SIZ_roll * 5; WWCGen.INT = WWCGen.INT_roll * 5; WWCGen.EDU = WWCGen.EDU_roll * 5; WWCGen.SAN = WWCGen.POW_roll * 5; x = (WWCGen.CON_roll + WWCGen.SIZ_roll) / 2; WWCGen.HP = parseInt(x); WWCGen.MP = WWCGen.POW_roll; WWCGen.Age = WWCGen.EDU_roll + 6; WWCGen.MOV = 8; WWCGen.PER = WWCGen.INT_roll * 10; WWCGen.OCC = WWCGen.EDU_roll * 20; WWCGen.DODGE = WWCGen.DEX_roll * 2; WWCGen.RADIO = WWCGen.INT_roll * 2; WWCGen.LO = WWCGen.EDU_roll * 5; WWCGen.CR = WWCGen.APP_roll; d = (WWCGen.STR_roll + WWCGen.SIZ_roll) / 2; if (d &lt;= 13) { WWCGen.DB = "-2"; } else if (d &lt;= 17) { WWCGen.DB = "-1"; } else if (d &lt;= 25) { WWCGen.DB = "0"; } else if (d &lt;= 33) { WWCGen.DB = "1d4"; } else { WWCGen.DB = "1d6"; } WWCGen.output['Attributes'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Are these values acceptable?" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['STR'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;STR&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.STR + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['CON'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;CON&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.CON + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SIZ'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SIZ&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SIZ + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DEX'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DEX&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.DEX + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['APP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;APP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.APP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['INT'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;INT&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.INT + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['POW'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;POW&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.POW + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['EDU'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;EDU&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.EDU + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['HP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;HP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.HP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;MP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DB'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DB&lt;/td&gt;&lt;td style='padding: 5px;'&gt;STR+SIZ/10" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['Age'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Age&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Age + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SAN'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SAN&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SAN + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MOV'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Movement&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MOV + "&lt;/td&gt;&lt;/tr&gt;"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText1); }, GenNationality: function (msg) { sendChat("API", "/roll 1t[Nationality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Birthplace = values[0]; WWCGen.Nationality = content.rolls[0].results[0].tableItem.name; WWCGen.output['Birthplace'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Nationality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Birthplace + "&lt;/td&gt;&lt;/tr&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Birthplace'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText3); }); }, GenUpbringing: function (msg) { sendChat("API", "/roll 1t[Upbringing]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Childhood = values[0]; WWCGen.Upbringing = content.rolls[0].results[0].tableItem.name; WWCGen.output['Childhood'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Upbringing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Childhood + "&lt;/td&gt;&lt;/tr&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Childhood'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText4); }); }, GenPersonality: function (msg) { sendChat("API", "/roll 1t[Personality]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Personality = values[0]; WWCGen.History = content.rolls[0].results[0].tableItem.name; WWCGen.output['Personality'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Personality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Personality + "&lt;/td&gt;&lt;/tr&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText6); }); }, GenPreWar: function (msg) { sendChat("API", "/roll 1t[Pre-War_Occupation]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.PreWar = values[0]; WWCGen.Job = content.rolls[0].results[0].tableItem.name; WWCGen.output['PreWar'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Pre-War Occupation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.PreWar + "&lt;/td&gt;&lt;/tr&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText7); }); }, GenMilServ: function (msg) { sendChat("API", "/roll 1t[Military_Service]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Military_Service = values[0]; WWCGen.Service = content.rolls[0].results[0].tableItem.name; WWCGen.output['Military_Service'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Military Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Military_Service + "&lt;/td&gt;&lt;/tr&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Military_Service'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText8); }); }, GenEncounter: function (msg) { sendChat("API", "/roll 1t[Mythos_Encounter]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Mythos_Encounter = values[0]; WWCGen.Encounter = content.rolls[0].results[0].tableItem.name; WWCGen.output['Mythos_Encounter'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Initial Mythos Encounter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Mythos_Encounter + "&lt;/td&gt;&lt;/tr&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Mythos_Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText9); }); }, GenRecruitment: function (msg) { sendChat("API", "/roll 1t[Reason_for_Joining]", function (result) { var content = JSON.parse(result[0].content); var values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Recruitment = values[0]; WWCGen.Reason_for_Joining = content.rolls[0].results[0].tableItem.name; WWCGen.output['Recruitment'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Reason for Joining&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Recruitment + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextA); }); }, GenIncome: function (msg) { sendChat("API", "/roll 1t[Income]", function (result) { var content = JSON.parse(result[0].content); WWCGen.Income = content.rolls[0].results[0].tableItem.name; WWCGen.output['Income'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Income&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Income + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Income'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextB); }); }, printSheet: function (msg) { var styleLabel = "style='font-weight: bold; padding: 5px;'"; var styleVal = "style='padding: 5px;'"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCGen.output['Instructions'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Follow these instructions to complete the character according to the options selected." + "&lt;/td&gt;&lt;/tr&gt;"; WWCText2 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Instructions'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Nationality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['NationalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Upbringing'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['UpbringingA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PersonalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['SkillPoints'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWarA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Milserv'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['MilservA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextAA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['RecruitmentA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Additional'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText = + WWCText3 + "&lt;p&gt;&lt;p&gt;" + WWCText4 + "&lt;p&gt;&lt;p&gt;" + WWCText5 + "&lt;p&gt;&lt;p&gt;" + WWCText7 + "&lt;p&gt;&lt;p&gt;" + WWCText8 + "&lt;p&gt;&lt;p&gt;" + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextA; sendChat(msg.who, "/direct " + WWCText); WWCPrint = WWCText2 + "&lt;p&gt;&lt;p&gt;" + WWCText3A + "&lt;p&gt;&lt;p&gt;" + WWCText4A + "&lt;p&gt;&lt;p&gt;" + WWCText5A + "&lt;p&gt;&lt;p&gt;" + WWCText6 + "&lt;p&gt;&lt;p&gt;" + WWCText7A + "&lt;p&gt;&lt;p&gt;" + WWCText8A + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextAA + "&lt;p&gt;&lt;p&gt;" + WWCTextB; }, save: function (msg) { sendChat(msg.who, "/direct saving character"), function (character) { var result = createObj('handout', { name: WWCGen.name + ' Instructions', inplayerjournals: "all", archived: false }); var character = createObj("character", { name: WWCGen.name, archived: false, inplayerjournals: "all", controlledby: "all" }); handout.set('notes', WWCPrint); character.set('bio', WWCText); createObj('attribute', { name: 'player_name', current: WWCGen.player, _characterid: character.id }); createObj('attribute', { name: 'recruitment', current: WWCGen.Recruitment, _characterid: character.id }); createObj('attribute', { name: 'STR', current: WWCGen.STR, _characterid: character.id }); createObj('attribute', { name: 'CON', current: WWCGen.CON, _characterid: character.id }); createObj('attribute', { name: 'SIZ', current: WWCGen.SIZ, _characterid: character.id }); createObj('attribute', { name: 'DEX', current: WWCGen.DEX, _characterid: character.id }); createObj('attribute', { name: 'APP', current: WWCGen.APP, _characterid: character.id }); createObj('attribute', { name: 'INT', current: WWCGen.INT, _characterid: character.id }); createObj('attribute', { name: 'POW', current: WWCGen.POW, _characterid: character.id }); createObj('attribute', { name: 'EDU', current: WWCGen.EDU, _characterid: character.id }); createObj('attribute', { name: 'san', current: WWCGen.SAN, _characterid: character.id }); createObj('attribute', { name: 'hp', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'hp_max', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'mp', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'mp_Max', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'MOV', current: '8', _characterid: character.id }); createObj('attribute', { name: 'Age', current: WWCGen.Age, _characterid: character.id }); createObj('attribute', { name: 'birthplace', current: WWCGen.Birthplace, _characterid: character.id }); createObj('attribute', { name: 'dodge', current: WWCGen.DODGE, _characterid: character.id }); createObj('attribute', { name: 'language_own', current: WWCGen.LO, _characterid: character.id }); createObj('attribute', { name: 'operate_radio', current: WWCGen.RADIO, _characterid: character.id }); createObj('attribute', { name: 'credit_rating', current: WWCGen.CR, _characterid: character.id }); createObj('attribute', { name: 'Nationality', current: WWCGen.Nationality, _characterid: character.id }); createObj('attribute', { name: 'Upbringing', current: WWCGen.Upbringing, _characterid: character.id }); createObj('attribute', { name: 'Personality', current: WWCGen.Personality, _characterid: character.id }); createObj('attribute', { name: 'occupation', current: WWCGen.PreWar, _characterid: character.id }); createObj('attribute', { name: 'milserv', current: WWCGen.Military_Service, _characterid: character.id }); createObj('attribute', { name: 'mythos_encounter', current: WWCGen.Mythos_Encounter, _characterid: character.id }); createObj('attribute', { name: 'reason_for_joining', current: WWCGen.Reason_for_Joining, _characterid: character.id }); createObj('attribute', { name: 'wealth', current: WWCGen.Income, _characterid: character.id }); sendChat('', 'Created &lt;a href="<a href="http://journal.roll20.net/character/" rel="nofollow">http://journal.roll20.net/character/</a>' + character.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); sendChat('', 'Created instructions for completing &lt;a href="<a href="http://journal.roll20.net/handout/" rel="nofollow">http://journal.roll20.net/handout/</a>' + handout.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); } } } on("ready", function () { WWCGen.listen(); });
1664149816

Edited 1664150100
Oosh
Sheet Author
There are quite a few issues going on there. I'd recommend getting a linter so you can see all the errors rather than needing to find them - eslint if you're using VSCode. You have a heap of WWCTextX variables which are declared without a keyword (try to use let and const , not var, var is very dated and should only be used in some very specific circumstances). I'm not sure why any of those would be running twice, I'd restart the sandbox and see if the issue persists. As for the save function: As soon as you start the save function, you're defining an anonymous function inside it. Defining a function isn't the same as calling it, so it's not actually going to run unless you make it an IIFE , or call it at the bottom after you've defined it with a name. I can't see any reason why you need an anonymous function nested in there, it contains your entire save() function except on sendChat line. So probably just remove the function(character) { line, and its matching closing brace. You're also passing a character parameter in to this function, then redefining it a few lines later (using var in this case could lead to very wonky results if you have "character" defined elsewhere in the script with var). "result" is underlined there because you've defined it then never used it - not necessarily a big issue. "handout" will crash your script, that's not defined at all - it looks like your meant to assign the created handout to "handout" and not "result". WWCPrint and WWCText are also undefined, as they're defined in a different scope to the save() function. Did you mean to assign them to the WWCGen object?
1664207348
Joab
Pro
I removed the&nbsp; function(character) { line, and its matching closing brace. I used jslint in Notepad++ to check for issues. Now I'm gettting&nbsp; SyntaxError: Identifier 'WWCGen' has already been declared. /** * Generates World War Cthulhu Characters * * Syntax: !WWCGen option * */ let WWCGen = WWCGen || { version: "0.0.4", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } switch (msg.content) { case '!GenStats': WWCGen.GenStats(msg); break; case '!GenNationality': WWCGen.GenNationality(msg); break; case '!GenUpbringing': WWCGen.GenUpbringing(msg); break; case '!GenPersonality': WWCGen.GenPersonality(msg); break; case '!GenPreWar': WWCGen.GenPreWar(msg); break; case '!GenMilServ': WWCGen.GenMilServ(msg); break; case '!GenEncounter': WWCGen.GenEncounter(msg); break; case '!GenRecruitment': WWCGen.GenRecruitment(msg); break; case '!GenIncome': WWCGen.GenIncome(msg); break; case '!CharView': WWCGen.printSheet(msg); break; case '!SaveChar': WWCGen.save(msg); break; default: WWCGen.showHelp(); } }); }, showHelp: function () { sendChat("API", "/direct &lt;table style='background: #DCD9D5; border-radius: 20px; font-size: 10px;'&gt;" + "&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Help&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;" + "&lt;tbody&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;!fodder help&lt;/strong&gt;&lt;br&gt;Show this help screen.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt;&lt;strong&gt;!WWCGen core&lt;/strong&gt;&lt;br&gt;Runs the script.&lt;/td&gt;&lt;/tr&gt;" + "&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;/tr&gt;" + "&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;"); }, GenStats: function (msg) { WWCGen.id = msg.playerid; WWCGen.name = msg.who + " #" + (findObjs({ _type: "character", controlledby: "all" }).length + 1); WWCGen.player = msg.who; WWCGen.STR_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.CON_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.POW_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.DEX_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.APP_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.SIZ_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.INT_roll = randomInteger(6) + randomInteger(6) + 6; WWCGen.EDU_roll = randomInteger(6) + randomInteger(6) + randomInteger(6) + 6; WWCGen.STR = WWCGen.STR_roll * 5; WWCGen.CON = WWCGen.CON_roll * 5; WWCGen.POW = WWCGen.POW_roll * 5; WWCGen.DEX = WWCGen.DEX_roll * 5; WWCGen.APP = WWCGen.APP_roll * 5; WWCGen.SIZ = WWCGen.SIZ_roll * 5; WWCGen.INT = WWCGen.INT_roll * 5; WWCGen.EDU = WWCGen.EDU_roll * 5; WWCGen.SAN = WWCGen.POW_roll * 5; x = (WWCGen.CON_roll + WWCGen.SIZ_roll) / 2; WWCGen.HP = parseInt(x); WWCGen.MP = WWCGen.POW_roll; WWCGen.Age = WWCGen.EDU_roll + 6; WWCGen.MOV = 8; WWCGen.PER = WWCGen.INT_roll * 10; WWCGen.OCC = WWCGen.EDU_roll * 20; WWCGen.DODGE = WWCGen.DEX_roll * 2; WWCGen.RADIO = WWCGen.INT_roll * 2; WWCGen.LO = WWCGen.EDU_roll * 5; WWCGen.CR = WWCGen.APP_roll; d = (WWCGen.STR_roll + WWCGen.SIZ_roll) / 2; if (d &lt;= 13) { WWCGen.DB = "-2"; } else if (d &lt;= 17) { WWCGen.DB = "-1"; } else if (d &lt;= 25) { WWCGen.DB = "0"; } else if (d &lt;= 33) { WWCGen.DB = "1d4"; } else { WWCGen.DB = "1d6"; } WWCGen.output['Attributes'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Are these values acceptable?" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['STR'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;STR&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.STR + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['CON'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;CON&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.CON + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SIZ'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SIZ&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SIZ + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DEX'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DEX&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.DEX + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['APP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;APP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.APP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['INT'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;INT&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.INT + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['POW'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;POW&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.POW + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['EDU'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;EDU&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.EDU + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['HP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;HP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.HP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MP'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;MP&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MP + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['DB'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;DB&lt;/td&gt;&lt;td style='padding: 5px;'&gt;STR+SIZ/10" + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['Age'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Age&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Age + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['SAN'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;SAN&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.SAN + "&lt;/td&gt;&lt;/tr&gt;"; WWCGen.output['MOV'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Movement&lt;/td&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.MOV + "&lt;/td&gt;&lt;/tr&gt;"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText1); }, GenNationality: function (msg) { sendChat("API", "/roll 1t[Nationality]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Birthplace = values[0]; WWCGen.Nationality = content.rolls[0].results[0].tableItem.name; WWCGen.output['Birthplace'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Nationality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Birthplace + "&lt;/td&gt;&lt;/tr&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Birthplace'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText3); }); }, GenUpbringing: function (msg) { sendChat("API", "/roll 1t[Upbringing]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Childhood = values[0]; WWCGen.Upbringing = content.rolls[0].results[0].tableItem.name; WWCGen.output['Childhood'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Upbringing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Childhood + "&lt;/td&gt;&lt;/tr&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Childhood'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText4); }); }, GenPersonality: function (msg) { sendChat("API", "/roll 1t[Personality]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Personality = values[0]; WWCGen.History = content.rolls[0].results[0].tableItem.name; WWCGen.output['Personality'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Personality&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Personality + "&lt;/td&gt;&lt;/tr&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText6); }); }, GenPreWar: function (msg) { sendChat("API", "/roll 1t[Pre-War_Occupation]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.PreWar = values[0]; WWCGen.Job = content.rolls[0].results[0].tableItem.name; WWCGen.output['PreWar'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Pre-War Occupation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.PreWar + "&lt;/td&gt;&lt;/tr&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText7); }); }, GenMilServ: function (msg) { sendChat("API", "/roll 1t[Military_Service]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Military_Service = values[0]; WWCGen.Service = content.rolls[0].results[0].tableItem.name; WWCGen.output['Military_Service'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Military Service&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Military_Service + "&lt;/td&gt;&lt;/tr&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Military_Service'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText8); }); }, GenEncounter: function (msg) { sendChat("API", "/roll 1t[Mythos_Encounter]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Mythos_Encounter = values[0]; WWCGen.Encounter = content.rolls[0].results[0].tableItem.name; WWCGen.output['Mythos_Encounter'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Initial Mythos Encounter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Mythos_Encounter + "&lt;/td&gt;&lt;/tr&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Mythos_Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCText9); }); }, GenRecruitment: function (msg) { sendChat("API", "/roll 1t[Reason_for_Joining]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); WWCGen.Recruitment = values[0]; WWCGen.Reason_for_Joining = content.rolls[0].results[0].tableItem.name; WWCGen.output['Recruitment'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Reason for Joining&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Recruitment + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextA); }); }, GenIncome: function (msg) { sendChat("API", "/roll 1t[Income]", function (result) { let content = JSON.parse(result[0].content); WWCGen.Income = content.rolls[0].results[0].tableItem.name; WWCGen.output['Income'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Income&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style='padding: 5px;'&gt;" + WWCGen.Income + "&lt;/td&gt;&lt;/tr&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Income'] + "&lt;/tbody&gt;&lt;/table&gt;"; sendChat(msg.who, "/direct " + WWCTextB); }); }, printSheet: function (msg) { let styleLabel = "style='font-weight: bold; padding: 5px;'"; let styleVal = "style='padding: 5px;'"; WWCText1 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['STR'] + WWCGen.output['CON'] + WWCGen.output['SIZ'] + WWCGen.output['DEX'] + WWCGen.output['APP'] + WWCGen.output['INT'] + WWCGen.output['POW'] + WWCGen.output['EDU'] + WWCGen.output['SAN'] + WWCGen.output['HP'] + WWCGen.output['MP'] + WWCGen.output['Age'] + WWCGen.output['MOV'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCGen.output['Instructions'] = "&lt;tr&gt;&lt;td style='font-weight: bold; padding: 5px;'&gt;Instructions&lt;/td&gt;&lt;td style='padding: 5px;'&gt;Follow these instructions to complete the character according to the options selected." + "&lt;/td&gt;&lt;/tr&gt;"; WWCText2 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Instructions'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Nationality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText3A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['NationalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Upbringing'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText4A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['UpbringingA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Personality'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText5A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PersonalityA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText6 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['SkillPoints'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWar'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText7A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['PreWarA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Milserv'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText8A = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['MilservA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText9 = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Encounter'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Recruitment'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextAA = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['RecruitmentA'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCTextB = "&lt;table font-size: 10px;'&gt; &lt;tbody&gt;" + WWCGen.output['Additional'] + "&lt;/tbody&gt;&lt;/table&gt;"; WWCText = + WWCText3 + "&lt;p&gt;&lt;p&gt;" + WWCText4 + "&lt;p&gt;&lt;p&gt;" + WWCText5 + "&lt;p&gt;&lt;p&gt;" + WWCText7 + "&lt;p&gt;&lt;p&gt;" + WWCText8 + "&lt;p&gt;&lt;p&gt;" + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextA; sendChat(msg.who, "/direct " + WWCText); WWCPrint = WWCText2 + "&lt;p&gt;&lt;p&gt;" + WWCText3A + "&lt;p&gt;&lt;p&gt;" + WWCText4A + "&lt;p&gt;&lt;p&gt;" + WWCText5A + "&lt;p&gt;&lt;p&gt;" + WWCText6 + "&lt;p&gt;&lt;p&gt;" + WWCText7A + "&lt;p&gt;&lt;p&gt;" + WWCText8A + WWCText9 + "&lt;p&gt;&lt;p&gt;" + WWCTextAA + "&lt;p&gt;&lt;p&gt;" + WWCTextB; }, save: function (msg) { sendChat(msg.who, "/direct saving character"); let handout = createObj('handout', { name: WWCGen.name + ' Instructions', inplayerjournals: "all", archived: false }); let character = createObj("character", { name: WWCGen.name, archived: false, inplayerjournals: "all", controlledby: "all" }); handout.set('notes', WWCPrint); character.set('bio', WWCText); createObj('attribute', { name: 'player_name', current: WWCGen.player, _characterid: character.id }); createObj('attribute', { name: 'recruitment', current: WWCGen.Recruitment, _characterid: character.id }); createObj('attribute', { name: 'STR', current: WWCGen.STR, _characterid: character.id }); createObj('attribute', { name: 'CON', current: WWCGen.CON, _characterid: character.id }); createObj('attribute', { name: 'SIZ', current: WWCGen.SIZ, _characterid: character.id }); createObj('attribute', { name: 'DEX', current: WWCGen.DEX, _characterid: character.id }); createObj('attribute', { name: 'APP', current: WWCGen.APP, _characterid: character.id }); createObj('attribute', { name: 'INT', current: WWCGen.INT, _characterid: character.id }); createObj('attribute', { name: 'POW', current: WWCGen.POW, _characterid: character.id }); createObj('attribute', { name: 'EDU', current: WWCGen.EDU, _characterid: character.id }); createObj('attribute', { name: 'san', current: WWCGen.SAN, _characterid: character.id }); createObj('attribute', { name: 'hp', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'hp_max', current: WWCGen.HP, _characterid: character.id }); createObj('attribute', { name: 'mp', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'mp_Max', current: WWCGen.MP, _characterid: character.id }); createObj('attribute', { name: 'MOV', current: '8', _characterid: character.id }); createObj('attribute', { name: 'Age', current: WWCGen.Age, _characterid: character.id }); createObj('attribute', { name: 'birthplace', current: WWCGen.Birthplace, _characterid: character.id }); createObj('attribute', { name: 'dodge', current: WWCGen.DODGE, _characterid: character.id }); createObj('attribute', { name: 'language_own', current: WWCGen.LO, _characterid: character.id }); createObj('attribute', { name: 'operate_radio', current: WWCGen.RADIO, _characterid: character.id }); createObj('attribute', { name: 'credit_rating', current: WWCGen.CR, _characterid: character.id }); createObj('attribute', { name: 'Nationality', current: WWCGen.Nationality, _characterid: character.id }); createObj('attribute', { name: 'Upbringing', current: WWCGen.Upbringing, _characterid: character.id }); createObj('attribute', { name: 'Personality', current: WWCGen.Personality, _characterid: character.id }); createObj('attribute', { name: 'occupation', current: WWCGen.PreWar, _characterid: character.id }); createObj('attribute', { name: 'milserv', current: WWCGen.Military_Service, _characterid: character.id }); createObj('attribute', { name: 'mythos_encounter', current: WWCGen.Mythos_Encounter, _characterid: character.id }); createObj('attribute', { name: 'reason_for_joining', current: WWCGen.Reason_for_Joining, _characterid: character.id }); createObj('attribute', { name: 'wealth', current: WWCGen.Income, _characterid: character.id }); sendChat('', 'Created &lt;a href="<a href="http://journal.roll20.net/character/" rel="nofollow">http://journal.roll20.net/character/</a>' + character.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); sendChat('', 'Created instructions for completing &lt;a href="<a href="http://journal.roll20.net/handout/" rel="nofollow">http://journal.roll20.net/handout/</a>' + handout.id + '" style="color:blue;text-decoration:underline;"&gt;WWCGen ' + WWCGen.name + '&lt;/a&gt;'); } }; on("ready", function () { WWCGen.listen(); });
1664241581

Edited 1664241681
Oosh
Sheet Author
The redeclaration error probably means you've pasted your script into two different tabs in the API script control panel, or pasted it in twice in a row to the same tab. Roll20 scripts are concatenated into one big script before hitting the interpreter, so you can't use the same variable name more than once across any enabled scripts. Essentially this is happening: let WWCGen = 'stuff'; let WWCGen = 'things'; The error was slipping through before because var doesn't throw an error in this case, which can lead to confusing behaviour. That's one of the reasons I'd recommend against using var , though in this case you're using an older singleton pattern which does rely on the use of var to make sense of the first line. Essentially this: var WWCGen = WWCGen || { //script... } Is a shortcut JS method of producing a singleton - a big global function that should only have one instance. Personally, I'd rather have an error thrown than let the interpreter figure out whether to throw my entire script in the bin or not, and just declare it with const - I'd rather know straight away that there's a namespace collision with a clear error. If you were getting double output from the script before, it doesn't seem like the pattern was working anyway. Most of the active script writers are using the Revealing Module Pattern - there's some background info on that in this old thread , Aaron has a more up-to-date template lying around somewhere. We also generally use fat arrow syntax for declaring functions, but it's not a requirement - here's an example using the old standard function() declaration. Don't feel compelled to use RMP if you don't like it, but it's a much more modern pattern for writing a script. The internet (including the Roll20 mod repo) is littered with code written before let/const existed, so you'll still see that var singleton pattern everywhere, but I'd recommend moving away from it in future. In terms of this script though, it's probably not worth the time restructuring it - I'd just change the declaration to const WWCGen = {} to ensure your declaration of WWCGen is the one source of WWCGen, and any attempt to redefine it will throw an error. Note that this doesn't protect the innards of the object - another script can still do WWCGen.save = null; and destroy your function. This is another advantage of the Revealing Module Pattern - you can declare which parts are visible to the global scope via the return interface.