Last active
August 19, 2021 20:48
-
-
Save player-03/377701e45742280c44a49375e27626fe to your computer and use it in GitHub Desktop.
A Roll20 API script allowing players to claim their imported characters.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode 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\"> 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 += " " + 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 | |
}; | |
})(); |
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
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.