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

Script that calculates terrain bonuses

Is there a script that does something like this? I have a terrain map with some rocks, some are big enough to grant cover to the pjs. I would like to place a token on the master layer with an aura, as soon as the token of the pj enters the area covered by the aura the script sends a message in the chat informing the bonus received and updates the value of CA in the pj's file, when he leaving the area the value is discounted to update the CA to the original value.
1607142397

Edited 1607142752
Oosh
Sheet Author
API Scripter
This Tiles script by The Usual Suspect has the detection you would need (almost... if you're using an aura you're going to need to add that math to the co-ordinate search, but this is stored on the token so it isn't hard). Other than that, the graphics change in the script would need to replaced with grabbing & setting the Attribute on the character sheet. It might be a little more complicated if you're using a round aura and you want some kind of "if the circle half-covers a tile, do this" rule, but for a square aura it shouldn't be too hard. Something else might exist that doesn't need customising, but I'm not sure. I can have a go at it if I get time, though Aaron could probably modify it in his sleep. You'd probably just need to decide on an area to store the cover value on your token (depending on your rules), which might be +10, or +50%, or whatever rules you're using. Bar 1, for example, if you're not using it for anything else. Unless it's a flat bonus regardless of terrain, then it can just be a constant in the script.
1607148390

Edited 1607213739
Oosh
Sheet Author
API Scripter
Here's a first pass at it: it's pinched almost entirely from Aaron's code, it's currently expecting the cover object to be on the map layer - that's easy enough to change later. It's also expecting the standard R20 map scale, of 70 pixels per 5ft grid. You can change this variable if you need something else, at the top of the script. The script has the same limitations as Aaron's Tiles, in that it only detects the final resting point - that shouldn't be an issue for cover mechanics though. But something to keep in mind if a player moves out of cover, then back into cover, and is shot in between by reaction fire. If they don't manually place their token out of cover so the script can register it, they will still have a cover bonus while getting shot (I have no idea what rules you're playing, just pointing it out :) ) All the script does now is detection, and throws a message in chat when a token enters or leaves cover. The rest is reasonably easy though. Let me know if it works - I didn't test with circular auras, but it should act just like a square. Oh... it also ignores anything without an aura - if you want a token to register as a cover object it needs to (currently): - be on the map layer - have an aura greater than 0 feet const coverMe = (() => { // eslint-disable-line no-unused-vars const checkInstall = () => { // eslint-disable-next-line no-prototype-builtins if (!state.hasOwnProperty('coverMe')) { state.coverMe = {tokenList: {}}; } let activeTokens = _.pluck(JSON.parse(Campaign().get('turnorder')),'id'); let counter = 0; for (let t in state.coverMe.tokenList) { if (!activeTokens.includes(t)) { delete state.coverMe.tokenList[t]; counter ++; } } log(`-= coverMe started: ${counter} entries cleaned up from state object, ${Object.keys(state.coverMe.tokenList).length} entries left. =-`); } const pixelsPerFoot = 14; const findContains = (obj,filter,layer) => { if(obj) { let cx = obj.get('left'), cy = obj.get('top'); filter = filter || (() => true); layer = layer || 'gmlayer'; return findObjs({ _pageid: obj.get('pageid'), _type: "graphic", layer: layer }) .filter(filter) .reduce((m,o) => { let aura = parseInt(o.get('aura1_radius'))*pixelsPerFoot; let l=o.get('left'); let t=o.get('top'); let w=parseInt(o.get('width')) + 2*aura; let h=parseInt(o.get('height')) + 2*aura; let ol=l-(w/2); let or=l+(w/2); let ot=t-(h/2); let ob=t+(h/2); if( ol <= cx && cx <= or && ot <= cy && cy <= ob ){ m.push(o); log(o); } return m; },[]); } return []; }; const onMoveGraphic = (obj,prev) => { if ( ['objects','gm'].includes(obj.get('layer')) && ( parseInt(obj.get('left')) !== parseInt(prev.left) || parseInt(obj.get('top')) !== parseInt(prev.top) )) { let objId = obj.get('_id'); let coverObj = findContains(obj,(o)=>parseInt(o.get('aura1_radius'))>0,'map'); if (coverObj.length) { if (state.coverMe.tokenList[objId]) return; state.coverMe.tokenList[objId] = coverObj[0].get('name'); sendChat('coverMe',`${obj.get('name')} entered cover of ${coverObj[0].get('name')}`); // do stuff to Attributes } else if (state.coverMe.tokenList[objId]) { sendChat('coverMe',`${obj.get('name')} left the cover of ${state.coverMe.tokenList[objId]}`); state.coverMe.tokenList[objId] = null; // undo stuff to Attributes } } } const handleInput = (msg) => { if (msg.type === 'api' && msg.content.search(/^!coverme\s/i) !== -1) { let lastPlayer = getObj('player', msg.playerid).get('displayname'); let commands = msg.content.split(/\s*--\s*/); if (commands.length > 1) { //let args = []; commands.shift(); commands.forEach(cmd => { switch(cmd) { case 'list': getList(); break; case 'settings': // settings break; case 'help': // sendHelp(); break; } }) } else sendChat('coverMe', `/w ${lastPlayer} No commands detected.`); } } const getList = () => { let coverList = []; for (let t in state.coverMe.tokenList) { if ([t] !== null && getObj('graphic', t)) { coverList.push(`${getObj('graphic', t).get('name')} is in cover of ${state.coverMe.tokenList[t]}`) } } let body = (coverList.length === 0) ? `No tokens currently in cover.` : coverList.join('<br>'); sendChat('coverMe', `/w gm &{template:default}{{name=List}}{{${body}}}`) } on('ready', () => { checkInstall(); on('change:graphic',onMoveGraphic); on('chat:message', handleInput); }) })();
thank you very much Oosh is a way forward! It was very good!
1607182894
Oosh
Sheet Author
API Scripter
No problem. I'm happy to keep working on it, if you let me know how you want the Attributes to be updated and the cover bonus to be calculated.