
So I was having an issue where some of my players that have darkvision wanted to also cary a light source. Either because they wanted to see color or to provide light for their party members that don't have darkvision. With a single light source on the player token, it's an "either one or the other" situation. My first "fix" was to give them another token without "sight" checked, but they had to remember to drag select both tokens, and they'd have to tell me that they wanted to turn off/on their light and I'd have to: *move their token to the back *edit the light radius of the "light" token. *move the light token to the back. This was too clunky for my taste. I wrote the following script to have the lightsource follow the player, and wrote a macro so they could set what light they have on or turn their light off. SCRIPT replace the GM_Player_ID with your own (probably a better way to do this). edit the light sources to suit your campaign, it should be pretty easy to see how to add new ones or edit the existing ones. In order for this to work you'll need to create a light token with the name "player name[LightSource]" So if I have a player Abraxafax Smith The light token would be named Abraxafax Smith[LightSource]. In order for the light source token to move with the player token it needs to be centered on the player token (layered behind it). I added in a command !ladd that will add a light source token to the selected token(s). I added !lhide and !lshow. !lhide puts the light source tokens in the upper left corner of the GM layer. !lshow puts them back under the player on the token layer. Useful for when a darkness spell is cast. I added a !lthrow [direction] and !lfetch this will let you pop the light source token out from under a player or re center under the player. Useful for when a player wants to throw their torch. Otherwise you'd have to change z levels to be able to select the light source token independently. var GM_Player_ID = "GMID";
function fixNewObject(obj)
{
var p = obj.changed._fbpath;
var new_p = p.replace(/([^\/]*\/){4}/, "/");
obj.fbpath = new_p;
return obj;
}
function find_player_light(playername){
var playerlights = findObjs({_type: "graphic", name: playername + "[LightSource]"});
if(playerlights.length > 0){
return playerlights[0]
}
return undefined;
}
function set_light_properties(obj, left, top, rotation, bright_radius, dim_radius, angle){
if(obj !== undefined){
if(left !== -1){
obj.set('left', left);
}
if(top !== -1){
obj.set('top', top);
}
if(rotation !== -1){
obj.set('rotation', rotation);
}
if(bright_radius !== -1){
obj.set('light_radius', bright_radius);
}
if(dim_radius !== -1){
obj.set('light_dimradius', dim_radius);
}
if(angle !== -1){
obj.set('light_angle', angle);
}
}
}
on("chat:message", function(msg){
if(msg.type == "api" && msg.content.indexOf("!ladd") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
character = getObj("character", token.get("represents"))
player = getObj("player", character.get("controlledby"))
if(msg.playerid == GM_Player_ID){
newlight = createObj("graphic", {
"_pageid":token.get("pageid"),
"left":token.get("left"),
"top":token.get("top"),
"width":token.get("width"),
"height":token.get("height"),
"rotation":token.get("rotation"),
"layer":"objects",
"imgsrc":"<a href="https://s3.amazonaws.com/files.d20.io/images/62266/thumb.png?1340841612" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/62266/thumb.png?1340841612</a>",
"name":token.get("name")+"[LightSource]",
"controlledby":player.get("_id"),
"tint_color":"transparent",
"light_radius":20,
"light_dimradius":10,
"light_otherplayers":true,
"light_hassight":false,
"light_angle":360,
"lastmove":token.get("lastmove")
});
newlight = fixNewObject(newlight);
}
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lthrow") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var left = 0;
var top = 0;
if(msg.content.indexOf('left') !== -1){
left = -70;
}
else if(msg.content.indexOf('right') !== -1){
left = 70;
}
else if(msg.content.indexOf('up') !== -1){
top = -70;
}
else if(msg.content.indexOf('down') !== -1){
top = 70;
}
var token = msg.selected[i]._id;
token = getObj("graphic", token);
var playerlight = find_player_light(token.get("name"))
set_light_properties(playerlight, token.get('left') + left,token.get('top') + top, -1, -1, -1, -1);
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lfetch") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
var playerlight = find_player_light(token.get("name"))
set_light_properties(playerlight, token.get('left'),token.get('top'), token.get('rotation'), -1, -1, -1);
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lhide") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
var playerlight = find_player_light(token.get("name"));
if(playerlight !== undefined){
playerlight.set('layer', 'gmlayer');
set_light_properties(playerlight, 0, 0, -1, -1, -1, -1);
}
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lshow") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
var playerlight = find_player_light(token.get("name"));
playerlight.set('layer', 'objects');
set_light_properties(playerlight, token.get('left'), token.get('top'),token.get('rotation'), -1, -1, -1);
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lnightvon") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
token.set('light_otherplayers',false)
set_light_properties(token, -1, -1, -1, 60, 30, 360);
}
return
}
else if(msg.type == "api" && msg.content.indexOf("!lnightvoff") !== -1){
for(var i = 0; i < msg.selected.length; i++){
var token = msg.selected[i]._id;
token = getObj("graphic", token);
token.set('light_otherplayers',false)
set_light_properties(token, -1, -1, -1, 0, 0, 360);
}
return
}
var bright_radius = -1;
var dim_radius = -1;
var angle = 360;
var message = "";
if(msg.type == "api" && msg.content.indexOf("!loff") !== -1){
bright_radius = 0;
dim_radius = 0;
message = " puts out thier light.";
}
else if(msg.type == "api" && msg.content.indexOf("!ltorch") !== -1){
bright_radius = 40;
dim_radius = 20;
message = " lights a torch.";
}
else if(msg.type == "api" && msg.content.indexOf("!lglowstone") !== -1){
bright_radius = 20;
dim_radius = 10;
message = " pulls out their glowstone.";
}
else if(msg.type == "api" && msg.content.indexOf("!lcandle") !== -1){
bright_radius = 10;
dim_radius = 5;
message = " lights a candle.";
}
else if(msg.type == "api" && msg.content.indexOf("!llamp") !== -1){
bright_radius = 45;
dim_radius = 15;
message = " lights a lamp.";
}
else if(msg.type == "api" && msg.content.indexOf("!lbullseye") !== -1){
bright_radius = 120;
dim_radius = 60;
angle = 60;
message = " lights a bullseye lantern.";
}
else if(msg.type == "api" && msg.content.indexOf("!lhooded") !== -1){
bright_radius = 60;
dim_radius = 30;
message = " lights a hooded lantern";
}
if(bright_radius === -1 || dim_radius === -1){
return
}
for(var i = 0; i < msg.selected.length; i++){
var playerlight = undefined
var token = msg.selected[i]._id;
token = getObj("graphic", token);
character = getObj("character", token.get("represents"))
player = getObj("player", character.get("controlledby"))
if(msg.playerid == GM_Player_ID || player.get('_id') == msg.playerid){
playerlight = find_player_light(token.get("name"));
}
if(playerlight !== undefined){
playerlight.set('light_radius', bright_radius);
playerlight.set('light_dimradius', dim_radius);
playerlight.set('light_angle', angle)
sendChat(player.get('_displayname'),"/em " + message)
}
}
});
on("change:token", function(obj, prev) {
//Only do this if we actually moved.
//if(obj.get("left") == prev["left"] && obj.get("top") == prev["top"]) return;
var playerlight = find_player_light(obj.get("name"))
if(playerlight !== undefined){
if(playerlight.get("left") == prev["left"] && playerlight.get("top") == prev["top"]){
playerlight.set('left', obj.get("left"));
playerlight.set('top', obj.get("top"));
playerlight.set('rotation', obj.get("rotation"))
}
}
});
Here is the macro. I made it a token macro so they can click their token, then click "light" then enter either "off"(default) or type in what light source they're using. You could also make a macro to turn off the light and turn on each individual light source, then add the macros to each token that has that light source available. !l?{Prompt|off}