Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions Its A Trap/ItsATrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* A script that checks the interpolation of a token's movement to detect
* whether they have passed through a square containing a trap.
*
* A trap can be any token on the GM layer for which the cobweb status is
* active. Flying tokens (ones with the fluffy-wing status or angel-outfit
* status active) will not set off traps unless the traps are also flying.
*
* This script works best for square traps equal or less than 2x2 squares or
* circular traps of any size.
*/
var ItsATrap = (function() {

/**
* Returns the first trap a token collided with during its last movement.
* If it didn't collide with any traps, return false.
* @param {Graphic} token
* @return {Graphic || false}
*/
var getTrapCollision = function(token) {
var traps = getTrapTokens();
traps = filterList(traps, function(trap) {
return !isTokenFlying(token) || isTokenFlying(trap);
});

return TokenCollisions.getFirstCollision(token, traps);
};


/**
* Determines whether a token is currently flying.
* @param {Graphic} token
* @return {Boolean}
*/
var isTokenFlying = function(token) {
return (token.get("status_fluffy-wing") ||
token.get("status_angel-outfit"));
};


/**
* Moves the specified token to the same position as the trap.
* @param {Graphic} token
* @param {Graphic} trap
*/
var moveTokenToTrap = function(token, trap) {
var x = trap.get("left");
var y = trap.get("top");

token.set("lastmove","");
token.set("left", x);
token.set("top", y);
};




/**
* Returns all trap tokens on the players' page.
*/
var getTrapTokens = function() {
var currentPageId = Campaign().get("playerpageid");
return findObjs({_pageid: currentPageId,
_type: "graphic",
status_cobweb: true,
layer: "gmlayer"});
};



/**
* Filters items out from a list using some filtering function.
* Only items for which the filtering function returns true are kept in the
* filtered list.
* @param {Object[]} list
* @param {Function} filterFunc Accepts an Object from list as a parameter.
* Returns true to keep the item, or false to
* discard.
* @return {Object[]}
*/
var filterList = function(list, filterFunc) {
var results = [];
for(var i=0; i<list.length; i++) {
var item = list[i];
if(filterFunc(item)) {
results.push(item);
}
}
return results;
}


/**
* When a graphic on the objects layer moves, run the script to see if it
* passed through any traps.
*/
on("change:graphic", function(obj, prev) {

// Objects on the GM layer don't set off traps.
if(obj.get("layer") === "objects") {
var trap = getTrapCollision(obj);

if(trap) {
sendChat("Admiral Ackbar", "IT'S A TRAP!!! " + obj.get("name") + " set off a trap!");

moveTokenToTrap(obj, trap);

if(trap.get("status_bleeding-eye")) {
trap.set("layer","objects");
toBack(trap);
}
}
}
});
})();
16 changes: 16 additions & 0 deletions Its A Trap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# It's A Trap!

This is a script that allows GMs to quickly and very easily set up traps on the GM layer, and detect when tokens on the objects layer move over them. This trap detection even works for tokens moving by waypoints.

### To set up traps:

Place the token for your trap on the GM layer. Give it the cobweb status marker.

By default, traps will only affect characters on the ground (ones that don't have a wing or angel status marker). To have a trap also affect flying characters, give it the wing or angel status marker.

By default, trap tokens won't appear when they are activated. If you would like the trap to become visible to the players when it is activated, give it the bleeding eye status marker.

### To set off traps:

If a token moves across a trap at ANY point during its movement, the trap will be activated!

24 changes: 24 additions & 0 deletions Its A Trap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "It's a Trap!",
"version": "2.0",
"description": "Provides a simple means to set traps and detect when tokens move across them.",
"authors": "Stephen Lindberg",
"roll20userid": 46544,
"dependencies": {
"Token Collisions": "1.0",
"Vector Math": "1.0"
},
"modifies": {
"chat": "write",
"lastmove": "write",
"layer": "read, write",
"left": "read, write",
"status_angel-outfit": "read",
"status_bleeding-eye": "read",
"status_cobweb": "read",
"status_fluffy-wing": "read",
"token": "read",
"top": "read, write"
},
"conflicts": []
}
201 changes: 201 additions & 0 deletions Token Collisions/TokenCollisions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/**
* A small library for testing collisions between moving tokens in Roll20.
*/
var TokenCollisions = (function() {
/**
* Returns the first token, from some list of tokens, that a token has
* collided with during its last movement.
* @param {Graphic} token The token that moved
* @param {Graphic[]} otherTokens The list of tokens we are testing collisions with.
* @return {Graphic || false} If the token collided with another token, the first token that
* it would collide with during its movement is returned. Otherwise it returns false.
*/
var getFirstCollision = function(token, otherTokens) {
// Get all the points for the movement.
var movePts = _getAllPointsDuringMovement(token);

// Check each segment of movement to see if we triggered a trap.
for(var i = 0; i < movePts.length-1; i++) {
var p1 = movePts[i];
var p2 = movePts[i+1];

// If we encountered a trap, we're done.
var collidedToken = _getFirstCollisionInWaypoint(token, otherTokens, p1, p2);
if(collidedToken) {
return collidedToken;
}
}
return false
};


/**
* Returns the list of tokens, from some list of tokens, that a token has
* collided with during its last movement.
* @param {Graphic} token The token that moved
* @param {Graphic[]} otherTokens The tokens we're checking collisions with.
* @return {Graphic[]}
*/
var getCollisions = function(token, otherTokens) {
var movePts = _getAllPointsDuringMovement(token);
var collisions = [];

// Check each segment of movement to see if we triggered a trap.
for(var i = 0; i < movePts.length-1; i++) {
var p1 = movePts[i];
var p2 = movePts[i+1];

// Append any collisions in this segment to the list.
var newCollisions = _getCollisionsInWaypoint(token, otherTokens, p1, p2);
collisions = collisions.concat(newCollisions);
}
return collisions
};


/**
* @private
* Gets the list of all points traversed during a token's movement, including
* its current position at the end of the movement.
* @param {Graphic} token
* @return {vec2[]}
*/
var _getAllPointsDuringMovement = function(token) {
var results = [];

var moveCoords = token.get("lastmove").split(",");
for(var i=0; i < moveCoords.length; i++) {
moveCoords[i] = parseInt(moveCoords[i]);
}
moveCoords.push(parseInt(token.get("left")));
moveCoords.push(parseInt(token.get("top")));

for(var i=0; i <= moveCoords.length - 2; i+=2) {
var x = moveCoords[i];
var y = moveCoords[i+1];
results.push([x, y]);
}

return results;
};


/**
* @private
* Checks if a token collided with another token during its movement between two points.
* @param {Graphic} token
* @param {vec2} startPt
* @param {vec2} endPt
* @return {Graphic || false} The first other token that token collided with in its movement. If there was no collision, return false.
*/
var _getFirstCollisionInWaypoint = function(token, otherTokens, startPt, endPt) {
var collisions = _getCollisionsInWaypoint(token, otherTokens, startPt, endPt);
if(collisions.length > 0) {
return _getNearestTokenToPoint(startPt, collisions);
}
else {
return false;
}
};


/**
* @private
* For some token, this gets the list of tokens it collided with during its
* movement between two points, from some list of other tokens.
* @param {Graphic} token
* @param {Graphic[]} otherTokens
* @param {vec2} startPt
* @param {vec2} endPt
* @return {Graphic[]}
*/
var _getCollisionsInWaypoint = function(token, otherTokens, startPt, endPt) {
var result = [];

for(var i in otherTokens) {
var other = otherTokens[i];
if(_checkCollisionInWaypoint(token, other, startPt, endPt)) {
result.push(other);
}
}

return result;
};

/**
* @private
* Checks if a token collides with the other token during its movement between
* two points.
* @param {Graphic} token The moving token
* @param {Graphic} other
* @param {vec2} startPt
* @param {vec2} endPt
* @return {Boolean} true iff there was a collision.
*/
var _checkCollisionInWaypoint = function(token, other, startPt, endPt) {
var otherPt = _getTokenPt(other);

// We assume that all tokens are circular, therefore width = diameter.
var thresholdDist = (parseInt(other.get("width")) + parseInt(token.get("width")))/2;

// Don't count the other token if our movement already started in it.
if(Math.round(VecMath.dist(startPt, otherPt)) >= thresholdDist) {

// Figure out the closest distance we came to the other token during
// the movement.
var dist = Math.round(VecMath.ptSegDist(otherPt, startPt, endPt));
if(dist < thresholdDist) {
return true;
}
}

return false;
};


/**
* @private
* Returns the token nearest to the specified point
* out of some list of tokens.
* @param {vec2} pt
* @param {Graphic[]} tokens
* @return {Graphic}
*/
var _getNearestTokenToPoint = function(pt, tokens) {
var result = null;
var bestDist = -1;
for(var i in tokens) {
var token = tokens[i];

var tokenPt = _getTokenPt(token);
var dist = VecMath.dist(pt, tokenPt);

if(bestDist === -1 || dist < bestDist) {
result = token;
bestDist = dist;
}
}

return result;
};


/**
* @private
* Gets the position of a token.
* @param {Graphic} token
* @return {vec2}
*/
var _getTokenPt = function(token) {
var x = token.get("left");
var y = token.get("top");
return [x, y];
};


// The exposed API.
return {
getFirstCollision: getFirstCollision,
getCollisions: getCollisions
};
})();
16 changes: 16 additions & 0 deletions Token Collisions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Token Collisions",
"version": "1.0",
"description": "A small library for detecting collisions among tokens.",
"authors": "Stephen Lindberg",
"roll20userid": 46544,
"dependencies": {
"Vector Math": "1.0"
},
"modifies": {
"lastmove": "read",
"left": "read",
"top": "read"
},
"conflicts": []
}
Loading