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

Simple API Script for player switching maps

April 06 (5 years ago)

Edited April 10 (5 years ago)

Hi,

I wanted to make something simple to allow players to switch from one map to another. While I saw a few scripts that did just that, they had a bunch of features that wouldn't really work for my campaign.Their code was difficult to read and some required a bunch of dependencies. I managed to script my own from scratch and figured if anyone else comes looking to do the same, my less than 100 lines of code might be easier to read. I've also commented the code for someone relatively new to scripting, sorry if it's a bit much.

Constructive criticism is always welcome, but keep in mind that I want to avoid bloat and so far I'm not running any other scripts. There's probably a whole bag of exceptions that the script can't handle, but we'll take that as it comes.

If anyone finds this useful, let me know.

/*
The following script allows players to switch to various maps without the GM's interaction.
While there are a few scripts out there that do this and more, they were quite complex with
more options and tweaks. If you want to just pop in a script and have it work without
needing to look under the hood, grab one of the other ones out there. On the otherhand,
if you're looking to scripting something yourself and want a barebones script to edit or 
work off of this one might give you an easier time.
*/

/*
This is the callback that waits for the right trigger before proceeding.
For some reason the 'msg' object doesn't get passed on if a temporary function
isn't defined in the callback. I defined the main function elsewhere for readability

*/
on('chat:message', function(msg){ 
    if(msg.type === 'api' && msg.content.startsWith('!map')){ movePlayer(msg) } 
    
})

/*
This is the main function
*/
function movePlayer(msg){
    /*the "playerspecificpages" holds the information for when one player is
    on a different map than the player banner, in essence, modifying it and putting
    it back is the purpose of the script*/
    let pageFile = Campaign().get('playerspecificpages')
    /*msg.playerid is a unique id for the player who sent the message*/
    let playerID = msg.playerid
    let mapID
    let backToBanner
    
    /*If you want to quickly adapt my script, just add to or modify the cases below
    basically, it checks to see what map it needs to send the player to and assigns
    the right mapID. The toMapID function is defined further on*/
    switch(msg.content){
        case '!map map1':
            mapID = toMapID('map1')
            log('case 1')
            break
        
        case '!map map2':
            mapID = toMapID('map2')
            log('case 2')
            break
        
        /*This case will signal to throw the player back with the player banner*/
        case '!map Return':
            backToBanner = true
            log('case 3')
            break
            
        /*Things shouldn't get this far without catching, but it might if you spell
        name of the destination map wrong*/
        default:
            log('switch default error')
            log(msg.content)
    }
    
    /*This puts the player's id and destination map id together, based on the player
    sending the msg and the destination map sorted out in the switch above*/
    if(pageFile === false){ pageFile = {playerID : mapID } }
    else { pageFile[playerID] = mapID }
    

    /*the "playerspecificpages" property comes out false if everyone is with the banner
    this verifies that if the player returns to the banner, the file won't be left empty
    at the same time, just setting the property to false might screw other players if
    they're also seperate*/
    if(backToBanner === true && Object.keys(pageFile).length < 2){
        pageFile = false
    }
    /*if the player returns to the banner, but he's not the only one to be seperate,
    this will simply remove him from the list*/
    else if (backToBanner === true){
        delete pageFile[playerID]
    }
    /*This is an important part, the "playerspecificpages" property won't properly
    update if it's not set to false first, and the maps won't update*/
    Campaign().set('playerspecificpages', false)
    /*the whole purpose of the script leads to the following line which updates the
    Campaign's "playerspecificpages" property*/
    Campaign().set('playerspecificpages', pageFile)
}

/*These are two simple functions that I refractored out of the main one, since
I find it easier to use names rather than the clunky and unreadable "_id"s*/
function toMapID(name){
    let mapID = findObjs({ type: 'page', name: name})[0].get('_id');
    log('returning mapID: '+ mapID)
    return mapID
}

function toMapName(mapID){
    let mapName = findObjs({ type: 'page', _id: mapID})[0].get('name');
    log('returning mapName: '+ mapName)
    return mapName
}

April 06 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

That's a great idea. I'll check it out when i can, but it sounds useful to me.

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

As a low-level scripter, I appreciate the thorough commenting. Thanks!

April 06 (5 years ago)
GiGs
Pro
Sheet Author
API Scripter

One drawback with this script is it looks like you have to define the maps yourself. Using an id-based approach, you can autogenerate the map list and dont need to configure that part. It;ll keep working if you delete maps or add new ones. You can also build a chat button that lets people choose which map to jump to (though you might not want to do that - maybe only show maps theyve actually visited before).

April 06 (5 years ago)

Edited April 07 (5 years ago)


GiGs said:

One drawback with this script is it looks like you have to define the maps yourself. Using an id-based approach, you can autogenerate the map list and dont need to configure that part. It;ll keep working if you delete maps or add new ones. You can also build a chat button that lets people choose which map to jump to (though you might not want to do that - maybe only show maps theyve actually visited before).


That's the next step, but part of me posting this now was to get the basic code out before adding too much to it.

April 06 (5 years ago)
Ravenknight
KS Backer
I've got some games going where such a feature would be very useful. Thanks for sharing.
April 07 (5 years ago)

Edited April 07 (5 years ago)

As a note, I've fixed a few bugs. Notably, the previous script didn't work if at least one person isn't with the player banner.
Also, if someone wants to have a little more flexibility for choosing which maps the players get pushed to, a simple function like this can search for a specifically named token hidden on the GM layer, and send players to that map. Just add the function and update the switch cases to use this rather than a static mapID or mapName.

function findMap(marker){
    let token = findObjs({
        type: 'graphic',
        layer: 'gmlayer',
        name: marker
    })[0];
    return token.get('_pageid')

Example of a modified case to include the findMap() function

        case '!map map2':
            mapID = findMap('mySecondMap')
            break

I'm not familiar with programming language, so I freely admit I'm probably missing something obvious. I replaced "map1", "map2", etc. with the names of a few of my maps, like so:

switch(msg.content){
        case '!map Breachill':
            mapID = toMapID('Breachill')
            break
        
        case '!map Goblinblood Caves':
            mapID = toMapID('Goblinblood Caves')
            break

All my players are currently on the same map, with the Player Ribbon. I asked one of them to type !map Breachill in chat. No visible change occurred, neither on his end nor mine. The API sandbox output the following, however:

"returning mapID: -LotwxbJZEOijD16vPoz"

Any idea what I'm doing wrong?

April 10 (5 years ago)

Edited April 10 (5 years ago)

I'd have to see the whole script to compare with my current one, you can pm me if you don't want to add another wall of text to this discussion (I updated it again in my original post to avoid others in the future getting stuck on bugs I've fixed)
That said, one of my previous bugs was something that made it so the script didn't work if at least one person wasn't split from the party. You can verify this by splitting yourself from the party and trying the buttons. It won't switch the map for a GM but it'll move the miniature portrait of you around as if you were on those pages separately from the other players.

Also the console printing that out is normal. It could be removed, but it just shows the mapID of the map the player is being sent to.