
I am reusing code I have used multiple times, but this time I am getting
TypeError: Cannot read properties of undefined (reading 'who')
I don't know what's different between this script and the others I have created.
/** * Generates Mothership Characters for the Residue Processing Scenario * * Syntax: !RPGen option * */ var RPG = RPG || { version: "0.0.1", output: [], listen: function () { on('chat:message', function (msg) { // Exit if not an api command if (msg.type != "api") { return; } if (msg.content.indexOf("!RPGen ") != -1) { var input = msg.content.split(" "); if (input[1] == "help") { RPG.showHelp(); } else { sendChat('API', "/direct <h6>Generating character</h6>"); RPG.Generate(msg, RPG.printSheet, RPG.save); } } else if (msg.content.indexOf("!RPGen") != -1) { RPG.showHelp(); } }); }, showHelp: function () { sendChat("API", "/direct <table style='background: #DCD9D5; border-radius: 20px; font-size: 10px;'>" + "<thead><tr><th>Help</th></tr></thead>" + "<tbody>" + "<tr><td><strong>!fodder</strong><br><strong>!fodder help</strong><br>Show this help screen.</td></tr>" + "<tr><td><strong>!fodder core</strong><br>Use default core DCC tables.</td></tr>" + "<tr><td><strong>!fodder brokenmoon</strong><br>Use Crawling Under A Broken Moon tables, including mutants and robots.</td></tr>" + "<tr><td><strong>!fodder crawl</strong><br>Use Crawl! tables for zero-level character generation, including gnomes and physical characteristics.</td></tr>" + "<tr><td> </td></tr>" + "</td></tr></tbody></table>"); }, Generate: function (msg, outputCallback, saveCallback) { RPG.id = msg.playerid; RPG.player = msg.who; RPG.name = msg.who + " #" + (findObjs({_type: "character", controlledby: msg.playerid}).length + 1) RPG.GenAbilities(); RPG.GenHistory(); RPG.GenSuit(); RPG.GenSkills(); if (typeof outputCallback === "function") { setTimeout(outputCallback, 2500, msg, saveCallback); } }, GenAbilities: function (msg) { sendChat("API", "/roll 1t[RP-Stats]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); RPG.Strength = values[0]; RPG.Speed = values[1]; RPG.Intellect = values[2]; RPG.Combat = values[3]; RPG.Health = 10; RPG.Stress = 5; RPG.Sanity = 20; RPG.Fear = 20; RPG.Body = 20; RPG.Armor = 20; RPG.output['Strength'] = "<tr><td style='font-weight: bold; padding: 5px;'>Strength</td></tr><tr><td style='padding: 5px;'>" + RPG.Strength + "</td></tr>"; RPG.output['Speed'] = "<tr><td style='font-weight: bold; padding: 5px;'>Speed</td></tr><tr><td style='padding: 5px;'>" + RPG.Speed + "</td></tr>"; RPG.output['Intellect'] = "<tr><td style='font-weight: bold; padding: 5px;'>Intellect</td></tr><tr><td style='padding: 5px;'>" + RPG.Intellect + "</td></tr>"; RPG.output['Combat'] = "<tr><td style='font-weight: bold; padding: 5px;'>Combat</td></tr><tr><td style='padding: 5px;'>" + RPG.Combat + "</td></tr>"; RPG.output['Health'] = "<tr><td style='font-weight: bold; padding: 5px;'>Health</td></tr><tr><td style='padding: 5px;'>" + RPG.Health + "</td></tr>"; RPG.output['Stress'] = "<tr><td style='font-weight: bold; padding: 5px;'>Stress</td></tr><tr><td style='padding: 5px;'>" + RPG.Stress + "</td></tr>"; RPG.output['Sanity'] = "<tr><td style='font-weight: bold; padding: 5px;'>Sanity Save</td></tr><tr><td style='padding: 5px;'>" + RPG.Sanity + "</td></tr>"; RPG.output['Fear'] = "<tr><td style='font-weight: bold; padding: 5px;'>Fear Save</td></tr><tr><td style='padding: 5px;'>" + RPG.Fear + "</td></tr>"; RPG.output['Body'] = "<tr><td style='font-weight: bold; padding: 5px;'>Body Save</td></tr><tr><td style='padding: 5px;'>" + RPG.Body + "</td></tr>"; RPG.output['Armor'] = "<tr><td style='font-weight: bold; padding: 5px;'>Armor Save</td></tr><tr><td style='padding: 5px;'>" + RPG.Armor + "</td></tr>"; RPGText1 = "<table font-size: 10px;'> <tbody>" + RPG.output['Strength'] + "</tbody></table>"; RPGText2 = "<table font-size: 10px;'> <tbody>" + RPG.output['Speed'] + "</tbody></table>"; RPGText3 = "<table font-size: 10px;'> <tbody>" + RPG.output['Intellect'] + "</tbody></table>"; RPGText4 = "<table font-size: 10px;'> <tbody>" + RPG.output['Combat'] + "</tbody></table>"; RPGText5 = "<table font-size: 10px;'> <tbody>" + RPG.output['Health'] + "</tbody></table>"; RPGText6 = "<table font-size: 10px;'> <tbody>" + RPG.output['Stress'] + "</tbody></table>"; RPGText7 = "<table font-size: 10px;'> <tbody>" + RPG.output['Sanity'] + "</tbody></table>"; RPGText8 = "<table font-size: 10px;'> <tbody>" + RPG.output['Fear'] + "</tbody></table>"; RPGText9 = "<table font-size: 10px;'> <tbody>" + RPG.output['Body'] + "</tbody></table>"; RPGText10 = "<table font-size: 10px;'> <tbody>" + RPG.output['Armor'] + "</tbody></table>"; sendChat(msg.who, sendto + RPGText1 + RPGText2 + RPGText3 + RPGText4 + RPGText5 + RPGText6 + RPGText7 + RPGText8 + RPGText9 + RPGText10); }); }, GenHistory: function (msg) { sendChat("API", "/roll 1t[RP-History]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); RPG.Study = values[0]; RPG.Effect = values[1]; if (RPG.Effect.includes('(A)')) { RPG.Fear = 50; } RPG.output['Study'] = "<tr><td style='font-weight: bold; padding: 5px;'>Study</td></tr><tr><td style='padding: 5px;'>" + RPG.Study + "</td></tr>"; RPG.output['Effect'] = "<tr><td style='font-weight: bold; padding: 5px;'>Side Effect</td></tr><tr><td style='padding: 5px;'>" + RPG.Effect + "</td></tr>"; RPGText11 = "<table font-size: 10px;'> <tbody>" + RPG.output['Study'] + "</tbody></table>"; RPGText12 = "<table font-size: 10px;'> <tbody>" + RPG.output['Effect'] + "</tbody></table>"; sendChat(msg.who, sendto + RPGText11); sendChat(msg.who, sendto + RPGText12); }); }, GenSuit: function (msg) { sendChat("API", "/roll 1t[RP-Expression]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); RPG.Expression = values[0]; RPG.output['Expression'] = "<tr><td style='font-weight: bold; padding: 5px;'>Doll Suit Expression</td></tr><tr><td style='padding: 5px;'>" + RPG.Expression + "</td></tr>"; RPGText13 = "<table font-size: 10px;'> <tbody>" + RPG.output['Expression'] + "</tbody></table>"; sendChat("API", "/roll 1t[RP-Quirk]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); RPG.Quirk = values[0]; RPG.output['Quirk'] = "<tr><td style='font-weight: bold; padding: 5px;'>Doll Suit Quirk</td></tr><tr><td style='padding: 5px;'>" + RPG.Quirk + "</td></tr>"; RPGText14 = "<table font-size: 10px;'> <tbody>" + RPG.output['Quirk'] + "</tbody></table>"; sendChat(msg.who, sendto + RPGText13); sendChat(msg.who, sendto + RPGText14); }); }); }, GenSkills: function (msg) { sendChat("API", "/roll 1t[RP-Skills]", function (result) { let content = JSON.parse(result[0].content); let values = content.rolls[0].results[0].tableItem.name.split(':'); RPG.Skill = values[0]; RPG.output['Skill'] = "<tr><td style='font-weight: bold; padding: 5px;'>Skill</td></tr><tr><td style='padding: 5px;'>" + RPG.Skill + "</td></tr>"; RPGText15 = "<table font-size: 10px;'> <tbody>" + RPG.output['Skill'] + "</tbody></table>"; sendChat(msg.who, sendto + RPGText15); }); }, printSheet: function (msg, saveCallback) { let styleLabel = "style='font-weight: bold; padding: 5px;'"; let styleVal = "style='padding: 5px;'"; RPG.output['Instructions'] = "<tr><td style='font-weight: bold; padding: 5px;'>Instructions</td><td style='padding: 5px;'>Follow these instructions to complete the character according to the options selected." + "</td></tr>"; RPG.output['Strength'] = "<tr><td style='font-weight: bold; padding: 5px;'>Strength</td><td style='padding: 5px;'>" + RPG.Strength + "</td></tr>"; RPG.output['Speed'] = "<tr><td style='font-weight: bold; padding: 5px;'>Speed</td><td style='padding: 5px;'>" + RPG.Speed + "</td></tr>"; RPG.output['Intellect'] = "<tr><td style='font-weight: bold; padding: 5px;'>Intellect</td><td style='padding: 5px;'>" + RPG.Intellect + "</td></tr>"; RPG.output['Combat'] = "<tr><td style='font-weight: bold; padding: 5px;'>Combat</td><td style='padding: 5px;'>" + RPG.Combat + "</td></tr>"; RPG.output['Health'] = "<tr><td style='font-weight: bold; padding: 5px;'>Health</td><td style='padding: 5px;'>" + RPG.Health + "</td></tr>"; RPG.output['Stress'] = "<tr><td style='font-weight: bold; padding: 5px;'>Stress</td><td style='padding: 5px;'>" + RPG.Stress + "</td></tr>"; RPG.output['Sanity'] = "<tr><td style='font-weight: bold; padding: 5px;'>Sanity Save</td><td style='padding: 5px;'>" + RPG.Sanity + "</td></tr>"; RPG.output['Fear'] = "<tr><td style='font-weight: bold; padding: 5px;'>Fear Save</td><td style='padding: 5px;'>" + RPG.Fear + "</td></tr>"; RPG.output['Body'] = "<tr><td style='font-weight: bold; padding: 5px;'>Body Save</td><td style='padding: 5px;'>" + RPG.Body + "</td></tr>"; RPG.output['Armor'] = "<tr><td style='font-weight: bold; padding: 5px;'>Armor Save</td><td style='padding: 5px;'>" + RPG.Armor + "</td></tr>"; RPG.output['Study'] = "<tr><td style='font-weight: bold; padding: 5px;'>Study</td><td style='padding: 5px;'>" + RPG.Study + "</td></tr>"; RPG.output['Effect'] = "<tr><td style='font-weight: bold; padding: 5px;'>Side Effect</td><td style='padding: 5px;'>" + RPG.Effect + "</td></tr>"; RPG.output['Quirk'] = "<tr><td style='font-weight: bold; padding: 5px;'>Doll Suit Quirk</td><td style='padding: 5px;'>" + RPG.Quirk + "</td></tr>"; RPG.output['Skill'] = "<tr><td style='font-weight: bold; padding: 5px;'>Skill</td><td style='padding: 5px;'>" + RPG.Skill + "</td></tr>"; RPG.output['Expression'] = "<tr><td style='font-weight: bold; padding: 5px;'>Doll Suit Expression</td><td style='padding: 5px;'>" + RPG.Expression + "</td></tr>"; "function" === typeof saveCallback && saveCallback(); RPGText1 = "<table font-size: 10px;'> <tbody>" + RPG.output['Study'] + RPG.output['Effect'] + RPG.output['Quirk'] + RPG.output['Expression'] + RPG.output['Skill'] + "</tbody></table>"; RPGText = RPGText1 + "<p><p>"; sendChat(msg.who, "/direct " + RPGText); if (typeof saveCallback === "function") { saveCallback(); } }, save: function (msg) { let character = createObj("character", { name: RPG.name, archived: false, inplayerjournals: "all", controlledby: "all" }); character.set('bio', RPGText); createObj('attribute', { name: 'strength', current: RPG.Strength, _characterid: character.id }); createObj('attribute', { name: 'speed', current: RPG.Speed, _characterid: character.id }); createObj('attribute', { name: 'intellect', current: RPG.Intellect, _characterid: character.id }); createObj('attribute', { name: 'combat', current: RPG.Combat, _characterid: character.id }); createObj('attribute', { name: 'health', current: RPG.Health, _characterid: character.id }); createObj('attribute', { name: 'stress', current: RPG.Stress, _characterid: character.id }); createObj('attribute', { name: 'sanity', current: RPG.Sanity, _characterid: character.id }); createObj('attribute', { name: 'fear', current: RPG.Fear, _characterid: character.id }); createObj('attribute', { name: 'body', current: RPG.Body, _characterid: character.id }); createObj('attribute', { name: 'armor', current: RPG.Armor, _characterid: character.id }); createObj('attribute', { name: 'omen', current: RPG.Omen, _characterid: character.id }); createObj('attribute', { name: 'trait1', current: RPG.Trait1, _characterid: character.id }); createObj('attribute', { name: 'trait2', current: RPG.Trait2, _characterid: character.id }); createObj('attribute', { name: 'body', current: RPG.Body, _characterid: character.id }); createObj('attribute', { name: 'habit', current: RPG.Habit, _characterid: character.id }); createObj('attribute', { name: 'story', current: RPG.Story, _characterid: character.id }); sendChat('', 'Created instructions for completing <a href="http:journal.roll20.net/handout/' + handout.id + '" style="color:blue;text-decoration:underline;">RPG ' + RPG.name + '</a>'); } }; on("ready", function () { RPG.listen(); });
The game uses the official Mothership by Tuesday Knight Games character sheet.