Skip to content

Instantly share code, notes, and snippets.

@goblinHordes
Last active March 31, 2018 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save goblinHordes/6894547 to your computer and use it in GitHub Desktop.
Save goblinHordes/6894547 to your computer and use it in GitHub Desktop.
CommandDoors for Roll20 API Use CommandTokens to toggle open/closed states for doors, including wall modification for dynamic lighting.
/*
CommandDoors
Use CommandTokens to toggle open/closed states for doors, including wall modification for dynamic lighting.
Requires: CommandTokens.js
Once configured, doors are easy to use - simply move the associated CommandToken to toggle the door from open
to closed and back again. Configuring the doors takes a bit of work, but there is a helper function to
make the process pretty easy.
Before configuring a door, first look at the components of a CommandDoor. A CommandDoor consists of three tokens
and an optional Polyline. The first token is a CommandToken that will be used to trigger the door toggle. The
other two tokens are the openDoor and closedDoor graphics for the door, and should be the same size as each other.
The optional Polyline path is used for blocking on the dynamic lighting layer. Toggling the door via the
ControlToken will cause the active door graphic to be shrunk, the new door graphic to be enlarged to its native
size, and the path to be switched beween 'wall' and 'gmlayers' depending if the door is closing or opening,
respectively.
The name of each of these tokens is very important. The CommandToken is configured to call the toggleDoor function
with two parameters, the door group ID and, optionally, the path ID. This follows the standard CommandToken naming
convention, please see CommandTokens.js for more details. The door images need to be named _door_open_X_ and
_door_closed_X_ where 'X' is a unique ID for that door group. Finally, the path doesn't need any special naming,
but its ID is used in the CommandToken. Here is an example of a complete door set by name/id:
openDoor: _door_open_01_
closedDoor: _door_closed_01_
path (id): -J5Glj9y-cipFNgrV141
CommandToken: __toggleDoor_:01:-J5Glj9y-cipFNgrV141__
Included in this script is a helper function for linking door sets. To use this script in quickly planning maps, I
maintain a page in my campaign with template sets. This way I am able to quickly copy and paste the templates then
link them into a new set. This makes door creation take only a few seconds. In order to link a door group you must
start with the three required tokens, but named in a specific way:
openDoor: _door_open_
closedDoor: _door_closed_
CommandToken: __toggleDoor_
With those three tokens selected, use the !linkDoors API chat command to update their names and link them into a new
door set. You'll have to link the path manually, this is on purpose since the wall layer doesn't understand rotation.
By default, !linkDoors will search for the next incremental integer ID from the existing _door_open_X_ objects.
You can also pass an ID that you'd like to use. In macro form this can be very useful to prompt for an ID:
!linkDoors ?{Door ID}
*/
on("ready", function() {
on("chat:message", function(msg){
if(msg.type == "api" && (msgMatch = msg.content.match(/^!linkDoors(?:\s(.*))?$/))){
if(linkSuccess = linkDoors(msg.selected, msgMatch[1])) {
//sendChat('API', "success!");
}
}
});
ctRegister("toggleDoor", function(opts){
doorID = opts[0];
pathID = opts[1];
if(doorID==undefined) return;
doorObj = {
openDoor: findObjs({_type:'graphic', name:'_door_open_'+doorID+'_'})[0],
closedDoor: findObjs({_type:'graphic', name:'_door_closed_'+doorID+'_'})[0],
pathObj: getObj('path', pathID)
}
if(doorObj.openDoor == undefined || doorObj.closedDoor == undefined) return;
if(isDoorOpen(doorObj)){
openDoorID(doorObj);
} else {
closeDoorID(doorObj);
}
return true;
});
});
function isDoorOpen(doorObj){
if(parseFloat(doorObj.openDoor.get('width')) < 0.01){
return true;
} else {
return false;
}
}
function closeDoorID(doorObj) {
if(parseFloat(doorObj.openDoor.get('width')) > 0.01){
doorObj.openDoor.set('width', parseFloat(doorObj.openDoor.get('width'))*.00001);
doorObj.openDoor.set('height', parseFloat(doorObj.openDoor.get('height'))*.00001);
}
if(parseFloat(doorObj.closedDoor.get('width')) < 0.01){
doorObj.closedDoor.set('width', parseFloat(doorObj.closedDoor.get('width'))*100000);
doorObj.closedDoor.set('height', parseFloat(doorObj.closedDoor.get('height'))*100000);
}
doorObj.closedDoor.set('rotation', doorObj.openDoor.get('rotation'));
doorObj.closedDoor.set('top', doorObj.openDoor.get('top'));
doorObj.closedDoor.set('left', doorObj.openDoor.get('left'));
if(doorObj.pathObj) doorObj.pathObj.set('layer', 'walls');
}
function openDoorID(doorObj) {
if(parseFloat(doorObj.closedDoor.get('width')) > 0.01){
doorObj.closedDoor.set('width', parseFloat(doorObj.closedDoor.get('width'))*.00001);
doorObj.closedDoor.set('height', parseFloat(doorObj.closedDoor.get('height'))*.00001);
}
if(parseFloat(doorObj.openDoor.get('width')) < 0.01){
doorObj.openDoor.set('width', parseFloat(doorObj.openDoor.get('width'))*100000);
doorObj.openDoor.set('height', parseFloat(doorObj.openDoor.get('height'))*100000);
}
doorObj.openDoor.set('rotation', doorObj.closedDoor.get('rotation'));
doorObj.openDoor.set('top', doorObj.closedDoor.get('top'));
doorObj.openDoor.set('left', doorObj.closedDoor.get('left'));
if(doorObj.pathObj) doorObj.pathObj.set('layer', 'gmlayer');
}
function linkDoors(objs, id) {
var doorOpen, doorClosed, doorCommandToken
_.each(objs, function(obj){
doorObj = getObj(obj['_type'], obj['_id'])
switch(doorObj.get('name')){
case "_door_open_":
doorOpen = doorObj;
break;
case "_door_closed_":
doorClosed = doorObj;
break;
case "__toggleDoor_":
doorCommandToken = doorObj;
break;
};
});
if(!(doorOpen && doorClosed && doorCommandToken)) return false;
if(id)
nextID = id;
else
nextID = nextDoorID();
doorOpen.set('name', '_door_open_' + nextID + '_');
doorClosed.set('name', '_door_closed_' + nextID + '_');
doorCommandToken.set('name', '__toggleDoor_:' + nextID + '__');
return true;
};
function nextDoorID(){
topID = -1;
doors = filterObjs(function(obj){
name = obj.get('name');
if(name && name.match(/^_door_open_\d+_$/)){
return true;
} else return false;
});
_.each(doors, function(door) {
doorID = parseInt(door.get('name').match(/^_door_open_(\d+)_$/)[1]);
if(doorID > topID) topID = doorID;
});
topID++;
return (topID);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment