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 .
×
We are currently investigating server problems that may lead to lag or loading errors on the platform.

TurnMarker1 - option for the marker not to follow?

TurnMarker1... the API we've all grown up with. So always, I say a quiet thank you to The Aaron. (thank you) Question: currently, the marker follows the token. What I find is that my players (and I, for NPCs) often want to retrace their steps back to the square where they started, because of a misunderstanding (e.g. a player says "Oh, I didn't realize we had already identified those squares as trapped, cuz I was in the bathroom or whatever... can I go back and redo my move?). Maybe I'm missing the option, but could we have one where the turn marker doesn't follow the character, but rather serves as an anchor for where it started its turn?
If you select the player's token and hold down the X key it should show you the path that they took.
Craig M. said: TurnMarker1... the API we've all grown up with. So always, I say a quiet thank you to The Aaron. (thank you) Question: currently, the marker follows the token. What I find is that my players (and I, for NPCs) often want to retrace their steps back to the square where they started, because of a misunderstanding (e.g. a player says "Oh, I didn't realize we had already identified those squares as trapped, cuz I was in the bathroom or whatever... can I go back and redo my move?). Maybe I'm missing the option, but could we have one where the turn marker doesn't follow the character, but rather serves as an anchor for where it started its turn? There was something produced by the Aaron I think which I have in my game called Mark Turn Start. This leaves a blue marker where the token starts its turn. Not sure where I got it from though. Hopefully a forum search might find it. 
1769536960
The Aaron
Roll20 Production Team
API Scripter
This is probably it: /* global GroupInitiative */ on('ready',()=&gt;{ const scriptName = 'MarkTurnStartLocation'; const version = '0.1.0'; const schemaVersion = 0.1; const lastUpdate = 1612845685; const getTurnArray = () =&gt; ( '' === Campaign().get('turnorder') ? [] : JSON.parse(Campaign().get('turnorder'))); const checkInstall = () =&gt; { log(`-=&gt; ${scriptName} v${version} &lt;=- [${lastUpdate}]`); if ( !state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion ) { log(` &gt; Updating Schema to v${schemaVersion} &lt;`); switch (state[scriptName] &amp;&amp; state[scriptName].version) { case 0.1: /* break; // intentional dropthrough */ /* falls through */ case "UpdateSchemaVersion": state[scriptName].version = schemaVersion; break; default: state[scriptName] = { version: schemaVersion, options: { markerImage: `<a href="https://s3.amazonaws.com/files.d20.io/images/4996490/Ii0ukmjd-IFyjKHtfRrG5w/thumb.png?1406962627`" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/4996490/Ii0ukmjd-IFyjKHtfRrG5w/thumb.png?1406962627`</a>, scale: 1.7 } }; break; } } }; const showImg = (img) =&gt; `&lt;img src="${img}" style="max-width: 3em;max-height: 3em;border:1px solid #333; background-color: #999; border-radius: .2em;"&gt;`; const getCleanImgsrc = (imgsrc) =&gt; { let parts = imgsrc.match(/(.*\/images\/.*)(thumb|med|original|max)([^?]*)(\?[^?]+)?$/); if(parts) { return parts[1]+'thumb'+parts[3]+(parts[4]?parts[4]:`?${Math.round(Math.random()*9999999)}`); } return; }; on('chat:message',msg=&gt;{ if('api'===msg.type &amp;&amp; /^!mark-start(\b\s|$)/i.test(msg.content) &amp;&amp; playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); let tokens = (msg.selected || []) .map(o=&gt;getObj('graphic',o._id)) .filter(g=&gt;undefined !== g) ; let msgs = []; let args = msg.content.split(/\s+--/).slice(1); if(0 === args.length){ args.push('help'); } args.forEach((a)=&gt;{ let parts = a.split(/\s+/); switch(parts.shift().toLowerCase()){ case 'set-marker': { if(1 === tokens.length){ let imgsrc = getCleanImgsrc(tokens[0].get('imgsrc')); if(imgsrc){ state[scriptName].options.markerImage = imgsrc; msgs.push(`&lt;div&gt;&lt;div&gt;${showImg(imgsrc)}&lt;/div&gt;Updated start marker.&lt;/div&gt;`); } else { msgs.push(`&lt;div&gt;&lt;div&gt;${showImg(imgsrc)}&lt;/div&gt;&lt;code&gt;Error: Cannot use Marketplace image.&lt;/code&gt;&lt;/div&gt;`); } } else { msgs.push(`&lt;div&gt;&lt;code&gt;Error: Select a single token to collect the image from.&lt;/code&gt;&lt;/div&gt;`); } } break; case 'set-scale': { let scale = parseFloat(parts.shift())||0; if(scale){ if(scale &gt; 5.0){ msgs.push(`&lt;div&gt;&lt;code&gt;Warning: Scale seams pretty large.&lt;/code&gt;&lt;/div&gt;`); } else if( scale &lt; 0.1 ) { msgs.push(`&lt;div&gt;&lt;code&gt;Warning: Scale seams pretty small.&lt;/code&gt;&lt;/div&gt;`); } state[scriptName].options.scale = scale; msgs.push(`&lt;div&gt;Updated start marker scale to &lt;code&gt;${scale}&lt;/code&gt;.&lt;/div&gt;`); } else { msgs.push(`&lt;div&gt;&lt;code&gt;Error: Scale should be a decimal number where 1 means the same size as the token, 1.5 means 50% larger, etc.&lt;/code&gt;&lt;/div&gt;`); } } break; case 'help': { msgs.push(`&lt;h3&gt;MarkTurnStartLocation v${version}&lt;/h3&gt;`); msgs.push(`&lt;div&gt;Creates a marker to show where a token started on when its turn began.&lt;/div&gt;`); msgs.push(`&lt;div&gt;&lt;code&gt;!mark-start [--set-marker] [--set-scale NUMBER]&lt;/code&gt;&lt;/div&gt;`); msgs.push(`&lt;ul&gt;&lt;li&gt;&lt;code&gt;--set-marker&lt;/code&gt; Set the marker image to the image source of the single selected token.&lt;/li&gt;&lt;li&gt;&lt;code&gt;--set-scale NUMBER&lt;/code&gt; Set the scale of the marker image to the supplied NUMBER. Default is &lt;code&gt;1.7&lt;/code&gt;. &lt;code&gt;1.0&lt;/code&gt; makes the image the same size as the token.&lt;/li&gt;&lt;/ul&gt;`); } break; } }); sendChat('',`/w "${who}" &lt;div style="border:1px solid #999;background-color: white;padding:.5em;"&gt;${msgs.join('')}&lt;/div&gt;`); } if('api'===msg.type &amp;&amp; /^!eot\b/.test(msg.content)){ setTimeout(()=&gt;handleTurnOrderChange(Campaign(),{initiativepage:false,turnorder:JSON.stringify([{id:-1}])}),1000); } }); let ClearedIDs = {}; const clearMarkers = ()=&gt;{ findObjs({ type: 'graphic', controlledby: scriptName }).forEach(g=&gt;{ ClearedIDs[g.id]=true; g.remove(); }); }; const addMarker = (tid) =&gt; { let t = getObj('graphic',tid); if(t){ let layer = (t.get('layer') == 'gmlayer') ? 'gmlayer' : 'map'; let props = [ 'pageid', 'left', 'top', 'width', 'height' ] .reduce((m,p)=&gt;({...m,[p]:t.get(p)}),{ imgsrc: state[scriptName].options.markerImage, controlledby: scriptName, layer }); props.width *= state[scriptName].options.scale; props.height *= state[scriptName].options.scale; let m = createObj('graphic', props); if('gmlayer' === layer){ toBack(m); } else { toFront(m); } } }; const handleTurnOrderChange = (obj,prev) =&gt; { let force = (obj.get('initiativepage') !== prev.initiativepage); if(obj.get('initiativepage')){ setTimeout(()=&gt;{ let t = getTurnArray()[0]; if(t){ let pto = (''===prev.turnorder ? [] : JSON.parse(prev.turnorder)); if(pto[0].id !== t.id || force){ clearMarkers(); addMarker(t.id); } } },100); } else { clearMarkers(); } }; const handleChangeGraphic = (obj,prev) =&gt; { if(scriptName === prev.controlledby){ obj.set(prev); } }; const handleDestroyGraphic = (obj) =&gt; { if(ClearedIDs[obj.id]){ setTimeout(()=&gt;delete ClearedIDs[obj.id],1000); } else { let prev = JSON.parse(JSON.stringify(obj)); if(scriptName === prev.controlledby){ prev.pageid=prev._pageid; let m = createObj('graphic',prev); if('gmlayer' === prev.layer){ toBack(m); } else { toFront(m); } } } }; const registerEventHandlers = () =&gt; { on('change:campaign:turnorder',handleTurnOrderChange); on('change:campaign:initiativepage',handleTurnOrderChange); on('change:graphic',handleChangeGraphic); on('destroy:graphic',handleDestroyGraphic); if('undefined' !== typeof GroupInitiative &amp;&amp; GroupInitiative.ObserveTurnOrderChange){ GroupInitiative.ObserveTurnOrderChange((o,p)=&gt;handleTurnOrderChange(Campaign(),{initiativepage: false, turnorder:p})); } }; checkInstall(); registerEventHandlers(); if(Campaign().get('initiativepage')){ handleTurnOrderChange(Campaign(),{initiativepage:false,turnorder:JSON.stringify([{id:-1}])}); } });
Super useful responses. I had no idea about the X trick: it's excellent, although it doesn't path back to the first position, but rather the last position where the token dropped. Sometimes players do a half move, reassess, then move again... in those cases, the X won't give us a path back to the beginning. Mark Turn Start sound exactly like what I need, although before I install it, I suppose I should ask how it interacts with TurnMarker1. I should . But I'm gonna install it anyway and report back.&nbsp;
Hmmm. Didn't seem to do anything, although the sandbox kicked back a bunch of errors. I should note that I do have GroupInit installed. I typically use it for the NPCs and let the players roll their own initiative. In this case, I just GroupInitted them. The errors were: "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named ogl5e" "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named initiative_bonus" "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named ogl5e" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named ogl5e" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named ogl5e" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named ogl5e" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named ogl5e" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named initiative_bonus" "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named initiative_bonus" "Error: No attribute or sheet field found for character_id -Oj8VjPIFxsw3ZcxchRu named init_tiebreaker" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjBeXpPryNT6U5hbjxD named init_tiebreaker" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjC_RHvWm1BQXd6pDdk named init_tiebreaker" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjCzLx3mkHl2qX6-wEf named init_tiebreaker" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjF1ObIDukcvtNmqggb named init_tiebreaker" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named dnd2024byroll20" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named initiative_bonus" "Error: No attribute or sheet field found for character_id -OjTd-tYEWojZ6fD0oLV named init_tiebreaker"
To add: I'm using 2024 sheets. This might be the issue.