Skip to content

Instantly share code, notes, and snippets.

@BaldarSilveraxe
Last active December 14, 2020 22:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save BaldarSilveraxe/30169b095929b7aa12a5 to your computer and use it in GitHub Desktop.
Save BaldarSilveraxe/30169b095929b7aa12a5 to your computer and use it in GitHub Desktop.
Roll20 SpeechBalloon
var SpeechBalloon = SpeechBalloon || (function(){
'use strict';
var version = 0.1,
schemaVersion = 0.4,
defaultShowLength = 4, // seconds
msPerSec = 1000, // for conversions.. no magic numbers!
checkStepRate = 1000, //ms = 1 second
checkInterval = false,
bustBalloon = function(pageObject) {
if(state.SpeechBalloon.bubbleShown) {
if (typeof(pageObject) != "undefined") {
var page = pageObject.get("_id"),
bubbleBorder = state.SpeechBalloon.pageGraphicMap[page].BorderId,
bubbleTail = state.SpeechBalloon.pageGraphicMap[page].TailId,
bubbleFill = state.SpeechBalloon.pageGraphicMap[page].FillId,
bubbleText = state.SpeechBalloon.pageGraphicMap[page].TextId,
hiddenState={
layer: "gmlayer",
width: 35,
height: 35,
left: 35,
top: 35
};
getObj("graphic" , bubbleBorder).set( hiddenState );
getObj("graphic" , bubbleTail).set( hiddenState );
getObj("graphic" , bubbleFill).set( hiddenState );
getObj("text" , bubbleText).set( hiddenState );
state.SpeechBalloon.bubbleShown=false;
}
}
},
checkBubbleDisplay = function() {
var nextBubble, newLastBubble, page, lastPage, token;
if(state.SpeechBalloon.validUntil < Date.now() ) {
if( state.SpeechBalloon.queue.length == 0 ) {
if ( state.lastBalloon != false ) {
bustBalloon(state.lastBalloon.page);
state.lastBalloon = false;
}
} else {
nextBubble=_.first(state.SpeechBalloon.queue);
newLastBubble = nextBubble;
state.SpeechBalloon.queue=_.rest(state.SpeechBalloon.queue);
page = nextBubble.page;
if ( state.lastBalloon != false ) {
lastPage = state.lastBalloon.page;
if ( page.id != lastPage.id ) {
bustBalloon(state.lastBalloon.page);
}
} else {
lastPage = false;
}
if (! page.get("archived") ) {
speechBalloon(nextBubble);
state.SpeechBalloon.validUntil = Date.now()+(nextBubble.duration*msPerSec);
state.lastBalloon = newLastBubble;
state.SpeechBalloon.bubbleShown = true;
}
}
}
},
findOrCreateBubbleParts = function(thisMap,thisX,thisY) {
var bubbleBorder, bubbleTail,bubbleFill,bubbleText,
creationDefaults = {
_pageid: thisMap.id,
top: thisY,
left: thisX,
width: 70,
height: 70,
layer: "gmlayer"
};
if( _.has(state.SpeechBalloon.pageGraphicMap, thisMap.id) ) {
bubbleBorder = getObj("graphic" , state.SpeechBalloon.pageGraphicMap[thisMap.id].BorderId);
bubbleTail = getObj("graphic" , state.SpeechBalloon.pageGraphicMap[thisMap.id].TailId);
bubbleFill = getObj("graphic" , state.SpeechBalloon.pageGraphicMap[thisMap.id].FillId);
bubbleText = getObj("text" , state.SpeechBalloon.pageGraphicMap[thisMap.id].TextId);
}
if ( ! bubbleBorder) {
bubbleBorder = fixNewObject(createObj("graphic", _.defaults({
imgsrc: "https://s3.amazonaws.com/files.d20.io/images/6565520/qJVbhBJQAw7FNDzBubKuNg/thumb.png?1417619659"
},creationDefaults)));
toFront(bubbleBorder);
}
if ( ! bubbleTail) {
bubbleTail = fixNewObject(createObj("graphic", _.defaults({
width: 140,
height: 140,
imgsrc: "https://s3.amazonaws.com/files.d20.io/images/6565493/BMPVhSPmlFaY_KyB7K8XHQ/thumb.png?1417619533"
},creationDefaults)));
toFront(bubbleTail);
}
if ( ! bubbleFill) {
bubbleFill = fixNewObject(createObj("graphic", _.defaults({
imgsrc: "https://s3.amazonaws.com/files.d20.io/images/6565524/yTHHF5NwFJcd0ddZ-9nyxg/thumb.png?1417619728"
},creationDefaults)));
toFront(bubbleFill);
}
if ( ! bubbleText) {
bubbleText = fixNewObject(createObj("text", _.defaults({
text: "DoubleBubbleBumBubblesDouble",
font_size: 16,
color: "rgb(0,0,0)",
font_family: "Courier"
},creationDefaults)));
toFront(bubbleText);
}
state.SpeechBalloon.pageGraphicMap[thisMap.id]={
BorderId: bubbleBorder.id,
TailId: bubbleTail.id,
FillId: bubbleFill.id,
TextId: bubbleText.id
};
return {
bubbleTail: bubbleTail,
bubbleFill: bubbleFill,
bubbleBorder: bubbleBorder,
bubbleText: bubbleText
};
},
speechBalloon = function(nextBubble) {
var theseWords = nextBubble.says, whoSaid = nextBubble.token.get("name"),
thisMap = nextBubble.page, thisY = nextBubble.token.get("top"),
thisX = nextBubble.token.get("left");
if (theseWords.indexOf("--show|") != 0) {
sendChat(whoSaid, theseWords);
theseWords = wordwrap(theseWords, 28, "\n");
} else {
theseWords = theseWords.replace("--show|", "");
theseWords = theseWords.replace(/~/g, " ");
theseWords = theseWords.replace(/::/g, "\n");
}
var thisParagraph = theseWords,
lineCount = 1 + (thisParagraph.match(/\n/g)||[]).length,
approximateWidth = 286,
approximateHeight = (lineCount * 25) + 7,
xAdjust = ((thisX-(thisMap.get('width') * 35)) >=0) ? -1 : 1,
yAdjust = ((thisY-(thisMap.get('height') * 35)) >=0) ? -1 : 1,
leftTail = thisX + (105 * xAdjust),
topTail = thisY + (105 * yAdjust),
leftOffsetBubble = thisX + (210 * xAdjust),
topOffsetBubble = thisY + ((Math.floor(approximateHeight/2) + 105 < 159 ? 159 : Math.floor(approximateHeight/2) + 105) * yAdjust),
bubbleParts = findOrCreateBubbleParts(thisMap,thisX,thisY),
tailFlipH = (-1 !== xAdjust),
tailFlipV = (-1 !== yAdjust);
if (bubbleParts.bubbleBorder) {
bubbleParts.bubbleBorder.set({
layer: "map",
width: approximateWidth + 6,
height: approximateHeight + 6,
top: topOffsetBubble,
left: leftOffsetBubble
});
toFront(bubbleParts.bubbleBorder);
}
if (bubbleParts.bubbleTail) {
bubbleParts.bubbleTail.set({
layer: "map",
width: 140,
height: 140,
top: topTail,
left: leftTail,
fliph: tailFlipH,
flipv: tailFlipV
});
toFront(bubbleParts.bubbleTail);
}
if (bubbleParts.bubbleFill) {
bubbleParts.bubbleFill.set({
layer: "map",
width: approximateWidth,
height: approximateHeight,
top: topOffsetBubble,
left: leftOffsetBubble
});
toFront(bubbleParts.bubbleFill);
}
if (bubbleParts.bubbleText) {
bubbleParts.bubbleText.set({
layer: "map",
text: thisParagraph,
top: topOffsetBubble,
left: leftOffsetBubble
});
toFront(bubbleParts.bubbleText);
}
state.SpeechBalloon.bubbleShown=true;
},
wordwrap = function( str, width, brk, cut ) {
brk = brk || '\n';
width = width || 75;
cut = cut || false;
if (!str) { return str; }
var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
return str.match( RegExp(regex, 'g') ).join( brk );
},
checkSelect = function(obj,type,command) {
if (obj === undefined || obj.length < 1) {
return false;
}
if (obj._type !== type) {
return false;
}
return true;
},
handleInput = function(msg) {
if ( "api" !== msg.type ) {return; }
var args = msg.content.split(' '),
obj = _.first(msg.selected),
playerid = msg.playerid,
token,
thisY,
thisX;
switch(args.shift()) {
case "!makebubble":
if ( ! checkSelect(obj,"graphic") ) {return; }
state.SpeechBalloon.queue.push({
token: getObj("graphic", obj._id),
page: getObj('page',getObj("graphic", obj._id).get('pageid')),
says: args.join(' '),
duration: defaultShowLength,
});
return;
case "!bustBubble":
if ( ! checkSelect(obj,"graphic") ) {return; }
bustBalloon(getObj('page',getObj("graphic", obj._id).get('pageid')));
return;
}
},
checkInstall = function() {
if( ! _.has(state,'SpeechBalloon') || state.SpeechBalloon.version !== schemaVersion) {
log('SpeechBalloon: Resetting state');
/* Default Settings stored in the state. */
state.SpeechBalloon = {
version: schemaVersion,
pageGraphicMap: {},
queue: [],
validUntil: 0,
bubbleShown: false
};
}
if( ! _.has(state,'lastBalloon')) {
log('SpeechBalloon.lastBalloon: Resetting state');
/* Default Settings stored in the state. */
state.lastBalloon = false;
}
checkInterval = setInterval(checkBubbleDisplay,checkStepRate);
},
fixNewObject = function(obj) {
var p = obj.changed._fbpath,
new_p = p.replace(/([^\/]*\/){4}/, "/");
obj.fbpath = new_p;
return obj;
},
registerEventHandlers = function() {
on('chat:message', handleInput);
};
return {
CheckInstall: checkInstall,
RegisterEventHandlers: registerEventHandlers
};
}());
on("ready",function(){
'use strict';
SpeechBalloon.CheckInstall();
SpeechBalloon.RegisterEventHandlers();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment