Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Use API to move drawn lines on/off Dynamic Lighting Layer

Is there a way to use the API to move drawn objects (e.g. made using the shape tool, such as lines drawn for dynamic lighting walls) on and off of the Dynamic Lighting Layer? The toolbar menu only allows you to move objects on/off the Objects, GM and Maps layers.

I am aware that TokenMod can move tokens on/off the lighting layer, but I don't think it works with shape drawings. Are there other API that make this possible?

I want to be able to toggle DL lines onto the GM Layer (so they don't block light/vision), then back to the DL Layer (so that they do).

Not sure about the API, but you can use the Advanced Keyboard Shortcuts (https://wiki.roll20.net/Advanced_Shortcuts) to move things between the GM Info Layer and the Dynamic Lighting Layer. It might be too clunky for your need.

April 21 (5 years ago)
The Aaron
Roll20 Production Team
API Scripter

The API is capable of doing that. I can't think of a script off the top of my head that's does that directly, but that's the essence of how door scripts like Door Knocker work. 

What's your use case? 

April 21 (5 years ago)
keithcurtis
Forum Champion
Marketplace Creator
API Scripter

Wouldn't it be simpler to just toggle DL on and off? Or depending on what you are looking for just "Enforce Line of Sight"? Come to think of it, though, I don't know if the API can access DL settings for a page.

April 21 (5 years ago)

Edited April 21 (5 years ago)

The Door Knocker script will move lines of the color you specify as doors.  Check it out -- it probably does what you want.

thread here: https://app.roll20.net/forum/post/7698809/script-door-knocker/?pagenum=1

April 21 (5 years ago)

Edited April 21 (5 years ago)

Thanks for chiming in to help. I'd like to keep the Dynamic Lighting on to take advantage of light and darkness.

Essentially, I'm after a TokenMod approach that allows GMs to target and interact with shapes drawn on the Dynamic Lighting Layer (e.g. if only to change the layer). Especially on purchased module maps, the existing DL Lines turn out to be hugely controlling, and we could really use an interface to work with them a little. For my purposes, I need a way to select the lines (or groups maybe), preferably using ID, to somehow activate/deactivate them them for Dynamic Lighting. This is the basis of what DoorKnocker does, but I only want the ability to select and control a specific set of lines so I can use macros to make adjustments for game scenarios that tend to repeat.

Use Case Example:

I'm running a city campaign (in Waterdeep) and want players to climb the buildings and run rooftops before going inside. Lots of the campaign maps show building interiors in the classic D&D cutaway style. But when players approach a building prepped for Dynamic Lighting, they see nothing but the margins around a black-box, since their vision is blocked by the DL walls [see my pic below: Standard Outside of Building Scene]. For a city campaign, I want my players to actually see a building that invites them to scale the roof, sneak in upstairs or whatever else (besides using a door). The black-box entry scenes upset immersion when part of the fun is skulking around probing for entry points.

So my solution is to add a "roof" as an overlay image, which hides the building interior but also helps players visualize the building outside [see my pic below: With Roof Overlay]. TokenMod makes adding/removing the roof a one-click process, so you can reveal the main inside-map when the players go inside. And away we go using the module's D&D cutaway map of the interior, with all the DL walls and doors. The bummer is that the Dynamic Lighting walls mess this all up (players cannot see the "roof" image because it's blocked by the DL walls).

So how do we temporarily disable certain DL walls so we can adjust for scenarios (i.e. outside/inside) on a single map, where we don't have to cut/paste walls or set up a separate map page? One solution is to target the walls of that building via IDs and change the layer via macro. The GM deactivates them by moving them off the DL layer when the players are outside and the roof is on, only to re-activate them when players go inside.

I dunno. Thanks for your ideas.




keithcurtis said:

Wouldn't it be simpler to just toggle DL on and off? Or depending on what you are looking for just "Enforce Line of Sight"? Come to think of it, though, I don't know if the API can access DL settings for a page.

Keith, I did consider this. The rub is that lots of map pages have several map areas set into them, and the page uses the DL to conceal other parts of the dungeon/building from the players. Turning off the Line of Sight would reveal stuff you don't want to reveal (the secret room! or the floorplan of the second floor). This is actually the reason my thinking has moved to finding ways to control certain lines on a map page - because the page level controls just aren't suitable for adjusting maps as scenarios unfold during play.

April 21 (5 years ago)

Edited April 21 (5 years ago)
Dumbhuman
Pro
Marketplace Creator

I have a solution that involves a good deal of pre-planning, so I'm not sure how ideal it is for your process, but if you reuse maps/buildings a lot it should pay off over the long term.

There's an API script called Dynamic Light Recorder which allows you to link Dynamic Lighting lines to individual art assets.  Thereafter anytime you place those art assets onto your map, the script automatically adds the linked Dynamic Lighting lines to the lighting layer, plus it will scale and rotate those lines appropriately as you scale and rotate the art assets. 

A little while ago Aaron was nice enough to add TokenMod event handling to the Dynamic Light Recorder script when I asked him for it.  So you can use the modified code found here which allows TokenMod to swap images while having the Dynamic Lighting update automatically to whatever you linked to those images.  I use it to make all kinds of things like doors (open/closed), walls (intact/destroyed), spell effects (so Cloudkill/Darkness/what-have-you obscure vision), etc.

To get the effect you'd like, you could either make two versions of the map (one with the building open having the Dynamic Lighting lines and one with the roof and no lines) or make two versions of the building to be placed on top of a base map.  Then you would make some TokenMod macros to handle switching between the images.  I do recommend uploading duplicates of art assets and appending something like DL to the ones you link to Dynamic Lighting just in case you'd like to use the version without linked walls at some point in the future without disabling Dynamic Lighting altogether.

April 21 (5 years ago)
keithcurtis
Forum Champion
Marketplace Creator
API Scripter

Now that you have described the goal a bit more, here's a different solution to consider: The SimpleRoofControl script. Basically, you put roof graphics over the building interiors, and there are trigger areas to temporarily move the roof graphic to the DL layer. It even moves DL lines for you, IIRC, so that it looks like a building from the outside, but as soon as a token enters, they have DL blockers for the walls.


April 22 (5 years ago)

Edited April 22 (5 years ago)

This "moveLighting" script is actually a good solution for what I am after (allows you to make a simple API call to move a path onto different layers, using the token ID). A nice utility for toggling on/off specific DL lines.

Command example: 

!movelight [layer] [ID]
!movelight gmlayer -ABC123


// By:       Kastion
// Contact: https://app.roll20.net/users/3173313/kastion

var moveLighting = moveLighting || (function(){
'use strict';

var showHelp = function() {
sendChat('Move Lighting Script',
'/w gm '+
'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'+
'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">'+
'moveLighting'+
'<div style="clear: both"></div>'+
'</div>'+
'<div style="padding-left:10px;margin-bottom:3px;">'+
'<p>Allows the GM to move what layer a lighting path is on.</p>'+
'</div>'+
'<b>Commands</b>'+
'<div style="padding-left:10px;"><b><span style="font-family: serif;">!movelight [layer] [ID]</span></b>'+
'<div style="padding-left: 10px;padding-right:20px; font-size:12px;">'+
'Valid Layer Options Are: map, objects, gmlayer, walls'+
'</div>'+
'</div>'
);
},

handleInput = function(msg) {

if ( "api" !== msg.type || !playerIsGM(msg.playerid) ) {
return;
}

let parts = msg.content.split(/\s+--\s+/);
let args = parts[0].split(/\s+/);

switch(args[0]) {
case '!movelight': {
var valid_layer = 0, new_layer = args[1];

switch (new_layer)
{
case "gmlayer":
case "walls":
case "objects":
case "map":
valid_layer = 1;
break;
}

if (valid_layer == 1)
{
var path_obj = findObjs({_type: "path", _id: args[2]})[0];
if (path_obj)
{
path_obj.set("layer", new_layer);
} else {
sendChat('Move Lighting Script', '/w gm No path object found with that ID.');
}
} else {
sendChat('Move Lighting Script', '/w gm Invalid Layer Specified.');
showHelp();
}
break;
}
}
},

checkInstall = function()
{
var script_version = "0.1.4";
if( ! state.moveLighting ) {
state.moveLighting = {
version: script_version,
};
}

if (state.moveLighting.version != script_version)
state.moveLighting.version = script_version;

log("-=> Move Lighting Script v"+state.moveLighting.version+" Initialized <=-")
},


registerEventHandlers = function() {
on('chat:message', handleInput);
};

return {
CheckInstall: checkInstall,
RegisterEventHandlers: registerEventHandlers
};

}());

on("ready", function() {
'use strict';

moveLighting.CheckInstall();
moveLighting.RegisterEventHandlers();
});
April 22 (5 years ago)
The Aaron
Roll20 Production Team
API Scripter

Cool.  If you need more than that, let me know and I can write you something. =D

Thanks Aaron. The one thing that would be a big upgrade to that particular script is if it could accept multiple IDs, like TokenMod can do. Not sure if that's a 2-minute alteration, but it would be fantastic.

So this:
!movelighting gmlayer -ABC123

but also this:
!movelighting gmlayer -ABC123, -XYZ456
April 22 (5 years ago)
The Aaron
Roll20 Production Team
API Scripter

Easier to do:

!movelighting gmlayer -ABC123 -XYZ456

(no comma)


Try this (completely untested) code:

// By:       Kastion
// Contact:  https://app.roll20.net/users/3173313/kastion

var moveLighting = moveLighting || (function(){
    'use strict';

    var showHelp = function() {
        sendChat('Move Lighting Script',
            '/w gm '+
                '<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">'+
                '<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">'+
                'moveLighting'+
                '<div style="clear: both"></div>'+
                '</div>'+
                '<div style="padding-left:10px;margin-bottom:3px;">'+
                '<p>Allows the GM to move what layer a lighting path is on.</p>'+
                '</div>'+
                '<b>Commands</b>'+
                '<div style="padding-left:10px;"><b><span style="font-family: serif;">!movelight [layer] [ID]</span></b>'+
                '<div style="padding-left: 10px;padding-right:20px; font-size:12px;">'+
                'Valid Layer Options Are: map, objects, gmlayer, walls'+
                '</div>'+
                '</div>'
        );
    },

    handleInput = function(msg) {

        if ( "api" !== msg.type || !playerIsGM(msg.playerid) ) {
            return;
        }

        let args = msg.content.split(/\s+/);

        switch(args.shift()) {
            case '!movelight': {
                let valid_layer = 0;
                let new_layer = args.shift();

                switch (new_layer)
                {
                    case "gmlayer":
                    case "walls":
                    case "objects":
                    case "map":
                        valid_layer = 1;
                        break;                    
                }

                if (valid_layer == 1)
                {
                    args.map(id=>getObj('path',id))
                        .filter(o => undefined !== o)
                        .forEach(p => p.set({layer: new_layer}))
                        ;

                } else {
                    sendChat('Move Lighting Script', '/w gm Invalid Layer Specified.');
                    showHelp();
                }
                break;
            }
        }
    },

    checkInstall = function()
    {
        var script_version = "0.1.5";
        if( ! state.moveLighting ) {
            state.moveLighting = {
                version: script_version
            };
        }    

        if (state.moveLighting.version != script_version)
            state.moveLighting.version = script_version;

        log("-=> Move Lighting Script v"+state.moveLighting.version+" Initialized <=-");
    },


    registerEventHandlers = function() {
        on('chat:message', handleInput);
    };

    return {
        CheckInstall: checkInstall,
        RegisterEventHandlers: registerEventHandlers
    };

}());

on("ready", function() {
    'use strict';

    moveLighting.CheckInstall();
    moveLighting.RegisterEventHandlers();        
});


The Aaron said:


Try this (completely untested) code:

That's my favorite kind of code! Thanks Aaron. I'll give it a swing.