|
| 1 | +// Github: https://github.com/shdwjk/Roll20API/blob/master/GMAura/GMAura.js |
| 2 | +// By: The Aaron, Arcane Scriptomancer |
| 3 | +// Contact: https://app.roll20.net/users/104025/the-aaron |
| 4 | + |
| 5 | +on('ready',()=>{ |
| 6 | + |
| 7 | + const version = '0.1.0'; |
| 8 | + const lastUpdate = 1567481378; |
| 9 | + const schemaVersion = 0.1; |
| 10 | + const clearURL = 'https://s3.amazonaws.com/files.d20.io/images/4277467/iQYjFOsYC5JsuOPUCI9RGA/thumb.png?1401938659'; |
| 11 | + const regex = { |
| 12 | + color : { |
| 13 | + ops: '([*=+\\-!])?', |
| 14 | + transparent: '(transparent)', |
| 15 | + html: '#?((?:[0-9a-f]{6})|(?:[0-9a-f]{3}))', |
| 16 | + rgb: '(rgb\\(\\s*(?:(?:\\d*\\.\\d+)\\s*,\\s*(?:\\d*\\.\\d+)\\s*,\\s*(?:\\d*\\.\\d+)|(?:\\d+)\\s*,\\s*(?:\\d+)\\s*,\\s*(?:\\d+))\\s*\\))', |
| 17 | + hsv: '(hsv\\(\\s*(?:(?:\\d*\\.\\d+)\\s*,\\s*(?:\\d*\\.\\d+)\\s*,\\s*(?:\\d*\\.\\d+)|(?:\\d+)\\s*,\\s*(?:\\d+)\\s*,\\s*(?:\\d+))\\s*\\))' |
| 18 | + } |
| 19 | + }; |
| 20 | + const colorReg = new RegExp(`^(?:${regex.color.transparent}|${regex.color.html}|${regex.color.rgb}|${regex.color.hsv})$`,'i'); |
| 21 | + const colorParams = /\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*\)/; |
| 22 | + |
| 23 | + let auraLookup = {}; |
| 24 | + |
| 25 | + |
| 26 | + const checkInstall = () => { |
| 27 | + log('-=> GMAura v'+version+' <=- ['+(new Date(lastUpdate*1000))+']'); |
| 28 | + |
| 29 | + if( ! _.has(state,'GMAura') || state.GMAura.version !== schemaVersion) { |
| 30 | + log(' > Updating Schema to v'+schemaVersion+' <'); |
| 31 | + switch(state.GMAura && state.GMAura.version) { |
| 32 | + case 0.0: |
| 33 | + case 'UpdateSchemaVersion': |
| 34 | + state.GMAura.version = schemaVersion; |
| 35 | + break; |
| 36 | + default: |
| 37 | + state.GMAura = { |
| 38 | + version: schemaVersion, |
| 39 | + lookup: { } |
| 40 | + }; |
| 41 | + break; |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + let cleanup = []; |
| 46 | + let keys = Object.keys(state.GMAura.lookup); |
| 47 | + const burndown = () => { |
| 48 | + if(keys.length){ |
| 49 | + let key = keys.shift(); |
| 50 | + let g = getObj('graphic',key); |
| 51 | + if(g){ |
| 52 | + state.GMAura.lookup[key].forEach( id => auraLookup[id]=key); |
| 53 | + handleTokenChange(g,{left:false,top:false,width:false,height:false}); |
| 54 | + } else { |
| 55 | + cleanup.push(key); |
| 56 | + } |
| 57 | + setTimeout(burndown,0); |
| 58 | + } else { |
| 59 | + cleanup.forEach(id => delete state.GMAura.lookup[id]); |
| 60 | + } |
| 61 | + }; |
| 62 | + burndown(); |
| 63 | + }; |
| 64 | + |
| 65 | + const processInlinerolls = (msg) => { |
| 66 | + if(_.has(msg,'inlinerolls')){ |
| 67 | + return _.chain(msg.inlinerolls) |
| 68 | + .reduce(function(m,v,k){ |
| 69 | + let ti=_.reduce(v.results.rolls,function(m2,v2){ |
| 70 | + if(_.has(v2,'table')){ |
| 71 | + m2.push(_.reduce(v2.results,function(m3,v3){ |
| 72 | + m3.push(v3.tableItem.name); |
| 73 | + return m3; |
| 74 | + },[]).join(', ')); |
| 75 | + } |
| 76 | + return m2; |
| 77 | + },[]).join(', '); |
| 78 | + m['$[['+k+']]']= (ti.length && ti) || v.results.total || 0; |
| 79 | + return m; |
| 80 | + },{}) |
| 81 | + .reduce(function(m,v,k){ |
| 82 | + return m.replace(k,v); |
| 83 | + },msg.content) |
| 84 | + .value(); |
| 85 | + } else { |
| 86 | + return msg.content; |
| 87 | + } |
| 88 | + }; |
| 89 | + |
| 90 | + class Color { |
| 91 | + static hsv2rgb(h, s, v) { |
| 92 | + let r, g, b; |
| 93 | + |
| 94 | + let i = Math.floor(h * 6); |
| 95 | + let f = h * 6 - i; |
| 96 | + let p = v * (1 - s); |
| 97 | + let q = v * (1 - f * s); |
| 98 | + let t = v * (1 - (1 - f) * s); |
| 99 | + |
| 100 | + switch (i % 6) { |
| 101 | + case 0: r = v, g = t, b = p; break; |
| 102 | + case 1: r = q, g = v, b = p; break; |
| 103 | + case 2: r = p, g = v, b = t; break; |
| 104 | + case 3: r = p, g = q, b = v; break; |
| 105 | + case 4: r = t, g = p, b = v; break; |
| 106 | + case 5: r = v, g = p, b = q; break; |
| 107 | + } |
| 108 | + |
| 109 | + return { r , g , b }; |
| 110 | + } |
| 111 | + |
| 112 | + static rgb2hsv(r,g,b) { |
| 113 | + let max = Math.max(r, g, b), |
| 114 | + min = Math.min(r, g, b); |
| 115 | + let h, s, v = max; |
| 116 | + |
| 117 | + let d = max - min; |
| 118 | + s = max == 0 ? 0 : d / max; |
| 119 | + |
| 120 | + if (max == min) { |
| 121 | + h = 0; // achromatic |
| 122 | + } else { |
| 123 | + switch (max) { |
| 124 | + case r: h = (g - b) / d + (g < b ? 6 : 0); break; |
| 125 | + case g: h = (b - r) / d + 2; break; |
| 126 | + case b: h = (r - g) / d + 4; break; |
| 127 | + } |
| 128 | + |
| 129 | + h /= 6; |
| 130 | + } |
| 131 | + |
| 132 | + return { h, s, v }; |
| 133 | + } |
| 134 | + |
| 135 | + static dec2hex (n){ |
| 136 | + n = (Math.max(Math.min(Math.round(n*255),255), 0)||0); |
| 137 | + return `${n<16?'0':''}${n.toString(16)}`; |
| 138 | + } |
| 139 | + |
| 140 | + static hex2dec (n){ |
| 141 | + return Math.max(Math.min(parseInt(n,16),255), 0)/255; |
| 142 | + } |
| 143 | + |
| 144 | + static html2rgb(htmlstring){ |
| 145 | + let s=htmlstring.toLowerCase().replace(/[^0-9a-f]/,''); |
| 146 | + if(3===s.length){ |
| 147 | + s=`${s[0]}${s[0]}${s[1]}${s[1]}${s[2]}${s[2]}`; |
| 148 | + } |
| 149 | + return { |
| 150 | + r: this.hex2dec(s.substr(0,2)), |
| 151 | + g: this.hex2dec(s.substr(2,2)), |
| 152 | + b: this.hex2dec(s.substr(4,2)) |
| 153 | + }; |
| 154 | + } |
| 155 | + |
| 156 | + static parseRGBParam(p){ |
| 157 | + if(/\./.test(p)){ |
| 158 | + return parseFloat(p); |
| 159 | + } |
| 160 | + return parseInt(p,10)/255; |
| 161 | + } |
| 162 | + static parseHSVParam(p,f){ |
| 163 | + if(/\./.test(p)){ |
| 164 | + return parseFloat(p); |
| 165 | + } |
| 166 | + switch(f){ |
| 167 | + case 'h': |
| 168 | + return parseInt(p,10)/360; |
| 169 | + case 's': |
| 170 | + case 'v': |
| 171 | + return parseInt(p,10)/100; |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + static parseColor(input){ |
| 176 | + return Color.buildColor(input.toLowerCase().match(colorReg)); |
| 177 | + } |
| 178 | + static buildColor(parsed){ |
| 179 | + const idx = { |
| 180 | + transparent: 1, |
| 181 | + html: 2, |
| 182 | + rgb: 3, |
| 183 | + hsv: 4 |
| 184 | + }; |
| 185 | + |
| 186 | + if(parsed){ |
| 187 | + let c = new Color(); |
| 188 | + if(parsed[idx.transparent]){ |
| 189 | + c.type = 'transparent'; |
| 190 | + } else if(parsed[idx.html]){ |
| 191 | + c.type = 'rgb'; |
| 192 | + _.each(Color.html2rgb(parsed[idx.html]),(v,k)=>{ |
| 193 | + c[k]=v; |
| 194 | + }); |
| 195 | + } else if(parsed[idx.rgb]){ |
| 196 | + c.type = 'rgb'; |
| 197 | + let params = parsed[idx.rgb].match(colorParams); |
| 198 | + c.r= Color.parseRGBParam(params[1]); |
| 199 | + c.g= Color.parseRGBParam(params[2]); |
| 200 | + c.b= Color.parseRGBParam(params[3]); |
| 201 | + } else if(parsed[idx.hsv]){ |
| 202 | + c.type = 'hsv'; |
| 203 | + let params = parsed[idx.hsv].match(colorParams); |
| 204 | + c.h= Color.parseHSVParam(params[1],'h'); |
| 205 | + c.s= Color.parseHSVParam(params[2],'s'); |
| 206 | + c.v= Color.parseHSVParam(params[3],'v'); |
| 207 | + } |
| 208 | + return c; |
| 209 | + } |
| 210 | + return new Color(); |
| 211 | + } |
| 212 | + |
| 213 | + constructor(){ |
| 214 | + this.type='transparent'; |
| 215 | + } |
| 216 | + |
| 217 | + clone(){ |
| 218 | + return Object.assign(new Color(), this); |
| 219 | + } |
| 220 | + |
| 221 | + toRGB(){ |
| 222 | + if('hsv'===this.type){ |
| 223 | + _.each(Color.hsv2rgb(this.h,this.s,this.v),(v,k)=>{ |
| 224 | + this[k]=v; |
| 225 | + }); |
| 226 | + this.type='rgb'; |
| 227 | + } else if ('transparent' === this.type){ |
| 228 | + this.type='rgb'; |
| 229 | + this.r=0.0; |
| 230 | + this.g=0.0; |
| 231 | + this.b=0.0; |
| 232 | + } |
| 233 | + delete this.h; |
| 234 | + delete this.s; |
| 235 | + delete this.v; |
| 236 | + return this; |
| 237 | + } |
| 238 | + |
| 239 | + toHSV(){ |
| 240 | + if('rgb'===this.type){ |
| 241 | + _.each(Color.rgb2hsv(this.r,this.g,this.b),(v,k)=>{ |
| 242 | + this[k]=v; |
| 243 | + }); |
| 244 | + this.type='hsv'; |
| 245 | + } else if('transparent' === this.type){ |
| 246 | + this.type='hsv'; |
| 247 | + this.h=0.0; |
| 248 | + this.s=0.0; |
| 249 | + this.v=0.0; |
| 250 | + } |
| 251 | + |
| 252 | + delete this.r; |
| 253 | + delete this.g; |
| 254 | + delete this.b; |
| 255 | + |
| 256 | + return this; |
| 257 | + } |
| 258 | + |
| 259 | + toHTML(){ |
| 260 | + switch(this.type){ |
| 261 | + case 'transparent': |
| 262 | + return 'transparent'; |
| 263 | + case 'hsv': { |
| 264 | + return this.clone().toRGB().toHTML(); |
| 265 | + } |
| 266 | + case 'rgb': |
| 267 | + return `#${Color.dec2hex(this.r)}${Color.dec2hex(this.g)}${Color.dec2hex(this.b)}`; |
| 268 | + } |
| 269 | + } |
| 270 | + } |
| 271 | + |
| 272 | + const parseColor = (color) => { |
| 273 | + let c = Color.parseColor(color).toHTML(); |
| 274 | + return 'transparent' === c ? '#ff00ff' : c; |
| 275 | + }; |
| 276 | + |
| 277 | + const AddAura = (token, options) => { |
| 278 | + let a = createObj('graphic', |
| 279 | + Object.assign({ |
| 280 | + imgsrc: clearURL, |
| 281 | + layer: 'gmlayer', |
| 282 | + pageid: token.get('pageid'), |
| 283 | + name: '', |
| 284 | + showname: false, |
| 285 | + width: token.get('width'), |
| 286 | + height: token.get('height'), |
| 287 | + top: token.get('top'), |
| 288 | + left: token.get('left'), |
| 289 | + showplayers_name: false, |
| 290 | + showplayers_bar1: false, |
| 291 | + showplayers_bar2: false, |
| 292 | + showplayers_bar3: false, |
| 293 | + showplayers_aura1: false, |
| 294 | + showplayers_aura2: false, |
| 295 | + playersedit_name: true, |
| 296 | + playersedit_bar1: true, |
| 297 | + playersedit_bar2: true, |
| 298 | + playersedit_bar3: true, |
| 299 | + playersedit_aura1: true, |
| 300 | + playersedit_aura2: true |
| 301 | + },options) |
| 302 | + ); |
| 303 | + state.GMAura.lookup[token.id] = state.GMAura.lookup[token.id] || []; |
| 304 | + state.GMAura.lookup[token.id].push(a.id); |
| 305 | + auraLookup[a.id] = token.id; |
| 306 | + }; |
| 307 | + |
| 308 | + const handleTokenChange = (obj, prev) => { |
| 309 | + if(state.GMAura.lookup.hasOwnProperty(obj.id)){ |
| 310 | + let changes = {}; |
| 311 | + let width = obj.get('width'); |
| 312 | + let height = obj.get('height'); |
| 313 | + let top = obj.get('top'); |
| 314 | + let left = obj.get('left'); |
| 315 | + |
| 316 | + if(width != prev.width) { changes.width = width; } |
| 317 | + if(height != prev.height) { changes.height = height; } |
| 318 | + if(top != prev.top) { changes.top = top; } |
| 319 | + if(left != prev.left) { changes.left = left; } |
| 320 | + |
| 321 | + if(Object.keys(changes).length){ |
| 322 | + state.GMAura.lookup[obj.id] |
| 323 | + .map( id => getObj('graphic',id)) |
| 324 | + .filter( g => undefined !== g) |
| 325 | + .forEach( g => g.set(changes)) |
| 326 | + ; |
| 327 | + } |
| 328 | + } else if(auraLookup.hasOwnProperty(obj.id)){ |
| 329 | + let changes = {}; |
| 330 | + let width = obj.get('width'); |
| 331 | + let height = obj.get('height'); |
| 332 | + let top = obj.get('top'); |
| 333 | + let left = obj.get('left'); |
| 334 | + |
| 335 | + if(width != prev.width) { changes.width = prev.width; } |
| 336 | + if(height != prev.height) { changes.height = prev.height; } |
| 337 | + if(top != prev.top) { changes.top = prev.top; } |
| 338 | + if(left != prev.left) { changes.left = prev.left; } |
| 339 | + |
| 340 | + if(Object.keys(changes).length){ |
| 341 | + obj.set(changes); |
| 342 | + } |
| 343 | + } |
| 344 | + }; |
| 345 | + |
| 346 | + const handleTokenRemove = (obj) => { |
| 347 | + if(state.GMAura.lookup.hasOwnProperty(obj.id)){ |
| 348 | + state.GMAura.lookup[obj.id] |
| 349 | + .map( id => getObj('graphic',id)) |
| 350 | + .filter( g => undefined !== g) |
| 351 | + .forEach( g => { |
| 352 | + delete auraLookup[g.id]; |
| 353 | + g.remove(); |
| 354 | + }); |
| 355 | + delete state.GMAura.lookup[obj.id]; |
| 356 | + } else if(auraLookup.hasOwnProperty(obj.id)){ |
| 357 | + let tid = auraLookup[obj.id]; |
| 358 | + state.GMAura.lookup[tid] = state.GMAura.lookup[tid].filter(id=>id !== obj.id); |
| 359 | + if(0 === state.GMAura.lookup[tid].length){ |
| 360 | + delete state.GMAura.lookup[tid]; |
| 361 | + } |
| 362 | + } |
| 363 | + }; |
| 364 | + |
| 365 | + const ch = (c) => { |
| 366 | + const entities = { |
| 367 | + '<' : 'lt', |
| 368 | + '>' : 'gt', |
| 369 | + "'" : '#39', |
| 370 | + '@' : '#64', |
| 371 | + '{' : '#123', |
| 372 | + '|' : '#124', |
| 373 | + '}' : '#125', |
| 374 | + '[' : '#91', |
| 375 | + ']' : '#93', |
| 376 | + '"' : 'quot', |
| 377 | + '*' : 'ast', |
| 378 | + '/' : 'sol', |
| 379 | + ' ' : 'nbsp' |
| 380 | + }; |
| 381 | + |
| 382 | + if( entities.hasOwnProperty(c) ){ |
| 383 | + return `&${entities[c]};`; |
| 384 | + } |
| 385 | + return ''; |
| 386 | + }; |
| 387 | + |
| 388 | + const _h = { |
| 389 | + outer: (...o) => `<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">${o.join(' ')}</div>`, |
| 390 | + title: (t,v) => `<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">${t} v${v}</div>`, |
| 391 | + subhead: (...o) => `<b>${o.join(' ')}</b>`, |
| 392 | + minorhead: (...o) => `<u>${o.join(' ')}</u>`, |
| 393 | + optional: (...o) => `${ch('[')}${o.join(` ${ch('|')} `)}${ch(']')}`, |
| 394 | + required: (...o) => `${ch('<')}${o.join(` ${ch('|')} `)}${ch('>')}`, |
| 395 | + header: (...o) => `<div style="padding-left:10px;margin-bottom:3px;">${o.join(' ')}</div>`, |
| 396 | + section: (s,...o) => `${_h.subhead(s)}${_h.inset(...o)}`, |
| 397 | + paragraph: (...o) => `<p>${o.join(' ')}</p>`, |
| 398 | + items: (o) => `<li>${o.join('</li><li>')}</li>`, |
| 399 | + ol: (...o) => `<ol>${_h.items(o)}</ol>`, |
| 400 | + ul: (...o) => `<ul>${_h.items(o)}</ul>`, |
| 401 | + grid: (...o) => `<div style="padding: 12px 0;">${o.join('')}<div style="clear:both;"></div></div>`, |
| 402 | + cell: (o) => `<div style="width: 130px; padding: 0 3px; float: left;">${o}</div>`, |
| 403 | + inset: (...o) => `<div style="padding-left: 10px;padding-right:20px">${o.join(' ')}</div>`, |
| 404 | + pre: (...o) =>`<div style="border:1px solid #e1e1e8;border-radius:4px;padding:8.5px;margin-bottom:9px;font-size:12px;white-space:normal;word-break:normal;word-wrap:normal;background-color:#f7f7f9;font-family:monospace;overflow:auto;">${o.join(' ')}</div>`, |
| 405 | + preformatted: (...o) =>_h.pre(o.join('<br>').replace(/\s/g,ch(' '))), |
| 406 | + code: (...o) => `<code>${o.join(' ')}</code>`, |
| 407 | + attr: { |
| 408 | + bare: (o)=>`${ch('@')}${ch('{')}${o}${ch('}')}`, |
| 409 | + selected: (o)=>`${ch('@')}${ch('{')}selected${ch('|')}${o}${ch('}')}`, |
| 410 | + target: (o)=>`${ch('@')}${ch('{')}target${ch('|')}${o}${ch('}')}`, |
| 411 | + char: (o,c)=>`${ch('@')}${ch('{')}${c||'CHARACTER NAME'}${ch('|')}${o}${ch('}')}` |
| 412 | + }, |
| 413 | + bold: (...o) => `<b>${o.join(' ')}</b>`, |
| 414 | + italic: (...o) => `<i>${o.join(' ')}</i>`, |
| 415 | + font: { |
| 416 | + command: (...o)=>`<b><span style="font-family:serif;">${o.join(' ')}</span></b>` |
| 417 | + } |
| 418 | + }; |
| 419 | + |
| 420 | + const showHelp = (who) =>{ |
| 421 | + let msg = _h.outer( |
| 422 | + _h.title('GMAura',version), |
| 423 | + _h.header( |
| 424 | + _h.paragraph('GMAura creates gm-only auras on selected or specified tokens.'), |
| 425 | + _h.paragraph(`Auras are created as invisible tokens on the GM layer with their aura 1 set up with the specified options. Changes to the location and size of the original token will be mimicked by the aura tokens. Changes to the location and size of the aura tokens will be reverted to keep them synchronized to the object layer token. To remove an aura, simply delete its gm layer token. Removal of the object layer token will cause the aura tokens to be cleaned up.`) |
| 426 | + ), |
| 427 | + _h.subhead('Commands'), |
| 428 | + _h.inset( |
| 429 | + _h.font.command( |
| 430 | + `!gm-aura`, |
| 431 | + `--help` |
| 432 | + ), |
| 433 | + _h.paragraph('Show this help.') |
| 434 | + ), |
| 435 | + _h.inset( |
| 436 | + _h.font.command( |
| 437 | + `!gm-aura`, |
| 438 | + _h.optional( |
| 439 | + `--color ${_h.required('color')}`, |
| 440 | + `--c ${_h.required('color')}` |
| 441 | + ), |
| 442 | + _h.optional( |
| 443 | + `--radius ${_h.required('radius')}`, |
| 444 | + `--r ${_h.required('radius')}` |
| 445 | + ), |
| 446 | + _h.optional( |
| 447 | + `--square`, |
| 448 | + `--s` |
| 449 | + ), |
| 450 | + _h.optional( |
| 451 | + `--ids ${_h.required(`token id`)} ${_h.optional(`token id ...`)}` |
| 452 | + ) |
| 453 | + ), |
| 454 | + _h.paragraph('This command creates an invisible ura token on the GM layer for each selected or specified token.'), |
| 455 | + _h.ul( |
| 456 | + `${_h.bold(`--color ${_h.required('color')}`)} -- Sets the color of the created aura. (Default: ${_h.code('#ff00ff')}) Any color format that works for TokenMod works (except ${_h.code('transparent')}).`, |
| 457 | + `${_h.bold(`--c ${_h.required('color')}`)} -- Shorthand for ${_h.bold('--color')}.`, |
| 458 | + `${_h.bold(`--radius ${_h.required('radius')}`)} -- Sets the radius of the created aura. The number is relative to the page just like regular auras, usually 5 for one square. (Default: ${_h.code('0')}, or just on the token.)`, |
| 459 | + `${_h.bold(`--r ${_h.required('radius')}`)} -- Shorthand for ${_h.bold('--radius')}.`, |
| 460 | + `${_h.bold(`--square`)} -- Sets the created aura to be square. (Default: ${_h.code('round')})`, |
| 461 | + `${_h.bold(`--s`)} -- Shorthand for ${_h.bold('--square')}.`, |
| 462 | + `${_h.bold(`--ids ${_h.required(`token id`)} ${_h.optional(`token id ...`)}`)} -- a list of token ids to create auras for.` |
| 463 | + ), |
| 464 | + _h.inset( |
| 465 | + _h.preformatted( |
| 466 | + `!gm-aura --color #ff0000 --radius 10 --square` |
| 467 | + ) |
| 468 | + ), |
| 469 | + _h.paragraph(`${_h.bold('Note:')} You can create multi-line commands by enclosing the arguments after ${_h.code('!gm-aura')} in ${_h.code('{{')} and ${_h.code('}}')}.`), |
| 470 | + _h.inset( |
| 471 | + _h.preformatted( |
| 472 | + `!gm-aura {{`, |
| 473 | + ` --c rgb(.7,.7,.3)`, |
| 474 | + ` --r 15`, |
| 475 | + `}}` |
| 476 | + ) |
| 477 | + ), |
| 478 | + _h.paragraph(`${_h.bold('Note:')} You can use inline rolls as part of your command`), |
| 479 | + _h.inset( |
| 480 | + _h.preformatted( |
| 481 | + `!gm-aura --radius ${ch('[')}${ch('[')}1d3*5${ch(']')}${ch(']')}` |
| 482 | + ) |
| 483 | + ) |
| 484 | + ), |
| 485 | + _h.subhead('Colors'), |
| 486 | + _h.inset( |
| 487 | + _h.paragraph(`Colors can be specified in multiple formats:`), |
| 488 | + _h.inset( |
| 489 | + _h.ul( |
| 490 | + `${_h.bold('HTML Color')} -- This is 6 or 3 hexadecimal digits, optionally prefaced by ${_h.code('#')}. Digits in a 3 digit hexadecimal color are doubled. All of the following are the same: ${_h.code('#ff00aa')}, ${_h.code('#f0a')}, ${_h.code('ff00aa')}, ${_h.code('f0a')}`, |
| 491 | + `${_h.bold('RGB Color')} -- This is an RGB color in the format ${_h.code('rgb(1.0,1.0,1.0)')} or ${_h.code('rgb(256,256,256)')}. Decimal numbers are in the scale of 0.0 to 1.0, integer numbers are scaled 0 to 256. Note that numbers can be outside this range for the purpose of doing math.`, |
| 492 | + `${_h.bold('HSV Color')} -- This is an HSV color in the format ${_h.code('hsv(1.0,1.0,1.0)')} or ${_h.code('hsv(360,100,100)')}. Decimal numbers are in the scale of 0.0 to 1.0, integer numbers are scaled 0 to 360 for the hue and 0 to 100 for saturation and value. Note that numbers can be outside this range for the purpose of doing math.` |
| 493 | + ) |
| 494 | + ) |
| 495 | + ) |
| 496 | + |
| 497 | + ); |
| 498 | + sendChat('',`/w "${who}" ${msg}`); |
| 499 | + }; |
| 500 | + |
| 501 | + /* |
| 502 | + * !gm-aura |
| 503 | + * !gm-aura --color #ff0000 |
| 504 | + * !gm-aura --color #ff0000 -- radius 5 |
| 505 | + * !gm-aura --color #ff0000 -- radius 5 --square |
| 506 | + * !gm-aura --color #ff0000 -- radius 5 --square --ids foo bar baz |
| 507 | + */ |
| 508 | + on('chat:message', msg => { |
| 509 | + if('api' === msg.type && /^!gm-aura(\b|$)/i.test(msg.content) && playerIsGM(msg.playerid) ){ |
| 510 | + let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); |
| 511 | + |
| 512 | + let args = processInlinerolls(msg) |
| 513 | + .replace(/<br\/>\n/g, ' ') |
| 514 | + .replace(/(\{\{(.*?)\}\})/g," $2 ") |
| 515 | + .split(/\s+--/); |
| 516 | + |
| 517 | + if(args.filter(a => /^help/i.test(a)).length){ |
| 518 | + showHelp(who); |
| 519 | + return; |
| 520 | + } |
| 521 | + |
| 522 | + let ids = (msg.selected||[]).map(o=>o._id); |
| 523 | + |
| 524 | + let opts = { |
| 525 | + aura1_color: '#ff00ff', |
| 526 | + aura1_radius: 0.0001, |
| 527 | + aura1_square: false |
| 528 | + }; |
| 529 | + |
| 530 | + args.forEach( arg => { |
| 531 | + let cmds = arg.split(/\s+/); |
| 532 | + switch(cmds.shift()){ |
| 533 | + case 'c': |
| 534 | + case 'color': |
| 535 | + opts.aura1_color = parseColor(cmds[0]); |
| 536 | + break; |
| 537 | + |
| 538 | + case 'r': |
| 539 | + case 'radius': |
| 540 | + opts.aura1_radius = parseInt(cmds[0]) || 0.00001; |
| 541 | + break; |
| 542 | + |
| 543 | + case 's': |
| 544 | + case 'square': |
| 545 | + opts.aura1_square = true; |
| 546 | + break; |
| 547 | + |
| 548 | + case 'ids': |
| 549 | + ids = [...ids,...cmds]; |
| 550 | + break; |
| 551 | + } |
| 552 | + }); |
| 553 | + |
| 554 | + [...new Set(ids)] |
| 555 | + .map(id=>getObj('graphic',id)) |
| 556 | + .filter(g=>undefined !== g) |
| 557 | + .forEach( t => AddAura(t,opts)) |
| 558 | + ; |
| 559 | + } |
| 560 | + }); |
| 561 | + |
| 562 | + |
| 563 | + |
| 564 | + checkInstall(); |
| 565 | + on('change:graphic', handleTokenChange); |
| 566 | + on('destroy:graphic', handleTokenRemove); |
| 567 | + |
| 568 | +}); |
0 commit comments