For anyone having problems with the script, please try this new version. If it fixes the problem, I'll make a pull request for it. If it doesn't fix the problem, this version has some new debugging features built in. Please copy-pasta any error messages that come up in your API console log to this thread. const WelcomePackage = (() => {
'use strict';
const CREATE_CHARACTER_CMD = '!welcomePackageCreateCharacter';
const CSS = {
'menu': {
'background': '#fff',
'border': 'solid 1px #a88cd5',
'border-radius': '5px',
'font-weight': 'bold',
'margin-bottom': '1em',
'overflow': 'hidden'
},
'menuBody': {
'padding': '5px',
'text-align': 'center'
},
'menuHeader': {
'background': '#a88cd5',
'color': '#fff',
'text-align': 'center'
}
};
const observers = [];
let isReady = false;
/**
* Report an error to the GM and log its stack trace.
* @param err The error.
*/
function _error(err) {
log(`WelcomePackage ERROR: ${err.message}`);
log(err.stack);
sendChat('WelcomePackage API',
`/w gm ERROR: ${err.message} --- See API console log for details.`);
}
/**
* Event handler for when a player logs into the game.
* @private
* @param {Player} player
*/
function _handlePlayerOnline(player) {
if (player.get('_online') && !playerIsGM(player.get('_id')))
createPlayerCharacter(player);
}
/**
* Registers a function to be notified when a character is created
* @param {function} func
*/
function onAddCharacter(func){
if('function' === typeof func){
observers.push(func);
}
}
/**
* Notifies all registered functions when a character is created
* @param {Character} character
*/
function notifyAddCharacter(character){
observers.forEach((f)=>f(character));
}
/**
* Creates a character creation macro for a player if they don't already
* have the macro.
* @param {Player} player
*/
function addCharacterCreateMacro() {
const macroName = 'CreateACharacter';
let allPlayers = findObjs({
_type: 'player'
});
_.each(allPlayers, player => {
let playerId = player.get('_id');
let macro = findObjs({
_type: 'macro',
_playerid: playerId,
name: macroName
})[0];
if(!macro) {
log(`WelcomePackage: Creating "CreateACharacter" macro for player ` +
`${player.get('_displayname')}`);
createObj('macro', {
_playerid: playerId,
name: macroName,
action: `${CREATE_CHARACTER_CMD} ?{Character Name:}`
});
}
});
}
/**
* Creates a character for a player if they don't already have any characters.
* @param {Player} player
* @param {string} [name] The name of the character. If this is provided,
* the character will be created regardless of whether
* the player has a character already.
*/
function createPlayerCharacter(player, name) {
let playerId = player.get('_id');
let who = player.get('_displayname');
// Check if there are any characters already controlled by the player.
let characters = findObjs({
_type: 'character'
}).filter(character => {
return character.get('controlledby').includes(playerId);
});
// Create the new character if there are none, or if a name has been
// provided for a character being explicitly created through the macro.
if(characters.length === 0 || name) {
if(!name) {
log(`WelcomePackage: Creating first-time character for player ` +
`${player.get('_displayname')}`);
name = `${who}'s character`;
}
let character = createObj('character', {
controlledby: playerId,
inplayerjournals: 'all',
name
});
// Disable the Charactermancer for the new character by creating
// the "version" attribute. This will be set to its correct value later
// by the character sheet's worker.
createObj('attribute', {
name: 'mancer_confirm_flag',
characterid: character.id,
current: '1'
});
createObj('attribute', {
name: 'l1mancer_status',
characterid: character.id,
current: 'completed'
});
setTimeout(() => {
notifyAddCharacter(character);
showCharacterLink(who, character);
}, 1000);
}
}
/**
* Shows a player the link to their new character with a welcoming message.
* @param {string} who The player's display name.
* @param {Character} character
*/
function showCharacterLink(who, character) {
let menu = new HtmlBuilder('.menu');
menu.append('.menuHeader', 'Welcome!');
let content = menu.append('.menuBody');
content.append('div', 'A blank character has been created for you to start building: ');
content.append('a', character.get('name'), {
href: '<a href="http://journal.roll20.net/character/" rel="nofollow">http://journal.roll20.net/character/</a>' + character.get('_id'),
style: {
color: '#a08'
}
});
let html = menu.toString(CSS);
sendChat('Welcome Package', '/w ' + who + ' ' + html);
}
// When a player logs in, create a character for them if they don't have one.
on('change:player:_online', player => {
if(isReady)
_handlePlayerOnline(player);
});
on('ready', () => {
try {
// Create the global macro for the script (visible to all players)
// if it doesn't already exist.
addCharacterCreateMacro();
log('*** Initialized Welcome Package v1.3.2 ***');
isReady = true;
// Wait some amount of time to give other scripts a chance to finish
// loading. Then process any pending logged in players.
setTimeout(() => {
try {
// Once we're loaded, create a character for any player that doesn't
// have one.
let players = findObjs({
_type: 'player'
});
_.each(players, player => {
_handlePlayerOnline(player);
});
}
catch (err) {
_error(err);
}
}, 5000);
}
catch (err) {
_error(err);
}
});
/**
* Process chat commands.
*/
on('chat:message', msg => {
let playerId = msg.playerid;
if(msg.content.startsWith(CREATE_CHARACTER_CMD)) {
let player = getObj('player', playerId);
let argv = msg.content.split(' ');
let name = argv.slice(1).join(' ');
createPlayerCharacter(player, name);
}
});
return {
OnAddCharacter: onAddCharacter,
onAddCharacter
};
})();
_.noop(WelcomePackage);