Skip to content

Instantly share code, notes, and snippets.

@player-03
Last active August 19, 2021 20:48
Show Gist options
  • Save player-03/377701e45742280c44a49375e27626fe to your computer and use it in GitHub Desktop.
Save player-03/377701e45742280c44a49375e27626fe to your computer and use it in GitHub Desktop.
A Roll20 API script allowing players to claim their imported characters.
var SheetSetup = SheetSetup || (function() {
'use strict';
var settings = {
/**
* Set null to disable chat.
*/
chatName: "Sheet Setup",
/**
* Set null to leave player name blank.
*/
playerNameAttr: "player_name",
/**
* These values will be used in place of any existing attributes.
*/
defaults: {
},
/**
* These values will be used in addition to `defaults`.
*/
npcDefaults: {
}
};
function chat(who, message) {
if(settings.chatName && who) {
if(who.indexOf(" ") > 0) {
who = who.substr(0, who.indexOf(" "));
}
sendChat(settings.chatName, "/w " + who + " " + message);
}
}
function onAddCharacter(character, extraAttrs) {
var characterid = character.get("_id");
var attrs = findObjs({
_type: "attribute",
_characterid: characterid
});
function setAttribute(name, value) {
for(var attr of attrs) {
if(attr.get("name") == name) {
attr.set("current", value);
return;
}
}
createObj("attribute", {
name: name,
current: value,
characterid: characterid
});
}
for(var key in settings.defaults) {
setAttribute(key, settings.defaults[key]);
}
if(extraAttrs) {
for(var key in extraAttrs) {
setAttribute(key, extraAttrs[key]);
}
}
}
function generateCharacter(player, args, who, extraAttrs) {
var name = args.join(" ") || player.get("_displayname") + "'s character";
var character = createObj("character", {
name: name,
archived: false,
inplayerjournals: player.id,
controlledby: player.id
});
if(settings.playerNameAttr) {
createObj("attribute", {
name: settings.playerNameAttr,
current: player.get("_displayname"),
_characterid: character.id
});
}
chat(who, "New character created - check your journal.");
onAddCharacter(character, extraAttrs);
return character;
}
function claimCharacter(player, args, who) {
var name = args.join(" ");
if(!name) {
chat(who, "Please enter a name.");
return;
}
var candidates = findObjs({
_type: "character",
name: name
});
function validate(playerID) {
//Empty = valid.
if(!playerID) {
return true;
}
for(var id of playerID.split(",")) {
//Controller must be "all" or the current player,
//or not a player in the current game.
if(id !== "all" && id !== player.id && getObj("player", id)) {
return false;
}
}
return true;
}
for(var candidate of candidates) {
if(validate(candidate.get("inplayerjournals"))
&& validate(candidate.get("controlledby"))) {
candidate.set("inplayerjournals", player.id);
candidate.set("controlledby", player.id);
onAddCharacter(candidate);
chat(who, "You claimed " + name + " - check your journal.");
return;
}
}
if(candidates.length > 0) {
chat(who, "Someone already claimed that character.");
} else {
chat(who, "Couldn't find a character named \"" + name + "\" - double check the spelling and capitalization.");
}
}
function generateHandout(name, link, clickHereTo, troubleshooting) {
var handout = createObj("handout", {
name: name,
inplayerjournals: "all"
});
var notes = "<h1><a href=\"" + link + "\">Click here</a><span style=\"font-weight: normal\">&nbsp;to " + clickHereTo + "</span></h1><br>";
if(troubleshooting && troubleshooting.length) {
notes += "<p>Troubleshooting:";
if(Array.isArray(troubleshooting)) {
notes += "</p><ul><li>" + troubleshooting.join("</li><li>") + "</li></ul>";
} else {
troubleshooting = troubleshooting.charAt(0).toLowerCase() + troubleshooting.substr(1);
notes += "&nbsp;" + troubleshooting + "</p>";
}
}
handout.set("notes", notes);
}
function generateHandouts() {
var create = "create an empty character sheet.";
var claim = "claim a character you imported from the vault.";
var exactName = "Make sure to enter your character's name exactly as it appears on the sheet, including last names, capitalization, and punctuation.";
var successOrFail = "Success or fail, you should get <i>some</i>response in the Roll20 chat box. If you get nothing, either you're talking to yourself (there will be a small message) or the API is down.";
generateHandout("How to create a character", "!charsheet ?{Character name}", create, successOrFail);
generateHandout("How to claim an imported character", "!claimsheet ?{Character name}", claim, [exactName, successOrFail]);
}
on("ready", function() {
on("add:character", onAddCharacter);
on("chat:message", function(msg) {
if(msg.type !== "api") {
return;
}
var args = msg.content.split(" ");
var command = args.shift();
var player = getObj("player", msg.playerid);
if(command === "!charsheet" || command === "!welcomePackageCreateCharacter") {
generateCharacter(player, args, msg.who);
} else if(command === "!claimsheet") {
claimCharacter(player, args, msg.who);
} else if(command === "!npcsheet") {
if(playerIsGM(msg.playerid)) {
log(msg.content);
//If the final argument is a number, create that many copies.
var count = 1;
if(args.length > 0 && /^\d+$/.test(args[args.length - 1])) {
count = parseInt(args.pop());
}
if(args.length == 0) {
args.push("NPC");
}
while(--count >= 0) {
var npc = generateCharacter(player, args, msg.who, settings.npcDefaults, count > 0);
//Remove "control" of this character so that HealthColors
//recognizes it as an NPC, but leave it in the GM's journal
//so no one else can claim it.
npc.set("controlledby", "");
}
} else {
log(command + "error: " + msg.who + " is not listed in sudoers. This incident will be reported.");
}
} else if(command === "!sheetsetuphandouts") {
if(playerIsGM(msg.playerid)) {
generateHandouts();
} else {
log(command + "error: " + msg.who + " is not listed in sudoers. This incident will be reported.");
}
}
});
});
return {
onAddCharacter: onAddCharacter,
claimCharacter: claimCharacter,
settings: settings
};
})();
@player-03
Copy link
Author

player-03 commented Nov 15, 2020

Once you install this script, you can create user-friendly instructional handouts by typing !sheetsetuphandouts in chat. There won't be any acknowledgement other than the handouts appearing.

You can edit the handouts if you like, as long as the links still work.

@player-03
Copy link
Author

player-03 commented Aug 14, 2021

Advanced users may want to take a look at the settings near the top of the code, especially defaults. You can use this to set attributes on every character sheet processed by SheetSetup.

For instance, imagine you're playing Pathfinder 2e, and players keep importing sheets without checking the "limit height of notes and description in roll template" box. This clogs up chat with unnecessary text, so you want to automatically change it on all imported sheets. You do this by adding to defaults:

defaults: {
    "roll_limit_height": "limit-height"
}

From now on, this will be applied any time someone creates or claims a sheet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment