From f873b096c1a67cd13fb9a5fd5f9aa9b830a1c37a Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 7 Jan 2015 16:59:57 -0600 Subject: [PATCH 01/29] Add Collision Detection script & package --- Collision Detection/Collision Detection.js | 150 +++++++++++++++++++++ Collision Detection/package.json | 15 +++ 2 files changed, 165 insertions(+) create mode 100644 Collision Detection/Collision Detection.js create mode 100644 Collision Detection/package.json diff --git a/Collision Detection/Collision Detection.js b/Collision Detection/Collision Detection.js new file mode 100644 index 0000000000..8aac327174 --- /dev/null +++ b/Collision Detection/Collision Detection.js @@ -0,0 +1,150 @@ +var coldtc = coldtc || {}; + +coldtc.polygonPaths = []; +coldtc.DONT_MOVE = 1; +coldtc.WARN_PLAYER = 2; +coldtc.STOP_AT_WALL = 4; + +/*** SCRIPT SETTINGS ***/ +coldtc.pathColor = '#ff00ff'; // Only paths with this color will be used for collisions +coldtc.layer = 'walls'; // Only paths on this layer will be used for collisions; set to 'all' to use all layers +coldtc.behavior = coldtc.STOP_AT_WALL|coldtc.WARN_PLAYER; // behavior for collision events + +on('add:path', function(obj) { + if(obj.get('pageid') != Campaign().get('playerpageid') + || obj.get('stroke').toLowerCase() != coldtc.pathColor) return; + if(coldtc.layer != 'all' && obj.get('layer') != coldtc.layer) return; + + var path = JSON.parse(obj.get('path')); + if(path.length > 1 && path[1][0] != 'L') return; // Add fushcia paths on current page's gm layer + coldtc.polygonPaths.push(obj); +}); + +on('destroy:path', function(obj) { + for(var i = 0; i < coldtc.polygonPaths.length; i++) + { + if(coldtc.polygonPaths[i].id == obj.id) + { + coldtc.polygonPaths = coldtc.polygonPaths.splice(i, 1); // Delete path if they're the same + break; + } + } +}); + +on('change:path', function(obj, prev) { + if(coldtc.layer == 'all') return; // changing path layer doesn't matter + + if(obj.get('layer') == coldtc.layer && prev.layer != coldtc.layer) // May need to add to list + { + if(obj.get('pageid') != Campaign().get('playerpageid') + || obj.get('stroke').toLowerCase() != coldtc.pathColor) return; + var path = JSON.parse(obj.get('path')); + if(path.length > 1 && path[1][0] != 'L') return; + coldtc.polygonPaths.push(obj); + } + if(obj.get('layer') != coldtc.layer && prev.layer == coldtc.layer) // May need to remove from list + { + for(var i = 0; i < coldtc.polygonPaths.length; i++) + { + if(coldtc.polygonPaths[i].id == obj.id) + { + coldtc.polygonPaths = coldtc.polygonPaths.splice(i, 1); + break; + } + } + } +}); + +on('change:graphic', function(obj, prev) { + if(obj.get('subtype') != 'token' + || (obj.get('top') == prev.top && obj.get('left') == prev.left)) return; + if(obj.get('represents') != '') + { + var character = getObj('character', obj.get('represents')); + if(character.get('controlledby') == '') return; // GM-only token + } + else if(obj.get('controlledby') == '') return; // GM-only token + + var l1 = coldtc.L(coldtc.P(prev.left, prev.top), coldtc.P(obj.get('left'), obj.get('top'))); + _.each(coldtc.polygonPaths, function(path) { + var pointA, pointB; + var x = path.get('left') - path.get('width') / 2; + var y = path.get('top') - path.get('height') / 2; + var parts = JSON.parse(path.get('path')); + pointA = coldtc.P(parts[0][1] + x, parts[0][2] + y); + parts.shift(); + _.each(parts, function(pt) { + pointB = coldtc.P(pt[1] + x, pt[2] + y); + var l2 = coldtc.L(pointA, pointB); + var denom = (l1.p1.x - l1.p2.x) * (l2.p1.y - l2.p2.y) - (l1.p1.y - l1.p2.y) * (l2.p1.x - l2.p2.x); + if(denom != 0) // Parallel + { + var intersect = coldtc.P( + (l1.p1.x*l1.p2.y-l1.p1.y*l1.p2.x)*(l2.p1.x-l2.p2.x)-(l1.p1.x-l1.p2.x)*(l2.p1.x*l2.p2.y-l2.p1.y*l2.p2.x), + (l1.p1.x*l1.p2.y-l1.p1.y*l1.p2.x)*(l2.p1.y-l2.p2.y)-(l1.p1.y-l1.p2.y)*(l2.p1.x*l2.p2.y-l2.p1.y*l2.p2.x) + ); + intersect.x /= denom; + intersect.y /= denom; + + if(coldtc.isBetween(pointA, pointB, intersect) + && coldtc.isBetween(l1.p1, l1.p2, intersect)) + { + // Collision event! + if((coldtc.behavior&coldtc.DONT_MOVE) == coldtc.DONT_MOVE) + { + obj.set({ + left: Math.round(l1.p1.x), + top: Math.round(l1.p1.y) + }); + } + if((coldtc.behavior&coldtc.WARN_PLAYER) == coldtc.WARN_PLAYER) + { + var who; + if(obj.get('represents')) + { + var character = getObj('character', obj.get('represents')); + who = character.get('name'); + } + else + { + var controlledby = obj.get('controlledby'); + if(controlledby == 'all') who = 'all'; + else + { + var player = getObj('player', controlledby); + who = player.get('displayname'); + } + } + who = who.indexOf(' ') > 0 ? who.substring(0, who.indexOf(' ')) : who; + if(who != 'all') + sendChat('SYSTEM', '/w '+who+' You are not permitted to move that token into that area.'); + else + sendChat('SYSTEM', 'Token '+obj.get('name')+' is not permitted in that area.'); + } + if((coldtc.behavior&coldtc.STOP_AT_WALL) == coldtc.STOP_AT_WALL) + { + var vec = coldtc.P(l1.p2.x - l1.p1.x, l1.p2.y - l1.p1.y); + var norm = Math.sqrt(vec.x * vec.x + vec.y * vec.y); + vec.x /= norm; + vec.y /= norm; + + obj.set({ + left: intersect.x - vec.x, + top: intersect.y - vec.y + }); + } + } + } + + pointA = coldtc.P(pointB.x, pointB.y); + }); + }); +}); + +coldtc.P = function(x, y) { return {x: x, y: y}; }; +coldtc.L = function(p1, p2) { return {p1: p1, p2: p2}; }; +coldtc.isBetween = function(a, b, c) { + var withinX = (a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x); + var withinY = (a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y); + return withinX && withinY; +}; \ No newline at end of file diff --git a/Collision Detection/package.json b/Collision Detection/package.json new file mode 100644 index 0000000000..c8f72e92bd --- /dev/null +++ b/Collision Detection/package.json @@ -0,0 +1,15 @@ +{ + "name": "Collision Detection", + "version": "1.0", + "description": "Watches for collisions between player-controlled tokens and a specified subset of path objects. When a collision is detected, the script may return the token to its position prior to the move, send a warning message to the token's controller, and/or halt the token at the path acting as a barrier to movement.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "token": "write", + "path": "read" + }, + "conflicts": [ + "none" + ] +} From dc170e069b20336eabbcfa1d9126e9cd5de16618 Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 7 Jan 2015 17:00:07 -0600 Subject: [PATCH 02/29] Begin collision detection help file --- Collision Detection/Help.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Collision Detection/Help.txt diff --git a/Collision Detection/Help.txt b/Collision Detection/Help.txt new file mode 100644 index 0000000000..5972050970 --- /dev/null +++ b/Collision Detection/Help.txt @@ -0,0 +1,7 @@ +## Collision Detection + +There are three configuration options available to you: + +* `coldtc.pathColor`: The script only considers paths of a specific color, allowing you to also use paths of other colors which your players will not collide with. By default, this is fuchsia (#ff00ff); the color is specified as a hexadecimal web color, which you can see when selecting a color from the drawing interface. A path's fill color is ignored. +* `coldtc.layer`: The script will only look at paths on the specified layer (valid values are "map", "objects", "gmlayer", or "walls"). You can also set this value to "all" and paths on every layer will be considered. +* `coldtc.behavior`: You can customize the script's behavior when a collision event is detected. \ No newline at end of file From c483e83df5de555db67dea1dbfa561756289406a Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 7 Jan 2015 17:00:23 -0600 Subject: [PATCH 03/29] Add dynamic lighting animation script --- .../Dynamic Lighting Animation.js | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Dynamic Lighting Animation/Dynamic Lighting Animation.js diff --git a/Dynamic Lighting Animation/Dynamic Lighting Animation.js b/Dynamic Lighting Animation/Dynamic Lighting Animation.js new file mode 100644 index 0000000000..babd947085 --- /dev/null +++ b/Dynamic Lighting Animation/Dynamic Lighting Animation.js @@ -0,0 +1,107 @@ +var animation = animation || {}; + +// Set to a list of the user IDs for each GM -- only the GM should be able to stop/start the animation! +// You can find your user ID by checking your userpage on the wiki, or the URL of your profile. +animation.gmIDs = [235259]; + +animation.running = false; + +on('chat:message', function(msg) { + if(msg.type != 'api') return; + + // Only use commands coming from a GM + var isGM = false; + var player = getObj('player', msg.playerid); + _.each(animation.gmIDs, function(n) { + if(player.get('d20userid') == n) + isGM = true; + }); + if(!isGM) return; + + var parts = msg.content.split(' '); + var command = parts.shift().substring(1); + + switch(command) + { + case 'snapshot': + if(animation.running) + { + sendChat("","/w gm You cannot add a frame while the animation is running."); + break; + } + + var pageid = Campaign().get('playerpageid'); + var dlPaths = findObjs({_type: 'path', _pageid: pageid, layer: 'walls'}); + var frames = parseInt(parts[0], 10); + var pathdata = []; + + _.each(dlPaths, function(path) { + var obj = { + id: path.id, + top: path.get('top'), + left: path.get('left'), + rotation: path.get('rotation'), + width: path.get('width'), + height: path.get('height'), + scaleX: path.get('scaleX'), + scaleY: path.get('scaleY') + }; + pathdata.push(obj); + }); + state.animation.frames.push({ data: pathdata, frames: frames }); + break; + case 'reset': + state.animation.curr_frame = 0; + state.animation.frames = []; + break; + case 'run': + animation.running = true; + break; + case 'stop': + animation.running = false; + break; + default: + break; + } +}); + +on('ready', function() { + if(!state.animation) state.animation = {}; + if(!state.animation.frames) state.animation.frames = []; + if(!state.animation.curr_frame) state.animation.curr_frame = 0; + + var frame = state.animation.curr_frame; + var frameCount = 0; + setInterval(function() { + if(!animation.running) return; + if(!state.animation.frames[frame]) return; + + frameCount++; + if(state.animation.frames[frame].frames <= frameCount) + { + animation.setupFrame(state.animation.frames[frame].data); + frameCount -= state.animation.frames[frame].frames; + frame++; + if(frame == state.animation.frames.length) frame = 0; + state.animation.curr_frame = frame; + } + }, 50); +}); + +/** + * Set the paths on the DL layer to the settings for the current animation frame. + */ +animation.setupFrame = function(pathdata) { + _.each(pathdata, function(obj) { + var path = getObj('path', obj.id);log(obj); + path.set({ + top: obj.top, + left: obj.left, + rotation: obj.rotation, + width: obj.width, + height: obj.height, + scaleX: obj.scaleX, + scaleY: obj.scaleY + }); + }); +}; \ No newline at end of file From 4dc7e533c7afb4db0768b4240815339b564a8b3d Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 7 Jan 2015 17:00:48 -0600 Subject: [PATCH 04/29] Create dynamic lighting animation package (copy of collision detection package) --- Dynamic Lighting Animation/package.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Dynamic Lighting Animation/package.json diff --git a/Dynamic Lighting Animation/package.json b/Dynamic Lighting Animation/package.json new file mode 100644 index 0000000000..c8f72e92bd --- /dev/null +++ b/Dynamic Lighting Animation/package.json @@ -0,0 +1,15 @@ +{ + "name": "Collision Detection", + "version": "1.0", + "description": "Watches for collisions between player-controlled tokens and a specified subset of path objects. When a collision is detected, the script may return the token to its position prior to the move, send a warning message to the token's controller, and/or halt the token at the path acting as a barrier to movement.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "token": "write", + "path": "read" + }, + "conflicts": [ + "none" + ] +} From 99e0ccf331e0d3d8e207e0215927334482bd6c1a Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:07:56 -0600 Subject: [PATCH 05/29] Completed Dynamic Lighting Animation help file --- Dynamic Lighting Animation/Help.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Dynamic Lighting Animation/Help.txt diff --git a/Dynamic Lighting Animation/Help.txt b/Dynamic Lighting Animation/Help.txt new file mode 100644 index 0000000000..1eb35250f0 --- /dev/null +++ b/Dynamic Lighting Animation/Help.txt @@ -0,0 +1,21 @@ +## Dynamic Lighting Animation + +There is one configuration option available to you: + +* `animation.gmIDs`: Set this to a list of d20userid values; only the specified users will be able to use the commands generated by the script. + +You can find a user's d20userid from either the URL of their profile page, or visit their userpage on the wiki: + +![Profile URL](https://wiki.roll20.net/images/thumb/0/03/Brian_Profile.jpg/120px-Brian_Profile.jpg) ![Userpage](https://wiki.roll20.net/images/thumb/e/e8/Brian_Userpage.jpg/119px-Brian_Userpage.jpg) + +### Commands +* !snapshot _frames_ +* !reset +* !run +* !stop + +You can record animation frames with `!snapshot`: set up the Dynamic Lighting as you like it, then use `!snapshot` along with the number of frames to hold that position. The animation runs as 20fps, so `!snapshot 20` will hold the position for 1s. + +`!reset` clears the animation buffer, and the commands `!run` and `!stop` predictably play or halt the animation. You cannot snapshot new positions while the animation is running. This script only stores a single animation, so you need to clear it before creating another one. + +Each snapshot only looks at the Dynamic Lighting paths on the page that currently has the player bookmark ribbon. \ No newline at end of file From bdc0e92c094b16a1d15642b2252132739bdae700 Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:10 -0600 Subject: [PATCH 06/29] Completed Dynamic Lighting Animation package file --- Dynamic Lighting Animation/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dynamic Lighting Animation/package.json b/Dynamic Lighting Animation/package.json index c8f72e92bd..0931045a34 100644 --- a/Dynamic Lighting Animation/package.json +++ b/Dynamic Lighting Animation/package.json @@ -1,13 +1,13 @@ { - "name": "Collision Detection", + "name": "Dynamic Lighting Animation", "version": "1.0", - "description": "Watches for collisions between player-controlled tokens and a specified subset of path objects. When a collision is detected, the script may return the token to its position prior to the move, send a warning message to the token's controller, and/or halt the token at the path acting as a barrier to movement.", + "description": "Animates paths on the Dynamic Lighting layer.", "authors": "Brian Shields", "roll20userid": "235259", "dependencies": {}, "modifies": { - "token": "write", - "path": "read" + "path": "write", + "message": "write" }, "conflicts": [ "none" From ffaae6f05c949e49acc333bd34cbd5997b8acc2f Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:20 -0600 Subject: [PATCH 07/29] Added Exalted Successes --- Exalted Successes/Exalted Successes.js | 65 ++++++++++++++++++++++++++ Exalted Successes/Help.txt | 5 ++ Exalted Successes/package.json | 14 ++++++ 3 files changed, 84 insertions(+) create mode 100644 Exalted Successes/Exalted Successes.js create mode 100644 Exalted Successes/Help.txt create mode 100644 Exalted Successes/package.json diff --git a/Exalted Successes/Exalted Successes.js b/Exalted Successes/Exalted Successes.js new file mode 100644 index 0000000000..fa4113582b --- /dev/null +++ b/Exalted Successes/Exalted Successes.js @@ -0,0 +1,65 @@ +var exalted = exalted || {}; + +exalted.sendChat = function(name, id, msg) +{ + var characters = findObjs({_type: 'character'}); + var speaking; + characters.forEach(function(chr) { if(chr.get('name') == name) speaking = chr; }); + if(speaking) sendChat('character|'+speaking.id, msg); + else sendChat('player|'+id, msg); +}; + +on('chat:message', function(msg) { + var json; + var inline = false; + try { json = JSON.parse(msg.content); } + catch(e) + { + if(msg.inlinerolls) inline = true; + else return; + } + + var results = []; + if(!inline) + { + json.rolls.forEach(function(j) { + if(j.sides != 10) return; + results.push(j.results); + }); + } + else + { + json = msg.inlinerolls; + json.forEach(function(j) { + var rolls = j.results.rolls; + rolls.forEach(function(r) { + if(r.sides != 10) return; + results.push(r.results); + }); + }); + } + + var successes = 0; + var botches = 0; + results.forEach(function(r) { + r.forEach(function(d) { + var die = d['v']; + successes += die >= 7 ? 1 : 0 + successes += die == 10 ? 1 : 0; + botches += die == 1 ? 1 : 0; + }); + }); + + if(successes == 0 && botches != 0) + { + exalted.sendChat(msg.who, msg.playerid, botches+' botch'+(botches>1?'es':'')); + } + else if(successes == 0) + { + exalted.sendChat(msg.who, msg.playerid, 'Failure'); + } + else + { + exalted.sendChat(msg.who, msg.playerid, successes+' success'+(successes>1?'es':'')); + } +}); \ No newline at end of file diff --git a/Exalted Successes/Help.txt b/Exalted Successes/Help.txt new file mode 100644 index 0000000000..056cbee518 --- /dev/null +++ b/Exalted Successes/Help.txt @@ -0,0 +1,5 @@ +## Exalted Successes + +Whenever a message is posted to the chat which includes rolling d10s, the script will foolow up by posting the number of successes for a roll in the _Exalted_ game system by White Wolf Publishing. Restuls of 7, 8, and 9 are each 1 success, 10s are 2 successes*, and 1s are botches _if and only if_ there are zero successes. + +\* There are corner cases in the game system where dice with values less than 7 count as successes (or 7, 8, and/or 9 _don't_ count as successes), and damage rolls do not normally count 10s as double. This script does not account for those cases. \ No newline at end of file diff --git a/Exalted Successes/package.json b/Exalted Successes/package.json new file mode 100644 index 0000000000..0baef81040 --- /dev/null +++ b/Exalted Successes/package.json @@ -0,0 +1,14 @@ +{ + "name": "Exalted Successes", + "version": "1.0", + "description": "Reports successes on d10 rolls for use with the Exalted game system.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "message": "write" + }, + "conflicts": [ + "none" + ] +} From 7bfe623e39c6d9cfd741c7f2ddc897cb1842a1dc Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:28 -0600 Subject: [PATCH 08/29] Added Flight --- Flight/Flight.js | 31 +++++++++++++++++++++++++++++++ Flight/Help.txt | 7 +++++++ Flight/package.json | 15 +++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 Flight/Flight.js create mode 100644 Flight/Help.txt create mode 100644 Flight/package.json diff --git a/Flight/Flight.js b/Flight/Flight.js new file mode 100644 index 0000000000..4423c98f00 --- /dev/null +++ b/Flight/Flight.js @@ -0,0 +1,31 @@ +on('chat:message', function(msg) { + if(msg.type != 'api') return; + var parts = msg.content.toLowerCase().split(' '); + var command = parts.shift().substring(1); + var selected = msg.selected; + if(command != 'fly' || !selected) return; // use the !fly command, and have one or more things selected + var height = +parts[0]; + if(!height) height = 0; // if no height is returned, treat as 0 + _.each(selected, function(obj) { + if(obj._type != 'graphic') return; // only fly graphics + var tok = getObj('graphic', obj._id); + if(tok.get('subtype') != 'token') return; // don't try to fly cards + tok.set('status_fluffy-wing', false); + var wings = ''; + while(height > 0) + { + // get current digit, starting from ones + var digit = height / 10; + digit -= Math.floor(digit); + digit = Math.round(digit * 10); + // shift height + height = Math.floor(height / 10); + wings += 'fluffy-wing@'+digit+','; + } + if(wings.length > 0) wings = wings.substring(0, wings.length - 1); // trim trailing comma + var markers = tok.get('statusmarkers'); + if(markers != '') markers += ','; + markers += wings; + tok.set('statusmarkers', markers); + }); +}); \ No newline at end of file diff --git a/Flight/Help.txt b/Flight/Help.txt new file mode 100644 index 0000000000..46cad3c096 --- /dev/null +++ b/Flight/Help.txt @@ -0,0 +1,7 @@ +## Flight + +Adds copies of the `fluffy-wing` status icon to the selected tokens with numbers indicating how high the token is flying. + +Use `!fly height` (where _height_ is a number) while selecting one or more tokens. If _height_ is 0 or is omitted, the wings will be removed. Any integer number can be used, although any digits of the number which are 0 will show up as wings without a number. + +![Flying High](https://wiki.roll20.net/images/3/3e/Flight_Example.jpg) \ No newline at end of file diff --git a/Flight/package.json b/Flight/package.json new file mode 100644 index 0000000000..4ebd96433d --- /dev/null +++ b/Flight/package.json @@ -0,0 +1,15 @@ +{ + "name": "Flight", + "version": "2.0", + "description": "Adds 'fluffy-wings' status icon to selected token to represent some hieght.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "message": "read", + "token": "write" + }, + "conflicts": [ + "none" + ] +} From a648ddbf72f18e6fca56e0f19dfc8d05127ac630 Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:36 -0600 Subject: [PATCH 09/29] Added Flip Tokens --- Flip Tokens/Flip Tokens.js | 18 ++++++++++++++++++ Flip Tokens/Help.txt | 3 +++ Flip Tokens/package.json | 15 +++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 Flip Tokens/Flip Tokens.js create mode 100644 Flip Tokens/Help.txt create mode 100644 Flip Tokens/package.json diff --git a/Flip Tokens/Flip Tokens.js b/Flip Tokens/Flip Tokens.js new file mode 100644 index 0000000000..fc5b896604 --- /dev/null +++ b/Flip Tokens/Flip Tokens.js @@ -0,0 +1,18 @@ +/** + * If a player or GM uses the `!flip' command, all graphics they have selected + * will flip horizontally. Try creating a macro button for this and making it + * visible to all players! + */ +on('chat:message', function(msg) { + if(msg.type == 'api' && msg.selected && msg.content.indexOf('!flip') == 0) + { + var selectedObjs = msg.selected; + _.each(selectedObjs, function(obj) { + if(obj._type == 'graphic') + { + var token = getObj('graphic', obj._id); + token.set('fliph', !token.get('fliph')); + } + }); + } +}); \ No newline at end of file diff --git a/Flip Tokens/Help.txt b/Flip Tokens/Help.txt new file mode 100644 index 0000000000..e16c7ef33f --- /dev/null +++ b/Flip Tokens/Help.txt @@ -0,0 +1,3 @@ +## Flip Tokens + +Call `!flip` while selecting one or more graphic objects (tokens, cards), and they will flip horizontally. \ No newline at end of file diff --git a/Flip Tokens/package.json b/Flip Tokens/package.json new file mode 100644 index 0000000000..726f1fa581 --- /dev/null +++ b/Flip Tokens/package.json @@ -0,0 +1,15 @@ +{ + "name": "Flip Tokens", + "version": "1.0", + "description": "Flips selected graphics horizontally. Useful for games with side-view tokens, and for players who do not have access to the same context menu as GMs.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "message": "read", + "token": "write" + }, + "conflicts": [ + "none" + ] +} From f56fd0de31b9994e24b55efa1fb95a6cfc381656 Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:44 -0600 Subject: [PATCH 10/29] Added No Token Rotation --- No Token Rotation/Help.txt | 3 +++ No Token Rotation/No Token Rotation.js | 1 + No Token Rotation/package.json | 14 ++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 No Token Rotation/Help.txt create mode 100644 No Token Rotation/No Token Rotation.js create mode 100644 No Token Rotation/package.json diff --git a/No Token Rotation/Help.txt b/No Token Rotation/Help.txt new file mode 100644 index 0000000000..8f89b48954 --- /dev/null +++ b/No Token Rotation/Help.txt @@ -0,0 +1,3 @@ +## No Token Rotation + +Prevents tokens from being rotated by anyone (including the GM). Any tokens which were already rotated will remain so. \ No newline at end of file diff --git a/No Token Rotation/No Token Rotation.js b/No Token Rotation/No Token Rotation.js new file mode 100644 index 0000000000..01b15fffc6 --- /dev/null +++ b/No Token Rotation/No Token Rotation.js @@ -0,0 +1 @@ +on('change:graphic:rotation', function(obj, prev) { obj.set('rotation', 0); }); \ No newline at end of file diff --git a/No Token Rotation/package.json b/No Token Rotation/package.json new file mode 100644 index 0000000000..0da1b5e1b0 --- /dev/null +++ b/No Token Rotation/package.json @@ -0,0 +1,14 @@ +{ + "name": "No Token Rotation", + "version": "1.0", + "description": "Prevents tokens from being rotated by anyone.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "token": "write" + }, + "conflicts": [ + "none" + ] +} From 2b3b74f0260c534bee8b7027f6c29ec87578907c Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:52 -0600 Subject: [PATCH 11/29] Added Raise Count --- Raise Count/Help.txt | 13 ++++++++ Raise Count/Raise Count.js | 65 ++++++++++++++++++++++++++++++++++++++ Raise Count/package.json | 14 ++++++++ 3 files changed, 92 insertions(+) create mode 100644 Raise Count/Help.txt create mode 100644 Raise Count/Raise Count.js create mode 100644 Raise Count/package.json diff --git a/Raise Count/Help.txt b/Raise Count/Help.txt new file mode 100644 index 0000000000..d0f45e1b6e --- /dev/null +++ b/Raise Count/Help.txt @@ -0,0 +1,13 @@ +## Raise Count + +Counts the "raises" of a die roll for the _Savage Worlds_ game system. + +Use `!rc roll|target` to roll. _roll_ should be a dice expression (do **not** include `/r`, `/roll`, or inline roll brackets `[[]]`), white _target_ should be the target number of the roll. + +### Output Format + +You can change the formatting of the script's output message by altering the `raise_count.OUTPUT_FORMAT` string. In the string, `%1$s` will end up as an inline roll, `%2$s` will be _target_, and `%3$s` will be the number of raises that resulted from the roll. + +### Raise Size + +You can chage the size of raises by modifying `raise_count.RAISE_SIZE`. \ No newline at end of file diff --git a/Raise Count/Raise Count.js b/Raise Count/Raise Count.js new file mode 100644 index 0000000000..ac5d031468 --- /dev/null +++ b/Raise Count/Raise Count.js @@ -0,0 +1,65 @@ +var raise_count = raise_count || {}; + +// Raise increment; generalized in case there are cases where it isn't 4 +raise_count.RAISE_SIZE = 4; +// Output formatting. %1$s will be replaced with an inline roll. %2$s will be replaced by the user's target input. +// %3$s will be replaced by the number of raises resulting the from roll. change this string if you want the results +// to show up differently in chat. +raise_count.OUTPUT_FORMAT = 'Roll: %1$s, Target: %2$s, Raises: %3$s'; + +on('chat:message', function(msg) { + if(msg.type != 'api' || msg.content.indexOf('!rc ')) return; + + var roll = msg.content.substring(4, msg.content.indexOf('|')); + var target = msg.content.substring(msg.content.indexOf('|')+1); + + var sendAs = 'system'; + var character = findObjs({_type: 'character', name: msg.who})[0]; + if(character) sendAs = 'character|'+character.id; + else sendAs = 'player|'+msg.playerid; + + sendChat(sendAs, '[['+roll+']]', function(fmsg) { + var expression = fmsg[0].inlinerolls['1'].expression; + var total = fmsg[0].inlinerolls['1'].results.total; + var raises = Math.floor((total - target) / raise_count.RAISE_SIZE); + + var rollOut = ''; + rollOut += value+'+'; + } + rollOut = rollOut.substring(0,rollOut.length - 1)+')+'; + } + rollOut = rollOut.substr(0, rollOut.length - 1); + rollOut += '" class="a inlinerollresult showtip tipsy-n'; + rollOut += (crit&&fail?' importantroll':(crit?' fullcrit':(fail?' fullfail':'')))+'">'+total+''; + + var message = '/direct '+raise_count.sprintf(raise_count.OUTPUT_FORMAT, rollOut, target, raises); + sendChat(sendAs, message); + }); +}); + +/** + * Really really really super naive implementation of the sprintf function, + * which will only really work for this script. I should be ashamed for qriting it. + */ +raise_count.sprintf = function(format, arg1, arg2, arg3) +{ + var out = format.replace('%1$s', arg1); + out = out.replace('%2$s', arg2); + out = out.replace('%3$s', arg3); + return out; +}; \ No newline at end of file diff --git a/Raise Count/package.json b/Raise Count/package.json new file mode 100644 index 0000000000..f1ab14a18c --- /dev/null +++ b/Raise Count/package.json @@ -0,0 +1,14 @@ +{ + "name": "Raise Count", + "version": "1.0", + "description": "Counts raises for the Savage Worlds system.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "message": "write" + }, + "conflicts": [ + "none" + ] +} From 2acbcb8d4b576b1cc1d95a06933825119c5c8cce Mon Sep 17 00:00:00 2001 From: lithl Date: Wed, 7 Jan 2015 23:08:59 -0600 Subject: [PATCH 12/29] Added Store Commands --- Store Commands/Help.txt | 11 +++++++ Store Commands/Store Commands.js | 56 ++++++++++++++++++++++++++++++++ Store Commands/package.json | 14 ++++++++ 3 files changed, 81 insertions(+) create mode 100644 Store Commands/Help.txt create mode 100644 Store Commands/Store Commands.js create mode 100644 Store Commands/package.json diff --git a/Store Commands/Help.txt b/Store Commands/Help.txt new file mode 100644 index 0000000000..742b8e4d17 --- /dev/null +++ b/Store Commands/Help.txt @@ -0,0 +1,11 @@ +## Store Commands + +Use `!delay time` (where _time_ is some number of milliseconds) to set the default delay between commands. If `!delay` is not used, nor is the _time_ parameter passed to `!store`, the time delay will be 500ms (0.5s). + +Use `!store -time command` or `!store command` to store a command. The command stored can be anything, including commands that are normally GM-only such as /direct or /emas. This will not give you access to API commands whose scripts restrict their use to GMs. + +Use `!clearstore` to clear the series of stored commands. This is the only way to remove commands from the sequence, meaning you can also use this script to store a series of commands you want to use over and over. Note that command sequences do not persist between game sessions, but they are unique per-player. + +Use `!echostore` to see a list of the commands in your serquence. + +Use `!run` to run the commands. \ No newline at end of file diff --git a/Store Commands/Store Commands.js b/Store Commands/Store Commands.js new file mode 100644 index 0000000000..218dfba25f --- /dev/null +++ b/Store Commands/Store Commands.js @@ -0,0 +1,56 @@ +var store_commands = store_commands || {}; + +store_commands.list = {}; + +on('chat:message', function(msg) { + if(msg.type != 'api') return; + + var parts = msg.content.split(' '); + var command = parts.shift().substring(1); + var id = msg.playerid; + + if(!store_commands.list[id]) store_commands.list[id] = { cmds: [], delay: 500 }; + + switch(command) + { + case 'delay': + store_commands.list[id].delay = parseInt(parts[0], 10); + break; + case 'store': + var delay = 500; + if(parts[0].indexOf('-') == 0) + delay = parseInt(parts.shift().substring(1), 10); + else if(store_commands.list[id].delay) + delay = store_commands.list[id].delay; + + var obj = { text: parts.join(' '), delay: delay }; + store_commands.list[id].cmds.push(obj); + break; + case 'clearstore': + store_commands.list[id].cmds = []; + break; + case 'echostore': + for(var i = 0; i < store_commands.list[id].cmds.length; i++) + { + var obj = store_commands.list[id].cmds[i]; + sendChat('Store Commands.js', '/w ' + msg.who + ' {' + obj.delay + 'ms, ' + obj.text + '}'); + } + break; + case 'run': + var count = 0; + for(var i = 0; i < store_commands.list[id].cmds.length; i++) + { + var obj = store_commands.list[id].cmds[i]; + store_commands.echo(id, obj.text, count + obj.delay); + count += obj.delay; + } + break; + default: + break; + } +}); + +store_commands.echo = function(id, text, delay) +{ + setTimeout(function(){ sendChat('player|'+id, text); }, delay); +}; \ No newline at end of file diff --git a/Store Commands/package.json b/Store Commands/package.json new file mode 100644 index 0000000000..abe991feae --- /dev/null +++ b/Store Commands/package.json @@ -0,0 +1,14 @@ +{ + "name": "Store Commands", + "version": "1.0", + "description": "Stores a series of commands (potentially with delays between them) to be executed in sequence later. Opens up GM-only commands such as /direct and /emas to players.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": { + "message": "write" + }, + "conflicts": [ + "none" + ] +} From 26d6609f4ad027471d09c4915ef93fb5d2a68a95 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 00:15:53 -0600 Subject: [PATCH 13/29] Rename sendChat script, upgrade to v2 v2 is a utility function, rather than a partial script --- Interpreted sendChat/Help.txt | 3 ++ Interpreted sendChat/Interpreted sendChat.js | 34 +++++++++++++++++++ Interpreted sendChat/package.json | 16 +++++++++ sendChat as a Player or Character/Help.txt | 2 -- .../package.json | 14 -------- .../sendChat as a Player or Character.js | 11 ------ 6 files changed, 53 insertions(+), 27 deletions(-) create mode 100644 Interpreted sendChat/Help.txt create mode 100644 Interpreted sendChat/Interpreted sendChat.js create mode 100644 Interpreted sendChat/package.json delete mode 100644 sendChat as a Player or Character/Help.txt delete mode 100644 sendChat as a Player or Character/package.json delete mode 100644 sendChat as a Player or Character/sendChat as a Player or Character.js diff --git a/Interpreted sendChat/Help.txt b/Interpreted sendChat/Help.txt new file mode 100644 index 0000000000..33eaf9fa43 --- /dev/null +++ b/Interpreted sendChat/Help.txt @@ -0,0 +1,3 @@ +## Interpreted sendChat + +Provides a function for other scripts to use to assist in sending messages to the chat. This script is not intended to stand alone. \ No newline at end of file diff --git a/Interpreted sendChat/Interpreted sendChat.js b/Interpreted sendChat/Interpreted sendChat.js new file mode 100644 index 0000000000..af3fbddb84 --- /dev/null +++ b/Interpreted sendChat/Interpreted sendChat.js @@ -0,0 +1,34 @@ +/** + * Sends a message to the chat as the same person who triggered a chat:message + * event. In other words, if you're speaking out of character, the message will + * be sent as you, and if you're speaking in-character, the message will be sent + * as the character you have selected. + * + * Useful for sending messages on behalf of the player/character in response to + * an API command. + * + * Example: + +on('chat:message', function(msg) { + if (msg.type === 'api') { + bshields.sendChat(msg, 'Hello World'); + } +}); + + */ +var bshields = bshields || {}; +bshields.sendChat = (function() { + 'use strict'; + + var version = 2.0; + + function interpretedSendChat(chatMsg, message) { + var who = chatMsg.who, + speaking = _.sortBy(filterObjs(function(obj) { return obj.get('type') === 'character' && obj.get('name').indexOf(who) >= 0; }), + function(chr) { return Math.abs(bshields.levenshteinDistance(chr.get('name'), who)); })[0]; + + sendChat(speaking ? 'character|' + speaking.id : 'player|' + chatMsg.playerid, message); + } + + return interpretedSendChat; +}()); \ No newline at end of file diff --git a/Interpreted sendChat/package.json b/Interpreted sendChat/package.json new file mode 100644 index 0000000000..5791cb8473 --- /dev/null +++ b/Interpreted sendChat/package.json @@ -0,0 +1,16 @@ +{ + "name": "Interpreted sendChat", + "version": "2.0", + "description": "Provides a function for other scripts to use to assist in sending messages to the chat. This script is not intended to stand alone.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": { + levenshteinDistance: "1.0" + }, + "modifies": { + "message": "write" + }, + "conflicts": [ + "none" + ] +} diff --git a/sendChat as a Player or Character/Help.txt b/sendChat as a Player or Character/Help.txt deleted file mode 100644 index d8887a7345..0000000000 --- a/sendChat as a Player or Character/Help.txt +++ /dev/null @@ -1,2 +0,0 @@ -sendChat as a Player or Character (Contributed by Brian Shields) -This is a few lines you can use in a chat:message event script to ensure you're sending a message with sendChat accurately as either a Player or a Character, depending on who triggered the event. \ No newline at end of file diff --git a/sendChat as a Player or Character/package.json b/sendChat as a Player or Character/package.json deleted file mode 100644 index f4350c75db..0000000000 --- a/sendChat as a Player or Character/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "sendChat as a Player or Character", - "version": "1.1", - "description": "This is a few lines you can use in a chat:message event script to ensure you're sending a message with sendChat accurately as either a Player or a Character, depending on who triggered the event.", - "authors": "Brian Shields", - "roll20userid": "235259", - "dependencies": {}, - "modifies": { - "message": "read" - }, - "conflicts": [ - "none" - ] -} diff --git a/sendChat as a Player or Character/sendChat as a Player or Character.js b/sendChat as a Player or Character/sendChat as a Player or Character.js deleted file mode 100644 index 827847028e..0000000000 --- a/sendChat as a Player or Character/sendChat as a Player or Character.js +++ /dev/null @@ -1,11 +0,0 @@ -on("chat:message", function(msg) { - var message = ''; - // Determine the contents of `message' - - var characters = findObjs({_type: 'character'}); - var speaking; - characters.forEach(function(chr) { if(chr.get('name') == msg.who) speaking = chr; }); - - if(speaking) sendChat('character|'+speaking.id, message); - else sendChat('player|'+msg.playerid, message); -}); \ No newline at end of file From a49a17e60b5ce1baf11b6ebe306f768338d8bc96 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 00:16:06 -0600 Subject: [PATCH 14/29] Add levenshteinDistance function --- levenshteinDistance/Help.txt | 3 ++ levenshteinDistance/levenshteinDistance.js | 50 ++++++++++++++++++++++ levenshteinDistance/package.json | 12 ++++++ 3 files changed, 65 insertions(+) create mode 100644 levenshteinDistance/Help.txt create mode 100644 levenshteinDistance/levenshteinDistance.js create mode 100644 levenshteinDistance/package.json diff --git a/levenshteinDistance/Help.txt b/levenshteinDistance/Help.txt new file mode 100644 index 0000000000..2fb394ac0e --- /dev/null +++ b/levenshteinDistance/Help.txt @@ -0,0 +1,3 @@ +## levenshteinDistance + +Provides a levenshteinDistance function for comparing strings. This script is not intended to stand alone. \ No newline at end of file diff --git a/levenshteinDistance/levenshteinDistance.js b/levenshteinDistance/levenshteinDistance.js new file mode 100644 index 0000000000..97b1ce6ceb --- /dev/null +++ b/levenshteinDistance/levenshteinDistance.js @@ -0,0 +1,50 @@ +/** + * Compares two strings and returns the number of changes (substitutions, + * insertions, and deletions) required to move from the first string to the + * second. + */ +var bshields = bshields || {}; +bshields.levenshteinDistance = (function() { + 'use strict'; + + var version = 1.0; + + function levenshteinDistance(a, b) { + var i, j, + matrix = []; + + if (a.length === 0) { + return b.length; + } + if (b.length === 0) { + return a.length; + } + + // Increment along the first column of each row + for (i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + // Increment each column in the first row + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + // Fill in the rest of the matrix + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // Substitution + Math.min(matrix[i][j - 1] + 1, // Insertion + matrix[i - 1][j] + 1)); // Deletion + } + } + } + + return matrix[b.length][a.length]; + } + + return levenshteinDistance; +}()); \ No newline at end of file diff --git a/levenshteinDistance/package.json b/levenshteinDistance/package.json new file mode 100644 index 0000000000..70fc0698c0 --- /dev/null +++ b/levenshteinDistance/package.json @@ -0,0 +1,12 @@ +{ + "name": "levenshteinDistance", + "version": "1.0", + "description": "Provides a levenshteinDistance function for comparing strings. This script is not intended to stand alone.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": {}, + "conflicts": [ + "none" + ] +} From 0180e7e382850a58b750544676ebabaacb26132a Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 00:16:13 -0600 Subject: [PATCH 15/29] Add splitArgs function --- splitArgs/Help.txt | 3 ++ splitArgs/package.json | 12 +++++++ splitArgs/splitArgs.js | 71 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 splitArgs/Help.txt create mode 100644 splitArgs/package.json create mode 100644 splitArgs/splitArgs.js diff --git a/splitArgs/Help.txt b/splitArgs/Help.txt new file mode 100644 index 0000000000..2401506a39 --- /dev/null +++ b/splitArgs/Help.txt @@ -0,0 +1,3 @@ +## splitArgs + +Provides a function for splitting arguments of an API command. This script is not intended to stand alone. \ No newline at end of file diff --git a/splitArgs/package.json b/splitArgs/package.json new file mode 100644 index 0000000000..1444e89988 --- /dev/null +++ b/splitArgs/package.json @@ -0,0 +1,12 @@ +{ + "name": "splitArgs", + "version": "1.0", + "description": "Provides a function for splitting arguments of an API command. This script is not intended to stand alone.", + "authors": "Brian Shields", + "roll20userid": "235259", + "dependencies": {}, + "modifies": {}, + "conflicts": [ + "none" + ] +} diff --git a/splitArgs/splitArgs.js b/splitArgs/splitArgs.js new file mode 100644 index 0000000000..0b7b646b1e --- /dev/null +++ b/splitArgs/splitArgs.js @@ -0,0 +1,71 @@ +/** + * Splits a string into arguments using some separator. If no separator is + * given, whitespace will be used. Most importantly, quotes in the original + * string will allow you to group delimited tokens. Single and double quotes + * can be nested one level. + * + * Example: + +on('chat:message', function(msg) { + var command, params; + + params = bshields.splitArgs(msg.content); + command = params.shift().substring(1); + + // msg.content: !command with parameters, "including 'with quotes'" + // command: command + // params: ["with", "parameters,", "including 'with quotes'"] +}); + */ +var bshields = bshields || {}; +bshields.splitArgs = (function() { + 'use strict'; + + var version = 1.0; + + function splitArgs(input, separator) { + var singleQuoteOpen = false, + doubleQuoteOpen = false, + tokenBuffer = [], + ret = [], + arr = input.split(''), + element, i, matches; + separator = separator || /\s/g; + + for (i = 0; i < arr.length; i++) { + element = arr[i]; + matches = element.match(separator); + if (element === '\'') { + if (!doubleQuoteOpen) { + singleQuoteOpen = !singleQuoteOpen; + continue; + } + } else if (element === '"') { + if (!singleQuoteOpen) { + doubleQuoteOpen = !doubleQuoteOpen; + continue; + } + } + + if (!singleQuoteOpen && !doubleQuoteOpen) { + if (matches) { + if (tokenBuffer && tokenBuffer.length > 0) { + ret.push(tokenBuffer.join('')); + tokenBuffer = []; + } + } else { + tokenBuffer.push(element); + } + } else if (singleQuoteOpen || doubleQuoteOpen) { + tokenBuffer.push(element); + } + } + if (tokenBuffer && tokenBuffer.length > 0) { + ret.push(tokenBuffer.join('')); + } + + return ret; + } + + return splitArgs; +}()); \ No newline at end of file From 67dd13e704da053ed720effe643f3794aad4b0dd Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 01:09:47 -0600 Subject: [PATCH 16/29] Update collision detection to v2 v2 is mostly just a refactor --- Collision Detection/Collision Detection.js | 300 ++++++++++++--------- Collision Detection/Help.txt | 6 +- Collision Detection/package.json | 2 +- 3 files changed, 171 insertions(+), 137 deletions(-) diff --git a/Collision Detection/Collision Detection.js b/Collision Detection/Collision Detection.js index 8aac327174..ab0f28dc8c 100644 --- a/Collision Detection/Collision Detection.js +++ b/Collision Detection/Collision Detection.js @@ -1,150 +1,184 @@ -var coldtc = coldtc || {}; - -coldtc.polygonPaths = []; -coldtc.DONT_MOVE = 1; -coldtc.WARN_PLAYER = 2; -coldtc.STOP_AT_WALL = 4; - -/*** SCRIPT SETTINGS ***/ -coldtc.pathColor = '#ff00ff'; // Only paths with this color will be used for collisions -coldtc.layer = 'walls'; // Only paths on this layer will be used for collisions; set to 'all' to use all layers -coldtc.behavior = coldtc.STOP_AT_WALL|coldtc.WARN_PLAYER; // behavior for collision events - -on('add:path', function(obj) { - if(obj.get('pageid') != Campaign().get('playerpageid') - || obj.get('stroke').toLowerCase() != coldtc.pathColor) return; - if(coldtc.layer != 'all' && obj.get('layer') != coldtc.layer) return; +/** + * Executes configured behavior when a player-controlled token moves and + * intersects a path of the configured color on the appropriate layer(s). + * + * behaviors: + * DONT_MOVE: token is returned to its starting location + * WARN_PLAYER: player is sent a message warning against the movement + * STOP_AT_WALL: token is moved to the edge of the path + * + * Set the pathColor as a hexadecimal color; only paths of the given color will + * be considered for collision events. + * + * Set the layer to the layer used for collision events. Valid values are "map", + * "objects", "gmlayer", and "walls". You may also use "all" to use paths on + * all layers. + * + * Set the behavior to the combination of behaviors you want to use. Combine + * behaviors with bitwise OR (|) like so: + * behaviors.STOP_AT_WALL | behaviors.WARN_PLAYER + * + * The STOP_AT_WALL and DONT_MOVE behaviors are incompatible with one another. + */ +var bshields = bshields || {}; +bshields.Collision = (function() { + 'use strict'; - var path = JSON.parse(obj.get('path')); - if(path.length > 1 && path[1][0] != 'L') return; // Add fushcia paths on current page's gm layer - coldtc.polygonPaths.push(obj); -}); - -on('destroy:path', function(obj) { - for(var i = 0; i < coldtc.polygonPaths.length; i++) - { - if(coldtc.polygonPaths[i].id == obj.id) - { - coldtc.polygonPaths = coldtc.polygonPaths.splice(i, 1); // Delete path if they're the same - break; - } + var version = 2.0, + polygonPaths = [], + behaviors = { + DONT_MOVE: 1, + WARN_PLAYER: 2, + STOP_AT_WALL: 4 + }, + config = { + pathColor: '#ff00ff', + layer: 'walls', + behavior: behaviors.STOP_AT_WALL | behaviors.WARN_PLAYER + }; + + function addPath(obj) { + var path; + + if (obj.get('pageid') !== Campaign().get('playerpageid') || + obj.get('stroke').toLowerCase() !== config.pathColor || + (config.layer !== 'all' && obj.get('layer') !== config.layer)) { return; } + + path = JSON.parse(obj.get('path')); + if (path.length > 1 && path[1][0] !== 'L') { return; } + polygonPaths.push(obj); } -}); - -on('change:path', function(obj, prev) { - if(coldtc.layer == 'all') return; // changing path layer doesn't matter - if(obj.get('layer') == coldtc.layer && prev.layer != coldtc.layer) // May need to add to list - { - if(obj.get('pageid') != Campaign().get('playerpageid') - || obj.get('stroke').toLowerCase() != coldtc.pathColor) return; - var path = JSON.parse(obj.get('path')); - if(path.length > 1 && path[1][0] != 'L') return; - coldtc.polygonPaths.push(obj); + function destroyPath(obj) { + polygonPaths = _.reject(polygonPaths, function(path) { return path.id === obj.id; }); } - if(obj.get('layer') != coldtc.layer && prev.layer == coldtc.layer) // May need to remove from list - { - for(var i = 0; i < coldtc.polygonPaths.length; i++) - { - if(coldtc.polygonPaths[i].id == obj.id) - { - coldtc.polygonPaths = coldtc.polygonPaths.splice(i, 1); - break; - } + + function changePath(obj, prev) { + var path; + + if (config.layer === 'all') { return; } + + if (obj.get('layer') === config.layer && prev.layer !== config.layer) { + if (obj.get('pageid') !== Campaign().get('playerpageid') || + obj.get('stroke').toLowerCase() !== config.pathColor) { return; } + + path = JSON.parse(obj.get('path')); + if (path.length > 1 && path[1][0] !== 'L') { return; } + polygonPaths.push(obj); + } + + if (obj.get('layer') !== config.layer && prev.layer === config.layer) { + polygonPaths = _.reject(polygonPaths, function(path) { return path.id === obj.id; }); } } -}); - -on('change:graphic', function(obj, prev) { - if(obj.get('subtype') != 'token' - || (obj.get('top') == prev.top && obj.get('left') == prev.left)) return; - if(obj.get('represents') != '') - { - var character = getObj('character', obj.get('represents')); - if(character.get('controlledby') == '') return; // GM-only token - } - else if(obj.get('controlledby') == '') return; // GM-only token - var l1 = coldtc.L(coldtc.P(prev.left, prev.top), coldtc.P(obj.get('left'), obj.get('top'))); - _.each(coldtc.polygonPaths, function(path) { - var pointA, pointB; - var x = path.get('left') - path.get('width') / 2; - var y = path.get('top') - path.get('height') / 2; - var parts = JSON.parse(path.get('path')); - pointA = coldtc.P(parts[0][1] + x, parts[0][2] + y); - parts.shift(); - _.each(parts, function(pt) { - pointB = coldtc.P(pt[1] + x, pt[2] + y); - var l2 = coldtc.L(pointA, pointB); - var denom = (l1.p1.x - l1.p2.x) * (l2.p1.y - l2.p2.y) - (l1.p1.y - l1.p2.y) * (l2.p1.x - l2.p2.x); - if(denom != 0) // Parallel - { - var intersect = coldtc.P( - (l1.p1.x*l1.p2.y-l1.p1.y*l1.p2.x)*(l2.p1.x-l2.p2.x)-(l1.p1.x-l1.p2.x)*(l2.p1.x*l2.p2.y-l2.p1.y*l2.p2.x), - (l1.p1.x*l1.p2.y-l1.p1.y*l1.p2.x)*(l2.p1.y-l2.p2.y)-(l1.p1.y-l1.p2.y)*(l2.p1.x*l2.p2.y-l2.p1.y*l2.p2.x) - ); - intersect.x /= denom; - intersect.y /= denom; + function changeGraphic(obj, prev) { + var character, l1 = L(P(prev.left, prev.top), P(obj.get('left'), obj.get('top'))); + + if (obj.get('subtype') !== 'token' || + (obj.get('top') === prev.top && obj.get('left') === prev.left)) { return; } + + if (obj.get('represents') !== '') { + character = getObj('character', obj.get('represents')); + if (character.get('controlledby') === '') { return; } // GM-only character + } else if (obj.get('controlledby') === '') { return; } // GM-only token + + _.each(polygonPaths, function(path) { + var x = path.get('left') - path.get('width') / 2, + y = path.get('top') - path.get('height') / 2, + parts = JSON.parse(path.get('path')), + pointA = P(parts[0][1] + x, parts[0][2] + y); + parts.shift(); + _.each(parts, function(pt) { + var pointB = P(pt[1] + x, pt[2] + y), + l2 = L(pointA, pointB), + denom = (l1.p1.x - l1.p2.x) * (l2.p1.y - l2.p2.y) - (l1.p1.y - l1.p2.y) * (l2.p1.x - l2.p2.x), + intersect, who, player, vec, norm; - if(coldtc.isBetween(pointA, pointB, intersect) - && coldtc.isBetween(l1.p1, l1.p2, intersect)) - { - // Collision event! - if((coldtc.behavior&coldtc.DONT_MOVE) == coldtc.DONT_MOVE) - { - obj.set({ - left: Math.round(l1.p1.x), - top: Math.round(l1.p1.y) - }); - } - if((coldtc.behavior&coldtc.WARN_PLAYER) == coldtc.WARN_PLAYER) - { - var who; - if(obj.get('represents')) - { - var character = getObj('character', obj.get('represents')); - who = character.get('name'); + if (denom !== 0) { + intersect = P( + (l1.p1.x * l1.p2.y - l1.p1.y * l1.p2.x) * (l2.p1.x - l2.p2.x) - (l1.p1.x - l1.p2.x) * (l2.p1.x * l2.p2.y - l2.p1.y * l2.p2.x), + (l1.p1.x * l1.p2.y - l1.p1.y * l1.p2.x) * (l2.p1.y - l2.p2.y) - (l1.p1.y - l1.p2.y) * (l2.p1.x * l2.p2.y - l2.p1.y * l2.p2.x) + ); + intersect.x /= denom; + intersect.y /= denom; + + if (isBetween(pointA, pointB, intersect) && + isBetween(l1.p1, l1.p2, intersect)) { + // Collision event! + if ((config.behavior & behaviors.DONT_MOVE) === behaviors.DONT_MOVE) { + obj.set({ + left: Math.round(l1.p1.x), + top: Math.round(l1.p1.y) + }); } - else - { - var controlledby = obj.get('controlledby'); - if(controlledby == 'all') who = 'all'; - else - { - var player = getObj('player', controlledby); + + if ((config.behavior & behaviors.WARN_PLAYER) === behaviors.WARN_PLAYER) { + if (obj.get('represents')) { + character = getObj('character', obj.get('represents')); + who = character.get('name'); + } else if (obj.get('controlledby') === 'all') { + who = 'all'; + } else { + player = getObj('player', obj.get('controlledby')); who = player.get('displayname'); } + + if (who !== 'all') { + who = who.indexOf(' ') > 0 ? who.substring(0, who.indexOf(' ')) : who; + sendChat('System', '/w ' + who + ' You are not permitted to move that token into that area.'); + } else { + sendChat('System', 'Token ' + obj.get('name') + ' is not permitted in that area.'); + } } - who = who.indexOf(' ') > 0 ? who.substring(0, who.indexOf(' ')) : who; - if(who != 'all') - sendChat('SYSTEM', '/w '+who+' You are not permitted to move that token into that area.'); - else - sendChat('SYSTEM', 'Token '+obj.get('name')+' is not permitted in that area.'); - } - if((coldtc.behavior&coldtc.STOP_AT_WALL) == coldtc.STOP_AT_WALL) - { - var vec = coldtc.P(l1.p2.x - l1.p1.x, l1.p2.y - l1.p1.y); - var norm = Math.sqrt(vec.x * vec.x + vec.y * vec.y); - vec.x /= norm; - vec.y /= norm; - obj.set({ - left: intersect.x - vec.x, - top: intersect.y - vec.y - }); + if ((config.behavior & behaviors.STOP_AT_WALL) === behaviors.STOP_AT_WALL) { + vec = P(l1.p2.x - l1.p1.x, l1.p2.y - l1.p1.y); + norm = Math.sqrt(vec.x * vec.x + vec.y * vec.y); + vec.x /= norm; + vec.y /= norm; + + obj.set({ + left: intersect.x - vec.x, + top: intersect.y - vec.y + }); + } } } - } - - pointA = coldtc.P(pointB.x, pointB.y); + + pointA = P(pointB.x, pointB.y); + }); }); - }); -}); + } + + function P(x, y) { return { x: x, y: y}; } + function L(p1, p2) { return { p1: p1, p2: p2 }; } + + function isBetween(a, b, c) { + var withinX = (a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x), + withinY = (a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y); + return withinX && withinY; + } + + function registerEventHandlersBeforeReady() { + on('add:path', addPath); + } + + function registerEventHandlers() { + on('destroy:path', destroyPath); + on('change:path', changePath); + on('change:graphic', changeGraphic); + } + + return { + registerPreloadEventHandlers: registerEventHandlersBeforeReady, + registerPostloadEventHandlers: registerEventHandlers + }; +}()); -coldtc.P = function(x, y) { return {x: x, y: y}; }; -coldtc.L = function(p1, p2) { return {p1: p1, p2: p2}; }; -coldtc.isBetween = function(a, b, c) { - var withinX = (a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x); - var withinY = (a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y); - return withinX && withinY; -}; \ No newline at end of file +bshields.Collision.registerPreloadEventHandlers(); +on('ready', function() { + 'use strict'; + + bshields.Collision.registerPostloadEventHandlers(); +} \ No newline at end of file diff --git a/Collision Detection/Help.txt b/Collision Detection/Help.txt index 5972050970..2e5b71f1fa 100644 --- a/Collision Detection/Help.txt +++ b/Collision Detection/Help.txt @@ -2,6 +2,6 @@ There are three configuration options available to you: -* `coldtc.pathColor`: The script only considers paths of a specific color, allowing you to also use paths of other colors which your players will not collide with. By default, this is fuchsia (#ff00ff); the color is specified as a hexadecimal web color, which you can see when selecting a color from the drawing interface. A path's fill color is ignored. -* `coldtc.layer`: The script will only look at paths on the specified layer (valid values are "map", "objects", "gmlayer", or "walls"). You can also set this value to "all" and paths on every layer will be considered. -* `coldtc.behavior`: You can customize the script's behavior when a collision event is detected. \ No newline at end of file +* `config.pathColor`: The script only considers paths of a specific color, allowing you to also use paths of other colors which your players will not collide with. By default, this is fuchsia (#ff00ff); the color is specified as a hexadecimal web color, which you can see when selecting a color from the drawing interface. A path's fill color is ignored. +* `config.layer`: The script will only look at paths on the specified layer (valid values are "map", "objects", "gmlayer", or "walls"). You can also set this value to "all" and paths on every layer will be considered. +* `config.behavior`: You can customize the script's behavior when a collision event is detected. \ No newline at end of file diff --git a/Collision Detection/package.json b/Collision Detection/package.json index c8f72e92bd..3ed6326469 100644 --- a/Collision Detection/package.json +++ b/Collision Detection/package.json @@ -1,6 +1,6 @@ { "name": "Collision Detection", - "version": "1.0", + "version": "2.0", "description": "Watches for collisions between player-controlled tokens and a specified subset of path objects. When a collision is detected, the script may return the token to its position prior to the move, send a warning message to the token's controller, and/or halt the token at the path acting as a barrier to movement.", "authors": "Brian Shields", "roll20userid": "235259", From ca2373630ed6d684af5c15209daa230cb73fdf6b Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 01:50:08 -0600 Subject: [PATCH 17/29] Update DL animation to v2 v2 is mostly a refactor --- .../Dynamic Lighting Animation.js | 234 ++++++++++-------- Dynamic Lighting Animation/package.json | 7 +- 2 files changed, 141 insertions(+), 100 deletions(-) diff --git a/Dynamic Lighting Animation/Dynamic Lighting Animation.js b/Dynamic Lighting Animation/Dynamic Lighting Animation.js index babd947085..0aa1b9e1b0 100644 --- a/Dynamic Lighting Animation/Dynamic Lighting Animation.js +++ b/Dynamic Lighting Animation/Dynamic Lighting Animation.js @@ -1,107 +1,145 @@ -var animation = animation || {}; - -// Set to a list of the user IDs for each GM -- only the GM should be able to stop/start the animation! -// You can find your user ID by checking your userpage on the wiki, or the URL of your profile. -animation.gmIDs = [235259]; - -animation.running = false; - -on('chat:message', function(msg) { - if(msg.type != 'api') return; - - // Only use commands coming from a GM - var isGM = false; - var player = getObj('player', msg.playerid); - _.each(animation.gmIDs, function(n) { - if(player.get('d20userid') == n) - isGM = true; - }); - if(!isGM) return; +/** + * Create snapshot of the Dynamic Lighting layer and transition between them on + * a timer, creating an animation. + * + * `!snapshot frames` will capture the current state of the Dynamic Lighting layer and be ready to animate it for frames/20 seconds. + * `!reset` will clear the animation buffer + * `!run` and `!stop` will predictably start and stop the animation sequence. + */ +var bshields = bshields || {}; +bshields.animation = (function() { + 'use strict'; - var parts = msg.content.split(' '); - var command = parts.shift().substring(1); + var version = 2.0, + running = false, + commands = { + snapshot: function(args, msg) { + var player = getObj('player', msg.playerid), + pageid = Campaign().get('playerpageid'), + dlPaths = findObjs({ type: 'path', pageid: pageid, layer: 'walls' }), + frames = parseInt(args[0], 10), + pathdata = []; + + if (running) { + sendChat('System', '/w ' + player.get('displayname') + ' You cannot add a frame while the animation is running.'); + return; + } + + _.each(dlPaths, function(path) { + var obj = { + id: path.id, + top: path.get('top'), + left: path.get('left'), + rotation: path.get('rotation'), + width: path.get('width'), + height: path.get('height'), + scaleX: path.get('scaleX'), + scaleY: path.get('scaleY') + }; + pathdata.push(obj); + }); + + state.bshields.animation.frames.push({ data: pathdata, frames: frames }); + }, + reset: function(args, msg) { + state.bshields.animation.currentFrame = 0; + state.bshields.animation.frames = []; + }, + run: function(args, msg) { running = true; }, + stop: function(args, msg) { running = false; } + }; - switch(command) - { - case 'snapshot': - if(animation.running) - { - sendChat("","/w gm You cannot add a frame while the animation is running."); - break; - } + function handleInput(msg) { + var isApi = msg.type === 'api', + args = bshields.splitArgs(msg.content.trim()), + command, args0, isHelp; + + if (!isGM(msg.playerid)) { return; } + + if (isApi) { + command = args.shift().substring(1).toLowerCase(); + arg0 = args.shift(); + isHelp = arg0.toLowerCase() === 'help' || arg0.toLowerCase() === 'h'; - var pageid = Campaign().get('playerpageid'); - var dlPaths = findObjs({_type: 'path', _pageid: pageid, layer: 'walls'}); - var frames = parseInt(parts[0], 10); - var pathdata = []; + if (!isHelp) { + if (arg0 && arg0.length > 0) { + args.unshift(arg0); + } + + if (_.isFunction(commands[command])) { + commands[command](args, msg); + } + } else if (_.isFunction(commands.help)) { + commands.help(command, args, msg); + } + } else if (_.isFunction(commands['msg_' + msg.type])) { + commands['msg_' + msg.type](args, msg); + } + } + + function runAnimationCycle() { + var frame = state.bshields.animation.currentFrame, + frameCount = 0; + + setInterval(function() { + if (!running || !state.bshields.animation.frames[frame]) { return; } - _.each(dlPaths, function(path) { - var obj = { - id: path.id, - top: path.get('top'), - left: path.get('left'), - rotation: path.get('rotation'), - width: path.get('width'), - height: path.get('height'), - scaleX: path.get('scaleX'), - scaleY: path.get('scaleY') - }; - pathdata.push(obj); + frameCount++; + if (state.bshields.animation.frames[frame].frames <= frameCount) { + setupFrame(state.bshields.animation.frames[frame].data); + frameCount -= state.bshields.animation.frames[frame].frames; + frame++; + + if (frame === state.bshields.animation.frames.length) frame = 0; + state.bshields.animation.currentFrame = frame; + } + }, 50); + } + + function setupFrame(pathdata) { + _.each(pathdata, function(obj) { + var path = getObj('path', obj.id); + path.set({ + top: obj.top, + left: obj.left, + rotation: obj.rotation, + width: obj.width, + height: obj.height, + scaleX: obj.scaleX, + scaleY: obj.scaleY }); - state.animation.frames.push({ data: pathdata, frames: frames }); - break; - case 'reset': - state.animation.curr_frame = 0; - state.animation.frames = []; - break; - case 'run': - animation.running = true; - break; - case 'stop': - animation.running = false; - break; - default: - break; + }); } -}); - -on('ready', function() { - if(!state.animation) state.animation = {}; - if(!state.animation.frames) state.animation.frames = []; - if(!state.animation.curr_frame) state.animation.curr_frame = 0; - var frame = state.animation.curr_frame; - var frameCount = 0; - setInterval(function() { - if(!animation.running) return; - if(!state.animation.frames[frame]) return; - - frameCount++; - if(state.animation.frames[frame].frames <= frameCount) - { - animation.setupFrame(state.animation.frames[frame].data); - frameCount -= state.animation.frames[frame].frames; - frame++; - if(frame == state.animation.frames.length) frame = 0; - state.animation.curr_frame = frame; + function checkInstall() { + if (!state.bshields || + !state.bshields.animation || + !state.bshields.animation.version || + state.bshields.animation.version !== version) { + state.bshields = state.bshields || {}; + state.bshields.animation = { + version: version, + frames: [], + currentFrame: 0 + } } - }, 50); -}); + } + + function registerEventHandlers() { + on('chat:message', handleInput); + } + + return { + checkInstall: checkInstall, + registerEventHandlers: registerEventHandlers, + run: runAnimationCycle + }; +}()); -/** - * Set the paths on the DL layer to the settings for the current animation frame. - */ -animation.setupFrame = function(pathdata) { - _.each(pathdata, function(obj) { - var path = getObj('path', obj.id);log(obj); - path.set({ - top: obj.top, - left: obj.left, - rotation: obj.rotation, - width: obj.width, - height: obj.height, - scaleX: obj.scaleX, - scaleY: obj.scaleY - }); - }); -}; \ No newline at end of file +on('ready', function() { + 'use strict'; + + bshields.animation.checkInstall(); + bshields.animation.registerEventHandlers(); + bshields.animation.run(); +}); \ No newline at end of file diff --git a/Dynamic Lighting Animation/package.json b/Dynamic Lighting Animation/package.json index 0931045a34..1d3407270c 100644 --- a/Dynamic Lighting Animation/package.json +++ b/Dynamic Lighting Animation/package.json @@ -1,10 +1,13 @@ { "name": "Dynamic Lighting Animation", - "version": "1.0", + "version": "2.0", "description": "Animates paths on the Dynamic Lighting layer.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + "IsGM Auth Module": "1.1", + splitArgs: "1.0" + }, "modifies": { "path": "write", "message": "write" From 5181c8b7f4e2b78c26e57ded70c77c05a3665c07 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:05:31 -0600 Subject: [PATCH 18/29] Update Exalted successes to v2 v2 is mostly a refactor --- Exalted Successes/Exalted Successes.js | 110 ++++++++++++------------- Exalted Successes/package.json | 6 +- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/Exalted Successes/Exalted Successes.js b/Exalted Successes/Exalted Successes.js index fa4113582b..75ae708bdb 100644 --- a/Exalted Successes/Exalted Successes.js +++ b/Exalted Successes/Exalted Successes.js @@ -1,65 +1,59 @@ -var exalted = exalted || {}; - -exalted.sendChat = function(name, id, msg) -{ - var characters = findObjs({_type: 'character'}); - var speaking; - characters.forEach(function(chr) { if(chr.get('name') == name) speaking = chr; }); - if(speaking) sendChat('character|'+speaking.id, msg); - else sendChat('player|'+id, msg); -}; - -on('chat:message', function(msg) { - var json; - var inline = false; - try { json = JSON.parse(msg.content); } - catch(e) - { - if(msg.inlinerolls) inline = true; - else return; - } +var bshields = bshields || {}; +bshields.exalted = (function() { + 'use strict'; - var results = []; - if(!inline) - { - json.rolls.forEach(function(j) { - if(j.sides != 10) return; - results.push(j.results); - }); - } - else - { - json = msg.inlinerolls; - json.forEach(function(j) { - var rolls = j.results.rolls; - rolls.forEach(function(r) { - if(r.sides != 10) return; - results.push(r.results); + var version = 2.0; + + function handleInput(msg) { + var json = msg.type === 'rollresult' ? JSON.parse(msg.content) : null, + inline = !!msg.inlinerolls, + results = [], + successes = 0, + botches = 0; + + if (json) { + _.each(json.rolls, function(roll) { + if (roll.sides !== 10) { return; } + results.push(roll.results); + }); + } else if (inline) { + _.each(msg.inlinerolls, function(rolldata) { + _.each(rolldata.results.rolls, function(roll) { + if (roll.sides !== 10) { return; } + results.push(roll.results); + }); + }); + } + + _.each(results, function(roll) { + _.each(roll, function(die) { + var value = die['v']; + successes += value >= 7 ? 1 : 0; + successes += value === 10 ? 1 : 0; + botches += value === 1 ? 1 : 0; }); }); + + if (successes === 0 && botches > 0) { + bshields.sendChat(msg, botches + ' botch' + (botches > 1 ? 'es' : '')) + } else if (successes === 0) { + bshields.sendChat(msg, 'Failure'); + } else { + bshields.sendChat(msg, successes + ' success' + (successes > 1 ? 'es' : '')); + } } - var successes = 0; - var botches = 0; - results.forEach(function(r) { - r.forEach(function(d) { - var die = d['v']; - successes += die >= 7 ? 1 : 0 - successes += die == 10 ? 1 : 0; - botches += die == 1 ? 1 : 0; - }); - }); - - if(successes == 0 && botches != 0) - { - exalted.sendChat(msg.who, msg.playerid, botches+' botch'+(botches>1?'es':'')); - } - else if(successes == 0) - { - exalted.sendChat(msg.who, msg.playerid, 'Failure'); - } - else - { - exalted.sendChat(msg.who, msg.playerid, successes+' success'+(successes>1?'es':'')); + function registerEventHandlers() { + on('chat:message', handleInput); } + + return { + registerEventHandlers: registerEventHandlers + }; +}()); + +on('ready', function() { + 'use strict'; + + bshields.exalted.registerEventHandlers(); }); \ No newline at end of file diff --git a/Exalted Successes/package.json b/Exalted Successes/package.json index 0baef81040..401a493c76 100644 --- a/Exalted Successes/package.json +++ b/Exalted Successes/package.json @@ -1,10 +1,12 @@ { "name": "Exalted Successes", - "version": "1.0", + "version": "2.0", "description": "Reports successes on d10 rolls for use with the Exalted game system.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + "Interpreted sendChat": "1.0" + }, "modifies": { "message": "write" }, From 67f9176c31af099960d92a54c388ea45fe5081e1 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:16:53 -0600 Subject: [PATCH 19/29] Update flight to v3 v3 is mainly a refactor --- Flight/Flight.js | 109 ++++++++++++++++++++++++++++++++------------ Flight/package.json | 6 ++- 2 files changed, 84 insertions(+), 31 deletions(-) diff --git a/Flight/Flight.js b/Flight/Flight.js index 4423c98f00..e85c91f1c7 100644 --- a/Flight/Flight.js +++ b/Flight/Flight.js @@ -1,31 +1,82 @@ -on('chat:message', function(msg) { - if(msg.type != 'api') return; - var parts = msg.content.toLowerCase().split(' '); - var command = parts.shift().substring(1); - var selected = msg.selected; - if(command != 'fly' || !selected) return; // use the !fly command, and have one or more things selected - var height = +parts[0]; - if(!height) height = 0; // if no height is returned, treat as 0 - _.each(selected, function(obj) { - if(obj._type != 'graphic') return; // only fly graphics - var tok = getObj('graphic', obj._id); - if(tok.get('subtype') != 'token') return; // don't try to fly cards - tok.set('status_fluffy-wing', false); - var wings = ''; - while(height > 0) - { - // get current digit, starting from ones - var digit = height / 10; - digit -= Math.floor(digit); - digit = Math.round(digit * 10); - // shift height - height = Math.floor(height / 10); - wings += 'fluffy-wing@'+digit+','; +var bshields = bshields || {}; +bshields.flight = (function() { + 'use strict'; + + var version = 3.0, + commands = { + fly: function(args, msg) { + var selected = msg.selected, + height = parseInt(args[0], 10) || 0; + + if (!selected) { return; } + _.each(selected, function(obj) { + var token = getObj('graphic', obj._id), + wings = '', + digit, markers; + + if (obj._type !== 'graphic' || !token || token.get('subtype') !== 'token') { return; } + token.set('status_fluffy-wing', false); + while (height > 0) { + // Iterate over digits, from ones on up + digit = height / 10; + digit -= Math.floor(digit); + digit = Math.round(digit * 10); + + // Shift height + height = Math.floor(height / 10); + + wings += 'fluffy-wing@' + digit + ','; + } + + if (wings.length > 0) { + wings = wings.substring(0, wings.length - 1); + } + + markers = token.get('statusmarkers'); + if (markers !== '') markers += ','; + markers += wings; + token.set('statusmarkers', markers); + }); + } + }; + + function handleInput(msg) { + var isApi = msg.type === 'api', + args = bshields.splitArgs(msg.content.trim()), + command, args0, isHelp; + + if (isApi) { + command = args.shift().substring(1).toLowerCase(); + arg0 = args.shift(); + isHelp = arg0.toLowerCase() === 'help' || arg0.toLowerCase() === 'h'; + + if (!isHelp) { + if (arg0 && arg0.length > 0) { + args.unshift(arg0); + } + + if (_.isFunction(commands[command])) { + commands[command](args, msg); + } + } else if (_.isFunction(commands.help)) { + commands.help(command, args, msg); + } + } else if (_.isFunction(commands['msg_' + msg.type])) { + commands['msg_' + msg.type](args, msg); } - if(wings.length > 0) wings = wings.substring(0, wings.length - 1); // trim trailing comma - var markers = tok.get('statusmarkers'); - if(markers != '') markers += ','; - markers += wings; - tok.set('statusmarkers', markers); - }); + } + + function registerEventHandlers() { + on('chat:message', handleInput); + } + + return { + registerEventHandlers: registerEventHandlers + }; +}()); + +on('ready', function() { + 'use strict'; + + bshields.flight.registerEventHandlers(); }); \ No newline at end of file diff --git a/Flight/package.json b/Flight/package.json index 4ebd96433d..43bbf84629 100644 --- a/Flight/package.json +++ b/Flight/package.json @@ -1,10 +1,12 @@ { "name": "Flight", - "version": "2.0", + "version": "3.0", "description": "Adds 'fluffy-wings' status icon to selected token to represent some hieght.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + splitArgs: "1.0" + }, "modifies": { "message": "read", "token": "write" From 31f548db83b1cce9d55e0bc653cda6fae1b08ef7 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:26:59 -0600 Subject: [PATCH 20/29] Update flip token to v2 v2 is mostly a refactor, but also added vertical flip capability --- Flip Tokens/Flip Tokens.js | 82 +++++++++++++++++++++++++++++++++----- Flip Tokens/Help.txt | 4 +- Flip Tokens/package.json | 8 ++-- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/Flip Tokens/Flip Tokens.js b/Flip Tokens/Flip Tokens.js index fc5b896604..d45c29eff6 100644 --- a/Flip Tokens/Flip Tokens.js +++ b/Flip Tokens/Flip Tokens.js @@ -2,17 +2,79 @@ * If a player or GM uses the `!flip' command, all graphics they have selected * will flip horizontally. Try creating a macro button for this and making it * visible to all players! + * + * You can also add any number of parameters which are each either "vertical" or + * "horizontal" in order to flip the selected graphic in the specified direction + * in sequence. */ -on('chat:message', function(msg) { - if(msg.type == 'api' && msg.selected && msg.content.indexOf('!flip') == 0) - { - var selectedObjs = msg.selected; - _.each(selectedObjs, function(obj) { - if(obj._type == 'graphic') - { - var token = getObj('graphic', obj._id); - token.set('fliph', !token.get('fliph')); +var bshields = bshields || {}; +bshields.flip = (function() { + 'use strict'; + + var version = 2.0, + commands = { + flip: function(args, msg) { + var selected = msg.selected; + + if (!selected) { return; } + + _.each(selected, function(obj) { + var token = getObj('graphic', obj._id); + + if (token) { + if (args.length === 0) { + token.set('fliph', !token.get('fliph')); + } else { + _.each(args, function(arg) { + if (arg.toLowerCase() === 'horizontal') { + token.set('fliph', !token.get('fliph')); + } else if (arg.toLowerCase() === 'vertical') { + token.set('flipv', !token.get('flipv')); + } + }); + } + } + }); } - }); + }; + + function handleInput(msg) { + var isApi = msg.type === 'api', + args = bshields.splitArgs(msg.content.trim()), + command, args0, isHelp; + + if (isApi) { + command = args.shift().substring(1).toLowerCase(); + arg0 = args.shift(); + isHelp = arg0.toLowerCase() === 'help' || arg0.toLowerCase() === 'h'; + + if (!isHelp) { + if (arg0 && arg0.length > 0) { + args.unshift(arg0); + } + + if (_.isFunction(commands[command])) { + commands[command](args, msg); + } + } else if (_.isFunction(commands.help)) { + commands.help(command, args, msg); + } + } else if (_.isFunction(commands['msg_' + msg.type])) { + commands['msg_' + msg.type](args, msg); + } + } + + function registerEventHandlers() { + on('chat:message', handleInput); } + + return { + registerEventHandlers: registerEventHandlers + }; +}()); + +on('ready', function() { + 'use strict'; + + bshields.flip.registerEventHandlers(); }); \ No newline at end of file diff --git a/Flip Tokens/Help.txt b/Flip Tokens/Help.txt index e16c7ef33f..7452e7a08c 100644 --- a/Flip Tokens/Help.txt +++ b/Flip Tokens/Help.txt @@ -1,3 +1,5 @@ ## Flip Tokens -Call `!flip` while selecting one or more graphic objects (tokens, cards), and they will flip horizontally. \ No newline at end of file +Call `!flip` while selecting one or more graphic objects (tokens, cards), and they will flip horizontally. + +You can add any number of "horizontal" and "vertical" parameters to `!flip` in order to flip in the specified direction multiple times in sequence. \ No newline at end of file diff --git a/Flip Tokens/package.json b/Flip Tokens/package.json index 726f1fa581..9161b66cb1 100644 --- a/Flip Tokens/package.json +++ b/Flip Tokens/package.json @@ -1,10 +1,12 @@ { "name": "Flip Tokens", - "version": "1.0", - "description": "Flips selected graphics horizontally. Useful for games with side-view tokens, and for players who do not have access to the same context menu as GMs.", + "version": "2.0", + "description": "Flips selected graphics horizontally and/or vertically. Especially useful for games with side-view tokens, and for players who do not have access to the same context menu as GMs.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + splitArgs: "1.0" + }, "modifies": { "message": "read", "token": "write" From 33572054de4825f79514071eab42f953dfa751ea Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:29:33 -0600 Subject: [PATCH 21/29] Update no rotation to v1.1 Not bothering with a full revealing module pattern for something so simple. However, updated script to return object to previous orientation rather than returning object to 0, in case object was already rotated. --- No Token Rotation/No Token Rotation.js | 2 +- No Token Rotation/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/No Token Rotation/No Token Rotation.js b/No Token Rotation/No Token Rotation.js index 01b15fffc6..3233ea2641 100644 --- a/No Token Rotation/No Token Rotation.js +++ b/No Token Rotation/No Token Rotation.js @@ -1 +1 @@ -on('change:graphic:rotation', function(obj, prev) { obj.set('rotation', 0); }); \ No newline at end of file +on('change:graphic:rotation', function(obj, prev) { obj.set('rotation', prev.rotation); }); \ No newline at end of file diff --git a/No Token Rotation/package.json b/No Token Rotation/package.json index 0da1b5e1b0..6a81f133d3 100644 --- a/No Token Rotation/package.json +++ b/No Token Rotation/package.json @@ -1,6 +1,6 @@ { "name": "No Token Rotation", - "version": "1.0", + "version": "1.1", "description": "Prevents tokens from being rotated by anyone.", "authors": "Brian Shields", "roll20userid": "235259", From 8eb6f4ee43ac1e7469a5fa537f86fc6158a39642 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:51:31 -0600 Subject: [PATCH 22/29] Fixed dependency version --- Exalted Successes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Exalted Successes/package.json b/Exalted Successes/package.json index 401a493c76..0868374e01 100644 --- a/Exalted Successes/package.json +++ b/Exalted Successes/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - "Interpreted sendChat": "1.0" + "Interpreted sendChat": "2.0" }, "modifies": { "message": "write" From 6cf95239ce8f2987a375cd9d38d7d55fca498d51 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:51:58 -0600 Subject: [PATCH 23/29] Update raise count to v2 v2 is mainly a refactor --- Raise Count/Help.txt | 6 +- Raise Count/Raise Count.js | 143 ++++++++++++++++++++++--------------- Raise Count/package.json | 7 +- 3 files changed, 94 insertions(+), 62 deletions(-) diff --git a/Raise Count/Help.txt b/Raise Count/Help.txt index d0f45e1b6e..15819318b7 100644 --- a/Raise Count/Help.txt +++ b/Raise Count/Help.txt @@ -2,12 +2,12 @@ Counts the "raises" of a die roll for the _Savage Worlds_ game system. -Use `!rc roll|target` to roll. _roll_ should be a dice expression (do **not** include `/r`, `/roll`, or inline roll brackets `[[]]`), white _target_ should be the target number of the roll. +Use `!rc roll target` to roll. _roll_ should be a dice expression (do **not** include `/r`, `/roll`, or inline roll brackets `[[]]`), white _target_ should be the target number of the roll. ### Output Format -You can change the formatting of the script's output message by altering the `raise_count.OUTPUT_FORMAT` string. In the string, `%1$s` will end up as an inline roll, `%2$s` will be _target_, and `%3$s` will be the number of raises that resulted from the roll. +You can change the formatting of the script's output message by altering the `config.outputFormat` string. In the string, `{0}` will end up as an inline roll, `{1}` will be _target_, and `{2}` will be the number of raises that resulted from the roll. ### Raise Size -You can chage the size of raises by modifying `raise_count.RAISE_SIZE`. \ No newline at end of file +You can chage the size of raises by modifying `config.raiseSize`. \ No newline at end of file diff --git a/Raise Count/Raise Count.js b/Raise Count/Raise Count.js index ac5d031468..fd78e170df 100644 --- a/Raise Count/Raise Count.js +++ b/Raise Count/Raise Count.js @@ -1,65 +1,94 @@ -var raise_count = raise_count || {}; - -// Raise increment; generalized in case there are cases where it isn't 4 -raise_count.RAISE_SIZE = 4; -// Output formatting. %1$s will be replaced with an inline roll. %2$s will be replaced by the user's target input. -// %3$s will be replaced by the number of raises resulting the from roll. change this string if you want the results -// to show up differently in chat. -raise_count.OUTPUT_FORMAT = 'Roll: %1$s, Target: %2$s, Raises: %3$s'; - -on('chat:message', function(msg) { - if(msg.type != 'api' || msg.content.indexOf('!rc ')) return; +var bshields = bshields || {}; +bshields.raiseCount = (function() { + 'use strict'; - var roll = msg.content.substring(4, msg.content.indexOf('|')); - var target = msg.content.substring(msg.content.indexOf('|')+1); + var version = 2.0, + config = { + raiseSize: 4, + outputFormat: 'Roll: {0}, Target: {1}, Raises: {2}' + }, + commands = { + rc: function(args, msg) { + var target = parseInt(args.pop(), 10), + roll = args.join(' '); + + sendChat('', '[[' + roll + ']]', function(ops) { + var expression = ops[0].inlinerolls[1].expression, + total = ops[0].inlinerolls[1].results.total, + raises = Math.floor((total - target) / config.raiseSize), + rollOut = ''; + rollOut += value + '+'; + }); + rollOut = rollOut.substring(0, rollOut.length - 1) + ')+'; + }); + + rollOut = rollOut.substr(0, rollOut.length - 1); + rollOut += '" class="a inlinerollresult showtip tipsy-n'; + rollOut += (crit && fail ? ' importantroll' : (crit ? ' fullcrit' : (fail ? ' fullfail' : ''))) + '">' + total + ''; + + bshields.sendChat(msg, '/direct ' + format(config.outputFormat, rollOut, target, raises)); + }); + } + }; - var sendAs = 'system'; - var character = findObjs({_type: 'character', name: msg.who})[0]; - if(character) sendAs = 'character|'+character.id; - else sendAs = 'player|'+msg.playerid; + function format(formatString) { + var args = arguments.slice(1); + _.each(args, function(arg, index) { + formatString = formatString.replace('{' + index + '}', arg); + }); + return formatString; + } - sendChat(sendAs, '[['+roll+']]', function(fmsg) { - var expression = fmsg[0].inlinerolls['1'].expression; - var total = fmsg[0].inlinerolls['1'].results.total; - var raises = Math.floor((total - target) / raise_count.RAISE_SIZE); + function handleInput(msg) { + var isApi = msg.type === 'api', + args = bshields.splitArgs(msg.content.trim()), + command, args0, isHelp; - var rollOut = ''; - rollOut += value+'+'; + if (!isHelp) { + if (arg0 && arg0.length > 0) { + args.unshift(arg0); + } + + if (_.isFunction(commands[command])) { + commands[command](args, msg); + } + } else if (_.isFunction(commands.help)) { + commands.help(command, args, msg); } - rollOut = rollOut.substring(0,rollOut.length - 1)+')+'; + } else if (_.isFunction(commands['msg_' + msg.type])) { + commands['msg_' + msg.type](args, msg); } - rollOut = rollOut.substr(0, rollOut.length - 1); - rollOut += '" class="a inlinerollresult showtip tipsy-n'; - rollOut += (crit&&fail?' importantroll':(crit?' fullcrit':(fail?' fullfail':'')))+'">'+total+''; - - var message = '/direct '+raise_count.sprintf(raise_count.OUTPUT_FORMAT, rollOut, target, raises); - sendChat(sendAs, message); - }); -}); + } + + function registerEventHandlers() { + on('chat:message', handleInput); + } + + return { + registerEventHandlers: registerEventHandlers + }; +}()); -/** - * Really really really super naive implementation of the sprintf function, - * which will only really work for this script. I should be ashamed for qriting it. - */ -raise_count.sprintf = function(format, arg1, arg2, arg3) -{ - var out = format.replace('%1$s', arg1); - out = out.replace('%2$s', arg2); - out = out.replace('%3$s', arg3); - return out; -}; \ No newline at end of file +on('ready', function() { + 'use strict'; + + bshields.raiseCount.registerEventHandlers(); +}); \ No newline at end of file diff --git a/Raise Count/package.json b/Raise Count/package.json index f1ab14a18c..5af4fd77a4 100644 --- a/Raise Count/package.json +++ b/Raise Count/package.json @@ -1,10 +1,13 @@ { "name": "Raise Count", - "version": "1.0", + "version": "2.0", "description": "Counts raises for the Savage Worlds system.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + splitArgs: "1.0", + "Interpreted sendChat": "2.0" + }, "modifies": { "message": "write" }, From 6ea3cb611a867f109a627424588154665f30db69 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 02:57:53 -0600 Subject: [PATCH 24/29] Add a little bit of info to comment at top of file --- Exalted Successes/Exalted Successes.js | 7 +++++++ Flight/Flight.js | 3 +++ Raise Count/Raise Count.js | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/Exalted Successes/Exalted Successes.js b/Exalted Successes/Exalted Successes.js index 75ae708bdb..ca0ee8684b 100644 --- a/Exalted Successes/Exalted Successes.js +++ b/Exalted Successes/Exalted Successes.js @@ -1,3 +1,10 @@ +/** + * Report number of successes on a roll for Exalted. Simply roll d10s, and the + * successes will be reported automatically. + * + * Does not account for damage rolls (10s don't count double) or Sidereal + * Astrology (change target number) + */ var bshields = bshields || {}; bshields.exalted = (function() { 'use strict'; diff --git a/Flight/Flight.js b/Flight/Flight.js index e85c91f1c7..a0955bd2b1 100644 --- a/Flight/Flight.js +++ b/Flight/Flight.js @@ -1,3 +1,6 @@ +/** + * Set selected tokens flying with !fly height. Clear the flight status markers with !fly. + */ var bshields = bshields || {}; bshields.flight = (function() { 'use strict'; diff --git a/Raise Count/Raise Count.js b/Raise Count/Raise Count.js index fd78e170df..5ad8fec181 100644 --- a/Raise Count/Raise Count.js +++ b/Raise Count/Raise Count.js @@ -1,3 +1,8 @@ +/** + * Counts raises for Savage Worlds. Use !rc roll-expression target-number + * + * Customize output with config.outputFormat, and customize raise size with config.raiseSize + */ var bshields = bshields || {}; bshields.raiseCount = (function() { 'use strict'; From 46cdb967e407296adb35a3fa02f53ee766f3dbb5 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 03:09:57 -0600 Subject: [PATCH 25/29] Update store commands to v2 v2 is mostly a refactor --- Store Commands/Store Commands.js | 137 ++++++++++++++++++++----------- Store Commands/package.json | 6 +- 2 files changed, 92 insertions(+), 51 deletions(-) diff --git a/Store Commands/Store Commands.js b/Store Commands/Store Commands.js index 218dfba25f..dfd58f06f3 100644 --- a/Store Commands/Store Commands.js +++ b/Store Commands/Store Commands.js @@ -1,56 +1,95 @@ -var store_commands = store_commands || {}; - -store_commands.list = {}; - -on('chat:message', function(msg) { - if(msg.type != 'api') return; +/** + * Stores a series of commands to be used later, or repeated later. + * + * !delay time :: sets the default delay in milliseconds between commands + * !store [-time] command :: stores a command, with an optional time overriding the default + * !clearstore :: clears the stored commands + * !echostore :: echoes the stored commands to you + * !run :: runs the series of stored commands + */ +var bshields = bshields || {}; +bshields.storeCommands = (function() { + 'use strict'; - var parts = msg.content.split(' '); - var command = parts.shift().substring(1); - var id = msg.playerid; + var version = 2.0, + list = {}, + commands = { + delay: function(args, msg) { + list[msg.playerid].delay = parseInt(args[0], 10); + }, + clearstore: function(args, msg) { + list[msg.playerid].cmds = []; + }, + store: function(args, msg) { + var delay = list[msg.playerid].delay || 500, + obj; + + if (args[0] && args[0].indexOf('-') === 0) { + delay = parseInt(args.shift().substring(1), 10); + } + + obj = { text: args.join(' '), delay: delay }; + list[msg.playerid].cmds.push(obj); + }, + echostore: function(args, msg) { + _.each(list[msg.playerid].cmds, function(cmd) { + sendChat('System', '/w ' + msg.who + ' {' + cmd.delay + 'ms, ' + cmd.text + '}'); + }); + }, + run: function(args, msg) { + var count = 0; + _.each(list[msg.playerid].cmds, function(cmd) { + echo(msg.playerid, cmd.text, count + cmd.delay); + count += cmd.delay; + }) + } + }; - if(!store_commands.list[id]) store_commands.list[id] = { cmds: [], delay: 500 }; + function echo(id, text, delay) { + setTimeout(function(){ sendChat('player|'+id, text); }, delay); + } - switch(command) - { - case 'delay': - store_commands.list[id].delay = parseInt(parts[0], 10); - break; - case 'store': - var delay = 500; - if(parts[0].indexOf('-') == 0) - delay = parseInt(parts.shift().substring(1), 10); - else if(store_commands.list[id].delay) - delay = store_commands.list[id].delay; + function handleInput(msg) { + var isApi = msg.type === 'api', + args = bshields.splitArgs(msg.content.trim()), + command, args0, isHelp; + + if (!list[msg.playerid]) { + list[msg.playerid] = { cmds: [], delay: 500 }; + } + + if (isApi) { + command = args.shift().substring(1).toLowerCase(); + arg0 = args.shift(); + isHelp = arg0.toLowerCase() === 'help' || arg0.toLowerCase() === 'h'; - var obj = { text: parts.join(' '), delay: delay }; - store_commands.list[id].cmds.push(obj); - break; - case 'clearstore': - store_commands.list[id].cmds = []; - break; - case 'echostore': - for(var i = 0; i < store_commands.list[id].cmds.length; i++) - { - var obj = store_commands.list[id].cmds[i]; - sendChat('Store Commands.js', '/w ' + msg.who + ' {' + obj.delay + 'ms, ' + obj.text + '}'); - } - break; - case 'run': - var count = 0; - for(var i = 0; i < store_commands.list[id].cmds.length; i++) - { - var obj = store_commands.list[id].cmds[i]; - store_commands.echo(id, obj.text, count + obj.delay); - count += obj.delay; + if (!isHelp) { + if (arg0 && arg0.length > 0) { + args.unshift(arg0); + } + + if (_.isFunction(commands[command])) { + commands[command](args, msg); + } + } else if (_.isFunction(commands.help)) { + commands.help(command, args, msg); } - break; - default: - break; + } else if (_.isFunction(commands['msg_' + msg.type])) { + commands['msg_' + msg.type](args, msg); + } } -}); + + function registerEventHandlers() { + on('chat:message', handleInput); + } + + return { + registerEventHandlers: registerEventHandlers + } +}()); -store_commands.echo = function(id, text, delay) -{ - setTimeout(function(){ sendChat('player|'+id, text); }, delay); -}; \ No newline at end of file +on('ready', function() { + 'use strict'; + + bshields.storeCommands.registerEventHandlers(); +}); \ No newline at end of file diff --git a/Store Commands/package.json b/Store Commands/package.json index abe991feae..951d2c8f84 100644 --- a/Store Commands/package.json +++ b/Store Commands/package.json @@ -1,10 +1,12 @@ { "name": "Store Commands", - "version": "1.0", + "version": "2.0", "description": "Stores a series of commands (potentially with delays between them) to be executed in sequence later. Opens up GM-only commands such as /direct and /emas to players.", "authors": "Brian Shields", "roll20userid": "235259", - "dependencies": {}, + "dependencies": { + splitArgs: "1.0" + }, "modifies": { "message": "write" }, From e737bac1e1bdd3046f58b48225ae5e0c0d7454bb Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 03:19:43 -0600 Subject: [PATCH 26/29] Add string utility functions to String.prototype --- levenshteinDistance/levenshteinDistance.js | 8 +++++++- splitArgs/splitArgs.js | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/levenshteinDistance/levenshteinDistance.js b/levenshteinDistance/levenshteinDistance.js index 97b1ce6ceb..4804d3e1c1 100644 --- a/levenshteinDistance/levenshteinDistance.js +++ b/levenshteinDistance/levenshteinDistance.js @@ -2,6 +2,8 @@ * Compares two strings and returns the number of changes (substitutions, * insertions, and deletions) required to move from the first string to the * second. + * + * As a convenience, this function has been added to the String prototype */ var bshields = bshields || {}; bshields.levenshteinDistance = (function() { @@ -47,4 +49,8 @@ bshields.levenshteinDistance = (function() { } return levenshteinDistance; -}()); \ No newline at end of file +}()); + +String.prototype.levenshteinDistance = String.prototype.levenshteinDistance || function(b) { + return bshields.levenshteinDistance(this, b); +}; \ No newline at end of file diff --git a/splitArgs/splitArgs.js b/splitArgs/splitArgs.js index 0b7b646b1e..a3b75fb94c 100644 --- a/splitArgs/splitArgs.js +++ b/splitArgs/splitArgs.js @@ -4,12 +4,15 @@ * string will allow you to group delimited tokens. Single and double quotes * can be nested one level. * + * As a convenience, this function has been added to the String prototype, + * letting you treat it like a function of the string object. + * * Example: on('chat:message', function(msg) { var command, params; - params = bshields.splitArgs(msg.content); + params = msg.content.splitArgs(); command = params.shift().substring(1); // msg.content: !command with parameters, "including 'with quotes'" @@ -68,4 +71,8 @@ bshields.splitArgs = (function() { } return splitArgs; -}()); \ No newline at end of file +}()); + +String.prototype.splitArgs = String.prototype.splitArgs || function(separator) { + return bshields.splitArgs(this, separator); +}; \ No newline at end of file From 17df6e916212dbb61ce276794a5fc5994c4ff625 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 03:23:33 -0600 Subject: [PATCH 27/29] Use prototyped versions of splitArgs and levenshteinDistance instead of using module version directly --- Dynamic Lighting Animation/Dynamic Lighting Animation.js | 2 +- Flight/Flight.js | 2 +- Flip Tokens/Flip Tokens.js | 2 +- Interpreted sendChat/Interpreted sendChat.js | 2 +- Raise Count/Raise Count.js | 2 +- Store Commands/Store Commands.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dynamic Lighting Animation/Dynamic Lighting Animation.js b/Dynamic Lighting Animation/Dynamic Lighting Animation.js index 0aa1b9e1b0..5d5d62135f 100644 --- a/Dynamic Lighting Animation/Dynamic Lighting Animation.js +++ b/Dynamic Lighting Animation/Dynamic Lighting Animation.js @@ -51,7 +51,7 @@ bshields.animation = (function() { function handleInput(msg) { var isApi = msg.type === 'api', - args = bshields.splitArgs(msg.content.trim()), + args = msg.content.trim().splitArgs(), command, args0, isHelp; if (!isGM(msg.playerid)) { return; } diff --git a/Flight/Flight.js b/Flight/Flight.js index a0955bd2b1..93eac00b1b 100644 --- a/Flight/Flight.js +++ b/Flight/Flight.js @@ -45,7 +45,7 @@ bshields.flight = (function() { function handleInput(msg) { var isApi = msg.type === 'api', - args = bshields.splitArgs(msg.content.trim()), + args = msg.content.trim().splitArgs(), command, args0, isHelp; if (isApi) { diff --git a/Flip Tokens/Flip Tokens.js b/Flip Tokens/Flip Tokens.js index d45c29eff6..7c28ebb85d 100644 --- a/Flip Tokens/Flip Tokens.js +++ b/Flip Tokens/Flip Tokens.js @@ -40,7 +40,7 @@ bshields.flip = (function() { function handleInput(msg) { var isApi = msg.type === 'api', - args = bshields.splitArgs(msg.content.trim()), + args = msg.content.trim().splitArgs(), command, args0, isHelp; if (isApi) { diff --git a/Interpreted sendChat/Interpreted sendChat.js b/Interpreted sendChat/Interpreted sendChat.js index af3fbddb84..97354bcdfe 100644 --- a/Interpreted sendChat/Interpreted sendChat.js +++ b/Interpreted sendChat/Interpreted sendChat.js @@ -25,7 +25,7 @@ bshields.sendChat = (function() { function interpretedSendChat(chatMsg, message) { var who = chatMsg.who, speaking = _.sortBy(filterObjs(function(obj) { return obj.get('type') === 'character' && obj.get('name').indexOf(who) >= 0; }), - function(chr) { return Math.abs(bshields.levenshteinDistance(chr.get('name'), who)); })[0]; + function(chr) { return Math.abs(chr.get('name').toLowerCase().levenshteinDistance(who.toLowerCase()); })[0]; sendChat(speaking ? 'character|' + speaking.id : 'player|' + chatMsg.playerid, message); } diff --git a/Raise Count/Raise Count.js b/Raise Count/Raise Count.js index 5ad8fec181..ea6e627e60 100644 --- a/Raise Count/Raise Count.js +++ b/Raise Count/Raise Count.js @@ -59,7 +59,7 @@ bshields.raiseCount = (function() { function handleInput(msg) { var isApi = msg.type === 'api', - args = bshields.splitArgs(msg.content.trim()), + args = msg.content.trim().splitArgs(), command, args0, isHelp; if (isApi) { diff --git a/Store Commands/Store Commands.js b/Store Commands/Store Commands.js index dfd58f06f3..96e35a6324 100644 --- a/Store Commands/Store Commands.js +++ b/Store Commands/Store Commands.js @@ -51,7 +51,7 @@ bshields.storeCommands = (function() { function handleInput(msg) { var isApi = msg.type === 'api', - args = bshields.splitArgs(msg.content.trim()), + args = msg.content.trim().splitArgs(), command, args0, isHelp; if (!list[msg.playerid]) { From 7886a8c4ce84d6aa106c3380b52c80b75432ca5f Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 03:26:23 -0600 Subject: [PATCH 28/29] Fix json --- Dynamic Lighting Animation/package.json | 2 +- Flight/package.json | 2 +- Flip Tokens/package.json | 2 +- Interpreted sendChat/package.json | 2 +- Raise Count/package.json | 2 +- Store Commands/package.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dynamic Lighting Animation/package.json b/Dynamic Lighting Animation/package.json index 1d3407270c..04e437117e 100644 --- a/Dynamic Lighting Animation/package.json +++ b/Dynamic Lighting Animation/package.json @@ -6,7 +6,7 @@ "roll20userid": "235259", "dependencies": { "IsGM Auth Module": "1.1", - splitArgs: "1.0" + "splitArgs": "1.0" }, "modifies": { "path": "write", diff --git a/Flight/package.json b/Flight/package.json index 43bbf84629..ceea8eda00 100644 --- a/Flight/package.json +++ b/Flight/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - splitArgs: "1.0" + "splitArgs": "1.0" }, "modifies": { "message": "read", diff --git a/Flip Tokens/package.json b/Flip Tokens/package.json index 9161b66cb1..3caddce4c3 100644 --- a/Flip Tokens/package.json +++ b/Flip Tokens/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - splitArgs: "1.0" + "splitArgs": "1.0" }, "modifies": { "message": "read", diff --git a/Interpreted sendChat/package.json b/Interpreted sendChat/package.json index 5791cb8473..202c7e4817 100644 --- a/Interpreted sendChat/package.json +++ b/Interpreted sendChat/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - levenshteinDistance: "1.0" + "levenshteinDistance": "1.0" }, "modifies": { "message": "write" diff --git a/Raise Count/package.json b/Raise Count/package.json index 5af4fd77a4..2af347f6a3 100644 --- a/Raise Count/package.json +++ b/Raise Count/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - splitArgs: "1.0", + "splitArgs": "1.0", "Interpreted sendChat": "2.0" }, "modifies": { diff --git a/Store Commands/package.json b/Store Commands/package.json index 951d2c8f84..5c452c0afa 100644 --- a/Store Commands/package.json +++ b/Store Commands/package.json @@ -5,7 +5,7 @@ "authors": "Brian Shields", "roll20userid": "235259", "dependencies": { - splitArgs: "1.0" + "splitArgs": "1.0" }, "modifies": { "message": "write" From b2397a7caa202ad2f567f3b1a2755443a202ed18 Mon Sep 17 00:00:00 2001 From: lithl Date: Thu, 8 Jan 2015 03:36:58 -0600 Subject: [PATCH 29/29] DL Animation and Store Commands conflict Both scripts create the API command !run, so they'll bump heads. --- Dynamic Lighting Animation/package.json | 2 +- Store Commands/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dynamic Lighting Animation/package.json b/Dynamic Lighting Animation/package.json index 04e437117e..52f5be3f44 100644 --- a/Dynamic Lighting Animation/package.json +++ b/Dynamic Lighting Animation/package.json @@ -13,6 +13,6 @@ "message": "write" }, "conflicts": [ - "none" + "Store Commands" ] } diff --git a/Store Commands/package.json b/Store Commands/package.json index 5c452c0afa..ad7d86c3b2 100644 --- a/Store Commands/package.json +++ b/Store Commands/package.json @@ -11,6 +11,6 @@ "message": "write" }, "conflicts": [ - "none" + "Dynamic Lighting Animation" ] }