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 (<a href="https://wiki.roll20.net/Advanced_Shortcuts" rel="nofollow">https://wiki.roll20.net/Advanced_Shortcuts</a>) to move things between the GM Info Layer and the Dynamic Lighting Layer. It might be too clunky for your need.
1587433866
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.&nbsp; What's your use case?&nbsp;
1587436798
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.
1587470397

Edited 1587470474
The Door Knocker script will move lines of the color you specify as doors.&nbsp; Check it out -- it probably does what you want. thread here:&nbsp; <a href="https://app.roll20.net/forum/post/7698809/script-door-knocker/?pagenum=1" rel="nofollow">https://app.roll20.net/forum/post/7698809/script-door-knocker/?pagenum=1</a>
1587496753

Edited 1587497291
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&amp;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&amp;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.
1587501104

Edited 1587501189
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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; Then you would make some TokenMod macros to handle switching between the images.&nbsp; 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.
1587504026
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.
1587586647

Edited 1587591212
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: <a href="https://app.roll20.net/users/3173313/kastion" rel="nofollow">https://app.roll20.net/users/3173313/kastion</a> var moveLighting = moveLighting || (function(){ 'use strict'; var showHelp = function() { sendChat('Move Lighting Script', '/w gm '+ '&lt;div style="border: 1px solid black; background-color: white; padding: 3px 3px;"&gt;'+ '&lt;div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;"&gt;'+ 'moveLighting'+ '&lt;div style="clear: both"&gt;&lt;/div&gt;'+ '&lt;/div&gt;'+ '&lt;div style="padding-left:10px;margin-bottom:3px;"&gt;'+ '&lt;p&gt;Allows the GM to move what layer a lighting path is on.&lt;/p&gt;'+ '&lt;/div&gt;'+ '&lt;b&gt;Commands&lt;/b&gt;'+ '&lt;div style="padding-left:10px;"&gt;&lt;b&gt;&lt;span style="font-family: serif;"&gt;!movelight [layer] [ID]&lt;/span&gt;&lt;/b&gt;'+ '&lt;div style="padding-left: 10px;padding-right:20px; font-size:12px;"&gt;'+ 'Valid Layer Options Are: map, objects, gmlayer, walls'+ '&lt;/div&gt;'+ '&lt;/div&gt;' ); }, 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("-=&gt; Move Lighting Script v"+state.moveLighting.version+" Initialized &lt;=-") }, registerEventHandlers = function() { on('chat:message', handleInput); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on("ready", function() { 'use strict'; moveLighting.CheckInstall(); moveLighting.RegisterEventHandlers(); });
1587586920
The Aaron
Roll20 Production Team
API Scripter
Cool.&nbsp; 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
1587588953
The Aaron
Roll20 Production Team
API Scripter
Easier to do: !movelighting gmlayer -ABC123 -XYZ456 (no comma) Try this (completely untested) code: // By: Kastion // Contact: <a href="https://app.roll20.net/users/3173313/kastion" rel="nofollow">https://app.roll20.net/users/3173313/kastion</a> var moveLighting = moveLighting || (function(){ 'use strict'; var showHelp = function() { sendChat('Move Lighting Script', '/w gm '+ '&lt;div style="border: 1px solid black; background-color: white; padding: 3px 3px;"&gt;'+ '&lt;div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;"&gt;'+ 'moveLighting'+ '&lt;div style="clear: both"&gt;&lt;/div&gt;'+ '&lt;/div&gt;'+ '&lt;div style="padding-left:10px;margin-bottom:3px;"&gt;'+ '&lt;p&gt;Allows the GM to move what layer a lighting path is on.&lt;/p&gt;'+ '&lt;/div&gt;'+ '&lt;b&gt;Commands&lt;/b&gt;'+ '&lt;div style="padding-left:10px;"&gt;&lt;b&gt;&lt;span style="font-family: serif;"&gt;!movelight [layer] [ID]&lt;/span&gt;&lt;/b&gt;'+ '&lt;div style="padding-left: 10px;padding-right:20px; font-size:12px;"&gt;'+ 'Valid Layer Options Are: map, objects, gmlayer, walls'+ '&lt;/div&gt;'+ '&lt;/div&gt;' ); }, 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=&gt;getObj('path',id)) .filter(o =&gt; undefined !== o) .forEach(p =&gt; 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("-=&gt; Move Lighting Script v"+state.moveLighting.version+" Initialized &lt;=-"); }, 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.