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] Hidden Flavor Messages

1627851036

Edited 1627851240
Ergo
Pro
As I posted previously I've been playing around with the Hidden Roll Messages. I have it working but found the overhead of setting up a campaign to run the way I wanted to do it was tedious. I looked through other scripts and found WorldMapDiscovery. So I have combined the scripts, and now I can place a token on the gm layer, and when a player intersects the aura of the token the script does a roll and whispers a message to the player based on a json script in the GMnotes of the token. So far I am very happy with it. Still testing. The script is here:&nbsp; <a href="https://github.com/ErgoIronfist/roll20-api-scripts/tree/master/Private%20Flavor%20Messages" rel="nofollow">https://github.com/ErgoIronfist/roll20-api-scripts/tree/master/Private%20Flavor%20Messages</a> One question I have is there a way to tell a player's character token entered the area of the aura before that drop the token. Currently, the script will let a character walk right over the token, but if they end their movement outside the aura nothing happens. Thanks /* Author: Ergo Ironfist Version: 0.1 Date: 08/01/2021 Inspired by Phillip Tran's Hidden Roll Messages and Stephen Lindberg's World Map discovery, this script is a combination of both and will look for a token within a given distance and if found will roll for the moving player token and provide flavor text via private message to the player. The script reads a JASON string from the tokens GM notes that contains the attribute to roll, the success required and an on pass ans on fail message. I use this mostly to roll passives for a player when they walk by a secret door, trap, on anything else I would normally check for them if they are not actually searching. Place the white-tower token marker on the token and the Jason in the GM Notes and set aura_1 to the distance you want. Place the location token on the GM Layer. Json for token: { "attr": "perception_bonus", // The attribute to use. Must match the exact name on the character sheet "pass": 20, //The value needed to pass the roll check "on_pass": "There is a masonry block that seems a little offset from the others. It looks as if it sticks out a little on one side.", //The sucess message "on_fail": "The goblin bodies appear to have been chewed on by rats." //The fail message } a whisper from "Your Intuition" is sent to the player and a pass/fail and copy of the text is whispered to the GM. */ var PrivateFlavorMessage = (function () { var DICE = 20; var PIXELS_PER_SQUARE = 70; var VERSION = '0.1'; /** * Causes a location to become discovered. * @param {Graphic} location The location that is being discovered. * @param {Graphic} discoverer The token that discovered the location. */ function discoverLocation(location, discoverer) { location.set('aura1_radius', ''); location.set('status_white-tower', false); toBack(location); let character = getObj('character', discoverer.get('represents')); rollTest(discoverer, location, character); } /** * Gets the discovery distance for a location based on its aura's radius. * @param {Graphic} location * @return {number} */ function getDiscoveryDistance(location) { var radiusUnits = location.get('aura1_radius'); var pageId = location.get('_pageid'); var page = getObj('page', pageId); var pageScale = page.get('scale_number'); return radiusUnits / pageScale * PIXELS_PER_SQUARE + location.get('width') / 2; } /** * Returns the first undiscovered location the token is in range of, or null * if the token is not in range of an undiscvered location. * @param {Graphic} token * @return {Graphic[]} */ function getLocationCollisions(token) { var pageId = token.get('_pageid'); var tokenPt = _getTokenPt(token); return _.filter(getLocationTokens(pageId), function (location) { var locationPt = _getTokenPt(location); var dist = VecMath.dist(tokenPt, locationPt); var threshold = getDiscoveryDistance(location); return (dist &lt;= threshold); }); } /** * Returns all location tokens on some page. * Undiscovered locations reside on the gm layer. * @param {string} pageId * @return {Graphic[]} */ function getLocationTokens(pageId) { return findObjs({ _pageid: pageId, _type: "graphic", 'status_white-tower': true, layer: "gmlayer" }); } /** * Gets the location of a token as a point. * @private * @param {Graphic} * @return {vec2} */ function _getTokenPt(token) { var x = token.get("left"); var y = token.get("top"); return [x, y]; } /** * Initialization log */ on('ready', function () { log('Initialized Private Flavor Messages v' + VERSION); }); /** * When a graphic on the objects layer moves, run the script to see if it * passed through any traps. */ on("change:graphic:lastmove", function (obj, prev) { var activePage = Campaign().get('playerpageid'); // Check if the moved token came within range of any locations. if (obj.get("layer") === "objects" &amp;&amp; obj.get("represents")) { var locations = getLocationCollisions(obj); _.each(locations, function (location) { discoverLocation(location, obj); }); } }); return { discoverLocation: discoverLocation, getDiscoveryDistance: getDiscoveryDistance, getLocationCollisions: getLocationCollisions, getLocationTokens: getLocationTokens }; /* Get the GM Notes, Test the roll, send a whisper to the player and GM */ function rollTest(discoverer, location, character) { let json = getGMNoteData(location); let attr = json["attr"]; let passValue = json["pass"]; let msgPass = json["on_pass"]; let msgFail = json["on_fail"]; let msgGM = ""; let diceRoll = ((DICE != 0) ? randomInteger(DICE) : 0); let result = parseInt(getAttrByName(character.id, attr)) + diceRoll; let recipient = discoverer.get("name").split(" ")[0]; // get first name only if (result &gt;= passValue) { let command = "/w " + recipient + " " + msgPass; msgGM = msgPass; log("CHAT SEND Pass " + command); sendChat("Your Intuition", command, null, { noarchive: true }); } else if (msgFail != undefined) { let command = "/w " + recipient + " " + msgFail; msgGM = msgFail; sendChat("Your Intuition", command, null, { noarchive: true }); } /* Let the GM know the results */ let command = "/w gm &lt;b&gt;" + discoverer.get("name").split(" ")[0] + " | " + (result &gt;= passValue) + "&lt;/b&gt;&lt;br&gt;" + msgGM; sendChat("PFM", command, null, { noarchive: true }); }; function getGMNoteData(location) { var decode let gmNote = location.get("gmnotes"); gmNote = decodeURIComponent(gmNote).toString().replace(/&lt;\/?[^&gt;]+&gt;/gi, ''); // Remove HTML gmNote = gmNote.replace(/&amp;nbsp;/g, ""); // Remove non breaking whitespaces gmNote = gmNote.replace(/\n/g, ""); // Remove newlines try { if (gmNote.length == 0) throw "not valid Json"; let json = JSON.parse(gmNote); return json; } catch (err) { sendChat("/w gm Status: &lt;b&gt;FAILED&lt;/b&gt; \n Json Formatter: <a href="https://jsonformatter.curiousconcept.com/" rel="nofollow">https://jsonformatter.curiousconcept.com/</a>", null, { noarchive: true }); } } })();
I've updated the script to add two settings to control whether to use a dice roll added to the selected or just use the attribute and compare it to the dc. Like "passive_wisdom" the second tells the script whether or not to reset the token after a failed attempt. This way if the first player that intersects the token fails the check the token is still active for the next player that intesects.
1628026274
David M.
Pro
API Scripter
Neat! In reference to your question about triggering when tokens move across but are not dropped within the aura: I don't believe the API has access to real-time movement like that. AFAIK, it responds to committed changes in token status. I suppose one thing you could do is compare the positions of the obj &amp; prev versions of the token after movement and see if the line intersects the aura bounding box. Might get you most of the way, though I suppose it wouldn't work if they "actually" moved in a curved or zigzag path. If you guys use waypoints for movement, you could probably grab that info though I don't have experience with handling waypoints in the api.