mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 19:52:29 +00:00
1494 lines
38 KiB
HTML
1494 lines
38 KiB
HTML
<!-- Sheet Workers -->
|
||
<script type="text/worker">
|
||
|
||
/* ---- BEGIN: TheAaronSheet.js ---- */
|
||
// Github: https://github.com/shdwjk/TheAaronSheet/blob/master/TheAaronSheet.js
|
||
// By: The Aaron, Arcane Scriptomancer
|
||
// Contact: https://app.roll20.net/users/104025/the-aaron
|
||
|
||
var TAS = TAS || (function(){
|
||
'use strict';
|
||
|
||
var version = '0.2.1',
|
||
lastUpdate = 1453794214,
|
||
|
||
loggingSettings = {
|
||
debug: {
|
||
key: 'debug',
|
||
title: 'DEBUG',
|
||
color: {
|
||
bgLabel: '#7732A2',
|
||
label: '#F2EF40',
|
||
bgText: '#FFFEB7',
|
||
text: '#7732A2'
|
||
}
|
||
},
|
||
error: {
|
||
key: 'error',
|
||
title: 'Error',
|
||
color: {
|
||
bgLabel: '#C11713',
|
||
label: 'white',
|
||
bgText: '#C11713',
|
||
text: 'white'
|
||
}
|
||
},
|
||
warn: {
|
||
key: 'warn',
|
||
title: 'Warning',
|
||
color: {
|
||
bgLabel: '#F29140',
|
||
label: 'white',
|
||
bgText: '#FFD8B7',
|
||
text: 'black'
|
||
}
|
||
},
|
||
info: {
|
||
key: 'info',
|
||
title: 'Info',
|
||
color: {
|
||
bgLabel: '#413FA9',
|
||
label: 'white',
|
||
bgText: '#B3B2EB',
|
||
text: 'black'
|
||
}
|
||
},
|
||
notice: {
|
||
key: 'notice',
|
||
title: 'Notice',
|
||
color: {
|
||
bgLabel: '#33C133',
|
||
label: 'white',
|
||
bgText: '#ADF1AD',
|
||
text: 'black'
|
||
}
|
||
},
|
||
log: {
|
||
key: 'log',
|
||
title: 'Log',
|
||
color: {
|
||
bgLabel: '#f2f240',
|
||
label: 'black',
|
||
bgText: '#ffff90',
|
||
text: 'black'
|
||
}
|
||
},
|
||
callstack: {
|
||
key: 'TAS',
|
||
title: 'function',
|
||
color: {
|
||
bgLabel: '#413FA9',
|
||
label: 'white',
|
||
bgText: '#B3B2EB',
|
||
text: 'black'
|
||
}
|
||
},
|
||
callstack_async: {
|
||
key: 'TAS',
|
||
title: 'ASYNC CALL',
|
||
color: {
|
||
bgLabel: '#413FA9',
|
||
label: 'white',
|
||
bgText: '#413FA9',
|
||
text: 'white'
|
||
}
|
||
},
|
||
TAS: {
|
||
key: 'TAS',
|
||
title: 'TAS',
|
||
color: {
|
||
bgLabel: 'grey',
|
||
label: 'black;background:linear-gradient(#304352,#d7d2cc,#d7d2cc,#d7d2cc,#304352)',
|
||
bgText: 'grey',
|
||
text: 'black;background:linear-gradient(#304352,#d7d2cc,#d7d2cc,#d7d2cc,#304352)'
|
||
}
|
||
}
|
||
},
|
||
|
||
|
||
config = {
|
||
debugMode: false,
|
||
logging: {
|
||
log: true,
|
||
notice: true,
|
||
info: true,
|
||
warn: true,
|
||
error: true,
|
||
debug: false
|
||
}
|
||
},
|
||
|
||
callstackRegistry = [],
|
||
queuedUpdates = {}, //< Used for delaying saves till the last momment.
|
||
|
||
complexType = function(o){
|
||
switch(typeof o){
|
||
case 'string':
|
||
return 'string';
|
||
case 'boolean':
|
||
return 'boolean';
|
||
case 'number':
|
||
return (_.isNaN(o) ? 'NaN' : (o.toString().match(/\./) ? 'decimal' : 'integer'));
|
||
case 'function':
|
||
return 'function: '+(o.name ? o.name+'()' : '(anonymous)');
|
||
case 'object':
|
||
return (_.isArray(o) ? 'array' : (_.isArguments(o) ? 'arguments' : ( _.isNull(o) ? 'null' : 'object')));
|
||
default:
|
||
return typeof o;
|
||
}
|
||
},
|
||
|
||
dataLogger = function(primaryLogger,secondaryLogger,data){
|
||
_.each(data,function(m){
|
||
var type = complexType(m);
|
||
switch(type){
|
||
case 'string':
|
||
primaryLogger(m);
|
||
break;
|
||
case 'undefined':
|
||
case 'null':
|
||
case 'NaN':
|
||
primaryLogger('['+type+']');
|
||
break;
|
||
case 'number':
|
||
case 'not a number':
|
||
case 'integer':
|
||
case 'decimal':
|
||
case 'boolean':
|
||
primaryLogger('['+type+']: '+m);
|
||
break;
|
||
default:
|
||
primaryLogger('['+type+']:=========================================');
|
||
secondaryLogger(m);
|
||
primaryLogger('=========================================================');
|
||
break;
|
||
}
|
||
});
|
||
},
|
||
|
||
|
||
colorLog = function(options){
|
||
var coloredLoggerFunction,
|
||
key = options.key,
|
||
label = options.title || 'TAS',
|
||
lBGColor = (options.color && options.color.bgLabel) || 'blue',
|
||
lTxtColor = (options.color && options.color.label) || 'white',
|
||
mBGColor = (options.color && options.color.bgText) || 'blue',
|
||
mTxtColor = (options.color && options.color.text) || 'white';
|
||
|
||
coloredLoggerFunction = function(message){
|
||
console.log(
|
||
'%c '+label+': %c '+message,
|
||
'background-color: '+lBGColor+';color: '+lTxtColor+'; font-weight:bold;',
|
||
'background-color: '+mBGColor+';color: '+mTxtColor+';'
|
||
);
|
||
};
|
||
return function(){
|
||
if('TAS'===key || config.logging[key]){
|
||
dataLogger(coloredLoggerFunction,function(m){console.log(m);},_.toArray(arguments));
|
||
}
|
||
};
|
||
},
|
||
|
||
logDebug = colorLog(loggingSettings.debug),
|
||
logError = colorLog(loggingSettings.error),
|
||
logWarn = colorLog(loggingSettings.warn),
|
||
logInfo = colorLog(loggingSettings.info),
|
||
logNotice = colorLog(loggingSettings.notice),
|
||
logLog = colorLog(loggingSettings.log),
|
||
log = colorLog(loggingSettings.TAS),
|
||
logCS = colorLog(loggingSettings.callstack),
|
||
logCSA = colorLog(loggingSettings.callstack_async),
|
||
|
||
registerCallstack = function(callstack,label){
|
||
var idx=_.findIndex(callstackRegistry,function(o){
|
||
return (_.difference(o.stack,callstack).length === _.difference(callstack,o.stack).length)
|
||
&& _.difference(o.stack,callstack).length === 0
|
||
&& o.label === label;
|
||
});
|
||
if(-1 === idx){
|
||
idx=callstackRegistry.length;
|
||
callstackRegistry.push({
|
||
stack: callstack,
|
||
label: label
|
||
});
|
||
}
|
||
return idx;
|
||
},
|
||
|
||
setConfigOption = function(options){
|
||
var newconf =_.defaults(options,config);
|
||
newconf.logging=_.defaults(
|
||
(options && options.logging)||{},
|
||
config.logging
|
||
);
|
||
config=newconf;
|
||
},
|
||
|
||
debugMode = function(){
|
||
config.logging.debug=true;
|
||
config.debugMode = true;
|
||
},
|
||
|
||
getCallstack = function(){
|
||
var e = new Error('dummy'),
|
||
stack = _.map(_.rest(e.stack.replace(/^[^\(]+?[\n$]/gm, '')
|
||
.replace(/^\s+at\s+/gm, '')
|
||
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
|
||
.split('\n')),function(l){
|
||
return l.replace(/\s+.*$/,'');
|
||
});
|
||
return stack;
|
||
},
|
||
logCallstackSub = function(cs){
|
||
var matches, csa;
|
||
_.find(cs,function(line){
|
||
matches = line.match(/TAS_CALLSTACK_(\d+)/);
|
||
if(matches){
|
||
csa=callstackRegistry[matches[1]];
|
||
logCSA( '===================='+(csa.label ? '> '+csa.label+' <' : '')+'====================');
|
||
logCallstackSub(csa.stack);
|
||
return true;
|
||
}
|
||
logCS(line);
|
||
return false;
|
||
});
|
||
},
|
||
logCallstack = function(){
|
||
var cs;
|
||
if(config.debugMode){
|
||
cs = getCallstack();
|
||
cs.shift();
|
||
log('==============================> CALLSTACK <==============================');
|
||
logCallstackSub(cs);
|
||
log('=========================================================================');
|
||
}
|
||
},
|
||
|
||
|
||
wrapCallback = function (label, callback,context){
|
||
var callstack;
|
||
if('function' === typeof label){
|
||
context=callback;
|
||
callback=label;
|
||
label=undefined;
|
||
}
|
||
if(!config.debugMode){
|
||
return (function(cb,ctx){
|
||
return function(){
|
||
cb.apply(ctx||{},arguments);
|
||
};
|
||
}(callback,context));
|
||
}
|
||
|
||
callstack = getCallstack();
|
||
callstack.shift();
|
||
|
||
return (function(cb,ctx,cs,lbl){
|
||
var ctxref=registerCallstack(cs,lbl);
|
||
return new Function('cb','ctx','TASlog',
|
||
"return function TAS_CALLSTACK_"+ctxref+"(){"+
|
||
"TASlog('Entering: '+(cb.name||'(anonymous function)'));"+
|
||
"cb.apply(ctx||{},arguments);"+
|
||
"TASlog('Exiting: '+(cb.name||'(anonymous function)'));"+
|
||
"};")(cb,ctx,log);
|
||
}(callback,context,callstack,label));
|
||
},
|
||
|
||
|
||
prepareUpdate = function( attribute, value ){
|
||
queuedUpdates[attribute]=value;
|
||
},
|
||
|
||
applyQueuedUpdates = function() {
|
||
setAttrs(queuedUpdates);
|
||
queuedUpdates = {};
|
||
},
|
||
|
||
namesFromArgs = function(args,base){
|
||
return _.chain(args)
|
||
.reduce(function(memo,attr){
|
||
if('string' === typeof attr) {
|
||
memo.push(attr);
|
||
} else if(_.isArray(args) || _.isArguments(args)){
|
||
memo = namesFromArgs(attr,memo);
|
||
}
|
||
return memo;
|
||
},(_.isArray(base) && base) || [])
|
||
.uniq()
|
||
.value();
|
||
},
|
||
|
||
addId = function(obj,value){
|
||
Object.defineProperty(obj,'id',{
|
||
value: value,
|
||
writeable: false,
|
||
enumerable: false
|
||
});
|
||
},
|
||
|
||
addProp = function(obj,prop,value,fullname){
|
||
(function(){
|
||
var pname=(_.contains(['S','F','I','D'],prop) ? '_'+prop : prop),
|
||
full_pname = fullname || prop,
|
||
pvalue=value;
|
||
|
||
_.each(['S','I','F'],function(p){
|
||
if( !_.has(obj,p)){
|
||
Object.defineProperty(obj, p, {
|
||
value: {},
|
||
enumerable: false,
|
||
readonly: true
|
||
});
|
||
}
|
||
});
|
||
if( !_.has(obj,'D')){
|
||
Object.defineProperty(obj, 'D', {
|
||
value: _.reduce(_.range(10),function(m,d){
|
||
Object.defineProperty(m, d, {
|
||
value: {},
|
||
enumerable: true,
|
||
readonly: true
|
||
});
|
||
return m;
|
||
},{}),
|
||
enumerable: false,
|
||
readonly: true
|
||
});
|
||
}
|
||
|
||
|
||
// Raw value
|
||
Object.defineProperty(obj, pname, {
|
||
enumerable: true,
|
||
set: function(v){
|
||
pvalue=v;
|
||
prepareUpdate(full_pname,v);
|
||
},
|
||
get: function(){
|
||
return pvalue;
|
||
}
|
||
});
|
||
|
||
// string value
|
||
Object.defineProperty(obj.S, pname, {
|
||
enumerable: true,
|
||
set: function(v){
|
||
var val=v.toString();
|
||
pvalue=val;
|
||
prepareUpdate(full_pname,val);
|
||
},
|
||
get: function(){
|
||
return pvalue.toString();
|
||
}
|
||
});
|
||
|
||
// int value
|
||
Object.defineProperty(obj.I, pname, {
|
||
enumerable: true,
|
||
set: function(v){
|
||
var val=parseInt(v,10) || 0;
|
||
pvalue=val;
|
||
prepareUpdate(full_pname,val);
|
||
},
|
||
get: function(){
|
||
return parseInt(pvalue,10) || 0;
|
||
}
|
||
});
|
||
|
||
// float value
|
||
Object.defineProperty(obj.F, pname, {
|
||
enumerable: true,
|
||
set: function(v){
|
||
var val=parseFloat(v) || 0;
|
||
pvalue=val;
|
||
prepareUpdate(full_pname,val);
|
||
},
|
||
get: function(){
|
||
return parseFloat(pvalue) || 0;
|
||
}
|
||
});
|
||
_.each(_.range(10),function(d){
|
||
Object.defineProperty(obj.D[d], pname, {
|
||
enumerable: true,
|
||
set: function(v){
|
||
var val=(parseFloat(v) || 0).toFixed(d);
|
||
pvalue=val;
|
||
prepareUpdate(full_pname,val);
|
||
},
|
||
get: function(){
|
||
return (parseFloat(pvalue) || 0).toFixed(d);
|
||
}
|
||
});
|
||
});
|
||
|
||
}());
|
||
},
|
||
|
||
repeating = function( section ) {
|
||
return (function(s){
|
||
var sectionName = s,
|
||
attrNames = [],
|
||
fieldNames = [],
|
||
operations = [],
|
||
after = [],
|
||
|
||
repAttrs = function TAS_Repeating_Attrs(){
|
||
attrNames = namesFromArgs(arguments,attrNames);
|
||
return this;
|
||
},
|
||
repFields = function TAS_Repeating_Fields(){
|
||
fieldNames = namesFromArgs(arguments,fieldNames);
|
||
return this;
|
||
},
|
||
repReduce = function TAS_Repeating_Reduce(func, initial, final, context) {
|
||
operations.push({
|
||
type: 'reduce',
|
||
func: (func && _.isFunction(func) && func) || _.noop,
|
||
memo: (_.isUndefined(initial) && 0) || initial,
|
||
final: (final && _.isFunction(final) && final) || _.noop,
|
||
context: context || {}
|
||
});
|
||
return this;
|
||
},
|
||
repMap = function TAS_Repeating_Map(func, final, context) {
|
||
operations.push({
|
||
type: 'map',
|
||
func: (func && _.isFunction(func) && func) || _.noop,
|
||
final: (final && _.isFunction(final) && final) || _.noop,
|
||
context: context || {}
|
||
});
|
||
return this;
|
||
},
|
||
repEach = function TAS_Repeating_Each(func, final, context) {
|
||
operations.push({
|
||
type: 'each',
|
||
func: (func && _.isFunction(func) && func) || _.noop,
|
||
final: (final && _.isFunction(final) && final) || _.noop,
|
||
context: context || {}
|
||
});
|
||
return this;
|
||
},
|
||
repTap = function TAS_Repeating_Tap(final, context) {
|
||
operations.push({
|
||
type: 'tap',
|
||
final: (final && _.isFunction(final) && final) || _.noop,
|
||
context: context || {}
|
||
});
|
||
return this;
|
||
},
|
||
repAfter = function TAS_Repeating_After(callback,context) {
|
||
after.push({
|
||
callback: (callback && _.isFunction(callback) && callback) || _.noop,
|
||
context: context || {}
|
||
});
|
||
return this;
|
||
},
|
||
repExecute = function TAS_Repeating_Execute(callback,context){
|
||
var rowSet = {},
|
||
attrSet = {},
|
||
fieldIds = [],
|
||
fullFieldNames = [];
|
||
|
||
repAfter(callback,context);
|
||
|
||
// call each operation per row.
|
||
// call each operation's final
|
||
getSectionIDs("repeating_"+sectionName,function(ids){
|
||
fieldIds = ids;
|
||
fullFieldNames = _.reduce(fieldIds,function(memo,id){
|
||
return memo.concat(_.map(fieldNames,function(name){
|
||
return 'repeating_'+sectionName+'_'+id+'_'+name;
|
||
}));
|
||
},[]);
|
||
getAttrs( _.uniq(attrNames.concat(fullFieldNames)), function(values){
|
||
_.each(attrNames,function(aname){
|
||
if(values.hasOwnProperty(aname)){
|
||
addProp(attrSet,aname,values[aname]);
|
||
}
|
||
});
|
||
|
||
rowSet = _.reduce(fieldIds,function(memo,id){
|
||
var r={};
|
||
addId(r,id);
|
||
_.each(fieldNames,function(name){
|
||
var fn = 'repeating_'+sectionName+'_'+id+'_'+name;
|
||
addProp(r,name,values[fn],fn);
|
||
});
|
||
|
||
memo[id]=r;
|
||
|
||
return memo;
|
||
},{});
|
||
|
||
_.each(operations,function(op){
|
||
var res;
|
||
switch(op.type){
|
||
case 'tap':
|
||
_.bind(op.final,op.context,rowSet,attrSet)();
|
||
break;
|
||
|
||
case 'each':
|
||
_.each(rowSet,function(r){
|
||
_.bind(op.func,op.context,r,attrSet,r.id,rowSet)();
|
||
});
|
||
_.bind(op.final,op.context,rowSet,attrSet)();
|
||
break;
|
||
|
||
case 'map':
|
||
res = _.map(rowSet,function(r){
|
||
return _.bind(op.func,op.context,r,attrSet,r.id,rowSet)();
|
||
});
|
||
_.bind(op.final,op.context,res,rowSet,attrSet)();
|
||
break;
|
||
|
||
case 'reduce':
|
||
res = op.memo;
|
||
_.each(rowSet,function(r){
|
||
res = _.bind(op.func,op.context,res,r,attrSet,r.id,rowSet)();
|
||
});
|
||
_.bind(op.final,op.context,res,rowSet,attrSet)();
|
||
break;
|
||
}
|
||
});
|
||
|
||
// finalize attrs
|
||
applyQueuedUpdates();
|
||
_.each(after,function(op){
|
||
_.bind(op.callback,op.context)();
|
||
});
|
||
});
|
||
});
|
||
};
|
||
|
||
return {
|
||
attrs: repAttrs,
|
||
attr: repAttrs,
|
||
|
||
column: repFields,
|
||
columns: repFields,
|
||
field: repFields,
|
||
fields: repFields,
|
||
|
||
reduce: repReduce,
|
||
inject: repReduce,
|
||
foldl: repReduce,
|
||
|
||
map: repMap,
|
||
collect: repMap,
|
||
|
||
each: repEach,
|
||
forEach: repEach,
|
||
|
||
tap: repTap,
|
||
'do': repTap,
|
||
|
||
after: repAfter,
|
||
last: repAfter,
|
||
done: repAfter,
|
||
|
||
execute: repExecute,
|
||
go: repExecute,
|
||
run: repExecute
|
||
};
|
||
}(section));
|
||
},
|
||
|
||
|
||
repeatingSimpleSum = function(section, field, destination){
|
||
repeating(section)
|
||
.attr(destination)
|
||
.field(field)
|
||
.reduce(function(m,r){
|
||
return m + (r.F[field]);
|
||
},0,function(t,r,a){
|
||
a[destination]=t;
|
||
})
|
||
.execute();
|
||
};
|
||
|
||
console.log('%c•.¸¸.•*´¨`*•.¸¸.•*´¨`*•.¸ The Aaron Sheet v'+version+' ¸.•*´¨`*•.¸¸.•*´¨`*•.¸¸.•','background: linear-gradient(to right,green,white,white,green); color:black;text-shadow: 0 0 8px white;');
|
||
console.log('%c•.¸¸.•*´¨`*•.¸¸.•*´¨`*•.¸ Last update: '+(new Date(lastUpdate*1000))+' ¸.•*´¨`*•.¸¸.•*´¨`*•.¸¸.•','background: linear-gradient(to right,green,white,white,green); color:black;text-shadow: 0 0 8px white;');
|
||
|
||
|
||
return {
|
||
/* Repeating Sections */
|
||
repeatingSimpleSum: repeatingSimpleSum,
|
||
repeating: repeating,
|
||
|
||
/* Configuration */
|
||
config: setConfigOption,
|
||
|
||
/* Debugging */
|
||
callback: wrapCallback,
|
||
callstack: logCallstack,
|
||
debugMode: debugMode,
|
||
_fn: wrapCallback,
|
||
|
||
/* Logging */
|
||
debug: logDebug,
|
||
error: logError,
|
||
warn: logWarn,
|
||
info: logInfo,
|
||
notice: logNotice,
|
||
log: logLog
|
||
};
|
||
}());
|
||
|
||
/* ---- END: TheAaronSheet.js ---- */
|
||
|
||
/* ---- START: ordinalInWord ---- */
|
||
function ordinalInWord( cardinal ) {
|
||
'use strict';
|
||
var ordinals = [ 'Zeroth ', 'First ', 'Second ', 'Third ', 'Fourth ', 'Fifth ', 'Sixth ', 'Seventh ', 'Eighth ', 'Ninth ', 'Tenth ', 'Eleventh ', 'Twelfth ', 'Thirteenth ', 'Fourteenth ', 'Fifteenth ', 'Sixteenth ','Seventeenth ', 'Eighteenth ', 'Nineteenth ', 'Twentieth ' ];
|
||
var tens = {
|
||
20: 'Twenty',
|
||
30: 'Thirty',
|
||
40: 'Forty' /* and so on up to 90 */
|
||
};
|
||
var ordinalTens = {
|
||
30: 'Thirtieth',
|
||
40: 'Fortieth',
|
||
50: 'Fiftieth' /* and so on */
|
||
};
|
||
|
||
if( cardinal <= 20 ) {
|
||
return ordinals[ cardinal ];
|
||
}
|
||
|
||
if( cardinal % 10 === 0 ) {
|
||
return ordinalTens[ cardinal ];
|
||
}
|
||
|
||
return tens[ cardinal - ( cardinal % 10 ) ] + ordinals[ cardinal % 10 ];
|
||
}
|
||
/* ---- END: ordinalInWord ---- */
|
||
|
||
/* ---- START: Build Dice Roll for OVA Template ---- */
|
||
on("sheet:opened change:numdicetoroll", function () {
|
||
'use strict';
|
||
getAttrs(["numdicetoroll"], function (value) {
|
||
console.log("Number of Dice to Roll: " + value.numdicetoroll);
|
||
var dice = parseInt(value.numdicetoroll);
|
||
console.log("Number of Dice Type = " + typeof dice);
|
||
var newFormula = " ";
|
||
var newOrd;
|
||
var i = 0;
|
||
for (i = 1; i <= dice; i += 1) {
|
||
console.log("i = " + i);
|
||
newOrd = ordinalInWord(i);
|
||
newFormula += "{{" + newOrd + "Die=[[1d6]]}} ";
|
||
console.log("Roll Formula = " + newFormula);
|
||
}
|
||
setAttrs({rollformula: newFormula});
|
||
});
|
||
});
|
||
//End Rank
|
||
|
||
/* ---- START: Build Dice Roll for OVA Template ATK Repeating Section ----*/
|
||
//First ATK Field (non-repeating)
|
||
on("sheet:opened change:roll1", function () {
|
||
'use strict';
|
||
getAttrs(["roll1"], function (value) {
|
||
console.log("Number of Dice to Roll: " + value.roll1);
|
||
var dice = parseInt(value.roll1);
|
||
console.log("Number of Dice Type = " + typeof dice);
|
||
var newFormula = " ";
|
||
var i = 0;
|
||
for (i = 1; i <= dice; i += 1) {
|
||
console.log("i = " + i);
|
||
newFormula += "[[1d6]] ";
|
||
console.log("Roll Formula = " + newFormula);
|
||
}
|
||
setAttrs({atk1: newFormula});
|
||
});
|
||
});
|
||
|
||
//Other ATK Fields (repeating section)
|
||
on("change:repeating_attacks:roll2", function (eventInfo) {
|
||
'use strict';
|
||
getAttrs(["repeating_attacks_roll2","repeating_attacks_atk"], function (value) {
|
||
console.log("Number of Dice to Roll: " + value.repeating_attacks_roll2);
|
||
var dice = parseInt(value.repeating_attacks_roll2);
|
||
var newFormula = " ";
|
||
var i = 0;
|
||
for (i = 1; i <= dice; i += 1) {
|
||
console.log("i = " + i);
|
||
newFormula += "[[1d6]] ";
|
||
console.log("Roll Formula = " + newFormula);
|
||
}
|
||
setAttrs({repeating_attacks_atk: newFormula});
|
||
});
|
||
});
|
||
|
||
/* ---- END: Build Dice Roll for OVA Template ---- */
|
||
</script>
|
||
<!-- End Sheet Workers -->
|
||
<div style="width: 810px; background-color: white">
|
||
<div
|
||
class="sheet-row"
|
||
style="
|
||
padding-left: 75px;
|
||
width: 733px;
|
||
background-color: white;
|
||
margin-bottom: 15px;
|
||
"
|
||
><!--Header-->
|
||
<div class="sheet-col">
|
||
<!--<img src="http://www.geeksville.us/roll20/OVALogo.png">-->
|
||
<img src="http://i.imgur.com/IDEiHQ2.png" />
|
||
</div>
|
||
|
||
<div
|
||
class="sheet-col sheet-red"
|
||
style="
|
||
padding-top: 5px;
|
||
padding-left: 25px;
|
||
padding-right: 38px;
|
||
margin-top: 25px;
|
||
margin-left: -25px;
|
||
z-index: -1;
|
||
"
|
||
>
|
||
<input
|
||
class="sheet-name"
|
||
type="text"
|
||
name="attr_character_name"
|
||
title="@{character_name}"
|
||
/>
|
||
|
||
<input
|
||
class="sheet-name"
|
||
type="text"
|
||
name="attr_player_name"
|
||
title="@{player_name}"
|
||
/><br />
|
||
<div class="sheet-names" style="width: 232px"
|
||
>Character Name</div
|
||
>
|
||
<div class="sheet-names">Player Name</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!--Character Sheet data -->
|
||
<div class="sheet-row" style="width: 800px; margin-top: -45px">
|
||
<span style="margin-left: 300px; font-weight: bold"
|
||
>Number of Dice to roll:</span
|
||
>
|
||
<input
|
||
type="number"
|
||
name="attr_numdicetoroll"
|
||
value="1"
|
||
style="
|
||
border: 1px solid #801421;
|
||
background-color: #eee2e2;
|
||
color: black;
|
||
text-align: center;
|
||
"
|
||
/>
|
||
<input
|
||
type="text"
|
||
name="attr_rollformula"
|
||
style="display: none"
|
||
value="{{Die one=[[1d6]]}}"
|
||
/>
|
||
<button
|
||
class="sheet-templatebutton"
|
||
type="roll"
|
||
name="roll_Generic-Roll"
|
||
title="@{Generic-Roll}"
|
||
value="&{template:ova} {{name=@{character_name}}} @{rollformula}"
|
||
></button>
|
||
<!--<button class="sheet-initiativebutton" type='roll' name='roll_initiative' title="@{initiative}" value='/roll @{numdicetoroll}d6 &{tracker}'>Initiative</button>-->
|
||
<span style="margin-left: 100px"> </span>
|
||
<input
|
||
type="radio"
|
||
class="sheet-tab sheet-tab1"
|
||
name="attr_core-tab"
|
||
value="1"
|
||
title="Stats & Abilities"
|
||
checked="checked"
|
||
/>
|
||
<span class="sheet-tab sheet-tab1">Stats</span>
|
||
<input
|
||
type="radio"
|
||
class="sheet-tab sheet-tab2"
|
||
name="attr_core-tab"
|
||
value="2"
|
||
title="Background & Notes"
|
||
/>
|
||
<span class="sheet-tab sheet-tab2">Misc</span>
|
||
<div class="sheet-shim" style="width: 790px; height: 100%"
|
||
> </div
|
||
>
|
||
<div class="sheet-section-core">
|
||
<div class="sheet-row" style="width: 100%">
|
||
<div
|
||
class="sheet-3colrow sheet-pink"
|
||
style="
|
||
width: 94%;
|
||
border-top-right-radius: 5px;
|
||
border-bottom-right-radius: 5px;
|
||
padding-bottom: 5px;
|
||
"
|
||
>
|
||
<div
|
||
class="sheet-col sheet-pink"
|
||
style="width: 80px; margin-right: -5px"
|
||
>
|
||
</div
|
||
>
|
||
<div
|
||
class="sheet-col sheet-pink"
|
||
style="width: 320px; margin-right: 5px"
|
||
>
|
||
<h4>Abilities</h4>
|
||
<fieldset
|
||
class="repeating_abilities"
|
||
style="background-color: #eee2e2"
|
||
>
|
||
<input
|
||
class="sheet-mod"
|
||
type="text"
|
||
name="attr_abilitymod"
|
||
title="@{repeating_abilities_#_abilitymod}"
|
||
/>
|
||
<input
|
||
class="sheet-ability"
|
||
type="text"
|
||
name="attr_ability"
|
||
title="@{repeating_abilities_#_ability}"
|
||
/>
|
||
<input
|
||
class="sheet-fold"
|
||
type="checkbox"
|
||
name="attr_abilityfold"
|
||
title="@{repeating_abilities_#_fold}"
|
||
/>
|
||
<textarea
|
||
class="sheet-desc"
|
||
name="attr_abilitydesc"
|
||
title="@{repeating_abilities_#_desc}"
|
||
></textarea>
|
||
</fieldset>
|
||
</div>
|
||
|
||
<div
|
||
class="sheet-col sheet-pink"
|
||
style="
|
||
width: 320px;
|
||
margin-left: -10px;
|
||
padding-left: 15px;
|
||
border-top-right-radius: 10px;
|
||
border-bottom-right-radius: 10px;
|
||
"
|
||
>
|
||
<h4>Weaknesses</h4>
|
||
<fieldset
|
||
class="repeating_weaknesses"
|
||
style="background-color: #eee2e2"
|
||
>
|
||
<input
|
||
class="sheet-mod"
|
||
type="text"
|
||
name="attr_weaknessmod"
|
||
title="@{repeating_abilities_#_weaknessmod}"
|
||
/>
|
||
<input
|
||
class="sheet-ability"
|
||
type="text"
|
||
name="attr_weakness"
|
||
title="@{repeating_abilities_#_weakness}"
|
||
/>
|
||
<input
|
||
class="sheet-fold"
|
||
type="checkbox"
|
||
name="attr_weaknessfold"
|
||
title="@{repeating_abilities_#_fold}"
|
||
/>
|
||
<textarea
|
||
class="sheet-desc"
|
||
name="attr_weaknessdesc"
|
||
title="@{repeating_abilities_#_desc}"
|
||
></textarea>
|
||
</fieldset>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Release Notes Info for New Buttons -->
|
||
<input
|
||
type="checkbox"
|
||
class="sheet-Dismiss"
|
||
style="display: none"
|
||
name="attr_dismissRN"
|
||
value="1"
|
||
/>
|
||
<div
|
||
class="sheet-row sheet-CSRelease"
|
||
style="width: 100%; padding-top: 5px; padding-bottom: 5px"
|
||
>
|
||
<div class="sheet-col" style="width: 80px"> </div>
|
||
<div
|
||
class="sheet-col"
|
||
style="width: 240px; text-align: right"
|
||
>Combat Stats Release Notes</div
|
||
>
|
||
<div class="sheet-col" style="width: 400px">
|
||
<input
|
||
type="checkbox"
|
||
class="sheet-releasenotes"
|
||
name="attr_releasenotes"
|
||
value="1"
|
||
checked
|
||
/><span class="sheet-releasenotes"></span>
|
||
<div
|
||
class="sheet-row sheet-releasenotes"
|
||
style="width: 100%; padding: 0px; margin: 0px"
|
||
>
|
||
<input
|
||
type="checkbox"
|
||
class="sheet-Dismiss"
|
||
name="attr_dismissRN"
|
||
value="1"
|
||
/><span class="sheet-Dismiss"
|
||
> Dismiss</span
|
||
>
|
||
|
||
<p>
|
||
In order to get the Combat Stats roll buttons
|
||
to work for existing characters with existing
|
||
Stats, each stat will need to be changed so
|
||
the sheet worker gets triggered. To
|
||
accomplish this, you will need to change the
|
||
number in each of the Roll boxes to a
|
||
different number and hit enter, then you'll
|
||
need to change it back and hit enter a second
|
||
time. After that, each roll button should
|
||
work. You should not need to do this when
|
||
creating a new a character, nor when adding a
|
||
new Combat Stat.<br /><br />
|
||
To hide this message, simply click on the
|
||
downward pointing triangle above this
|
||
message.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- End Release Notes Info for New Buttons -->
|
||
<div class="sheet-row" style="width: 100%; padding-top: 25px">
|
||
<div class="sheet-row" style="width: 100%">
|
||
<div
|
||
class="sheet-col"
|
||
style="width: 80px; margin-right: -5px"
|
||
>
|
||
</div
|
||
>
|
||
<div
|
||
class="sheet-col sheet-pink"
|
||
style="
|
||
width: 240px;
|
||
font-weight: bold;
|
||
border-radius: 5px;
|
||
padding: 5px;
|
||
padding-right: 0px;
|
||
margin-right: -5px;
|
||
"
|
||
>
|
||
COMBAT NOTES <br />
|
||
<div
|
||
class="sheet-row"
|
||
style="
|
||
border: none;
|
||
border-radius: 5px;
|
||
background-color: white;
|
||
color: black;
|
||
font-size: 0.75em;
|
||
"
|
||
>
|
||
<textarea
|
||
name="attr_combatnotes"
|
||
style="
|
||
border-radius: 5px;
|
||
height: 420px;
|
||
"
|
||
></textarea
|
||
><br />
|
||
<span
|
||
style="
|
||
color: #eee2e2;
|
||
text-transform: uppercase;
|
||
font-size: 1.5em;
|
||
"
|
||
> Damage, Complications, Etc.</span
|
||
>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="sheet-col sheet-red"
|
||
style="
|
||
width: 400px;
|
||
padding: 5px;
|
||
border-radius: 10px;
|
||
font-weight: bold;
|
||
font-size: 2em;
|
||
margin-top: -10px;
|
||
"
|
||
>
|
||
Combat Stats<br />
|
||
<div class="sheet-row sheet-combatstatsmain">
|
||
<div
|
||
class="sheet-row"
|
||
style="
|
||
width: 100%;
|
||
padding: 5px;
|
||
vertical-align: top;
|
||
"
|
||
>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 198px;
|
||
text-align: left;
|
||
height: 85px;
|
||
"
|
||
>
|
||
<input
|
||
type="text"
|
||
name="attr_mode1"
|
||
placeholder="Attack"
|
||
style="
|
||
width: 146px;
|
||
border: none;
|
||
color: #801421;
|
||
font-weight: bold;
|
||
margin: 0px;
|
||
padding: 0px;
|
||
"
|
||
/><button
|
||
class="sheet-templatebutton"
|
||
type="roll"
|
||
name="roll_attack-roll1"
|
||
title="@{attack-roll}"
|
||
value="@{bv1}"
|
||
></button
|
||
><button
|
||
class="sheet-GM-button"
|
||
type="roll"
|
||
name="roll_attack-roll1"
|
||
title="@{attack-roll}"
|
||
value="/w gm @{bv1}"
|
||
></button
|
||
><br />
|
||
<textarea
|
||
name="attr_modedesc1"
|
||
placeholder="description"
|
||
class="sheet-combattext"
|
||
></textarea>
|
||
<input
|
||
type="text"
|
||
style="display: none"
|
||
name="attr_bv1"
|
||
value="&{template:ova} {{name=@{character_name}}} {{attack=@{mode1}}} {{ATK (@{roll1})=@{atk1}}} {{DX=@{dx1}}} {{END=@{end1}}} {{desc=@{modedesc1}}}"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 50px;
|
||
margin-left: -7px;
|
||
vertical-align: top;
|
||
height: 85px;
|
||
"
|
||
>
|
||
ROLL
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 100%;
|
||
height: 70px;
|
||
"
|
||
name="attr_roll1"
|
||
/><br />
|
||
<input
|
||
type="text"
|
||
name="attr_atk1"
|
||
style="display: none"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 45px;
|
||
margin-left: -7px;
|
||
height: 85px;
|
||
"
|
||
>
|
||
DX
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 43px;
|
||
height: 70px;
|
||
"
|
||
name="attr_dx1"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 50px;
|
||
margin-left: -7px;
|
||
border-right: none;
|
||
height: 85px;
|
||
"
|
||
>
|
||
END
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 47px;
|
||
height: 70px;
|
||
"
|
||
name="attr_end1"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<fieldset
|
||
class="repeating_attacks"
|
||
style="font-size: 1em"
|
||
>
|
||
<div
|
||
class="sheet-row"
|
||
style="
|
||
width: 100%;
|
||
padding: 5px;
|
||
padding-top: 0px;
|
||
vertical-align: top;
|
||
margin-top: -5px;
|
||
"
|
||
>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 198px;
|
||
text-align: left;
|
||
height: 85px;
|
||
"
|
||
>
|
||
<input
|
||
type="text"
|
||
name="attr_mode"
|
||
placeholder="Attack"
|
||
style="
|
||
width: 146px;
|
||
border: none;
|
||
color: #801421;
|
||
font-weight: bold;
|
||
margin: 0px;
|
||
padding: 0px;
|
||
"
|
||
/><button
|
||
class="
|
||
sheet-templatebutton
|
||
"
|
||
type="roll"
|
||
name="roll_attack-roll"
|
||
title="@{attack-roll}"
|
||
value="@{bv}"
|
||
></button
|
||
><button
|
||
class="sheet-GM-button"
|
||
type="roll"
|
||
name="roll_attack-roll1"
|
||
title="@{attack-roll}"
|
||
value="/w gm @{bv}"
|
||
></button
|
||
><br />
|
||
<!--<span style="color: black;">Affinity: </span><input type="text" name="attr_affinity2" style="width:150px; border: none; color: black;"/><br />-->
|
||
<textarea
|
||
name="attr_modedesc2"
|
||
placeholder="description"
|
||
class="sheet-combattext"
|
||
></textarea>
|
||
<input
|
||
type="text"
|
||
style="display: none"
|
||
name="attr_bv"
|
||
value="&{template:ova} {{name=@{character_name}}} {{attack=@{mode}}} {{ATK (@{roll2})=@{atk}}} {{DX=@{dx2}}} {{END=@{end2}}} {{desc=@{modedesc2}}}"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 50px;
|
||
margin-left: -7px;
|
||
vertical-align: top;
|
||
height: 85px;
|
||
"
|
||
>
|
||
ROLL
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 47px;
|
||
height: 70px;
|
||
"
|
||
name="attr_roll2"
|
||
/><br />
|
||
<input
|
||
type="text"
|
||
name="attr_atk"
|
||
style="display: none"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 45px;
|
||
margin-left: -7px;
|
||
height: 85px;
|
||
"
|
||
>
|
||
DX
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 43px;
|
||
height: 70px;
|
||
"
|
||
name="attr_dx2"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="sheet-combatstat"
|
||
style="
|
||
width: 50px;
|
||
margin-left: -7px;
|
||
border-right: none;
|
||
height: 85px;
|
||
"
|
||
>
|
||
END
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
style="
|
||
width: 47px;
|
||
height: 70px;
|
||
"
|
||
name="attr_end2"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</fieldset>
|
||
<!--
|
||
<div class='sheet-row' style='width: 100%; padding: 5px; padding-top: 0px; vertical-align: top; margin-top: -5px;'>
|
||
<div class='sheet-combatstat' style='width:198px; text-align: left; height: 85px;'>
|
||
Mode 3: <input type='text' name='attr_mode3' style="width:145px; border: none; color: #801421;" /><br />
|
||
<span style="color: black;">Affinity: </span><input type="text" name="attr_affinity3" style="width:150px; border: none; color: black;"/><br />
|
||
<textarea name="attr_modedesc3" placeholder="description" class="sheet-combattext"></textarea>
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:50px; margin-left: -7px; vertical-align: top; height: 85px;'>
|
||
ROLL
|
||
<input type="number" class="sheet-combatnum" style='width: 47px; height: 70px;' name="attr_roll3" /><br />
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:45px; margin-left: -7px; height: 85px;'>
|
||
DX
|
||
<input type="number" class="sheet-combatnum" style="width:43px; height: 70px;" name="attr_dx3" />
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:50px; margin-left: -7px; border-right: none; height: 85px;'>
|
||
END
|
||
<input type="number" class="sheet-combatnum" style='width: 47px; height: 70px;' name="attr_end3" />
|
||
</div>
|
||
</div>
|
||
<div class='sheet-row' style='width: 100%; padding: 5px; padding-top: 0px; vertical-align: top; margin-top: -5px;'>
|
||
<div class='sheet-combatstat' style='width:198px; text-align: left; height: 85px;'>
|
||
Mode 4: <input type='text' name='attr_mode4' style="width:145px; border: none; color: #801421;" /><br />
|
||
<span style="color: black;">Affinity: </span><input type="text" name="attr_affinity4" style="width:150px; border: none; color: black;"/><br />
|
||
<textarea name="attr_modedesc4" placeholder="description" class="sheet-combattext"></textarea>
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:50px; margin-left: -7px; vertical-align: top; height: 85px;'>
|
||
ROLL
|
||
<input type="number" class="sheet-combatnum" style='width: 47px; height: 70px;' name="attr_roll4" /><br />
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:45px; margin-left: -7px; height: 85px;'>
|
||
DX
|
||
<input type="number" class="sheet-combatnum" style="width:43px; height: 70px;" name="attr_dx4" />
|
||
</div>
|
||
<div class='sheet-combatstat' style='width:50px; margin-left: -7px; border-right: none; height: 85px;'>
|
||
END
|
||
<input type="number" class="sheet-combatnum" style='width: 47px; height: 70px;' name="attr_end4" />
|
||
</div>
|
||
</div>
|
||
-->
|
||
<div
|
||
class="sheet-row"
|
||
style="
|
||
width: 100%;
|
||
margin-top: -6px;
|
||
border-top: 1px solid #801421;
|
||
"
|
||
>
|
||
<div class="sheet-col sheet-defense">
|
||
DEFENSE<br />
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
name="attr_defense"
|
||
/>
|
||
</div>
|
||
<div class="sheet-col sheet-health">
|
||
HEALTH<br />
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
name="attr_health"
|
||
/>
|
||
</div>
|
||
<div class="sheet-col sheet-endurance">
|
||
ENDURANCE<br />
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
name="attr_endurance"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="
|
||
sheet-col sheet-red sheet-tv
|
||
"
|
||
>
|
||
TV<br />
|
||
<input
|
||
type="number"
|
||
class="sheet-combatnum"
|
||
name="attr_threatvalue"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="sheet-col sheet-pink"
|
||
style="
|
||
width: 50px;
|
||
height: 475px;
|
||
font-weight: bold;
|
||
padding: 5px;
|
||
padding-right: 2px;
|
||
margin-left: -3px;
|
||
"
|
||
>
|
||
<!--
|
||
<div class='sheet-rotate'>
|
||
Character Record
|
||
</div>
|
||
-->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sheet-section-misc sheet-pink"
|
||
style="padding-bottom: 5px"
|
||
>
|
||
<div class="sheet-shim" style="width: 100%; height: 15px"
|
||
> </div
|
||
>
|
||
<div
|
||
class="sheet-col sheet-notes"
|
||
style="width: 97%; margin: 10px; border-radius: 5px"
|
||
>
|
||
Background<br />
|
||
<textarea name="attr_background"></textarea>
|
||
</div>
|
||
|
||
<div class="sheet-shim" style="width: 100%; height: 15px"
|
||
> </div
|
||
>
|
||
|
||
<div class="sheet-2colrow sheet-pink" style="width: 100%">
|
||
<div
|
||
class="sheet-col sheet-notes"
|
||
style="width: 46%; margin: 10px; border-radius: 5px"
|
||
>
|
||
Appearance <br />
|
||
<textarea name="attr_appearance"></textarea>
|
||
</div>
|
||
<div
|
||
class="sheet-col sheet-notes"
|
||
style="
|
||
width: 47%;
|
||
margin: 10px;
|
||
margin-right: 5px;
|
||
border-radius: 5px;
|
||
"
|
||
>
|
||
Personality <br />
|
||
<textarea name="attr_personality"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sheet-row sheet-notes"
|
||
style="
|
||
width: 97%;
|
||
margin: 10px;
|
||
margin-right: 5px;
|
||
border-radius: 5px;
|
||
padding-bottom: 5px;
|
||
"
|
||
>
|
||
Other Notes <br />
|
||
<textarea name="attr_othernotes"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!--End Character Sheet data -->
|
||
|
||
<!-- Roll Template Section -->
|
||
|
||
<rolltemplate class="sheet-rolltemplate-ova">
|
||
<!--<h2 class="sheet-caption">{{name}}</h2>-->
|
||
<div class="sheet-rtcard">
|
||
<table>
|
||
{{#name}}
|
||
<caption>{{name}}</caption>
|
||
{{/name}} {{#attack}}
|
||
<tbody>
|
||
<tr class="sheet-rth">
|
||
<th colspan="2">{{attack}}</th>
|
||
</tr>
|
||
|
||
{{/attack}} {{#allprops() attack desc}}
|
||
<tr>
|
||
<td class="sheet-rresults">{{key}}</td>
|
||
<td
|
||
class="sheet-rollresults"
|
||
style="text-align: right"
|
||
>{{value}}</td
|
||
>
|
||
</tr>
|
||
{{/allprops() attack desc}} {{#desc}}
|
||
<tr>
|
||
<td colspan="2" class="sheet-rollresults"
|
||
>{{desc}}</td
|
||
>
|
||
</tr>
|
||
{{/desc}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</rolltemplate>
|
||
</div>
|
||
|
||
|
||
|
||
|