Hi ! I try to add this script ("new script" button) to my campaign, but I get this error (in API Output Console) : "SyntaxError: Unexpected token )\n at eval (<anonymous>)\n at messageHandler (evalmachine.<anonymous>:284:6)\n at process.<anonymous> (/home/node/d20-api-server/node_modules/tiny-worker/lib/worker.js:60:55)\n at emitTwo (events.js:106:13)\n at process.emit (events.js:194:7)\n at process.nextTick (internal/child_process.js:766:12)\n at _combinedTickCallback (internal/process/next_tick.js:73:7)\n at process._tickCallback (internal/process/next_tick.js:104:9)" The script : on('chat:message',function(msg) { //example roll: !openroll 90 mastery inhumanity // will roll 1d100+90 including open roll values, limited to values below zen, with a reduced chance to fumble from mastery // Adding 'initiative' as a flag will dictate the result with appropriate flags to the initiative tracker. if(!(msg.selected && msg.selected.length > 0)) return; // Make sure there's a selected object var token = getObj('graphic', msg.selected[0]._id); if(token.get('subtype') != 'token') return; // Don't try to perform this command on a drawing or card //code supplied by Brian for determining if a player is speaking in character or not. var name = msg.who; var character = findObjs({ _type: 'character', name: name })[0]; var who; if(character) // Player is speaking as a character { who = 'character|'+character.id; } else // Player is speaking as a player { who = 'player|'+msg.playerid; } //parse the message type if(msg.type != 'api') return; var parts = msg.content.toLowerCase().split(' '); log( 'parts: '+parts); var command = parts.shift().substring(1); //remove the ! if(command == 'openroll') { //Variable Declarations var openFloor = 90; // The minimum result needed to roll again. set to the default described in the core rules. var fumbleCeil = 3; // Rolls below or equal to this number are considered fumbles. set to core rules default. var open; // Flag for the player having rolled a number above openFloor. var dice; // Numberical value of latest dice rolled (1d100). Good for debugging/transparency. var mod = 0; // Variable to keep track of mod throughout number calculations. var rollCount=1; // Number of times dice have been rolled. Good for debugging and fumble validation. var fumbleLevel; // Number used when fumble is achieved. Fumbles depend on context, so little automated use. var total = 0; // The final result of the roll. var inhumanity = false; // Limit rolls below 320 unless this is true. var zen = false; // Limit rolls below 440 unless this is true. var rollTracking=''; // Track rolls in string form for logs / output. var initiative = false; // Flag to set the result after flag calculation to the initiative table. var reset = false; // Flag to reset the initiative window in case pages were changed. var capped = false; // Flag to display to the player when their roll is limited. //loop through the remaining parts for flags and other mods _.each(parts,function(curPart){ if(!isNaN(Number(curPart))) { //add numbers to mod mod+= Number(curPart); }else{ if(curPart == 'inhumanity' || curPart == 'inhuman') inhumanity = true; if(curPart == 'zen') zen = true; if(curPart == 'reset') reset = true; if(curPart == 'initiative') initiative = true; if(curPart == 'complex') { if(!initiative) { //complex doesn't apply to initiative rolls. fumbleCeil+=2; } } if(_.contains(parts, 'mastery')) { if(!initiative) { //mastery doesn't apply to initiative rolls. fumbleCeil--; } } } }); total += mod; log('Openroll Floor: '+openFloor); log('Fumble Ceiling: '+fumbleCeil); //Roll the actual dice already! do{ open=false; // Reset open roll flag. dice = randomInteger(100); // Roll 1d100. log('Dice roll ' +rollCount + ': '+dice); //check if dice result was > openFloor, if so, will roll again. if(dice >= openFloor) { if(openFloor <100)openFloor++; // Increment openFloor, can never exceed 100. sendChat( who,rollCount+' Open Roll! <a style="color:DeepSkyBlue"> <b>('+dice+')</b></a>'); // Bragging / transparency. rollTracking += '<a style="color:DeepSkyBlue"><b>('+dice+')</b></a>+'; // Add dice roll to string for output later. open=true; rollCount++; } else if(dice <= fumbleCeil && rollCount==1) { // if dice result was < fumbleCeil, a fumble has occured. // NOTE: fumbles cannot happen after an open roll occurs, hence the check on rollCount. mod = 0; modArray = [15, 0, -15, -15, -15]; modArrayMastery = [0, -15]; if (fumbleCeil == 2) mod = modArrayMastery[dice-1]; else mod = modArray[dice-1]; fumbleLevel=randomInteger(100) + mod; //roll 1d100 to determine fumble level + modifier (if first dice is a 3, and second dice 80 : fumbleLevel = 80 - 15 log('Fumble Level: '+fumbleLevel); rollTracking += '('+dice+')+'; sendChat( who, 'Fumble! level: ('+fumbleLevel+')'); }else{ rollTracking += '('+dice+')+'; } total += dice; //record the total so far. } while(open); //roll again if openroll occured. //take off the last + in the rollTracking string so the output doesn't look stupid. rollTracking = rollTracking.substring(0,(rollTracking.length)-1); //apply any and all limitations. if(inhumanity && !zen)//only one can apply, as zen overwrites inhumanity. { if(total>439) { capped = true; // the player was limited. dice = total; //reuse variable, save original (uncapped) roll. total = 439; //zen starts at 440. } }else if(!zen) { if(total > 319) { capped = true; // the player was limited. dice = total; // reuse variable, save original (uncapped) roll. total = 319; // standard roll, limit below inhumanity (320). } } //no else case for if(zen), as all rolls above 440 are allowed if the roll has zen. if(capped) { sendChat(who,'/direct <p style="color:GoldenRod"><b>CAPPED! ('+dice+')</b></p>'); } //finally, make sure total result is above 0, as rolls in A:BF cannot be negative, and output. if(total<0) total=0; sendChat(who, '/direct Roll Result: ['+rollTracking+']+('+mod+') = ('+total+')'); //Separate logic for applying the above roll to the current token's Initiative. if(initiative) { var turnorder; //turn object that keeps all initiative information. if(Campaign().get("turnorder") == "" || reset) turnorder = []; //check for reset flag or empty init else turnorder = JSON.parse(Campaign().get("turnorder")); //reject any tokens from turnorder that match this ID . turnorder = _.reject(turnorder,function(curChar){ if(curChar.id == msg.selected[0]._id) //same character return true; }); //push new token onto the turn order. turnorder.push({ id: msg.selected[0]._id, pr: total, }); //If the initiative window is not displayed, open it. Campaign().initiativepage=true; //Add a new custom entry to the end of the turn order. Campaign().set("turnorder", JSON.stringify(turnorder)); } } }); Someone can help me ? Thx ! Max