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

Getting the current object layer

November 11 (1 year ago)
GM
Pro

So I want to click a button that leverages TokenMod to move a token (using the tokenId) to the objects layer from the GM layer.
Easy enough: sendChat("!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");
The macro is super simple too. Ok!

However, I want to click that same button a second time to send the token back to the GM layer, like a toggle of sorts.

To do this, I need to pull the current layer of that token, and use that in an if-statement. I understand those.
For the life of me I can't seem to figure out how to get the current layer of the object with the objectId.

HOW??

November 11 (1 year ago)

Edited November 11 (1 year ago)

So tokens and other graphics have a property named layer. Roll20 Objects documentation link

You can get that layer information from the object, I have an example via ScriptCards to get that info. I'm also guessing that timmaugh can get that info from something in the MetaScript toolbox but I'm not sure exactly how. Not sure if Fetch can grab any and all properties like that.

If you'd like to see an example via ScriptCards, I can show that if that would be helpful to you.

EDIT:

Sure enough the MetaScript Toolbox can be used to retrieve that property.

!forselected(^) My name is @^(selected.token_name)and I'm on @^(selected.layer) {^&simple}

That uses a few mods from there including SelectManager, ZeroFrame, and Fetch but that example shows that it can be done with those tools.

So there are some options besides ScriptCards as well.

November 12 (1 year ago)
GM
Pro

Very Cool stuff, but what I require can't be onSelected.

Ive made this since then from a dbl-drop-down macro, as I just can't give up on coding once I start. Add in that I suck, and it's exhausting.
(And yes, once I get the layer change working, I'm going to sub in tokenID variables so I can run fewer functions).

  • It doesn't error out on save.
  • When I run the macro, it plays the sound but doesn't move the objects to the objects(token) layer.
  • when I enter that "!token" line directly into chat, it works like a charm. Token changes layers. WTH?

Do you see where I'm making the error? I dont

___________

on("chat:message",function(msg){

    if(msg.type=="api" && msg.content.indexOf("!Caution")==0){

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

        var cautionType = args[1];

        var toggle = args[2];

        switch(cautionType){

            case "ObsDet":

                ObsDet(toggle);

                break;

            case "Scan":

                Scan(toggle);

                break;

            case "IFF":

                IFF(toggle);

                break;

            case "SenserLock":

                SenserLock(toggle);

                break;

            case "Fuel":

                Fuel(toggle);

                break;

            case "Antenna":

                Antenna(toggle);

                break;

            case "Clear":

                Clear(toggle);

                break;

        }

    }

});            

            

function ObsDet(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|gmlayer");

    }

}


function Scan(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkTaqbIjgPdKGBQE --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkTaqbIjgPdKGBQE --set layer|gmlayer");

    }

}


function IFF(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkTNTvmWlTMtFj2H --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkTNTvmWlTMtFj2H --set layer|gmlayer");

    }

}


function SenserLock(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkUYTs5qJZkYAVM_ --set layer|objects");

     sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|gmlayer");

    }

}


function Fuel(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkQo-VTBDocqDzxv --set layer|objects");

       // sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQo-VTBDocqDzxv --set layer|gmlayer");

    }

}


function Antenna(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkUcXQcOMyZ5cZms --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkUcXQcOMyZ5cZms --set layer|gmlayer");

    }

}


function Clear(toggle){

    sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

}on("chat:message",function(msg){

    if(msg.type=="api" && msg.content.indexOf("!Caution")==0){

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

        var cautionType = args[1];

        var toggle = args[2];

        switch(cautionType){

            case "ObsDet":

                ObsDet(toggle);

                break;

            case "Scan":

                Scan(toggle);

                break;

            case "IFF":

                IFF(toggle);

                break;

            case "SenserLock":

                SenserLock(toggle);

                break;

            case "Fuel":

                Fuel(toggle);

                break;

            case "Antenna":

                Antenna(toggle);

                break;

            case "Clear":

                Clear(toggle);

                break;

        }

    }

});            

            

function ObsDet(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|gmlayer");

    }

}


function Scan(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkTaqbIjgPdKGBQE --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkTaqbIjgPdKGBQE --set layer|gmlayer");

    }

}


function IFF(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkTNTvmWlTMtFj2H --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkTNTvmWlTMtFj2H --set layer|gmlayer");

    }

}


function SenserLock(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkUYTs5qJZkYAVM_ --set layer|objects");

     sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQufZE-vGhsfxOq --set layer|gmlayer");

    }

}


function Fuel(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkQo-VTBDocqDzxv --set layer|objects");

       // sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkQo-VTBDocqDzxv --set layer|gmlayer");

    }

}


function Antenna(toggle){

    if(toggle == "on"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|objects");

        sendChat("API","!token-mod --ids -NiyJkUcXQcOMyZ5cZms --set layer|objects");

        sendChat("API","!roll20AM --audio,play|Beeping");

    }

    if(toggle == "off"){

        sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

        sendChat("API","!token-mod --ids -NiyJkUcXQcOMyZ5cZms --set layer|gmlayer");

    }

}


function Clear(toggle){

    sendChat("API","!token-mod --ids -NiUuLERR0k00TKbcBUo --set layer|gmlayer");

}

November 12 (1 year ago)
GM
Pro


November 12 (1 year ago)

So let me start by saying I'm not really the right person to help you with Javascript. That said I don't think you need a whole JS Mod to call token-mod here.

First SelectManager can do the "selecting" for you if you want. It can be done by name, naming globs like Ship*, or by ID.

Also here's an example ScriptCard that can do it as well:

!scriptcard {{
    --#title|NPC Token Toggle
    --#reentrant|npctokentoggle
    --#whisper|gm

    --~tokenCount|array;pagetokens;NPCTokenArray;[*C:playerpageid];npc
    --%npcTID|foreach;NPCTokenArray
        --?"[*[&npcTID]:t-layer]" -eq "objects"|&branch;SendToGMLayer|&branch;SendToObjectLayer
        --+[*[&npcTID]:t-name]|Current Layer:[*[&npcTID]:t-layer] [rbutton]Toggle Layer::[&branch];[&npcTID][/rbutton]
    --%|

    --:Done|
    --x|

    --:SendToObjectLayer|TokenID
        --#hidecard|1
        --!t:[&reentryval]|layer:objects
    -->Done|

    --:SendToGMLayer|TokenID
        --#hidecard|1
        --!t:[&reentryval]|layer:gmlayer
    -->Done|
}}

That will whisper a chat menu for all the npc tokens, ie tokens that dont' have a player in the controlledby field, on the currently active player page along with a button to toggle the layer between objects and gmlayer. It looks like this with the above code:

At a glance it looks like most of your functions are the same, so you could add the roll20AM call into the SendToObjectLayer branch like so:

--:SendToObjectLayer|TokenID
        --#hidecard|1
        --!t:[&reentryval]|layer:objects
        --@roll20AM|_audio,play|Beeping
    -->Done|

So the above toggle script is adapted from a ScriptCard I use to start battle where I get a chat menu with buttons for object layer, gmlayer, initiative, and opening the character sheet. So it can be modified fairly easily. More ScriptCards info here in the documentation link

I use ScriptCards more than most here so that's what I'm providing but I also believe you can write a macro using the MetaScripts to do much the same for your setup. You don't need to have tokens actually selected, SelectManager can do that for you in a few different ways.

Let me know if you have any questions about the ScriptCard portion, that I could help with. If you do want to go down the custom JS route, I'm afraid I'm not the person to help there.

November 12 (1 year ago)
GM
Pro

This looks astoundingly useful, especially for combat (ship vs ship) since the ships scanning and cloaking are a thing in the SWN system.
For my specific purpose I'm going for what amounts to a ship Console. It's informational more than player interactive (not that you're on that track I know).
I'm excited about how this works, and I thank you.
Basically I have a series of conditions to turn on or off.

  • Cautions and Warnings, and alarms for each.
  • will want two sounds for each alert, Beeping, then a ship's computer voice message.

You think this wil handle that? I think you're correct, and I'll take all the advice or code examples you are gracious enough to offer.
Real examples of things you've used (or dummy values) work 1000x better for me than typical syntax examples. Guess I'm not that smart!

November 12 (1 year ago)

I think if you wanted, ScriptCards can serve that function for you. There is a bit of a learning curve to it initially but it's very flexible really. I think there are other possible solutions that could be offered as well.

So this is my actual StartBattle script if you want to see additional code examples:

!script {{
  --/|name: StartBattle
  --/|version: 0.2.0
  --/|requires: SC 2.1.8+, Roll20 Audio Master
  
  --/|Script Variables
  --&FightSong|combat_eternal_war
  
  --#hidecard|1
  --#reentrant|gmstartbattle
  
  --?[*C:initiativepage] -ne "false"|PromptForInitiative
  --!campaign:root|initiativepage:true
  
  --:PromptForInitiative|
  --~NPCTokenNum|array;pagetokens;NPCTokens;[*C:playerpageid];npc
  --~AllNPCTokens|array;stringify;NPCTokens;,
  --*Initiative|[rbutton]All NPCs::SendTheTokens;[&AllNPCTokens][/rbutton]
  --%npc|foreach;NPCTokens
    --&npcname|[*[&npc]:t-name]
    --&CharID|[*[&npc]:t-represents]
    --*[&npcname]|[rbutton][&npcname] Init::SendTheTokens;[&npc][/rbutton] [rbutton]O::SendToObjectLayer;[&npc][/rbutton] [rbutton]G::SendToGMLayer;[&npc][/rbutton] [button]CS::https://journal.roll20.net/character/[&CharID][/button]
  --%|
  
  --:PlayFightMusic|
  --?[&FightSong] -eq "false"|Done
  --@roll20AM|_audio,play|[&FightSong]
  
  --X|
  
  --:SendTheTokens|TokenID
  --?[&reentryval] -ninc ","|>RollInitiative;[&reentryval]|>RollAll;[&reentryval]
  -->Done|
  
  --:RollInitiative|TokenID
    --&CharID|[*[%1%]:t-represents]
    --=InitRoll|1d20 + [*[&CharID]:initiative_bonus] {ROUND:2}
    --~|turnorder;addtoken;[%1%];[$InitRoll]
  --<|
  
  --:RollAll|
    --~|array;fromstring;NPCTokenInit;,;[%1%]
    --%allnpcs|foreach;NPCTokenInit
      -->RollInitiative|[&allnpcs]
    --%|
  --<|

  --:SendToObjectLayer|TokenID
    --!t:[&reentryval]|layer:objects
  -->Done|

  --:SendToGMLayer|TokenID
    --!t:[&reentryval]|layer:gmlayer
  -->Done|
  
  --:Done|
  --X|
}}

So this will check if the turn tracker is already open and if it's not open, open it. It will then loop through the NPC tokens and whisper to me a menu of buttons to move the tokens to a different layer, roll initiative either individually or all at once, move them to different layers, open their character sheets, and start the music with Roll20AM script.

It looks like this as an example:

With ScriptCards many things are possible to do and even the output formatting is customizable. So the above example shows some ways that ScriptCards can modify objects directly itself, like with opening the turn tracker or with moving the tokens to the layers. Those lines use Object Modification and start with `--!`. ScriptCards can also call other API Mods/Scripts and those start with `--@`. In the StartBattle example you can use that with the call to Roll20AM.

ScriptCards has a lot of examples to look at for various things. There is a thread with many working examples posted, mostly D&D 5e admittedly. Kurt J's GitHub has a lot of other examples for various things, some D&D stuff but also some system neutral examples as well. There is a Discord as well to ask questions and here's the link to the active ScriptCards thread for questions as well.

Not saying ScriptCards is the only option to build what you are looking for but I think it certainly could eventually if you want to go down that route. I personally found learning and working in ScriptCards to be much simpler than writing my own JavaScript for things.