Skip to content

Instantly share code, notes, and snippets.

@SplenectomY
Last active December 22, 2018 17:36
Show Gist options
  • Save SplenectomY/f30a7e562d6ba1ae922ef96156160428 to your computer and use it in GitHub Desktop.
Save SplenectomY/f30a7e562d6ba1ae922ef96156160428 to your computer and use it in GitHub Desktop.
Blocks tokens from going out "windows" on a Roll20 map, but allows them to see through it!
// This script will block tokens from going through "windows",
// or other transparent obstacles but allow sight through them.
// Will work for any paths drawn, even freehand and circles.
// (Circle/oval detection accuracy is reduced to save on processing time,
// because it's WAY harder to determine the intersect of an elipsoid vs.
// a line rather than line segments vs. a line. For better accuracy on curves,
// draw it freehand. Excessive use of this might cause slow performance)
// Default color for detecting the window path is yellow (#ffff00)
// You may change this color in the config below
// By SplenectomY
// https://github.com/SplenectomY
on("ready", function()
{
// Begin Config
const debug = false;
// Verbose logging to console
const pathColor = "#ffff00"; // yellow
// Color the script will look for when finding window drawings
const sendMsgToChat = true;
// If true, a message will be sent to chat
// when a character's movement is blocked by this script
const messageToSend = "$TOKENNAME$ tried to go through a closed window! OUCH!";
// message the narrator will send.
// $TOKENNAME$ will be replaced with the name of the token
const narrator = "Narrator";
// name of the narrator who "sends" the message
// End Config
const FindIntersect = function(x1, y1, x2, y2, x3, y3, x4, y4)
{
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4))
return false;
const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
if (denominator === 0)
return false;
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
return false;
const x = x1 + ua * (x2 - x1);
const y = y1 + ua * (y2 - y1);
return {x, y};
}
const WindowChecker = function(obj)
{
try
{
if (obj.get("layer") != "objects")
return;
let windows = findObjs(
{
_pageid: obj.get("_pageid"),
_type: "path",
stroke: pathColor,
layer: "gmlayer"
});
if (debug)
log("Found " + windows.length + " windows!");
const lastmoveSplit = obj.get("lastmove").split(",");
if (lastmoveSplit.length < 2)
return;
let xCoords = [];
let yCoords = [];
let tokenPoints = [];
let j = 0;
while (j <= lastmoveSplit.length - 1)
{
if (j % 2 === 0 || j === 0)
xCoords.push(lastmoveSplit[j]);
else
yCoords.push(lastmoveSplit[j]);
j++;
}
j = 0;
while (j <= xCoords.length - 1)
{
tokenPoints.push({"x":xCoords[j],"y":yCoords[j]});
j++;
}
tokenPoints.push({"x":obj.get("left"),"y":obj.get("top")});
const lastLeft = lastmoveSplit[lastmoveSplit.length - 2];
const lastTop = lastmoveSplit[lastmoveSplit.length - 1];
const firstLeft = lastmoveSplit[0];
const firstTop = lastmoveSplit[1];
if (debug)
{
log("lastmoveSplit = " + lastmoveSplit);
log("lastLeft = " + lastLeft);
log("lastTop = " + lastTop);
log("firstLeft = " + firstLeft);
log("firstTop = " + firstTop);
}
_.each(windows, function(win)
{
const segments = JSON.parse(win.get("_path"));
let points = [];
let i = 0;
while (i <= segments.length - 1)
{
let seg = segments[i];
let x, y;
switch (seg[0])
{
case "M":
case "L":
x = seg[1];
y = seg[2];
break;
case "C":
x = seg[5];
y = seg[6];
break;
case "Q":
x = seg[3];
y = seg[4];
break;
}
points.push({"x":x,"y":y});
i++;
}
i = 0;
while (i < points.length - 1)
{
if (debug)
{
log("seg start = " + starts[i][1] + "," + starts[i][2]);
log("seg end = " + stops[i][1] + "," + stops[i][2]);
}
var foundIntersect = false;
let k = 0;
while (k < tokenPoints.length - 1)
{
if (FindIntersect((points[i].x - win.get("width")/2) + win.get("left"),
(points[i].y - win.get("height")/2) + win.get("top"),
(points[i + 1].x - win.get("width")/2) + win.get("left"),
(points[i + 1].y - win.get("height")/2) + win.get("top"),
tokenPoints[k].x, tokenPoints[k].y, tokenPoints[k + 1].x, tokenPoints[k + 1].y) != false)
{
log("PathBlocker: Token '" + obj.get("name") + "' was blocked from transversing a path.");
if (sendMsgToChat && typeof narrator == "string" && typeof messageToSend == "string")
sendChat(narrator, messageToSend.replace("$TOKENNAME$", obj.get("name")), null, { noarchive: true });
obj.set("left", parseInt(tokenPoints[k].x));
obj.set("top", parseInt(tokenPoints[k].y));
foundIntersect = true;
break;
}
k++;
}
if (foundIntersect)
break;
i++;
}
});
}
catch(e)
{
sendChat("PathBlocker", "/w gm An error has occurred. Check the log.");
log(e);
}
}
on ("change:graphic:left", function(obj)
{ WindowChecker(obj); });
on ("change:graphic:top", function(obj)
{ WindowChecker(obj); });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment