Merge pull request #9936 from Kurohyou/rapidfireinitialrelease

Rapidfire system v1.0 for Outrider Studios
This commit is contained in:
Nic Bradley
2021-12-23 12:59:36 -08:00
committed by GitHub
39 changed files with 8076 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

@@ -0,0 +1,69 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Calculation Functions
const calcInitiative = function(trigger,attributes,sections){
debug('calculating initiative');
let awarenessID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'awareness');
let awareness = attributes[`repeating_skill_${awarenessID}_level`];
return awareness + attributes.mind + attributes.initiative_mod;
};
const calcDefence = function(trigger,attributes,sections){
debug('calculating defense');
let dodgeID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'dodge');
let dodge = attributes[`repeating_skill_${dodgeID}_level`];
let damagePenalty = determinePenalty('health',attributes,sections);
let total = 3 + attributes.body + dodge + attributes.shield_bonus + damagePenalty + attributes.defence_mod;
return Math.max(total,0);
};
const totalHealth = function(trigger,attributes,sections){
const type = trigger.name.replace(/_max/,'');
debug(`calculating total ${type}`);
return sections[`repeating_${type}`].reduce((total,id)=>{
total -= attributes[`repeating_${type}_${id}_damaged`] + attributes[`repeating_${type}_${id}_fill`];
return total;
},attributes[`${type}_max`]);
};
const calcResist = function(trigger,attributes,sections){
debug('calculating resist');
return Math.max(attributes.spirit + attributes.armour_bonus + attributes.resist_mod,0);
};
const calcSA = function(trigger,attributes,sections){
debug('calculating situational awareness');
return ['body','mind','spirit'].reduce((m,attr)=>{
return m + attributes[attr];
},0);
};
const calcRemnantInitiative = function(trigger,attributes,sections){
debug('calculating remnant initiative');
let awarenessID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'awareness');
let awareness = attributes[`repeating_skill_${awarenessID}_level`];
let penalty = determinePenalty('structure',attributes,sections);
return attributes.situational_awareness + awareness + penalty;
};
const calcRemnantDefence = function(trigger,attributes,sections){
debug('calculating remnant defence');
let damagePenalty = determinePenalty('structure',attributes,sections);
debug({damagePenalty});
return Math.max(attributes.speed + attributes.situational_awareness + attributes.motion_level + damagePenalty,0);
};
const calcStrikeRange = function(trigger,attributes,sections){
debug('calculating strike range');
const [section,rowID,field] = parseTriggerName(trigger.name);
let strike = section ? `${section}_${rowID}_strike` : 'strike_level';
return attributes[strike] * 100;
};
const calcRemnantDamage = function(trigger,attributes,sections){
debug('calculating remnant damage');
let [section,rowID,field] = parseTriggerName(trigger.name);
let skill = attributes[`${section}_${rowID}_skill`];
return attributes[`${skill}_damage`] + attributes[`${section}_${rowID}_damage_bonus`];
};
@@ -0,0 +1,92 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Expands/collapses the sections
const expandHeader = function(jEvent){
debug('entering expandHeader');
const name = jEvent.htmlAttributes.name.replace(/act_/,'');
const target = `.${name}.expandable-header`;
_getAttrs(['collapsed'],(attributes)=>{
attributes.collapsed = attributes.collapsed.split(/\s*,\s*/).filter(b => b!==name && !/^\s*$/.test(b) );
if(!/collapse/.test(jEvent.htmlAttributes.class)){
attributes.collapsed.push(name);
}
attributes.collapsed = attributes.collapsed.join(',');
$20(target).toggleClass('collapse');
attributes.set();
});
};
const imageInput = function(trigger,attributes,sections){
debug('entering imageInput');
let [section,rowID,field] = parseTriggerName(trigger.name);
if(!field) return;
if(!attributes[field]){
attributes[field] = 'https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png';
}
$20(`.${field.replace(/_/g,'-')} .image-container__input`).removeClass('active');
};
const toggleImageInput = function(jEvent){
let field = parseHTMLName(jEvent.htmlAttributes.name);
$20(`.${field.replace(/_/g,'-')} .image-container__input`).toggleClass('active');
};
//Calls the appropriate dynamic query function for the event
const dynamicSelect = function(event){
let[section,rowID,field] = parseClickTrigger(event.triggerName);
field = field.replace(/-action$/,'');
getAllAttrs({callback:async (attributes,sections)=>{
let selection = await dynamicQueries[field]({section,rowID,field},attributes,sections);
attributes[`${section}_${rowID}_${field}`] = selection;
attributes[`${section}_${rowID}_translate_${field}`] = getTranslationByKey(selection) ? 1 : 0;
attributes.set();
}});
};
const skillQuery = function(click,attributes,sections){
let query = sections.repeating_skill.reduce((prompt,id)=>{
if(attributes[`repeating_skill_${id}_raw`]){
prompt.push(`${capitalize(getTranslationByKey(attributes[`repeating_skill_${id}_name`]))},${attributes[`repeating_skill_${id}_name`]}`);
}else{
prompt.push(capitalize(attributes[`repeating_skill_${id}_name`]));
}
return prompt;
},[`${getTranslationByKey('skill query')}`]).join('|');
query = `${query}`
return extractQueryResult(query);
};
//Click Functions
//These functions are fired in response to an action button being clicked.
//Clears a tracker of all damage
const clearTracker = function(event){
debug('clearing tracker');
let [section,rowID,field] = parseClickTrigger(event.triggerName);
getAllAttrs({callback:(attributes,sections,casc)=>{
sections[section].forEach((id)=>{
attributes[`${section}_${id}_damaged`] = 0;
attributes[`${section}_${id}_fill`] = 0;
});
let attr = section.replace(/repeating_/,'');
attributes[attr] = attributes[`${attr}_max`];
let trigger = casc[attr];
processSheet(trigger,attributes,sections,casc);
}});
};
const addItem = function(event){
debug(`adding repeating item from ${event.triggerName}`);
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/add-/,'');
let rowID = generateRowID();
const setObj = {};
setObj[`repeating_${section}_${rowID}_name`] = '';
set(setObj);
};
const editSection = function(event){
debug(`editing repeating section from ${event.triggerName}`);
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/edit-/,'');
let target = `fieldset.repeating_${section} + .repcontainer`;
$20(target).toggleClass('editmode');
};
@@ -0,0 +1,16 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Initial change functions
const syncHealth = function(trigger,attributes,sections){
debug('syncing token and character health');
const type = trigger.name.replace(/_max/,'');
let damage = attributes[`${type}_max`] - attributes[type];
let damageIndex = damage - 1;
sections[`repeating_${type}`].forEach((id,index)=>{
attributes[`repeating_${type}_${id}_damaged`] = index === damageIndex ? 1 : 0;
attributes[`repeating_${type}_${id}_fill`] = index < damageIndex ? 1 : 0;
});
if(attributes[type] > attributes[`${type}_max`]){
attributes[type] = attributes[`${type}_max`];
}
};
+59
View File
@@ -0,0 +1,59 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//accesses the sheet and iterates through all changes necessary before calling setAttrs
const accessSheet = function(event){
debug({event});
getAllAttrs({callback:(attributes,sections,casc)=>{
let trigger = casc[event.sourceAttribute];
processSheet(trigger,attributes,sections,casc);
}});
};
const processSheet = function(trigger,attributes,sections,casc){
debug({trigger});
if(!trigger){
return;
}
initialFunction(trigger,attributes,sections);//functions that should only be run if the attribute was the thing changed
triggerFunctions(trigger,attributes,sections);//functions that cause complex changes to the character
let queue = [...trigger.affects];
while(queue.length){
let name = queue.shift();
debug(`processing ${name}`);
let obj = casc[name];
triggerFunctions(obj,attributes,sections);
if(obj.calculation){
attributes[name] = obj.calculation(obj,attributes,sections);//Basic calculation of an attribute value
}
queue = [...queue,...obj.affects];
}
debug('sheet accessed, setting attributes');
attributes.set();
};
const registerEventHandlers = function(){
on('sheet:opened',updateSheet);
toMonitor.forEach((m)=>{
on(`change:${m}`,accessSheet);
});
//Click Listeners
navButtons.forEach((button)=>{
on(`clicked:nav-${button}`,navigateSheet);
});
trackerMonitors.forEach((button)=>{
on(`clicked:${button}`,clearTracker);
});
queryButtons.forEach((button)=>{
on(`clicked:${button}`,dynamicSelect);
});
rollMonitor.forEach((button)=>{
on(`clicked:${button}`,initiateRoll);
});
repeatAddMonitors.forEach((button)=>{
on(`clicked:add-${button}`,addItem);
on(`clicked:edit-${button}`,editSection);
})
//jQuery listeners
$20('.expandable-header').on('click',expandHeader);
$20('.image-container__button').on('click',toggleImageInput);
};
@@ -0,0 +1,178 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Roll Functions
//Begins the parsing of a roll
const skillRollDetails = function({section,rowID,sections,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
rollObj.header = attributes[`${section}_${rowID}_raw`] ? `^{${rollObj.header}}` : rollObj.header;
let skill = attributes[`${section}_${rowID}_level`];
let stat = attributes[`${section}_${rowID}_stat`];
let penalty = determinePenalty('health',attributes,sections);
if(stat === 'query'){
stat = `?{${getTranslationByKey('stat query')}|${['body','mind','spirit'].map((s)=>`${getTranslationByKey(s)},@{${s}}[${s}]`).join('|')}}`;
}else{
stat = `[[0@{${stat}}]][${stat}]`;
}
rollObj.roll = `[[1d6 + ${stat} + [[0${skill}]][${rollObj.header}] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
};
const weaponRollDetails = function({section,rowID,field,sections,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
let skill;
let stat;
let healthType = determineHealthType(section,field);
let penalty = determinePenalty(healthType,attributes,sections);
debug({healthType,penalty});
if(section === 'repeating_weapon'){
let skillID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === attributes[`${section}_${rowID}_skill`]);
let skillRef = `repeating_skill_${skillID}`;
skill = attributes[`${skillRef}_level`];
stat = attributes[`${skillRef}_stat`];
if(stat === 'query'){
stat = `?{${getTranslationByKey('stat query')}|${['body','mind','spirit'].map((s)=>`${getTranslationByKey(s)},@{${s}}[${s}]`).join('|')}}`;
}else{
stat = `@{${stat}}[${stat}]`;
}
rollObj.roll = `[[1d6 + ${stat} + ${skill}[${attributes[`${skillRef}_name`]}] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
rollObj.damage = `[[${attributes[`${section}_${rowID}_damage`]} + Lead]]`;
}else if(section === 'repeating_remnant-weapon'){
let skillName = attributes[`${section}_${rowID}_skill`];
skill = attributes[`${skillName}_level`];
stat = attributes.situational_awareness;
rollObj.roll = `[[1d6 + [[0${stat}]][SA] + [[0${skill}]][${skillName}] + [[0${attributes[`${section}_${rowID}_bonus`]}]] + ${penalty}[Damage Penalty]${actionPenalty} ]]`;
rollObj.damage = `[[${attributes[`${section}_${rowID}_damage`]+ attributes[`${section}_${rowID}_damage_bonus`]} + Lead]]`;
}
debug({roll:rollObj.roll});
rollObj.description = `@{${section}_${rowID}_notes}`;
};
const droneRollDetails = async function({section,rowID,field,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
let numQuery = _.range(attributes[`${section}_${rowID}_active`]).map((num)=>{
return num +1;
});
let attackNum;
debug({numQuery});
if(numQuery.length <= 1){
attackNum = await pseudoQuery(1);
}else{
attackNum = await extractQueryResult(`${getTranslationByKey('drone query')}|${numQuery.join('|')}`);
}
debug({attackNum});
attackNum = +attackNum;
let bonus = Math.floor(attackNum / 2);
_.range(attackNum).forEach((n)=>{
let num = n+1;
rollObj[`roll${num}`] = `[[1d6 + [[0@{${section}_${rowID}_${field}}]][${field}] + ${bonus}[multiple attack]${actionPenalty}]]`;
rollObj[`status${num}`] = `[[0[computed value]]]`;
});
rollObj.damage = `[[${attributes[`${section}_${rowID}_${field}_dam`]} + Lead]]`;
rollObj.description = `@{${section}_${rowID}_description}`;
if(field === 'strike'){
rollObj.description = `^{strike range}: @{${section}_${rowID}_strikerange}\n@{${section}_${rowID}_description}`;
}
};
const initiateRoll = function(event){
let [section,rowID,field] = parseClickTrigger(event.triggerName);
field = field.replace(/-action/,'');
const rollSwitch = {
repeating_weapon:weaponRollDetails,
repeating_skill:skillRollDetails,
'repeating_remnant-weapon':weaponRollDetails,
repeating_drone:droneRollDetails
};
getAllAttrs({props:['difficulty','action_penalty_automation',...baseGet],callback:async (attributes,sections)=>{
let rollObj = {status:'[[0[computed value]]]'};
let actionPenalty = determineActionPenalty(attributes);
if(/^(?:assault|strike|motion)-roll$/.test(field)){
let penalty = determinePenalty('structure',attributes,sections);
field = field.replace(/-roll/,'');
rollObj.header = `^{${field}}`;
rollObj.roll = `[[1d6 + [[0@{${field}_level}]][${field}] + [[0@{situational_awareness}]][SA] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
rollObj.damage = /assault|strike/.test(field) ? `[[[[0@{${field}_damage}]] + Lead]]` : undefined;
}else if(!section){
//Base rolling for simple stats
let healthType = determineHealthType(section,field);
let penalty = determinePenalty(healthType,attributes,sections);
rollObj.header = `^{${field.replace(/-/g,' ')}}`;//output translation of name
rollObj.roll = `[[1d6 + [[0@{${field.replace(/-/g,'_')}}]][${field.replace(/-/g,' ')}] + ${penalty}[Damage Penalty]${actionPenalty}${/initiative/.test(field) ? '&{tracker}' : ''}]]`;
}else{
//rolling for more complex fields. These are typically repeating sections
if(section === 'repeating_drone'){
await rollSwitch[section]({section,rowID,field,sections,attributes,rollObj,actionPenalty});
}else{
rollSwitch[section]({section,rowID,field,sections,attributes,rollObj,actionPenalty});
}
}
if(attributes.difficulty && !/initiative/.test(field)){
rollObj.difficulty = `[[?{${getTranslationByKey('difficulty query')}|0}]]`;
rollObj.damage_label = rollObj.damage ? undefined : '[[0[computed value]]]';
rollObj.damage = rollObj.damage || `[[0[computed value]]]`;
}
if(!rollObj.damage && !rollObj.difficulty){
rollObj.singleroll = 'true';
}
executeRoll(rollObj);
}});
};
const determineActionPenalty = function(attributes){
const stateSwitch = {
disabled:'',
ask:`+ [[abs(0?{${getTranslationByKey('action penalty query')}|0}) * -1]][Action Penalty]`,
'use input':`+ ${attributes.action_penalty}[Action Penalty]`
};
return stateSwitch[attributes.action_penalty_automation];
};
const executeRoll = async function(rollObj){
let rollText = Object.entries(rollObj).reduce((text,[field,content])=>{
if(content){
text += `{{${field}=${content}}}`;
}
return text;
},'@{template_start}');
let roll = await startRoll(rollText);
const computeObj = {};
if(roll.results.roll1){
computeMultipleRolls(roll.results,computeObj,rollObj);
}else{
computeSingleRoll(roll.results,computeObj,rollObj);
}
finishRoll(roll.rollId,computeObj);
};
const computeMultipleRolls = function(results,computeObj,rollObj){
let rollNums = [6,5,4,3,2,1].find((num)=>results.hasOwnProperty(`roll${num}`));
computeObj[`damage`] = rollObj[`damage`] && rollObj.difficulty ? 0 : undefined;
_.range(rollNums).forEach((n)=>{
let num = n + 1;
if(rollObj[`damage`] && rollObj.difficulty){
let damage = results[`damage`].result + (results[`roll${num}`].result - results.difficulty.result);
if(damage < results[`damage`].result){
damage = 0;
}
computeObj.damage += damage;
}
if(rollObj.difficulty){
computeObj[`status`] = checkResult(results[`roll${num}`],results.difficulty);
}
});
};
const computeSingleRoll = function(results,computeObj,rollObj){
if(rollObj.damage && rollObj.difficulty){
computeObj.damage = results.damage.result + (results.roll.result - results.difficulty.result);
}
if(results.damage_label){
computeObj.damage_label = getTranslationByKey(computeObj.damage < 0 ? 'trail' : 'lead');
}
if(rollObj.difficulty){
computeObj[`status`] = checkResult(results[`roll`],results.difficulty);
}
};
const checkResult = function(roll,difficulty){
return roll.result - difficulty.result;
};
@@ -0,0 +1,65 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/*
Sheet Styling
*/
const navigateSheet = function(event){
debug('navigating sheet');
const setObj = {};
let [,,page] = parseClickTrigger(event.triggerName);
page = page.replace(/nav-|-action/g,'');
debug({page});
navButtons.forEach((button)=>{
let element = button.replace(/-action/,'');
let action = element === page ? 'addClass' : 'removeClass';
$20(`.${element}`)[action]('active');
});
setObj.sheet_state = page;
debug({setObj});
set(setObj);
};
const setupSystem = function(trigger,attributes,sections){
setupAttributes(attributes,sections);
setupSkills(attributes,sections);
setupHealth(attributes,sections);
};
const setupAttributes = function(attributes,sections){
const attrDetails = systemDefaults[attributes.system].attributes;
Object.entries(attrDetails).forEach(([attr,value])=>{
attributes[attr] = value;
});
};
const setupSkills = function(attributes,sections){
let skillDetails = systemDefaults[attributes.system].repeating_skill.values;
let missingSkills = Object.keys(skillDetails).filter((skill)=>sections.repeating_skill.every((id)=>attributes[`repeating_skill_${id}_name`]===skill));
missingSkills.forEach((skill)=>{
let newID = generateRowID();
attributes[`repeating_skill_${newID}_name`] = skill;
attributes[`repeating_skill_${newID}_stat`] = /\//.test(skillDetails[skill]) ? 'query' : skillDetails[skill];
attributes[`repeating_skill_${newID}_raw`] = 1;
});
};
const setupHealth = function(attributes,sections){
let healthDetails = systemDefaults[attributes.system].repeating_health.values;
if(healthDetails && !sections.repeating_health.length){
healthDetails.forEach((penalty)=>{
let newID = generateRowID();
attributes[`repeating_health_${newID}_penalty`] = penalty;
});
attributes.health = healthDetails.length;
attributes.health_max = attributes.health;
}
let structureDetails = systemDefaults[attributes.system].repeating_structure.values;
if(structureDetails && !sections.repeating_structure.length){
structureDetails.forEach((penalty)=>{
let newID = generateRowID();
attributes[`repeating_structure_${newID}_penalty`] = penalty;
});
attributes.structure = structureDetails.length;
attributes.structure_max = attributes.structure;
attributes.structure_base = attributes.structure;
}
};
@@ -0,0 +1,95 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
// Triggered Functions
const setActionCalls = function(trigger,attributes,sections){
debug('setting action calls');
actionAttributes.forEach((base)=>{
let [section,,field] = parseTriggerName(base);
let fieldAction = field.replace(/_/g,'-');
if(section){
sections[section].forEach((id)=>{
attributes[`${section}_${id}_${field}`] = `%{${attributes.character_name}|${section}_${id}_${fieldAction}}`;
});
}else{
attributes[`${field}`] = `%{${attributes.character_name}|${fieldAction}}`;
}
});
debug({updates:attributes.updates});
};
const checkHealth = function(trigger,attributes,sections){
debug('checking current health trackers')
let [section,rowID] = parseRepeatName(trigger.name);
let currDamage = sections[section].indexOf(rowID);
sections[section].forEach((id,index)=>{
if(index !== currDamage){
attributes[`${section}_${id}_damaged`] = 0;
}
attributes[`${section}_${id}_fill`] = index < currDamage ? 1 : 0;
});
};
const updateTrack = function(section,attributes,sections){
let newTracks = attributes[`${section}_max`] - sections[`repeating_${section}`].length;
if(newTracks > 0){
_.range(newTracks).forEach((n)=>{
let newID = generateRowID();
let priorPenalties = sections[`repeating_${section}`].slice(Math.max(0,sections[`repeating_${section}`].length - 2))
.map((id)=>{
return attributes[`repeating_${section}_${id}_penalty`];
});
let newPenalty;
if(priorPenalties[0] !== priorPenalties[1]){
newPenalty = priorPenalties[1] || priorPenalties[0];
}else{
let penStart = priorPenalties[1] || priorPenalties[0];
newPenalty = Math.max(Math.min(-3,penStart),penStart - 1);
}
attributes[`repeating_${section}_${newID}_damaged`] = 0;
attributes[`repeating_${section}_${newID}_penalty`] = newPenalty;
sections[`repeating_${section}`].push(newID);
});
}else if(newTracks < 0){
let slicePoint = Math.max(0,sections[`repeating_${section}`].length + newTracks);
let extraTracks = sections[`repeating_${section}`].slice(slicePoint);
extraTracks.forEach((id)=>{
_removeRepeatingRow(`repeating_${section}_${id}`,attributes,sections);
});
}
};
const maxHealth = function(type,attributes){
const typeSwitch = {
structure:()=> attributes.structure_base,
health:()=> 3 + attributes.body + attributes.spirit + attributes.health_mod
}
return Math.max(typeSwitch[type](),1);
};
const calcHealth = function(trigger,attributes,sections){
const type = trigger.name.replace(/_max/,'');
debug(`calculating ${type}`);
attributes[`${type}_max`] = maxHealth(type,attributes);
let healthDiff = attributes[`${type}_max`] - value(attributes.attributes[`${type}_max`]);
updateTrack(type,attributes,sections);
attributes[type] = attributes[type] + healthDiff;
syncHealth(trigger,attributes,sections);
};
const skillEffects = function(trigger,attributes,sections){
let [section,rowID,field] = parseRepeatName(trigger.name);
let skillName = attributes[`${section}_${rowID}_name`];
const skillSwitch = {
dodge:['defence'],
awareness:['initiative']
};
if(skillSwitch[skillName]){
trigger.affects = [...trigger.affects,...skillSwitch[skillName]];
}
};
const validateActionPenalty = function(trigger,attributes,sections){
if(attributes.action_penalty > 0){
attributes.action_penalty = attributes.action_penalty * -1;
}
};
+354
View File
@@ -0,0 +1,354 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//# Attribute Obj Proxy handler
const createAttrProxy = function(attributes){
//creates a proxy for the attributes object so that values can be worked with more easily.
const attrTarget = {
updates:{},
attributes:{...attributes},
repOrders:{}
};
const attrHandler = {
get:function(obj,prop){//gets the most value of the attribute.
//If it is a repeating order, returns the array, otherwise returns the update value or the original value
if(prop === 'set'){
return function(){
set(obj.updates,...arguments);
}
}else if(Object.keys(obj).some(key=>key===prop)){
return Reflect.get(...arguments)
}else{
let retValue;
switch(true){
case obj.repOrders.hasOwnProperty(prop):
retValue = obj.repOrders[prop];
break;
case obj.updates.hasOwnProperty(prop):
retValue = obj.updates[prop];
break;
default:
retValue = obj.attributes[prop];
break;
}
let cascRef = prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$x');
let numRetVal = +retValue;
if(!Number.isNaN(numRetVal)){
retValue = numRetVal;
}else if(cascades[cascRef] && cascades[cascRef].type === 'number'){
retValue = cascades[cascRef].defaultValue || 0;
}
return retValue;
}
},
set:function(obj,prop,value){
//Sets the value. Also verifies that the value is a valid attribute value
//e.g. not undefined, null, or NaN
if(value || value===0 || value===''){
if(/reporder|^repeating_[^_]+$/.test(prop)){
let section = prop.replace(/_reporder_/,'');
obj.repOrders[section] = value;
}else{
obj.updates[prop] = value;
}
}else{
debug(`!!!Warning: Attempted to set ${prop} to an invalid value:${value}; value not stored!!!`);
}
return true;
},
deleteProperty(obj,prop){
//removes the property from the original attributes, updates, and the reporders
Object.keys(obj).forEach((key)=>{
delete obj[key][prop.toLowerCase()];
});
}
};
return new Proxy(attrTarget,attrHandler);
};
const _setSectionOrder = function(section,order){
let trueSection = section.replace(/repeating_/,'');
setSectionOrder(trueSection,order);
};
const _removeRepeatingRow = function(row,attributes,sections){
debug(`removing ${row}`);
Object.keys(attributes.attributes).forEach((key)=>{
if(key.startsWith(row)){
delete attributes[key];
}
});
let [,section,rowID] = row.match(/(repeating_[^_]+)_(.+)/,'');
sections[section] = sections[section].filter((id)=>id!==rowID);
removeRepeatingRow(row);
};
const _getAttrs = function(props=baseGet,callback){
getAttrs(props,(values)=>{
const attributes = createAttrProxy(values);
callback(attributes);
});
};
const getAllAttrs = function({callback,props=baseGet,sectionDetails=repeatingSectionDetails}){
getSections(sectionDetails,(repeats,sections)=>{
getAttrs([...props,...repeats],(values)=>{
const attributes = createAttrProxy(values);
orderSections(attributes,sections);
const casc = expandCascade(cascades,sections,attributes);
callback(attributes,sections,casc);
})
});
};
const getSections = function(section_details,callback){
let queueClone = _.clone(section_details);
const worker = (queue,repeatAttrs=[],sections={})=>{
let detail = queue.shift();
getSectionIDs(detail.section,(IDs)=>{
sections[detail.section] = IDs;
IDs.forEach((id)=>{
detail.fields.forEach((f)=>{
repeatAttrs.push(`${detail.section}_${id}_${f}`);
});
});
repeatAttrs.push(`_reporder_${detail.section}`);
if(queue.length){
worker(queue,repeatAttrs,sections);
}else{
callback(repeatAttrs,sections);
}
});
};
if(!queueClone[0]){
callback([],{});
}
worker(queueClone);
};
// Sets the attributes while always calling with {silent:true}
// Can be awaited to get the values returned from _setAttrs
const set = function(obj,vocal=false){
setAttrs(obj,{silent:!vocal});
};
//sanitizes text for use as a regex
sanitizeForRegex = function(text){
return text.replace(/\.|\||\(|\)|\[|\]|\-|\+|\?|\/|\{|\}|\^|\$|\*/g,'\\$&');
};
//converts a value to a number, it's default value, or 0
const value = function(val,def){
return (+val||def||0);
};
//extracts the section, rowID, and field name from a repeating attribute name
/*
e.g. repeating_equipment_-09Jhu89z_bulk would give:
section: equipment
rowID: -09Jhu89z
field: bulk
*/
const parseRepeatName = function(string){
let match = string.match(/(repeating_[^_]+)_([^_]+)(?:_(.+))?/);
match.shift();
return match;
};
const parseTriggerName = function(string){
let match = string.match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
match.shift();
return match;
};
const parseClickTrigger = function(string){
let match = string.match(/clicked:(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
match.shift();
return match;
};
const parseHTMLName = function(string){
let match = string.match(/(?:attr|act|roll)_(.+)/);
match.shift();
return match[0];
};
const capitalize = function(string){
return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase());
};
const extractQueryResult = async function(query){
debug('entering extractQueryResult');
let queryRoll = await startRoll(`!{{query=[[0[response=?{${query}}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
const pseudoQuery = async function(value){
debug('entering pseudoQuery');
let queryRoll = await startRoll(`!{{query=[[0[response=${value}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
//Determines which penalty to apply (if any)
const determinePenalty = function(type,attributes,sections){
let damageID = sections[`repeating_${type}`].find((id)=> attributes[`repeating_${type}_${id}_damaged`] === 1);
return damageID ? attributes[`repeating_${type}_${damageID}_penalty`] : 0;
};
//determines which health track's penalties should affect an attribute
const determineHealthType = function(section,field){
const attrCharacterLookup = {
'situational-awareness':'structure',
'remnant-initiative':'structure',
'assault-roll':'structure',
'strike-roll':'structure',
'motion-roll':'structure',
repeating_drone:'structure',
'repeating_remnant-weapon':'structure'
};
return attrCharacterLookup[section] || attrCharacterLookup[field] || 'health';
};
//# Utility Functions #
const log = function(msg){
if(typeof msg === 'string'){
console.log(`%c${sheetName} log| ${msg}`,"background-color:#159ccf");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${sheetName} log| ${m}: ${msg[m]}`,"background-color:#159ccf");
}else{
console.log(`%c${sheetName} log| ${typeof msg[m]} ${m}`,"background-color:#159ccf");
console.table(msg[m]);
}
});
}
};
const debug = function(msg,force){
if(!debugMode && !force && version > 0) return;
if(typeof msg === 'string'){
console.log(`%c${sheetName} DEBUG| ${msg}`,"background-color:tan;color:red;");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${sheetName} DEBUG| ${m}: ${msg[m]}`,"background-color:tan;color:red;");
}else{
console.log(`%c${sheetName} DEBUG| ${typeof msg[m]} ${m}`,"background-color:tan;color:red;font-weight:bold;");
console.table(msg[m]);
}
});
}
};
//Section ordering
//orders the section id arrays to match the repOrder attribute
const orderSections = function(attributes,sections){
Object.keys(sections).forEach((section)=>{
attributes.attributes[`_reporder_${section}`] = commaArray(attributes[`_reporder_${section}`]);
orderSection(attributes.attributes[`_reporder_${section}`],sections[section]);
});
};
//orders a single ID array
const orderSection = function(repOrder,IDs=[]){
IDs.sort((a,b)=>{
return repOrder.indexOf(a.toLowerCase()) - repOrder.indexOf(b.toLowerCase());
});
};
//splits a comma delimited string into an array
const commaArray = function(string=''){
return string.toLowerCase().split(/\s*,\s*/);
};
//Creates a row ID specific for an add section for pseudo nested repeating sections
const generateCustomID = function(string,rowID){
rowID = rowID || generateRowID();
let re = new RegExp(`^.{${string.length}}`);
return `${string}${rowID.replace(re,'')}`;
};
//Sheet Updaters and styling functions
const updateSheet = function(){
debug('updating sheet');
getAllAttrs({props:['sheet_version','debug_mode','collapsed',...baseGet],callback:(attributes,sections,casc)=>{
debugMode = !!attributes.debug_mode;
attributes.sheet_version = version;
if(!attributes.sheet_version){
initialSetup(attributes,sections);
}
log(`Sheet Update applied. Current Sheet Version ${version}`);
styleOnOpen(attributes,sections);
setActionCalls({},attributes,sections);
attributes.set();
log('Sheet ready for use');
}})
};
const initialSetup = function(attributes,sections){
debug('Initial sheet setup');
};
const styleOnOpen = function(attributes,sections){
navigateSheet({triggerName:`clicked:${attributes.sheet_state}`});
setupSystem({},attributes,sections);
};
/*
Cascade Expansion functions
*/
//Expands the repeating section templates in cascades to reflect the rows actually available
const expandCascade = function(cascade,sections,attributes){
return _.keys(cascade).reduce((memo,key)=>{//iterate through cascades and replace references to repeating attributes with correct row ids.
if(/^repeating/.test(key)){//If the attribute is a repeating attribute, do special logic
expandRepeating(memo,key,cascade,sections,attributes);
}else{//for non repeating attributes do this logic
expandNormal(memo,key,cascade,sections);
}
return memo;
},{});
};
const expandRepeating = function(memo,key,cascade,sections,attributes){
key.replace(/(repeating_[^_]+)_[^_]+?_(.+)/,(match,section,field)=>{
(sections[section]||[]).forEach((id)=>{
memo[`${section}_${id}_${field}`]=_.clone(cascade[key]);//clone the details so that each row's attributes have correct ids
memo[`${section}_${id}_${field}`].name = `${section}_${id}_${field}`;
memo[`${section}_${id}_${field}`].affects = memo[`${section}_${id}_${field}`].affects.reduce((m,affected)=>{
if(section === affected){//otherwise if the affected attribute is in the same section, simply set the affected attribute to have the same row id.
m.push(applyID(affected,id));
}else if(/repeating/.test(affected)){//If the affected attribute isn't in the same repeating section but is still a repeating attribute, add all the rows of that section
addAllRows(affected,m,sections);
}else{//otherwise the affected attribute is a non repeating attribute. Simply add it to the computed affected array
m.push(affected);
}
return m;
},[]);
});
});
};
const applyID = function(affected,id){
return affected.replace(/(repeating_[^_]+_)[^_]+(.+)/,`$1${id}$2`);
};
const expandNormal = function(memo,key,cascade,sections){
memo[key] = _.clone(cascade[key]);
memo[key].affects = memo[key].affects.reduce((m,a)=>{
if(/^repeating/.test(a)){
addAllRows(a,m,sections);
}else{
m.push(a);
}
return m;
},[]);
};
const triggerFunctions = function(obj,attributes,sections){
debug(`triggering functions for ${obj.name}`);
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>func(obj,attributes,sections));
};
const initialFunction = function(obj,attributes,sections){
debug(`initial functions for ${obj.name}`);
obj.initialFunc && obj.initialFunc(obj,attributes,sections);
};
const addAllRows = function(affected,memo,sections){
affected.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
sections[section].forEach(id=>memo.push(`${section}_${id}_${field}`));
});
};
let clickDelay = 500;
const defaultThrottle = callback => _.throttle(callback,clickDelay,{trailing:false});
const isDieRoll = function(field){
return /(?:d(?:8|10|12)(?:Crit)?|exertion)/.test(field);
};
+131
View File
@@ -0,0 +1,131 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Sheet Variables
const sheetName = 'Rapidfire System';
const version = 1.0;
let debugMode = false;
//Object that defines how the attributes affect each other
const repeatingSectionDetails = [
{section:'repeating_health',fields:['damaged','penalty','fill']},
{section:'repeating_structure',fields:['damaged','penalty','fill']},
{section:'repeating_skill',fields:['name','level','stat','xp','raw','roll_action']},
{section:'repeating_weapon',fields:['name','skill','stat','translate skill','damage','notes','collapse','roll_action']},
{section:'repeating_remnant-weapon',fields:['name','skill','bonus','damage','damage_bonus','type','notes','collapse','roll_action','notes']},
{section:'repeating_drone',fields:['name','active','sa','collapse','def','arm','spd','struct','assault','assault_dam','strike','strike_dam','strikerange','strike_action','assault_action','description']}
];
//const actionAttributes = ['body','mind','spirit','initiative','repeating_skill_$x_roll','repeating_weapon_$x_roll','situational_awareness','remnant_initiative','assault_roll','strike_roll','motion_roll','repeating_remnant-weapon_$x_roll','repeating_drone_$x_assault','repeating_drone_$x_strike'];
const systemDefaults = {
'remnants':{
repeating_skill:{field:'name',additional:true,values:{"archery / throwing":"body","athletics":"body","awareness":"mind","bureaucracy":"mind","command":"mind or spirit","craft":"body / mind / spirit","dodge":"body","etiquette":"spirit","history":"mind","investigation":"mind / spirit","language":"mind","larceny":"mind / body / spirit","lore":"mind","medicine":"mind","melee":"body","perform":"spirit","ride":"body / spirit","sail":"mind / spirit","sciences":"mind","social sciences":"mind","stealth":"body","survival":"mind","unarmed combat":"body"}},
repeating_health:{field:'penalty',additional:true,values:[0,-1,-1],alternate:true},
repeating_structure:{field:'penalty',additional:true,values:[0,0,-1,-1,-2],alternate:true},
attributes:{system_header:'https://s3.amazonaws.com/files.d20.io/images/260489274/xCVgiDGF-dMYpVGKXL5UXA/original.png',armour:5,speed:3,assault_damage:4,strike_damage:2,inspiring:1,terrifying:1}
}
};
const cascades = {
/*template
attribute_name:{
name:'attribute_name',
affects:['array','of','attributes','affected'],
calculation:funcCall,//Calculation for this attribute; optional property
triggeredFuncs:[func,func]
}*/
// ## Character stats ##
character_name:{name:'character_name',type:'text',affects:[],triggeredFuncs:[setActionCalls]},
sheet_version:{name:'sheet_version',type:'number',defaultValue:0,affects:[]},
system:{name:'system',affects:[],triggeredFuncs:[setupSystem]},
//Stats
body:{name:'body',type:'number',defaultValue:0,affects:['defence','health_max','situational_awareness'],triggerdFuncs:[syncHealth]},
mind:{name:'mind',type:'number',defaultValue:0,affects:['initiative','situational_awareness']},
spirit:{name:'spirit',type:'number',defaultValue:0,affects:['resist','health_max','situational_awareness'],triggerdFuncs:[syncHealth]},
//Secondary Stats
initiative:{name:'initiative',type:'number',defaultValue:0,affects:[],calculation:calcInitiative},
initiative_mod:{name:'initiative_mod',type:'number',defaultValue:0,affects:['initiative']},
defence:{name:'defence',type:'number',defaultValue:3,affects:[],calculation:calcDefence},
resist:{name:'resist',type:'number',defaultValue:0,affects:[],calculation:calcResist},
defence_mod:{name:'defence_mod',type:'number',defaultValue:0,affects:['defence']},
resist_mod:{name:'resist_mod',type:'number',defaultValue:0,affects:['resist']},
health:{name:'health',type:'number',defaultValue:3,affects:['defence'],calculation:totalHealth,initialFunc:syncHealth},
health_max:{name:'health_max',type:'number',defaultValue:3,affects:['defence'],triggeredFuncs:[calcHealth]},
health_mod:{name:'health_mod',type:'number',defaultValue:0,affects:['health_max']},
//Combat Gear
armour_bonus:{name:'armour_bonus',type:'number',defaultValue:0,affects:['resist']},
shield_bonus:{name:'shield_bonus',type:'number',defaultValue:0,affects:['defence']},
action_penalty:{name:'action_penalty',type:'number',defaultValue:0,affects:[],triggeredFuncs:[validateActionPenalty]},
//Health Track
repeating_health_$x_damaged:{name:'repeating_health_$x_damaged',type:'number',defaultValue:0,affects:['health'],triggeredFuncs:[checkHealth]},
repeating_health_$x_fill:{name:'repeating_health_$x_fill',type:'number',defaultValue:0,affects:[]},
repeating_health_$x_penalty:{name:'repeating_health_$x_penalty',type:'number',defaultValue:0,affects:['defence']},
//Skills
repeating_skill_$x_level:{name:'repeating_skill_$x_level',type:'number',defaultValue:0,affects:[],triggeredFuncs:[skillEffects]},
// ## Remnant Attributes ##
remnant_token:{name:'structure',type:'number',defaultValue:3,affects:[],triggeredFuncs:[imageInput]},
//Ratings
situational_awareness:{name:'situational_awareness',type:'number',defaultValue:0,affects:['remnant_initiative','remnant_defence'],calculation:calcSA},
remnant_defence:{name:'remnant_defence',type:'number',defaultValue:0,affects:[],calculation:calcRemnantDefence},
remnant_initiative:{name:'remnant_initiative',type:'number',defaultValue:0,affects:[],calculation:calcRemnantInitiative},
//Stats
speed:{name:'speed',type:'number',defaultValue:0,affects:['remnant_defence']},
strike_range:{name:'strike_range',type:'number',defaultValue:0,affects:[],calculation:calcStrikeRange},
structure:{name:'structure',type:'number',defaultValue:5,affects:['remnant_defence'],calculation:totalHealth,initialFunc:syncHealth},
structure_base:{name:'structure_base',type:'number',defaultValue:5,affects:['structure_max']},
structure_max:{name:'structure_max',type:'number',defaultValue:5,affects:['remnant_defence'],triggeredFuncs:[calcHealth]},
strike_damage:{name:'strike_damage',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
assault_damage:{name:'assault_damage',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
//Remnant Skills
strike_level:{name:'strike_level',type:'number',defaultValue:0,affects:['strike_range']},
assault_level:{name:'assault_level',type:'number',defaultValue:0,affects:[]},
motion_level:{name:'motion_level',type:'number',defaultValue:0,affects:['remnant_defence']},
//Structure Track
repeating_structure_$x_damaged:{name:'repeating_structure_$x_damaged',type:'number',defaultValue:0,affects:['structure'],triggeredFuncs:[checkHealth]},
repeating_structure_$x_fill:{name:'repeating_structure_$x_fill',type:'number',defaultValue:0,affects:[]},
repeating_structure_$x_penalty:{name:'repeating_structure_$x_penalty',type:'number',defaultValue:0,affects:['remnant_defence']},
//remnant-weapons
'repeating_remnant-weapon_$x_damage':{name:'repeating_remnant-weapon_$x_damage',type:'number',defaultValue:0,affects:[],calculation:calcRemnantDamage},
'repeating_remnant-weapon_$x_skill':{name:'repeating_remnant-weapon_$x_skill',type:'text',defaultValue:'assault',affects:['repeating_remnant-weapon_$x_damage']},
'repeating_remnant-weapon_$x_damage_bonus':{name:'repeating_remnant-weapon_$x_damage_bonus',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
//remnant Drones
'repeating_drone_$x_strike':{name:'repeating_drone_$x_strike',type:'number',defaultValue:0,affects:['repeating_drone_$x_strikerange']},
'repeating_drone_$x_active':{name:'repeating_drone_$x_active',type:'number',defaultValue:0,affects:[]},
'repeating_drone_$x_strikerange':{name:'repeating_drone_$x_strikerange',type:'number',defaultValue:0,affects:[],calculation:calcStrikeRange},
};
const systems = ['remnants'];
const repeatMonitor = [];
//const rollMonitor = ['body-action','mind-action','spirit-action','initiative-action','situational-awareness-action','remnant-initiative-action','assault-roll-action','strike-roll-action','motion-roll-action','repeating_skill:roll-action','repeating_weapon:roll-action','repeating_drone:assault-action','repeating_drone:strike-action','repeating_remnant-weapon:roll-action'];
//const navButtons = ['character','settings','battle-remnant'];
//const repeatAddMonitors = ['skill','weapon','advantage','disadvantage','adjustment','specialty','trait','drone','remnant-weapon'];
//const queryButtons = ['repeating_weapon:skill'];
//const trackerMonitors = ['repeating_structure:clear','repeating_health:clear'];
const dynamicQueries = {skill:skillQuery};
const baseGet = ['sheet_state','section_state','character_name'];
let toMonitor = Object.keys(cascades).reduce((m,k)=>{
if(!/repeating/.test(k)){
baseGet.push(k);
if(!/max/.test(k)){
m.push(k);
}
}else if(/repeating/.test(k)){
k.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
repeatMonitor.push(`${section}:${field}`);
});
}
return m;
},[]);
debug({cascades,baseGet});
toMonitor = [...toMonitor,...repeatMonitor];
registerEventHandlers();
log(`Sheet Loaded`);
+42
View File
@@ -0,0 +1,42 @@
//- File for system specific mixins
- const characterFields = ['character name','player name','nationality','organization affiliations'];
- const stats = ['body','mind','spirit'];
- const secondaries = ['defence','resist'];
- const secondaryDefaults = {defence:3,resist:0};
- const combatGear = ['armour bonus','shield bonus','action penalty'];
- const remnantRatings = ['situational awareness','remnant defence'];
- const remnantStats = ['armour','speed','inspiring','terrifying','structure'];
- const remnantCombatStats = ['assault damage','strike damage','strike range'];
- const droneStats = ['sa','def','arm','spd','struct','assault','assault dam','strike','strike dam','strikerange'];
- const abbreviations = {'situational awareness':'situation awareness abbreviation','remnant defence':'defence','strike dam':'strike damage','assault dam':'assault damage','strikerange':'strike range',sa:'situation awareness abbreviation',def:'defence',arm:'armour',spd:'speed',struct:'structure'};
- const remnantSkills = ['assault','strike','motion'];
- const remnantWeaponInputs = ['name','skill','bonus','damage','damage bonus','type'];
- varObjects.trackerMonitors = [];
mixin track(header,name)
.health-track
+h2({'data-i18n':header,class:'health-track__header'})
+hidden({name:`${name} max`,value:'1',class:'health-track__max-control'})
+fieldset(name)
+checkbox({name:'fill',hidden:'',class:'health-track__fill-control',value:'1'})
+checkbox({name:'damaged',class:'health-track__clear-control health-track__checkbox',value:'1',title:attrTitle('health','damaged')})
- addIfUnique(`repeating_${name}:clear`,'trackerMonitors');
+action({name:'clear',class:'health-track__clearer',title:buttonTitle('health','clear')})
+number({name:'penalty',class:'boxed health-track__number',title:attrTitle('health','penalty')})
//- End Mixin
mixin hexagon(attribs={},status)
.hexagon&attributes(attributes)&attributes(attribs)
if status
+rollLess({values:`computed::${status} -2`})
span(hidden='').fumble-toggle
+rollGreater({values:`computed::${status} 4`})
span(hidden='').crit-toggle
.hexagon__hex-wrapper
.hexagon__hex-wrapper__hex-border
.hexagon__hex-wrapper__hex-border__hex-content
block
//-End Mixin
mixin userTemplateFields
+allProps({values:`description damage difficulty roll ${[1,2,3,4,5,6].map((num)=>`roll${num}`).join(' ')} header footer character_id system single-roll custom_description status`})
h4 {{key}}
span {{value}}
//- End Mixin
@@ -0,0 +1,658 @@
@import url('https://fonts.googleapis.com/css?family=Bakbak+One|Oxanium:wght@700|Raleway:wght@500;600;700;800&display=swap');
//Mixins
@mixin borderedLabel($color:black){
color: $color;
padding: 5px;
border-radius: 5px;
border: 1px solid black;
}
@mixin selectedLabel($backColor:black, $color:white){
background-color: $backColor;
color: $color;
}
@mixin pseudoRadio($backColor:black, $color:white){
input:checked+label,
label:hover{
@include selectedLabel($backColor, $color);
}
}
%font-vars{
--topHeaderFont:Bakbak One;
--midHeaderFont:Oxanium;
--contentFont:Raleway;
}
//Placeholders
// Clears the default Roll20 styling from all elements
.ui-dialog{
@extend %font-vars;
.tab-content .charsheet{
select,
textarea,
input,
.uneditable-input,
label,
button{
all: initial;
}
}
}
%stackedGrid{
&.stacked{
flex-direction:column-reverse;
&.center>*{
text-align: center;
}
&:not(.center)> *{
text-align: left;
}
> [name]{
width:100%;
}
}
}
%inputHighlight{
border-width: 1px 3px;
border-style: solid;
border-color: black;
border-radius: 5px;
box-sizing: border-box;
transition: var(--focusTrans);
}
%ratio1_1{
aspect-ratio: 1/1;
}
%grid{
display:grid;
}
%inline-grid{
display: inline-grid;
}
%circle{
border-radius: 50%;
}
%split{
display:flex;
gap:var(--grid-gap);
> *{
width:0px !important;
flex: 1 1 0;
}
}
%base-border{
border-width: 1px 3px;
border-style: solid;
border-radius: 5px;
border-color: transparent;
box-sizing: border-box;
}
%base-button{
background-color: #DCDCDC33;
border-radius: 5px;
box-shadow: 0 2px 4px black;
border-width: 0;
transition:{
property:box-shadow,backdrop-filter;
duration:200ms;
timing-function:ease-out;
};
backdrop-filter:blur(1px);
overflow:hidden;
&:is(:hover,:focus){
background-color: #85858580;
box-shadow: 0 4px 6px black;
backdrop-filter:blur(2px);
}
&:active{
background-color: #858585ff;
box-shadow: 0 1px 2px black;
backdrop-filter:blur(0px);
}
}
%die-button{
@extend %base-button;
line-height: 14px;
/*height to vertically center a 2rem dicefontd10*/
font-size: 2rem;
font-weight: normal;
font-style: normal;
padding: 5px 3px 7px;
}
%text-button{
padding: 5px 7px;
@extend %base-button;
}
%adaptiveText{
@extend %grid;
grid-template-columns: auto;
grid-template-rows: auto;
grid-template-areas: "content";
>*{
grid-area: content;
}
>span{
opacity: 0;
z-index: -10;
white-space: pre-wrap;
@extend %base-border;
text-transform: initial;
}
textarea{
resize: none;
}
}
%pictos{
font-family: pictos;
text-transform: initial;
}
%pictos3{
font-family: pictos three;
text-transform: initial;
}
%pictosCustom{
font-family: pictos custom;
text-transform: initial;
}
%uppercase{
text-transform:uppercase;
}
%roller{
display: inline-flex;
align-items: center;
align-self:start;
gap: var(--half-gap);
&:before{
content:'L';
font-family:dicefontd6;
}
}
%hidden{
display: none!important;
}
%italics{
font-style: italic;
}
%baseText{
line-height: normal;
}
%baseHeader{
@extend %baseText;
color:black;
display: block;
white-space: nowrap;
margin-top: 0px;
margin-bottom: 0px;
font-weight:normal;
}
%importantHeader{
text-transform: uppercase;
font-weight: normal;
}
%midHeader{
&:not(input){
text-transform:capitalize;
}
}
%h1-style{
@extend %baseHeader;
@extend %importantHeader;
font-size: 2rem;
font-family: var(--topHeaderFont);
&:not(input){
text-align: center;
}
}
%h1{
@extend %h1-style;
*{
@extend %h1-style;
}
}
%h2-style{
@extend %baseHeader;
@extend %importantHeader;
font-size: 1.75rem;
font-family: var(--topHeaderFont);
grid-area:header;
&:not(input){
text-align: center;
}
}
%h2{
@extend %h2-style;
*{
@extend %h2-style;
}
}
%h3-style{
@extend %baseHeader;
@extend %importantHeader;
font-size: 1.5rem;
font-family: var(--topHeaderFont);
&:not(input){
text-align: center;
}
}
%h3{
@extend %h3-style;
*{
@extend %h3-style;
}
}
%h4-style{
@extend %baseHeader;
@extend %midHeader;
font-size: 1.25rem;
font-family: var(--midHeaderFont);
&:not(input){
text-align: center;
}
}
%h4{
@extend %h4-style;
*{
@extend %h4-style;
}
}
%h5-style{
@extend %baseHeader;
@extend %midHeader;
font-size: 1rem;
font-style:normal;
font-family: var(--midHeaderFont);
&:not(input){
text-align: center;
}
}
%h5{
@extend %h5-style;
*{
@extend %h5-style;
}
}
%contents{
display:contents;
}
%checked{
background-color: var(--selectedColor);
}
%boxed{
border: 1px solid black;
border-radius:0;
box-sizing:border-box;
}
//- Default styles
%defaultStyles{
--grid-gap: 1rem;
--half-gap: calc(var(--grid-gap) / 2);
--tiny-gap: calc(var(--half-gap) / 2);
--big-gap: calc(var(--grid-gap) * 2);
--revealTime: 500ms;
--delayTime: 200ms;
--inputSwap: opacity var(--revealTime) ease var(--delayTime);
--focusTiming: 500ms ease;
--focusTrans: all var(--focusTiming);
--borderColor: black;
--fontColor: black;
--backColor: #ffffff;
--spanSectionColumn:1/-1;
--displayBorderWidth: 2px;
--subHeadBackColor:grey;
--unselectedColor:lightgrey;
--selectedColor:black;
//Images
--topCorner:url('https://s3.amazonaws.com/files.d20.io/images/258525471/MZYT-HjOvfJOqUXufyBkww/original.png');
--bottomCorner:url('https://s3.amazonaws.com/files.d20.io/images/258525472/d7gSQyNdXW4tfBUa3osltQ/original.png');
--headerBottom:url('https://s3.amazonaws.com/files.d20.io/images/258525865/9moWl3pE8miL8gqQC2UPRw/original.png');
--footerTop:url('https://s3.amazonaws.com/files.d20.io/images/258525864/dzZKV6kH_fltY8yH9D-ptA/original.png');
position: relative;
gap: var(--grid-gap);
button{
cursor: pointer;
}
textarea{
resize: vertical;
min-height: 3.5rem;
white-space: pre-wrap;
&.fixed{
resize: none;
overflow: auto;
}
}
.span-2{
grid-column:span 2;
}
.span-all{
grid-column:1/-1;
}
select,
.pseudo-select span,
textarea,
input:not(:is([type='radio'],[type='checkbox'])),
.uneditable-input{
padding: 2px;
font-size:1rem;
}
input:is([type="text"],[type="number"]),textarea{
cursor:auto;
}
select,
.pseudo-select span,
input:not(:where([type='checkbox'], [type='radio'])),
.uneditable-input,
textarea{
@extend %base-border;
}
select,
.pseudo-select span{
-webkit-apperance: none;
appearance: none;
text-transform: capitalize;
overflow: hidden!important;
white-space: nowrap;
text-overflow: ellipsis;
text-align: center;
}
input{
width: auto;
&:placeholder{
color: #ededed80;
}
&.plus-control:not([value*="-"])+span:before{
content: '+';
}
}
span,
input,
textarea{
font-size: 1rem;
@extend %baseText;
}
span,input,textarea,button,select{
font-family:var(--contentFont);
--fontWght: 600;
font-weight: var(--fontWght);
font-variation-settings: "wght" var(--fontWght);
}
.hidden,
*[hidden]{
@extend %hidden;
}
.italics{
@extend %italics;
}
h1,
*[aria-level='1']{
@extend %h1;
}
h2,
*[aria-level='2']{
@extend %h2;
}
h3,
*[aria-level='3']{
@extend %h3;
}
h4,
*[aria-level='4']{
@extend %h4;
}
h5,
*[aria-level='5']{
@extend %h5;
}
input{
&[type='checkbox']{
align-self: center;
border: 1px solid black;
cursor: pointer;
&:checked{
@extend %checked;
}
}
&[type='radio']{
appearance: radio;
}
&[type='number']{
width: 3rem;
-moz-appearance: textfield !important;
text-align: center;
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button{
-webkit-appearance: none;
margin: 0;
}
}
}
.number{
width:2rem;
box-sizing:border-box;
}
section{
@extend %grid;
gap:var(--half-gap);
align-self:start;
&.page{
display: none;
}
&.inactive{
display:none !important;
}
}
.pictos{
@extend %pictos;
}
.pictos3{
@extend %pictos3;
}
.pictoscustom{
@extend %pictosCustom;
}
.pseudo-button{
display: inline;
cursor: pointer;
border-radius: 5px;
text-transform: uppercase;
border: 1px solid black;
text-align: center;
padding: 0 5px;
}
.input-label{
display: inline-flex;
gap: var(--half-gap);
align-items:center;
@extend %stackedGrid;
&:not(.stacked){
> [name]:not([type="number"],[type="checkbox"],[type='radio']){
width:0;
flex:1 1 0;
}
>:not([name]){
flex:0 0 auto;
}
&.large > [name]{
@extend h2;
}
}
}
.input-label--dual{
grid-template-columns: 1fr auto 1fr;
grid-template-areas: 'over over over' 'under under under';
>span:last-of-type{
grid-column: 1 / -1;
}
>input{
width: 100%;
}
>.slash{
line-height: 0rem;
font-weight: normal;
}
}
.headed-textarea{
@extend %grid;
grid-template-rows: auto 1fr;
grid-template-columns: auto;
h5{
justify-self: start;
}
}
.contents,
.sheet-contents{
@extend %contents;
}
.boxed,
.sheet-boxed{
@extend %boxed;
&.thick-left{
border-left-width: 5px;
}
&.thick-bottom{
border-bottom-width: 5px;
}
&.thick-right{
border-right-width: 5px;
}
&.thick-top{
border-top-width: 5px;
}
}
.underlined,
.sheet-underlined{
border-radius: 0;
border-bottom: 1px solid black;
transition: var(--focusTrans);
}
:is(.underlined,.boxed){
&:not([readonly]):is(:hover, :focus,:focus-within){
@extend %inputHighlight;
}
&:not([readonly]):is(:focus,:focus-within){
background-color: #DCDCDC;
transition: var(--focusTrans);
}
}
.ratio1-1{
@extend %ratio1_1;
}
.small-scale-grid,
fieldset.small-scale-grid+.repcontainer{
@extend %grid;
grid-template-columns: [header-start]repeat(auto-fit, minmax(15rem, 1fr))[header-end];
grid-template-rows: [header-start]auto[header-end];
gap: var(--half-gap);
>.repitem{
display: flex;
gap: var(--half-gap);
> :is(.dot-row,input[type='text']){
flex: 1 1 0;
width: 100%;
}
}
.repcontrol_move{
display: none;
}
}
.short-text{
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
text-transform: initial;
}
.roll-container{
@extend %grid;
align-self:center;
opacity:0;
grid-row:1 / 2;
}
.repitem{
> input:not([type='checkbox']){
width:100%;
}
.headed-textarea{
grid-column:1/-1;
}
.collapse{
opacity:0;
position:absolute;
right:-10px;
top:0px;
border:0px solid black;
border-radius:0;
color:var(--selectedColor);
text-transform:none;
&:before{
content:'y';
font-family:pictos;
}
&:checked{
background-color:transparent;
color:var(--unselectedColor);
~ .expanded{
display:none;
}
~ .expanded--empty:is(:not([value]),[value='']) + *{
display:none
}
}
&:hover{
color:var(--selectedColor);
}
}
&:hover{
.collapse,.roll-container{
opacity:1;
}
}
}
.repcontainer.editmode{
.collapse{
display:none;
}
}
.repcontainer{
display:grid;
row-gap:var(--grid-gap);
}
.repeating-container{
&:is(:hover,:focus-within,:focus) .repcontrol-button{
opacity:1;
}
}
.repcontrol-button{
@extend %text-button;
grid-area:header;
align-self:center;
text-transform:none;
opacity:0;
transition:opacity var(--revealTime);
~ .repcontrol{
display:none;
}
}
.repcontrol-button--add{
justify-self:start;
&:before{
font-family:pictos;
content:'&';
}
}
.repcontrol-button--edit{
justify-self:end;
&:before{
font-family:pictos;
content:'p';
}
}
.box{
border-radius:0;
width:10px;
height:10px;
}
}
#main{
.uppercase{
@extend %uppercase;
}
}
+400
View File
@@ -0,0 +1,400 @@
//- Element shortcut mixins
- const varObjects = {};
mixin input(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
input&attributes(obj)
//-End Mixin
mixin text(obj)
-obj.type = 'text';
+input(obj)
//-End Mixin
mixin checkbox(obj)
-obj.type = 'checkbox';
+input(obj)
//-End Mixin
mixin radio(obj)
-obj.type = 'radio';
+input(obj)
//-End Mixin
mixin number(obj)
-obj.type = 'number';
+input(obj)
//-End Mixin
mixin range(obj)
-obj.type = 'range';
+input(obj)
//-End Mixin
mixin hidden(obj)
-obj.type = 'hidden';
+input(obj)
//-End Mixin
mixin radioGroup(objArray)
each obj in objArray
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.type='radio';
+input(obj)
//-End Mixin
mixin textarea(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
textarea&attributes(obj)
//-End Mixin
mixin option(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
option&attributes(obj)
block
//-End Mixin
mixin select(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
select&attributes(obj)
if block
block
else
+option({value:'empty select, PEBKAC',selected:true})
//-End Mixin
mixin img(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj['data-i18n-alt'] = obj['data-i18n-alt'] || obj.name;
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
img&attributes(obj)
//-End Mixin
mixin span(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
if obj.name
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
span&attributes(obj)
block
//-End Mixin
mixin datalist(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
datalist&attributes(obj)
block
//-End Mixin
mixin div(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
if obj.name
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || attrTitle(obj.name);
- obj.name = `attr_${obj.name}`;
div&attributes(obj)
block
//-End Mixin
mixin h1(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h1&attributes(obj)
block
//-End Mixin
mixin h2(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h2&attributes(obj)
block
//-End Mixin
mixin h3(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h3&attributes(obj)
block
//-End Mixin
mixin h4(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h4&attributes(obj)
block
//-End Mixin
mixin h5(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h5&attributes(obj)
block
//-End Mixin
mixin h6(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
h6&attributes(obj)
block
//-End Mixin
mixin p(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
p&attributes(obj)
block
//-End Mixin
mixin button(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.name = replaceSpaces(obj.name);
- obj.title = obj.title || `%{${obj.name}}`;
if obj.type === 'action'
- obj.name = `act_${obj.name}`;
else
- obj.type = 'roll';
- obj.name = `roll_${obj.name}`;
button&attributes(obj)
block
//-End Mixin
mixin action(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
- obj.type = 'action';
- obj.name = obj.name.replace(/\s+/g,'-');
+button(obj)
block
//- End Mixin
mixin navButton(obj)
-obj.name = `nav ${obj.name}`;
+action(obj)
block
- let actionTitle = obj.title.replace(/(repeating_[^_]+_).+/,`$1:${actionName}`).replace(/%{|}|nav-/g,'');
- addIfUnique(actionTitle,'navButtons');
//- End Mixin
mixin actionQuery(obj)
-obj.class = obj.class ? `${obj.class} pseudo-select` : 'pseudo-select';
+action(obj)
block
- let actionTitle = obj.title.replace(/(repeating_[^_]+).+/,`$1:${actionButtonName(obj.name)}`).replace(/%{|}/g,'');
- addIfUnique(actionTitle,'queryButtons');
//- End Mixin
mixin rollerExtras(obj)
- obj.class = obj.class ? `${replaceProblems(obj.class)} roller` : 'roller';
- let attrName = replaceSpaces(actionInputName(obj.name));
- let actionName = actionButtonName(obj.name);
- obj.value = `@{${attrName}}`;
!=`<!-- ${obj.name} | attrName:${attrName} | actionName:${actionName} -->`
block
!=`<!-- ${obj.name} post block | attrName:${attrName} | actionName:${actionName} -->`
- let actionTitle = /repeating/.test(obj.title) ? obj.title.replace(/(repeating_[^_]+).+/,`$1:${actionName}`).replace(/%{|}/g,'') : actionName;
- actionTitle = `${actionTitle.replace(/-action$/,'')}-action`;
- let attrTitle = `${obj.title.replace(/(repeating_[^_]+_).+/,`$1\$x_${attrName}`).replace(/%{|}/g,'').replace(/_action$/,'')}_action`;
!=`<!-- ${obj.name} | attrName:${attrName} | actionName:${actionName} | actionTitle:${actionTitle} | attrTitle:${attrTitle} -->`
- addIfUnique(actionTitle,'rollMonitor');
- addIfUnique(attrTitle,'actionAttributes');
+action({name:actionName,hidden:''})
+hidden({name:attrName})
//- End Mixin
mixin roller(obj)
+rollerExtras(obj)
+button(obj)
block
mixin section(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
section&attributes(obj)
block
//-End Mixin
mixin main(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
main&attributes(obj)
block
//-End Mixin
mixin header(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
header&attributes(obj)
block
//-End Mixin
mixin footer(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
footer&attributes(obj)
block
//-End Mixin
mixin details(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
details&attributes(obj)
block
//-End Mixin
mixin summary(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
summary&attributes(obj)
block
//-End Mixin
mixin label(obj)
- obj.class = obj.class ? replaceProblems(obj.class) : undefined;
label&attributes(obj)
block
//-End Mixin
mixin repeating_section(name,header,columnArr,id)
section(class=`repeating-container--${name} repeating-container section` id=`${(id||name).replace(/\s+/g,'-')}`)
if header
h2(data-i18n=header)
if columnArr
.repeat-columns
each head in columnArr
h5(data-i18n=head)
+customControlFieldset(name)
block
mixin customControlFieldset(name)
- addIfUnique(name.replace(/\s+/g,'-'),'repeatAddMonitors');
+action({name:`add ${name}`,class:'repcontrol-button repcontrol-button--add'})
+action({name:`edit ${name}`,class:'repcontrol-button repcontrol-button--edit'})
+fieldset(name)
block
mixin fieldset(name)
- name = name.replace(/\s+/g,'-');
fieldset(class=`repeating_${name}`)
block
//-End Mixin
mixin pseudo-button(label,inputObj)
- inputObj.class = inputObj.class ? replaceProblems(inputObj.class) : undefined;
- hiddenObj = inputObj;
- hiddenObj.type = 'hidden';
+input(hiddenObj)
label.pseudo-button
-inputObj.class = inputObj.class ? `${inputObj.class} pseudo-button__input` : 'pseudo-button__input';
span(data-i18n=label class='pseudo-button__span')
+input(inputObj)
//-End Mixin
mixin button-label(inputObj,buttonObj,divObj)
if divObj
- divObj.class = divObj.class ? replaceProblems(divObj.class) : undefined;
- inputObj.class = inputObj.class ? replaceProblems(inputObj.class) : undefined;
- buttonObj.class = buttonObj.class ? replaceProblems(buttonObj.class) : undefined;
- inputObj.name = inputObj.name.replace(/\s+/g,'_');
- buttonObj.name = (buttonObj.name || inputObj.name).replace(/\s+/g,'_');
.input-label.input-label--button&attributes(divObj)
- inputObj.class = inputObj.class ? `${inputObj.class} input-label__input` : 'input-label__input';
if spanObj
- buttonObj.class = buttonObj.class ? `${buttonObj.class} input-label__text` : 'input-label__text';
+button(buttonObj)
+input(inputObj)
//-End Mixin
mixin roller-label(inputObj,buttonObj,divObj)
+rollerExtras(buttonObj)
+button-label(inputObj,buttonObj,divObj)
//-End Mixin
mixin action-label(inputObj,buttonObj,divObj)
- buttonObj.type = 'action';
+button-label(inputObj,buttonObj,divObj)
//-End Mixin
mixin select-label(label,inputObj,divObj,spanObj)
if divObj
- divObj.class = divObj.class ? replaceProblems(divObj.class) : undefined;
if inputObj
- inputObj.class = inputObj.class ? replaceProblems(inputObj.class) : undefined;
if spanObj
- spanObj.class = spanObj.class ? replaceProblems(spanObj.class) : undefined;
label.input-label&attributes(divObj)
- inputObj.name = inputObj.name.replace(/\s+/g,'_');
- inputObj.class = inputObj.class ? `${inputObj.class} input-label__input` : 'input-label__input';
if spanObj
- spanObj.class = spanObj.class ? `${spanObj.class} input-label__text` : 'input-label__text';
span(data-i18n=label)&attributes(spanObj)
+select(inputObj)
block
//-End Mixin
mixin input-label(label,inputObj,divObj,spanObj)
if divObj
- divObj.class = divObj.class ? replaceProblems(divObj.class) : undefined;
if inputObj
- inputObj.class = inputObj.class ? replaceProblems(inputObj.class) : undefined;
if spanObj
- spanObj.class = spanObj.class ? replaceProblems(spanObj.class) : undefined;
label.input-label&attributes(divObj)
- inputObj.name = inputObj.name.replace(/\s+/g,'_');
- inputObj.class = inputObj.class ? `${inputObj.class} input-label__input` : 'input-label__input';
if spanObj
- spanObj.class = spanObj.class ? `${spanObj.class} input-label__text` : 'input-label__text';
span(data-i18n=label)&attributes(spanObj)
+input(inputObj)
//-End Mixin
mixin dual-input-label(label,inputArr,divObj,spanObj)
if divObj
- divObj.class = divObj.class ? replaceProblems(divObj.class) : undefined;
if spanObj
- spanObj.class = spanObj.class ? replaceProblems(spanObj.class) : undefined;
.input-label.input-label--dual&attributes(divObj)
span(data-i18n=label)&attributes(spanObj)
each inputObj,i in inputArr
if inputObj
- inputObj.class = inputObj.class ? replaceProblems(inputObj.class) : undefined;
- inputObj.name = inputObj.name.replace(/\s+/g,'_');
+input(inputObj)
if i<1
span(class='slash h2')='/'
//-End Mixin
mixin headedTextarea(obj,header)
.headed-textarea&attributes(attributes)
- obj.class = obj.class ? `${obj.class} headed-textarea__textarea` : 'headed-textarea__textarea';
+h3({'data-i18n':header,class:'headed-textarea__header'})
+textarea(obj)
//-End Mixin
mixin script()
script(type='text/worker')
block
//-End Mixin
mixin pseudoEdit(name)
+action({name:`edit-${name}`,class:'repcontrol-edit','data-i18n-title':`edit section`})
//- End Mixin
mixin pseudoAdd(name,content)
- let obj = {name:`add-${name}`,class:'repcontrol-add','data-i18n-title':`create a ${name}`};
if content
- obj['data-i18n'] = content;
+action(obj)
//- End Mixin
mixin pseudoControl(name)
.pseudo-control
+pseudoAdd(name)
+pseudoEdit(name)
//- End Mixin
mixin pseudoCollapse(attrName,labelObj)
+hidden({name:attrName,value:'0',class:'collapse-control'})
-labelObj.class = labelObj.class ? `${labelObj.class} collapse-interface`:'collapse-interface';
+label(labelObj)
+checkbox({name:attrName,value:'1',hidden:''})
//- End Mixin
mixin expandHeader(obj)
- obj.role='heading';
- obj['aria-level'] = obj['aria-level'] || 2;
- obj.class = obj.class ? `${obj.class} expandable-header ${obj.name}` : `${replaceSpaces(obj.name)} expandable-header`;
- obj['data-i18n-title'] = obj['data-i18n-title'] || 'collapse/expand the section';
+action(obj)
if !obj['data-i18n']
block
//- End Mixin
mixin subsectionContent(attribs)
.display-target&attributes(attribs)
if value!=='add-detail'
- collapseTitle;
if value==='master'
-collapseTitle = 'collapse/expand section';
else if /short/.test(value)
- collapseTitle = 'expand subsection';
else
- collapseTitle = 'collapse subsection';
+pseudoCollapse('collapse',{'data-i18n-title':collapseTitle})
block
mixin subsection(value)
+radio({name:'display state',value,class:'display-control',hidden:''})
+subsectionContent(attributes)
block
//- End Mixin
mixin complexSubsection(def)
+hidden({name:'display state',value:def,class:'expanded-control'})
+subsectionContent(attributes)
block
//- End Mixin
mixin adaptiveTextarea(textObj)
.adaptive-text&attributes(attributes)
- let spanObj = {name:textObj.name,class:'adaptive-text__span'};
- textObj.class = textObj.class ? `${textObj.class} adaptive-text__textarea` : 'adaptive-text__textarea';
+span(spanObj)
+textarea(textObj)
//- Functions
- const attrTitle = (string,section) => section ? repeatAttrTitle(string,section) : `@{${replaceSpaces(string).replace(/_max$/,'|max')}}`;
- const buttonTitle = (string,section) => section ? repeatButtonTitle(string,section) : `%{${replaceSpaces(string)}}`;
- const replaceSpaces = (string) => string.replace(/\s+/g,'_');
- const replaceProblems = (string) => string.replace(/[\(\)\[\]\|\/\\]/g,'-');
- const repeatAttrTitle = (section,attr) => `@{repeating_${section}_$X_${replaceSpaces(attr).replace(/_max$/,'|max')}}`;
- const repeatButtonTitle = (section,attr) => `%{repeating_${section}_$X_${replaceSpaces(attr||'roll')}}`;
- const actionButtonName = (name) => `${name.replace(/act_/,'').replace(/_|\s+/g,'-').replace(/-action$/,'')}-action`;
- const actionInputName = (name) => `${name}_action`;
-
const addIfUnique = (item,arrName) => {
varObjects[arrName] = varObjects[arrName] || [];
if(varObjects[arrName].indexOf(item) === -1){
varObjects[arrName].push(item);
}
};
@@ -0,0 +1,85 @@
//-Roll Template Mixins to be included in Sheet files
mixin rolltemplate(name)
rolltemplate(class=`sheet-rolltemplate-${name}`)
div(class=`template ${name} system-{{system}}`)
block
div(class='image-border{{#header}} has-header{{/header}}{{#footer}} has-footer{{/footer}}')
mixin multiPartTemplate(name)
rolltemplate(class=`sheet-rolltemplate-${name}`)
+templateHelperFunction({func:'rollTotal',invert:true,values:'computed::finished 0'})
div(class=`template ${name} system-{{system}}{{#continuation}} continuation{{/continuation}}{{#first}} first{{/first}} finished`)
block
+templateHelperFunction({func:'rollBetween',values:'computed::finished 0 0'})
div(class=`template ${name} system-{{system}}{{#continuation}} continuation{{/continuation}}{{#first}} first{{/first}}`)
block
//- End Mixin
mixin characterLink()
+templateConditionalDisplay('footer')
.footer
.footer__background.background
+templateConditionalDisplay('character_id')
h4(tabindex='-1').character_name [{{footer}}](http://journal.roll20.net/character/{{character_id}})
+templateConditionalDisplay('character_id',true)
h4(tabindex='-1').character_name {{footer}}
.footer__content
+templateConditionalDisplay('character_id')
h4.character_name [{{footer}}](http://journal.roll20.net/character/{{character_id}})
+templateConditionalDisplay('character_id',true)
h4.character_name {{footer}}
.footer__image
//-End mixin
mixin templateBase(name)
- name = name.replace(/\s+/g,'_');
+rolltemplate(name)
+characterLink()
block
//-End mixin
mixin templateConditionalDisplay(fieldBool,invert)
!=`{{${invert ? '^' : '#'}${fieldBool}}}`
block
!=`{{/${fieldBool}}}`
//- End mixin
mixin templateHelperFunction(helperObj)
!=`{{${helperObj.invert ? '#^':'#'}${helperObj.func ? `${helperObj.func}() `:''}${helperObj.values ? `${helperObj.values}` : ''}}}`
block
!=`{{/${(helperObj.invert) ? '^' : ''}${helperObj.func ? `${helperObj.func}() `:''}${helperObj.values}}}`
//- End Mixin
mixin rollWasCrit(helperObj)
- helperObj.func = 'rollWasCrit';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin rollWasFumble(helperObj)
- helperObj.func = 'rollWasFumble';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin rollTotal(helperObj)
- helperObj.func = 'rollTotal';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin rollGreater(helperObj)
- helperObj.func = 'rollGreater';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin rollLess(helperObj)
- helperObj.func = 'rollLess';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin rollBetween(helperObj)
- helperObj.func = 'rollBetween';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin allProps(helperObj)
- helperObj.func = 'allprops';
+templateHelperFunction(helperObj)
block
//- End Mixin
mixin templateField(name)
!= `${tField(name)}`
//- End Mixin
- const tField = name => `{{${name}}}`;
@@ -0,0 +1,76 @@
/*
Roll Template
#rolltemplate
*/
.sheet-rolltemplate-storypath{
--templateRadius: 1rem;
--dieSize: 50px;
--templateBorderW: 2px 5px 2px 5px;
--fumbleColor: red;
--critColor: green;
--innerBorderRadius: 5px;
--borderSize:1px;
margin:{//ensures that the template fills the chat window
left: -40px;
right: -5px;
}
&:not(:first-child){//Moves the template to accomodate the sender tag
margin-top:10px;
}
> .sheet-template{
overflow:hidden;
span{
font-size:13px;
}
}
.inlinerollresult{
position: relative;
&[title*="[computed value]"],
&[original-title*="[computed value]"]{
pointer-events: none;
}
&,
&.fullcrit,
&.fullfail,
&.importantroll{
border: none;
background-color: transparent;
}
&.fullcrit{
color: green;
}
&.fullfail{
color: red;
}
&.importantroll{
color: blue;
}
}
.sheet-header{
padding: 5px;
display:grid;
grid-template-columns:1fr 2rem 1fr;
gap:var(--half-gap);
.sheet-character_name{
grid-column:1/-1;
place-self:center end;
}
.sheet-title{
grid-column:1/-1;
place-self:center;
}
}
.sheet-template-row{
padding: 5px;
@extend %grid;
grid-template-columns: minmax(6em, 1fr) repeat(auto-fit, minmax(1rem, 1fr));
place-items: center;
&:nth-child(odd){
background-color: lightgrey;
}
>*:first-child{
place-self: center start;
}
}
}
@@ -0,0 +1,124 @@
@forward '_genericplaceholders.scss';
@mixin activeTab{
--tabBorder:drop-shadow(1px 0 0 black) drop-shadow(-1px 0 0 black) drop-shadow(0 -1px 0 black);
top:1px;
color:black;
}
.ui-dialog .tab-content .charsheet{
*[data-i18n='xp']{
text-transform:uppercase;
}
.stat-grid{
gap:var(--half-gap) var(--tiny-gap);
> :not(input):not(h2){
place-self:center end;
}
}
.boxed.span-2{
@extend %grid;
grid-auto-flow: column;
place-content:center;
width:6.5rem;
}
.roll-plus{
justify-self: center;
}
.health-track{
.repcontainer{
@extend %grid;
grid-template-columns:repeat(auto-fill,2rem);
row-gap:var(--tiny-gap);
&:hover{
.health-track__clearer{
background-image:linear-gradient(45deg, transparent 0% 48%, white 48% calc(48% + 1px), transparent calc(48% + 1px)), linear-gradient(-45deg, transparent 0% 48%, white 48% calc(48% + 1px), transparent calc(48% + 1px));
}
}
> .repitem{
@extend %grid;
grid-template:{
columns:2rem;
rows:repeat(2,2rem);
areas:
'check'
'penalty'
;
}
&:first-child{
.itemcontrol{
display:none !important;
}
}
.repcontrol_move{
display:none !important;
}
}
}
.repcontrol{
display:none;
}
}
.health-track__max-control[value="1"] ~ .repcontrol .repcontrol_edit{
display:none;
}
.health-track__number{
&:is(:hover,:focus){
border-radius:0;
}
}
.health-track__checkbox{
border-radius:0;
width:100%;
height:100%;
box-sizing:border-box;
grid-area:check;
}
.health-track__clear-control:not(:checked) ~ .health-track__clearer{
display:none;
}
.health-track__fill-control{
&:checked + *{
@extend %checked;
}
}
.health-track__clearer{
grid-area:check;
button{
width:100%;
height:100%;
}
}
}
%hex-base{
@extend %grid;
overflow:hidden;
grid-template-areas:'content';
}
%hex-component{
@extend %hex-base;
width:100%;
height:100%;
grid-area:content;
}
%hex-rotate{
@extend %hex-component;
transform:rotate(-60deg);
}
%hex-shape{
box-sizing:border-box;
content:'';
box-shadow:0 0 0 1px black inset, 0 0 4px #ffffff inset;
}
%article-layout{
@extend %grid;
gap:var(--big-gap);
&.system-remnant{
grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
}
}
%repeat-section-layout{
@extend %grid;
grid-template-areas:"header";
gap:var(--half-gap);
align-self:start;
}
// End Placeholders
+930
View File
@@ -0,0 +1,930 @@
{
"version": "7",
"about": "This is a Prepros (https://prepros.io) configuration file. You can commit this file to a git repo to backup and sync project configurations.",
"config": {
"proxy": {
"enable": false,
"target": "",
"useLocalAssets": false
},
"reload": {
"enable": true,
"delay": 0,
"animate": true,
"afterUpload": false
},
"sync": {
"enable": false,
"mouse": true,
"keyboard": true,
"form": true,
"scroll": true
},
"watcher": {
"enable": true,
"maxFiles": 2000,
"usePolling": false,
"pollingInterval": 500,
"extensions": [
".html",
".htm",
".php"
],
"ignore": {
"patterns": [
".*",
"wp-admin",
"wp-includes",
"node_modules",
"Prepros Export",
"bower_components"
],
"exceptions": []
}
},
"exporter": {
"ignore": {
"patterns": [
".*",
"desktop.ini",
"prepros.cfg",
"node_modules",
"Prepros Export",
"prepros.config",
"prepros-6.config",
"*-original.jpg",
"*-original.jpeg",
"*-original.png",
"*-original.svg",
"*.scss",
"*.sass",
"*.less",
"*.pug",
"*.jade",
"*.styl",
"*.haml",
"*.slim",
"*.coffee",
"*.kit",
"*.turf",
"*.ts"
],
"exceptions": []
}
},
"uploader": {
"remotePath": "",
"timeout": 20000,
"autoUpload": false,
"reuseConnection": true,
"connectionType": "ftp",
"history": []
},
"packages": {
"createPackageLock": true
},
"images": {
"preserveOriginal": true
},
"tasks": {
"autoprefixer": {
"cascade": true,
"add": true,
"remove": true,
"supports": true,
"flexbox": true,
"grid": "autoplace",
"browsers": [
"last 2 versions"
],
"sourceMap": false
},
"babel": {
"sourceMap": false,
"presets": {
"@babel/preset-env": {
"enable": true,
"options": {
"targets": [
"last 2 versions"
],
"preserveImports": false,
"polyfills": false
}
},
"@babel/preset-react": true,
"@babel/preset-flow": false
},
"plugins": {
"@babel/plugin-proposal-class-static-block": false,
"@babel/plugin-proposal-class-properties": false,
"@babel/plugin-proposal-decorators": {
"enable": false,
"options": {
"decoratorsBeforeExport": true
}
},
"@babel/plugin-proposal-export-namespace-from": false,
"@babel/plugin-proposal-function-sent": false,
"@babel/plugin-proposal-logical-assignment-operators": false,
"@babel/plugin-proposal-nullish-coalescing-operator": false,
"@babel/plugin-proposal-numeric-separator": false,
"@babel/plugin-proposal-optional-chaining": false,
"@babel/plugin-proposal-private-methods": false,
"@babel/plugin-proposal-throw-expressions": false
},
"customPresets": [],
"customPlugins": []
},
"bundle-js": {
"sourceMap": false,
"exclude": [
"node_modules",
"bower_components"
],
"devMode": true,
"globals": [],
"externals": [],
"babel": {
"enable": true,
"options": {
"sourceMap": false,
"presets": {
"@babel/preset-env": {
"enable": true,
"options": {
"targets": [
"last 2 versions"
],
"preserveImports": false,
"polyfills": false
}
},
"@babel/preset-react": true,
"@babel/preset-flow": false
},
"plugins": {
"@babel/plugin-proposal-class-static-block": false,
"@babel/plugin-proposal-class-properties": false,
"@babel/plugin-proposal-decorators": {
"enable": false,
"options": {
"decoratorsBeforeExport": true
}
},
"@babel/plugin-proposal-export-namespace-from": false,
"@babel/plugin-proposal-function-sent": false,
"@babel/plugin-proposal-logical-assignment-operators": false,
"@babel/plugin-proposal-nullish-coalescing-operator": false,
"@babel/plugin-proposal-numeric-separator": false,
"@babel/plugin-proposal-optional-chaining": false,
"@babel/plugin-proposal-private-methods": false,
"@babel/plugin-proposal-throw-expressions": false
},
"customPresets": [],
"customPlugins": []
}
},
"css": {
"enable": true
},
"fonts": {
"enable": true
}
},
"coffeescript": {
"header": false,
"bare": false,
"sourceMap": false
},
"command": {
"command": "",
"rootDir": ""
},
"concat-js": {
"sourceMap": false,
"rootDir": ""
},
"copy": {
"sourceMap": false
},
"dart-sass": {
"indentType": "space",
"allowWildcardImports": false,
"indentWidth": 2,
"linefeed": "lf",
"sourceMap": false
},
"haml": {
"doubleQuoteAttributes": true
},
"jpg": {
"quality": 90
},
"less": {
"javascriptEnabled": false,
"strictImports": false,
"insecure": false,
"math": "always",
"strictUnits": false,
"dumpLineNumbers": false,
"sourceMap": false
},
"markdown": {
"githubFlavored": true,
"wrapWithHtml": false
},
"minify-css": {
"sourceMap": false
},
"minify-html": {
"caseSensitive": false,
"collapseBooleanAttributes": true,
"collapseInlineTagWhitespace": false,
"collapseWhitespace": true,
"conservativeCollapse": false,
"decodeEntities": false,
"html5": true,
"includeAutoGeneratedTags": true,
"keepClosingSlash": false,
"minifyCSS": true,
"minifyJS": true,
"preserveLineBreaks": false,
"preventAttributesEscaping": false,
"processConditionalComments": false,
"removeAttributeQuotes": false,
"removeComments": true,
"removeEmptyAttributes": false,
"removeEmptyElement": false,
"removeOptionalTags": false,
"removeRedundantAttributes": false,
"removeScriptTypeAttributes": false,
"removeStyleLinkTypeAttributes": false,
"removeTagWhitespace": false,
"sortAttributes": false,
"sortClassName": false,
"useShortDoctype": true
},
"minify-js": {
"parse": {
"bare_returns": false
},
"compress": {
"arrows": true,
"arguments": false,
"booleans": true,
"booleans_as_integers": false,
"collapse_vars": true,
"comparisons": true,
"computed_props": true,
"conditionals": true,
"dead_code": true,
"directives": true,
"drop_console": false,
"drop_debugger": true,
"evaluate": true,
"expression": false,
"global_defs": [],
"hoist_funs": false,
"hoist_props": true,
"hoist_vars": false,
"if_return": true,
"inline": 3,
"join_vars": true,
"keep_fargs": true,
"keep_infinity": false,
"loops": true,
"negate_iife": true,
"properties": true,
"pure_funcs": [],
"pure_getters": false,
"reduce_funcs": true,
"reduce_vars": true,
"sequences": true,
"side_effects": true,
"switches": true,
"top_retain": [],
"typeofs": true,
"unsafe": false,
"unsafe_arrows": false,
"unsafe_comps": false,
"unsafe_Function": false,
"unsafe_math": false,
"unsafe_proto": false,
"unsafe_regexp": false,
"unsafe_undefined": false,
"unused": true
},
"mangle": {
"eval": false,
"reserved": []
},
"output": {
"ascii_only": false,
"braces": false,
"comments": "none",
"inline_script": true,
"keep_numbers": false,
"keep_quoted_props": false,
"preamble": null,
"quote_keys": false,
"quote_style": 0,
"semicolons": true,
"shebang": true,
"webkit": false,
"wrap_iife": false,
"wrap_func_args": true
},
"sourceMap": false,
"toplevel": false,
"ie8": false,
"keep_classnames": false,
"keep_fnames": false,
"safari10": false
},
"node-sass": {
"indentType": "space",
"allowWildcardImports": false,
"indentWidth": 2,
"linefeed": "lf",
"outputStyle": "expanded",
"precision": 10,
"sourceMap": false,
"sourceComments": false
},
"png": {
"quality": 90
},
"postcss-import": {
"ignoreKeywords": [],
"sourceMap": false
},
"postcss-preset-env": {
"stage": 2,
"browsers": [
"last 2 versions"
],
"sourceMap": false
},
"pug": {
"pretty": true
},
"slim": {
"indent": "space",
"indentSize": 2,
"pretty": true
},
"stylus": {
"useNib": true,
"sourceMap": false,
"linenos": false
},
"svg": {
"cleanupAttrs": true,
"removeDoctype": true,
"removeXMLProcInst": true,
"removeComments": true,
"removeMetadata": true,
"removeTitle": true,
"removeDesc": true,
"removeUselessDefs": true,
"removeEditorsNSData": true,
"removeEmptyAttrs": true,
"removeHiddenElems": true,
"removeEmptyText": true,
"removeEmptyContainers": true,
"removeViewBox": false,
"cleanupEnableBackground": true,
"convertStyleToAttrs": true,
"convertColors": true,
"convertPathData": true,
"convertTransform": true,
"removeUnknownsAndDefaults": true,
"removeNonInheritableGroupAttrs": true,
"removeUselessStrokeAndFill": true,
"removeUnusedNS": true,
"cleanupIDs": true,
"cleanupNumericValues": true,
"moveElemsAttrsToGroup": true,
"moveGroupAttrsToElems": true,
"collapseGroups": true,
"removeRasterImages": false,
"mergePaths": true,
"convertShapeToPath": true,
"sortAttrs": true,
"removeDimensions": true
},
"turf": {
"rootDir": ""
},
"typescript": {
"allowJs": false,
"allowSyntheticDefaultImports": true,
"allowUmdGlobalAccess": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"alwaysStrict": false,
"charset": "utf8",
"checkJs": false,
"declaration": false,
"disableSizeLimit": false,
"downlevelIteration": false,
"emitBOM": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": false,
"forceConsistentCasingInFileNames": false,
"importHelpers": false,
"jsx": "React",
"keyofStringsOnly": false,
"lib": [],
"maxNodeModuleJsDepth": 0,
"module": "ES2015",
"moduleResolution": "NodeJs",
"newLine": "LineFeed",
"noFallthroughCasesInSwitch": false,
"noImplicitAny": false,
"noImplicitReturns": false,
"noImplicitThis": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitUseStrict": false,
"noLib": false,
"noResolve": false,
"preserveConstEnums": false,
"jsxFactory": "React.createElement",
"removeComments": false,
"skipLibCheck": false,
"sourceMap": false,
"strict": false,
"strictFunctionTypes": false,
"strictBindCallApply": false,
"strictNullChecks": false,
"strictPropertyInitialization": false,
"suppressExcessPropertyErrors": false,
"suppressImplicitAnyIndexErrors": false,
"target": "ES3",
"resolveJsonModule": false,
"esModuleInterop": false,
"useDefineForClassFields": false
}
},
"fileTypes": {
"sass": {
"extensions": [
".scss",
".sass"
],
"autoCompile": true,
"sourceMap": false,
"tasks": [
{
"task": "dart-sass",
"enable": true
},
{
"task": "autoprefixer",
"enable": false
},
{
"task": "minify-css",
"enable": false
}
],
"output": {
"extension": ".css",
"type": "SOURCE_RELATIVE",
"segments": [
{
"segment": "scss",
"replaceWith": "css"
},
{
"segment": "sass",
"replaceWith": "css"
}
]
}
},
"less": {
"extensions": [
".less"
],
"autoCompile": true,
"sourceMap": false,
"tasks": [
{
"task": "less",
"enable": true
},
{
"task": "autoprefixer",
"enable": true
},
{
"task": "minify-css",
"enable": false
}
],
"output": {
"extension": ".css",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "less",
"replaceWith": "css"
}
]
}
},
"pug": {
"extensions": [
".pug",
".jade"
],
"autoCompile": true,
"tasks": [
{
"task": "pug",
"enable": true
},
{
"task": "minify-html",
"enable": false
}
],
"output": {
"extension": ".html",
"type": "SOURCE_RELATIVE",
"segments": [
{
"segment": "pug",
"replaceWith": "html"
}
]
}
},
"css": {
"extensions": [
".css"
],
"autoCompile": false,
"sourceMap": false,
"tasks": [
{
"task": "copy",
"enable": true
},
{
"task": "postcss-import",
"enable": false
},
{
"task": "postcss-preset-env",
"enable": false
},
{
"task": "autoprefixer",
"enable": false
},
{
"task": "minify-css",
"enable": false
}
],
"output": {
"extension": ".css",
"type": "SOURCE_RELATIVE",
"relativePath": "",
"suffix": "-dist",
"alwaysSuffix": false
}
},
"javascript": {
"extensions": [
".js",
".jsx"
],
"autoCompile": false,
"sourceMap": false,
"tasks": [
{
"task": "copy",
"enable": true
},
{
"task": "concat-js",
"enable": false
},
{
"task": "babel",
"enable": false
},
{
"task": "bundle-js",
"enable": false
},
{
"task": "minify-js",
"enable": true
}
],
"output": {
"extension": ".js",
"type": "SOURCE_RELATIVE",
"relativePath": "",
"suffix": "-dist",
"alwaysSuffix": false
}
},
"stylus": {
"extensions": [
".styl"
],
"autoCompile": true,
"sourceMap": false,
"tasks": [
{
"task": "stylus",
"enable": true
},
{
"task": "autoprefixer",
"enable": true
},
{
"task": "minify-css",
"enable": false
}
],
"output": {
"extension": ".css",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "stylus",
"replaceWith": "css"
},
{
"segment": "styl",
"replaceWith": "css"
}
]
}
},
"markdown": {
"extensions": [
".md",
".markdown",
".mkd"
],
"autoCompile": false,
"tasks": [
{
"task": "markdown",
"enable": true
},
{
"task": "minify-html",
"enable": false
}
],
"output": {
"extension": ".html",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "markdown",
"replaceWith": "html"
}
]
}
},
"haml": {
"extensions": [
".haml"
],
"autoCompile": true,
"tasks": [
{
"task": "haml",
"enable": true
},
{
"task": "minify-html",
"enable": false
}
],
"output": {
"extension": ".html",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "haml",
"replaceWith": "html"
}
]
}
},
"slim": {
"extensions": [
".slim"
],
"autoCompile": true,
"tasks": [
{
"task": "slim",
"enable": true
},
{
"task": "minify-html",
"enable": false
}
],
"output": {
"extension": ".html",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "slim",
"replaceWith": "html"
}
]
}
},
"coffeescript": {
"extensions": [
".coffee"
],
"autoCompile": true,
"sourceMap": false,
"tasks": [
{
"task": "coffeescript",
"enable": true
},
{
"task": "babel",
"enable": false
},
{
"task": "bundle-js",
"enable": false
},
{
"task": "minify-js",
"enable": false
}
],
"output": {
"extension": ".js",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "coffee-script",
"replaceWith": "js"
},
{
"segment": "coffeescript",
"replaceWith": "js"
},
{
"segment": "coffee",
"replaceWith": "js"
}
]
}
},
"turf": {
"extensions": [
".turf",
".kit"
],
"autoCompile": true,
"tasks": [
{
"task": "turf",
"enable": true
},
{
"task": "minify-html",
"enable": false
}
],
"output": {
"extension": ".html",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "turf",
"replaceWith": "html"
}
]
}
},
"typescript": {
"extensions": [
".ts",
".tsx"
],
"autoCompile": true,
"sourceMap": false,
"tasks": [
{
"task": "typescript",
"enable": true
},
{
"task": "babel",
"enable": false
},
{
"task": "bundle-js",
"enable": false
},
{
"task": "minify-js",
"enable": false
}
],
"output": {
"extension": ".js",
"type": "REPLACE_SEGMENTS",
"segments": [
{
"segment": "typescript",
"replaceWith": "js"
},
{
"segment": "ts",
"replaceWith": "js"
}
]
}
},
"jpg": {
"extensions": [
".jpg",
".jpeg"
],
"tasks": [
{
"task": "jpg",
"enable": true
}
],
"output": {
"extension": ".jpg",
"type": "SOURCE_RELATIVE",
"relativePath": ""
}
},
"png": {
"extensions": [
".png"
],
"tasks": [
{
"task": "png",
"enable": true
}
],
"output": {
"extension": ".png",
"type": "SOURCE_RELATIVE",
"relativePath": ""
}
},
"svg": {
"extensions": [
".svg"
],
"tasks": [
{
"task": "svg",
"enable": true
}
],
"output": {
"extension": ".svg",
"type": "SOURCE_RELATIVE",
"relativePath": ""
}
}
},
"files": [
{
"file": "style.scss",
"config": {
"customOutput": "style.css"
}
},
{
"file": "system.pug",
"config": {
"customOutput": "system.html"
}
}
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

+9
View File
@@ -0,0 +1,9 @@
{
"html": "system.html",
"css": "style.css",
"authors": "Scott Casey",
"roll20userid": "459831",
"preview": "rapidfire_preview.jpg",
"instructions": "This sheet is compatible with all Rapidfire system games by [Outrider Studios](https://outriderstudios.com/). Check out the [Rapidfire System sheet guide](https://wiki.roll20.net/Rapidfire_System) for full details on how to use the sheet and all the features included in it.",
"legacy": false
}
File diff suppressed because it is too large Load Diff
+902
View File
@@ -0,0 +1,902 @@
@use 'Mixins/_systemplaceholders.scss';
//Sets the font-size to be used by rem unit calls
html{
font-size: 13px;
}
//Sets the bio/charsheet/a&a tab container to be sticky and float at the top
ul.nav-tabs.nav{
position:sticky;
top:0;
background-color:white;
z-index:9999;
margin-bottom:0px;
}
.nav-tabs li a{
font-family:var(--midHeaderFont);
padding:{
top: 18px;
bottom:15px;
};
&[data-tab='charsheet']{
background-image: url(https://s3.amazonaws.com/files.d20.io/images/259063058/V4Hm27FqfgBujU8bI74Xaw/original.png);
background-size: contain;
background-repeat: no-repeat;
padding:{
left:50px;
right:12px;
};
}
}
.itemcontrol{
z-index:10;
}
.image-input{
cursor:pointer;
}
//Default settings for the .charsheet container
.ui-dialog .tab-content .charsheet{
padding-top:0px;
@extend %defaultStyles;
.adaptive-text{
@extend %adaptiveText;
}
.repeat-columns{
@extend %grid;
> *{
background-color:var(--subHeadBackColor);
border:1px solid black;
}
}
.image-container{
@extend %grid;
grid-template-columns:"content";
}
.image-container__input{
z-index:10;
background-color:white;
box-shadow:0 0 var(--grid-gap) var(--grid-gap) white;
grid-area:content;
place-self:center;
width:calc(100% - (var(--big-gap) * 2));
transition:opacity var(--focusTiming),height var(--focusTiming);
opacity:0;
height:0;
overflow:hidden;
span{
white-space: pre-wrap;
}
input{
width:100%;
box-sizing:border-box;
}
&.active{
opacity:1;
height:auto;
}
}
.image-container__button{
grid-area:content;
}
.base-container{
@extend %grid;
grid-template-areas:'content';
}
.base-container__actual{
grid-area:content;
}
.base-container__interact{
grid-area:content;
z-index:1;
}
}
#main-nav{
--tabColor:white;
--tabBorder:drop-shadow(1px 0 0 grey) drop-shadow(-1px 0 0 grey) drop-shadow(0 -1px 0 grey) drop-shadow(0 1px 0 black);
display:grid;
gap:var(--half-gap);
grid-template-columns:1fr auto 1fr;
align-items:center;
position:sticky;
top:60px;
background:white;
box-shadow:-10px 0 0 0 white,10px 0 0 0 white,0 -6px white;
z-index:100;
border-bottom:1px solid black;
.nav__button{
font-style:normal;
color:grey;
}
.nav__button--settings{
@extend %pictos;
&.active,
&:hover{
color:black;
}
}
&.system-remnant{
.nav__button--tab {
background-color: var(--tabColor);
position: relative;
display: inline-block;
z-index: 2;
padding:calc(var(--half-gap) + 0.1rem);
filter:var(--tabBorder);
&.active,
&:hover{
@include systemplaceholders.activeTab;
}
&.active{
pointer-events: none;
}
&:before{
height: calc(100% - 1px);
width: 50px;
background-color: var(--tabColor);
content: "";
position: absolute;
z-index: -1;
}
}
.nav__button--tab--left{
border-radius:0 50% 0 0;
margin-left:1px;
justify-self:start;
&:before{
border-radius: 0 2px 0 0;
left: calc(100% - 40px);
top: 0px;
-webkit-transform: skewX(45deg);
transform: skewX(45deg);
}
}
.nav__button--tab--right{
border-radius:50% 0 0 0;
margin-right:1px;
justify-self:end;
&:before{
border-radius: 2px 0 0 0;
left: -5px;
top: 0px;
-webkit-transform: skewX(-45deg);
transform: skewX(-45deg);
}
}/*
.tab:nth-of-type(2) {
background-color: #555;
top: 10px;
left: 30px;
z-index: 1;
color: #EEE;
}
.tab:nth-of-type(2):before {
background-color: #555;
}
.tab:nth-of-type(2):hover,
.tab:nth-of-type(2):hover:before {
background-color: #159;
transition: 0.3s ease-out;
}*/
}
}
#main{
.system-header{
margin:0 auto;
width:200px;
}
article{
padding:var(--grid-gap);
}
article:not(.active){
display:none;
}
.roller{
@extend %roller;
}
.translate-control{
&:checked ~ .untranslated,
&:not(:checked) ~ .translated{
display:none;
}
}
.section{
transition:box-shadow var(--focusTiming),transform;
border-radius:6px;
padding:var(--half-gap);
&:hover{
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
}
}
}
// Settings Styling
#settings{
@extend %article-layout;
.input-label{
justify-content: space-between;
[name]:not([type=number], [type=checkbox], [type=radio]){
width:auto;
flex: 0 0 auto;
}
}
section{
align-self:stretch;
align-content:start;
}
}
// Character styling
#character{
@extend %article-layout;
&.system-remnant{
grid-template-areas:
"."
"skills"
"skills"
;
border:{
left:1px solid black;
}
grid-template-rows:repeat(2,auto) 1fr;
}
}
#character-info{
@extend %repeat-section-layout;
> .health-track{
margin:0 -5px;
}
}
#stats{
grid-template-columns: auto 1fr repeat(2,auto);
grid-template-areas:
'header header header header'
'stat_points . . .'
'reserve . . .'
'. . . .'
'header2 header2 header2 header2'
;
h2{
grid-area:auto;
grid-column:1/-1;
}
> button{
grid-column:2;
}
@each $point in stat_points,reserve{
> .#{$point}{
grid-area:$point;
justify-self:end;
}
}
> .reserve{
box-sizing:border-box;
}
.equipment{
@extend %grid;
grid-template-areas: 'header';
gap:var(--half-gap);
grid-column:1/-1;
place-self:stretch;
h2{
grid-area:header;
}
.repitem{
@extend %grid;
}
.repeat-columns,
.repitem{
grid-template-columns: 1fr 2.5rem;
}
}
}
#secondary{
> .secondaries{
@extend %grid;
grid-template-columns:1fr repeat(3,auto);
grid-template-areas:'header header header header';
}
> .combat-gear{
@extend %grid;
grid-template-areas:'header';
}
}
#skill{
@extend %repeat-section-layout;
grid-area:skills;
.repcontainer{
gap:0;
}
.repitem{
@extend %grid;
position:relative;
span{
align-self:center;
text-align:left;
}
}
.repeat-columns,
.repitem{
grid-template-columns:1rem 1fr 3rem 4rem 2.5rem;
> input{
width:100%;
}
}
.repeat-columns{
@each $num in 1,2,3,4{
:nth-child(#{$num}){
grid-column:$num + 1;
}
}
}
.raw-control:not(:checked) + .raw,
.raw-control:checked ~ .custom{
display:none;
}
.roller.raw{
grid-column:span 2;
}
}
@media (min-width:680px) {
#weapon,#remnant-weapons{
grid-column:span 2;
}
}
@media(min-width:1300px){
#main{
display:grid;
grid-template-columns:1fr 1fr;
column-gap:var(--big-gap);
.system-header{
grid-column:1/-1;
}
.sheet-state:not([value='settings']){
~ article:not(#settings){
display:grid;
align-self:start;
}
~ #main-nav{
.nav__button--tab{
@include systemplaceholders.activeTab;
pointer-events: none;
}
}
}
}
#settings{
grid-column:1/-1;
}
#main-nav{
grid-column:1/-1;
}
}
#weapon{
@extend %repeat-section-layout;
input[type='text']{
width:100%;
}
.repitem{
@extend %grid;
row-gap:var(--half-gap);
> .underlined{
width:100%;
}
}
span.underlined{
display:block;
text-align:center;
padding:2px;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
border:{
width:1px 3px;
style:solid;
color:transparent transparent black transparent;
};
}
.repeat-columns,
.repitem{
grid-template-columns:1rem 1fr 10rem 7rem;
align-items:end;
}
.repeat-columns{
@each $num in 1,2,3{
:nth-child(#{$num}){
grid-column:$num + 1;
}
}
}
.headed-textarea{
grid-column:1/-1;
}
}
#adjustments{
display:contents;
.repitem{
@extend %grid;
gap:var(--half-gap);
}
input[type='text']{
width:100%;
}
.subsection{
@extend %repeat-section-layout;
}
}
// Battle Remnant Styling
#remnant{
@extend %article-layout;
&.system-remnant{
grid-template-areas:
"image"
"image"
;
border:{
right:1px solid black;
}
grid-template-rows:auto 1fr;
}
}
#remnant-pic{
grid-area:image;
>.input-label{
justify-self:start;
}
}
#remnant-ratings{
@extend %grid;
grid-template-columns:1fr repeat(2,auto);
grid-template-areas:'header header header';
.remnant-skills{
@extend %repeat-section-layout;
grid-template-areas:'header header header';
place-self:stretch;
grid-column:1/-1;
gap:0;
*{
place-self:stretch;
}
input{
width:100%;
}
&,
.repeat-columns{
grid-template-columns:1fr 4rem 3rem;
*{
align-self:center;
}
}
.repeat-columns{
grid-column:1/-1;
:not(:first-child){
text-align:center;
}
}
.roller{
justify-self:start;
}
input{
justify-self:center;
}
}
}
#remnant-stats{
@extend %grid;
grid-template-columns:1fr repeat(2,auto) 1fr repeat(2,auto);
grid-template-areas:
'header header header header header header'
'. . . . . .'
'. . . . . .'
'. . . . . .'
'assault_damage_label assault_damage_label assault_damage_label assault_damage_label assault_damage_label assault_damage_input'
'strike_damage_label strike_damage_label strike_damage_label strike_damage_label strike_damage_label strike_damage_input'
'strike_range_label strike_range_label strike_range_label strike_range_label strike_range_label strike_range_input'
;
@each $field in strike_damage,strike_range,assault_damage{
.#{$field}_label{
grid-area:#{$field}_label;
}
.#{$field}_input{
grid-area:#{$field}_input;
}
}
}
#remnant-specialty{
@extend %repeat-section-layout;
}
#remnant-trait{
@extend %repeat-section-layout;
.repeat-columns,
.repitem{
grid-template-columns:1fr 4rem 4rem;
}
.repitem{
@extend %grid;
}
}
#remnant-drone{
@extend %repeat-section-layout;
.repitem{
@extend %grid;
grid-template-columns:repeat(2,1fr);
gap:var(--tiny-gap) var(--half-gap);
}
.input-label--button{
grid-column:1;
}
.strikerange{
grid-column:2;
}
.input-label{
span,button{
text-align:left;
}
&:not(.input-label--dual){
:not([name]){
flex:1 1 0;
}
}
}
.structure{
display:flex;
gap:var(--half-gap);
grid-column:1/-1;
place-self:center end;
grid-column:span 2;
*{
align-self:center;
}
}
}
#remnant-weapons{
@extend %repeat-section-layout;
.repeat-columns,
.repitem{
grid-template-columns:1rem 1fr 5rem 4rem 7rem 8rem 5rem;
}
.repeat-columns{
@each $num in 1,2,3,4,5,6{
:nth-child(#{$num}){
grid-column:$num + 1;
}
}
}
.repitem{
@extend %grid;
}
}
/*
Roll Template
#rolltemplate
*/
.sheet-rolltemplate-rapidfire{
@extend %font-vars;
--templateRadius: 1rem;
--dieSize: 50px;
--templateBorderW: 2px 5px 2px 5px;
--fumbleColor: red;
--critColor: green;
--innerBorderRadius: 5px;
--borderSize:1px;
--sizeFactor: 6;
--cornerSize: calc(1107px / var(--sizeFactor)) calc(952px / var(--sizeFactor));
--headSideMargin:40px;
--headVerticalMargin:10px;
--circleSize:73px;
--hexagonBackgroundImage:url('https://s3.amazonaws.com/files.d20.io/images/258492885/gzYgspdnfa8K2zUgTrgV3g/original.png');
margin:{//ensures that the template fills the chat window
left: -40px;
right: -5px;
}
@extend %defaultStyles;
a[href^='!'],
a[href^='~']{
font-family: var(--topHeaderFont);
background-color: transparent;
color: black;
font-weight: 700;
padding: 0;
}
h1,h2,h3,h4,h5,a{
white-space: pre-wrap;
}
&:not(:first-child){//Moves the template to accomodate the sender tag
margin-top:10px;
}
.sheet-content{
@extend %grid;
overflow:hidden;
}
> .sheet-template{
overflow:hidden;
--hexColor:#000000;
&.sheet-system-remnants{
padding:15px 10px;
> .sheet-content{
background-color: #c2c2c2;
background:{
image:var(--hexagonBackgroundImage);
position:top left;
size:400px auto;
repeat:no-repeat;
};
padding:0 10px;
border-radius:10px;
border:5px solid black;
grid-template-columns:[roll-start footer-start] var(--headSideMargin) [header-start] 1fr [damage-start] 1fr [roll-end difficulty-start] 1fr [damage-end] 1fr [footer-end] var(--headSideMargin)[difficulty-end header-end];
grid-template-areas:
'. header header header header header'
'. . . . . .'
'. . . . . .'
'. . . . . .'
'. . . . . .'
'description description description description description description'
'footer footer footer footer footer .'
;
&.sheet-single-roll{
.sheet-hexagon{
grid-column:damage-start / damage-end;
}
}
}
+ .sheet-image-border{
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
background-repeat:no-repeat;
&.sheet-has-header.sheet-has-footer{
background:{
image:var(--topCorner), var(--bottomCorner);
position:top left, bottom right;
size:var(--cornerSize),var(--cornerSize);
};
}
&.sheet-has-header:not(.sheet-has-footer){
background:{
image:var(--topCorner);
position:top left;
size:var(--cornerSize);
};
}
&.sheet-has-footer:not(.sheet-has-header){
background:{
image:var(--bottomCorner);
position:bottom right;
size:var(--cornerSize);
};
}
}
.sheet-header,.sheet-footer{
@extend %grid;
min-height: 30px;
align-items: center;
grid-template-areas:'content';
*{
box-sizing:border-box;
grid-area:content;
}
}
.sheet-header{
--headBorder:0 3px 3px 3px;
--headRadius:0 0 10px 10px;
--headItems:center start;
--headSelf:start;
--headMargin:0 0 10px -3px;
--headPadding:5px 10px 10px 15px;
grid-column:2/7;
*{
text-align:left;
}
}
.sheet-footer{
--headBorder:3px 3px 0 3px;
--headRadius:10px 10px 0 0;
--headItems:center end;
--headSelf:end;
--headMargin:9px -3px 0 0;
--headPadding:15px 15px 5px 5px;
grid-column:1/6;
*{
text-align:right;
}
}
.sheet-background{
@extend %grid;
grid-template-areas: 'content';
background-color: #aac1dd80;
border-color:black;
border-style: solid;
border-color: black;
border-width:var(--headBorder);
border-radius:var(--headRadius);
place-items:var(--headPlace);
margin:var(--headMargin);
place-self:var(--headSelf);
padding:var(--headPadding);
box-sizing:border-box;
backdrop-filter: blur(2px);
min-width:120px;
*{
opacity:0;
}
}
.sheet-header__content{
@extend %grid;
place-self:var(--headSelf);
grid-template-areas:'content';
padding:var(--headPadding);
place-items:var(--headItems);
margin:var(--headMargin);
border-radius:var(--headRadius);
}
.sheet-header__image{
width:100%;
height:100%;
background:{
image:var(--headerBottom);
position:bottom left;
size:calc(699px / var(--sizeFactor)) calc(130px / var(--sizeFactor));
repeat:no-repeat;
};
z-index:1;
}
.sheet-footer__content{
@extend %grid;
place-self:var(--headSelf);
grid-template-areas:'content';
place-items:var(--headItems);
padding: var(--headPadding);
margin:var(--headMargin);
border-radius:var(--headRadius);
}
.sheet-footer__image{
width:100%;
height:100%;
background:{
image:var(--footerTop);
position:top right;
size:calc(699px / var(--sizeFactor)) calc(130px / var(--sizeFactor));
repeat:no-repeat;
};
z-index:1;
}
.sheet-fumble-toggle + .sheet-hexagon__hex-wrapper{
--hexColor:#db1a1a;
}
.sheet-crit-toggle + .sheet-hexagon__hex-wrapper{
--hexColor:#02a98d;
}
.sheet-hexagon{
@extend %hex-base;
--shadow:black;
width:var(--circleSize);
aspect-ratio: 1 / 1.1547005;
transform:rotate(150deg);
h4 *{
font-family:var(--midHeaderFont);
font-weight:normal;
}
}
.sheet-hexagon__hex-wrapper{
@extend %hex-rotate;
}
.sheet-hexagon__hex-wrapper__hex-border{
@extend %hex-rotate;
@extend %hex-shape;
background:radial-gradient(at 150%,#585858,var(--hexColor));
&:before{
@extend %hex-shape;
@extend %hex-rotate;
}
&:after{
@extend %hex-shape;
@extend %hex-component;
transform:rotate(-120deg);
}
}
.sheet-hexagon__hex-wrapper__hex-border__hex-content{
@extend %hex-component;
transform:rotate(-30deg);
grid-template-columns:auto;
grid-template-rows:auto auto;
gap:var(--half-gap);
place-items:center;
place-content:center;
}
.sheet-roll,
.sheet-difficulty{
align-self:start;
}
.sheet-container-background{
background-color:white;
margin:{
right:-10px;
left:-10px;
bottom: -17px;
};
border:{
top:2px solid black;
bottom:2px solid black;
}
&,
+ div{
margin-top:15px;
padding-block:5px;
}
}
.sheet-description.sheet-text{
h4{
text-align:left;
margin-top:var(--grid-gap);
font-weight:700;
}
}
}
span:not(.inlinerollresult){
font-size:13px;
}
}
.sheet-roll,
.sheet-difficulty{
&:nth-child(even){
grid-column:roll-start / roll-end;
}
&:nth-child(odd){
grid-column:difficulty-start / difficulty-end;
}
}
.sheet-damage{
&:nth-child(even){
grid-column:damage-start / damage-end;
}
&:nth-child(odd){
grid-column:difficulty-start / difficulty-end;
}
}
@each $area in header,description,footer{
.sheet-#{$area}{
grid-area:$area;
}
}
:not(div),
.sheet-hexagon,
.sheet-hexagon div,
.sheet-text,
.sheet-description:not(.sheet-container-background){
position:relative;
z-index:10;
}
.inlinerollresult{
font-size:1.5rem;
&[title*="[computed value]"],
&[original-title*="[computed value]"]{
pointer-events: none;
}
&,
&.fullcrit,
&.fullfail,
&.importantroll{
border: none;
background-color: transparent;
}
&.fullcrit{
}
&.fullfail{
}
&.importantroll{
}
}
.sheet-hexagon,.sheet-circle{
place-self:center;
&.sheet-damage{
justify-self:center;
}
:not(div){
color:white;
}
}
}
File diff suppressed because it is too large Load Diff
+321
View File
@@ -0,0 +1,321 @@
include Mixins/_htmlelements.pug
include Mixins/_rolltemplate mixins.pug
include Mixins/_Sheet Mixins.pug
+hidden({name:'template_start',value:'&{template:rapidfire} {{footer=@{character_name}}} {{character_id=@{character_id}}} {{system=@{system}}}'})
+hidden({name:'collapsed',value:''})
+hidden({name:'system',value:'remnants'})
main#main.subsystem-style.system-remnant
+hidden({name:'sheet_state',value:'settings',class:'sheet-state'})
+hidden({name:'system header'})
+img({name:'system header',role:'heading','aria-level':1,class:'system-header','data-i18n':'system header'})
nav#main-nav.subsystem-style.system-remnant
+navButton({name:'character','data-i18n':'character',class:'nav__button nav__button--tab nav__button--tab--left character',role:'heading','aria-level':4})
+navButton({name:'settings',class:'active nav__button nav__button--settings settings'})
|y
+navButton({name:'battle remnant','data-i18n':'battle remnant',class:'nav__button nav__button--tab nav__button--tab--right battle-remnant',role:'heading','aria-level':4})
article#settings.subsystem-style.system-remnant.settings.active
section#system-behavior.section
+select-label('setting',{name:'system',class:'underlined'},null,{role:'heading','aria-level':4})
+option({value:'remnants','data-i18n':'remnants',selected:''})
+input-label('difficulty automation',{name:'difficulty',value:'1',checked:'',class:'box',type:'checkbox'},null,{role:'heading','aria-level':4})
+select-label('action penalty automation',{name:'action penalty automation',class:'underlined'},null,{role:'heading','aria-level':4})
each value,index in ['disabled','ask','use input']
- let optObj = {value,'data-i18n':value};
if index === 0
- optObj.selected = '';
+option({value,'data-i18n':value})
section#sheet-behavior.section
+select-label('whisper rolls to gm',{name:'whisper',class:'underlined'},null,{role:'heading','aria-level':4})
+option({value:' ','data-i18n':'never',selected:''})
+option({value:'/w gm ','data-i18n':'always'})
+option({value:'?{Whisper|No, |Yes,/w gm }','data-i18n':'ask'})
article#character.character.subsystem-style.system-remnant
section#character-info.section
+h2({'data-i18n':'character info'})
each field in characterFields
+input-label(field,{name:field,type:'text',class:`underlined ${replaceSpaces(field)}`},{},{role:'heading','aria-level':5})
.headed-textarea.appearance
+h5({'data-i18n':'appearance / mannerisms'})
+adaptiveTextarea({name:'appearance/mannerisms',class:'underlined'})
+track('health track','health')
section#stats.stat-grid.section.repeating-container
+h2({'data-i18n':'stats'})
+input-label('stat points',{name:'stat points',class:'boxed',type:'number'},{class:'stat_points'},{role:'heading','aria-level':4})
+input-label('reserve',{name:'reserve',class:'boxed',type:'number'},{class:'reserve'},{role:'heading','aria-level':4})
each name in stats
+roller({name,role:'heading','aria-level':4,'data-i18n':name})
+select({name,class:'boxed number'})
each value in [-3,-2,-1,0,1,2,3]
- let optObj = {value};
if value === 0
- optObj.selected = '';
+option(optObj)
!=value
+number({name:`${name} mod`,class:'boxed'})
.repeating-container__equipment.equipment.subsection
+h2({'data-i18n':'gear and money'})
+input-label('easy living',{name:'easy living',class:'boxed',type:'number'},{},{role:'heading','aria-level':4})
.repeat-columns
h5(data-i18n='name')
h5(data-i18n='quantity abbreviation')
+customControlFieldset('equipment')
+checkbox({name:'collapse',class:'collapse',title:attrTitle('equipment','collapse')})
+text({name:'name',class:'underlined',role:'heading','aria-level':3,title:attrTitle('equipment','name')})
+number({name:'quantity',class:'underlined',title:attrTitle('equipment','quantity')})
.headed-textarea.notes.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('equipment','notes')})
section#secondary.section
.secondaries.stat-grid
+h2({'data-i18n':'secondary stats'})
+roller({name:'initiative',class:`initiative-label span-2`,'data-i18n':'initiative',role:'heading','aria-level':4})
+number({name:'initiative',class:`boxed initiative`,readonly:'',value:'0'})
+number({name:`initiative mod`,class:`boxed initiative_mod`})
each name in secondaries
label(for=`${name}-mod` class=`${name}-label span-2` data-i18n=name role='heading' aria-level=4)
+number({name,class:`boxed ${name}`,readonly:'',value:secondaryDefaults[name]})
+number({name:`${name} mod`,class:`boxed ${name}_mod`,id:`${name}-mod`})
label(for='health' class='health-label' data-i18n='health',role='heading' aria-level=4)
.boxed.span-2
+number({name:'health',id:'health'})
span.slash /
+number({name:'health max',readonly:''})
+number({name:`health mod`,class:`boxed health_mod`})
.combat-gear.stat-grid
+h2({'data-i18n':'combat gear'})
each name in combatGear
+input-label(name,{name,class:'boxed',type:'number'},{class:`${name} contents`},{role:'heading','aria-level':4})
+repeating_section('skill','skills',['skills','level','stat','xp'])
+checkbox({name:'raw',hidden:'',class:'raw-control',value:1,title:attrTitle('skill','raw')})
+roller({name:'roll',title:buttonTitle('skill','roll'),class:'raw'})
+span({name:'name','data-i18n-dynamic':'',role:'heading','aria-level':5,title:attrTitle('skill','name')})
+roller({name:'roll',title:buttonTitle('skill','roll'),class:'custom'})
+text({name:'name',role:'heading','aria-level':5,class:'custom underlined',title:attrTitle('skill','name')})
+number({name:'level',class:'underlined',title:attrTitle('skill','level')})
+select({name:'stat',class:'underlined',title:attrTitle('skill','stat')})
+option({value:'query','data-i18n':'ask'})
each value in stats
+option({value,'data-i18n':value})
+number({name:'xp',class:'underlined',title:attrTitle('skill','xp')})
section#adjustments
.repeating-container.repeating-container--advantages.advantages.subsection.section
+h2({'data-i18n':'advantages'})
.repeat-columns
h5(data-i18n='name')
+customControlFieldset('advantage')
+checkbox({name:'collapse',class:'collapse',title:attrTitle('advantage','collapse')})
+text({name:'name',class:'underlined',role:'heading','aria-level':3,title:attrTitle('advantage','name')})
.headed-textarea.notes.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('advantage','notes')})
.repeating-container.repeating-container--disadvantages.disadvantages.subsection.section
+h2({'data-i18n':'disadvantages'})
.repeat-columns
h5(data-i18n='name')
+customControlFieldset('disadvantage')
+checkbox({name:'collapse',class:'collapse',title:attrTitle('disadvantage','collapse')})
+text({name:'name',class:'underlined',role:'heading','aria-level':3,title:attrTitle('disadvantage','name')})
.headed-textarea.notes.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('disadvantage','notes')})
.repeating-container.repeating-container--adjustments.adjustments.subsection.section
+h2({'data-i18n':'adjustments'})
.repeat-columns
h5(data-i18n='name')
+customControlFieldset('adjustment')
+checkbox({name:'collapse',class:'collapse',title:attrTitle('adjustment','collapse')})
+text({name:'name',class:'underlined',role:'heading','aria-level':3,title:attrTitle('adjustment','name')})
.headed-textarea.mod.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('adjustment','notes')})
//-
.headed-textarea.mods.expanded
+h5({'data-i18n':'mods'})
+adaptiveTextarea({name:'mods',class:'underlined',title:attrTitle('adjustment','mods')})
+repeating_section('weapon','weapons',['weapon','skill','damage'])
+checkbox({name:'collapse',class:'collapse'})
+roller({name:'roll',title:buttonTitle('weapon','roll')})
+text({name:'name',class:'underlined',title:attrTitle('weapon','name')})
+hidden({name:'skill',value:'select',title:attrTitle('weapon','skill')})
+actionQuery({name:'skill action',title:buttonTitle('weapon','skill')})
+checkbox({name:'translate skill',hidden:'',value:'1',class:'translate-control',checked:''})
+span({name:'skill',class:'underlined translated','data-i18n-dynamic':'',title:attrTitle('weapon','skill')})
+span({name:'skill',class:'underlined untranslated',title:attrTitle('weapon','skill')})
.roll-plus
+span({'data-i18n':'lead'})
span.plus +
+number({name:'damage',class:'underlined',title:attrTitle('weapon','damage')})
.headed-textarea.notes.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('weapon','notes')})
article#remnant.battle-remnant.subsystem-style.system-remnant
section#remnant-pic.section
+track('structure track','structure')
+input-label('duress',{name:'duress',type:'number',class:'boxed'},{},{role:'heading','aria-level':2})
.image-container.remnant-token
+input-label('image prompt',{name:'remnant token',type:'text',class:'boxed',value:'https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png'},{class:'image-container__input stacked'},{role:'heading','aria-level':5})
+action({name:'remnant token',class:'image-container__button'})
+img({name:'remnant token',class:'image-container__image',src:'https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png'})
section#remnant-ratings.stat-grid.section
+h2({'data-i18n':'total ratings'})
each name in remnantRatings
- let label = abbreviations[name] || name;
if name === 'situational awareness'
+roller({name:'situational awareness',class:'uppercase',role:'heading','aria-level':4,'data-i18n':label,class:'uppercase'})
else
h4(data-i18n=label)
+number({name,readonly:'',class:'boxed'})
+number({name:`${name} mod`,class:'boxed'})
+roller({name:'remnant initiative','data-i18n':'initiative',role:'heading','aria-level':4})
+number({name:'remnant initiative',readonly:'',class:'boxed'})
+number({name:'remnant initiative mod',class:'boxed'})
.remnant-skills
+h2({'data-i18n':'skills'})
.repeat-columns
each value in ['skill','level','xp']
+h5({'data-i18n':value})
each name in remnantSkills
+roller({name:`${name} roll`,'data-i18n':name,role:'heading','aria-level':4})
+number({name:`${name} level`,class:'underlined'})
+number({name:`${name} experience`,class:'underlined'})
section#remnant-stats.stat-grid.section
+h2({'data-i18n':'remnant stats'})
each name in remnantStats
if name === 'structure'
.input-label.span-all.structure
label(data-i18n='structure' for='structure' role='heading' aria-level=4)
.boxed.span-2
+number({name:'structure',id:'structure'})
span.slash /
+hidden({name:'structure max'})
+number({name:'structure base',title:attrTitle('structure max')})
else
label(data-i18n=name for=replaceSpaces(name) role='heading' aria-level=4).span-2
+number({name,class:'boxed',type:'number',id:replaceSpaces(name)})
each name in remnantCombatStats
- let inputObj = {name,class:`boxed ${replaceSpaces(name)}_input`,type:'number',id:replaceSpaces(name)};
label(data-i18n=name role='heading' aria-level=4 for=replaceSpaces(name) class=`${replaceSpaces(name)}_label`)
+input(inputObj)
+repeating_section('specialty','specialty',['name'],'remnant-specialty')
+checkbox({name:'collapse',class:'collapse'})
+text({name:'name',class:'underlined',title:attrTitle('specialty','name')})
.headed-textarea.description.expanded
+h5({'data-i18n':'description'})
+adaptiveTextarea({name:'description',class:'underlined',title:attrTitle('specialty','description')})
+repeating_section('trait','traits',['name','duress','focus'],'remnant-trait')
+checkbox({name:'collapse',class:'collapse'})
+text({name:'name',class:'underlined'})
+number({name:'duress',class:'underlined',title:attrTitle('trait','duress')})
+number({name:'focus',class:'underlined',title:attrTitle('trait','focus')})
.headed-textarea.description.expanded
+h5({'data-i18n':'description'})
+adaptiveTextarea({name:'description',class:'underlined',title:attrTitle('trait','description')})
section#remnant-drone.repeating-container.repeating-container--drone.section
+h2({'data-i18n':'drones'})
+customControlFieldset('drone')
+checkbox({name:'collapse',class:'collapse'})
+text({name:'name',class:'underlined remnant-drone__name',role:'heading','aria-level':3,title:attrTitle('drone','name')})
+input-label('# active',{name:'active',type:'number',class:'boxed'},null,{role:'heading','aria-level':5,'data-i18n':label,'data-i18n':'# active'})
each name in droneStats
- let label = abbreviations[name] || name;
if /^(?:strike|assault)/.test(label)
- let hiddenName = /strike/.test(label) ? 'strike' : 'assault';
+hidden({name:hiddenName,type:'number',class:`underlined expanded--empty`})
if /^(?:strike|assault)$/.test(name)
+roller-label({name,type:'number',class:`boxed`,title:attrTitle('drone',name)},{name,role:'heading','aria-level':5,'data-i18n':label,title:buttonTitle('drone',name)},{class:`${replaceSpaces(name)}`})
else if name === 'struct'
.structure
+span({role:'heading','aria-level':5,'data-i18n':label})
.boxed.span-2
+number({name:'structure',id:'structure'})
span.slash /
+number({name:'structure max'})
else if name=== 'strikerange'
+input-label(label,{name,type:'number',class:`boxed`,title:attrTitle('drone',name),readonly:''},{class:replaceSpaces(name)},{role:'heading','aria-level':5,'data-i18n':label})
else
+input-label(label,{name,type:'number',class:`boxed`,title:attrTitle('drone',name)},{class:replaceSpaces(name)},{role:'heading','aria-level':5,'data-i18n':label})
.headed-textarea.description.expanded
+h5({'data-i18n':'description'})
+adaptiveTextarea({name:'description',class:'underlined',title:attrTitle('drone','description')})
section#remnant-weapons.repeating-container.repeating-container--remnant-weapons.section
+h2({'data-i18n':'weapons'})
.repeat-columns
each name in remnantWeaponInputs
+h5({'data-i18n':name})
+customControlFieldset('remnant weapon')
+checkbox({name:'collapse',class:'collapse'})
+roller({name:'roll',title:buttonTitle('remnant-weapon','roll')})
+text({name:'name',class:'underlined',title:attrTitle('remnant-weapon','name'),role:'heading','aria-level':4})
+select({name:'skill',class:'underlined',title:attrTitle('remnant-weapon','skill')})
+option({value:' ','data-i18n':'select',selected:''})
+option({value:'assault','data-i18n':'assault'})
+option({value:'strike','data-i18n':'strike'})
+number({name:'bonus',class:'underlined',title:attrTitle('remnant-weapon','bonus')})
.roll-plus
+span({'data-i18n':'lead'})
span.plus +
+number({name:'damage',class:'underlined',title:attrTitle('weapon','damage'),readonly:''})
+number({name:'damage bonus',class:'underlined damage-bonus',title:attrTitle('remnant-weapon','damage bonus')})
+text({name:'type',class:'underlined',title:attrTitle('remnant-weapon','type')})
.headed-textarea.description.expanded
+h5({'data-i18n':'notes'})
+adaptiveTextarea({name:'notes',class:'underlined',title:attrTitle('remnant-weapon','notes')})
//-Rolltemplate definitions
+rolltemplate('rapidfire')
.content(class='{{#singleroll}}single-roll{{/singleroll}}')
+templateConditionalDisplay('header')
.header
.header__background.background
h3(tabindex='-1') {{header}}
.header__content
h3 {{header}}
.header__image
+templateConditionalDisplay('roll')
+hexagon({},'status').sheet-roll
h4(data-i18n='roll')
|{{roll}}
each num in [1,2,3,4,5,6]
+templateConditionalDisplay(`roll${num}`)
- let hexObj = {class:`sheet-roll`};
+hexagon(hexObj,`status${num}`)
h4(data-i18n='roll')
|{{roll#{num}}}
+templateConditionalDisplay('difficulty')
+hexagon().difficulty
h4(data-i18n='difficulty')
|{{difficulty}}
+templateConditionalDisplay('damage')
+hexagon().damage
+templateConditionalDisplay('damage_label')
h4 {{computed::damage_label}}
+templateConditionalDisplay('damage_label',true)
h4(data-i18n='damage')
|{{computed::damage}}
+templateConditionalDisplay('description')
.container-background.description
.description.text
h4(data-i18n='notes')
span {{description}}
+userTemplateFields
+templateConditionalDisplay('description',true)
+templateConditionalDisplay('custom_description')
.container-background.description
.description.text
+userTemplateFields
+characterLink()
+script()
include Javascript/utility.js
include Javascript/setupfunctions.js
include Javascript/clickfunctions.js
include Javascript/rollfunctions.js
include Javascript/initialchangefunctions.js
include Javascript/triggeredfunctions.js
include Javascript/calculators.js
include Javascript/listeners.js
//- The below declarations import variables from the pug file and mixins into the sheetworker code
each content,prop in varObjects
|
|const !{prop} = !{JSON.stringify(content)};
|
|
include Javascript/variables.js
+128
View File
@@ -0,0 +1,128 @@
{
"remnants":"remnants",
"character info":"character info",
"character name":"character name",
"player name":"player name",
"nationality":"nationality",
"organization affiliations":"organization affiliations",
"appearance / mannerisms":"appearance / mannerisms",
"stats":"stats",
"body":"body",
"mind":"mind",
"spirit":"spirit",
"stat points":"stat points",
"reserve":"reserve",
"secondary stats":"secondary stats",
"defence":"defence",
"resist":"resist",
"health":"health",
"initiative":"initiative",
"combat gear":"combat gear",
"armour bonus":"armour bonus",
"shield bonus":"shield bonus",
"action penalty":"action penalty",
"health track":"health track",
"gear and money":"gear and money",
"easy living":"easy living",
"skills":"skills",
"level":"level",
"stat":"stat",
"xp":"xp",
"weapons":"weapons",
"weapon":"weapon",
"attack":"attack",
"damage":"damage",
"query":"query",
"ask":"ask",
"select":"select",
"1d6":"1d6",
"lead":"lead",
"notes":"notes",
"mods":"mods",
"adjustments":"adjustments",
"advantages":"advantages",
"disadvantages":"disadvantages",
"structure track":"structure track",
"duress":"duress",
"total ratings":"total ratings",
"situational awareness":"situational awareness",
"remnant stats":"remnant stats",
"armour":"armour",
"speed":"speed",
"structure":"structure",
"inspiring":"inspiring",
"terrifying":"terrifying",
"assault damage":"assault damage",
"strike damage":"strike damage",
"strike range":"strike range",
"strike range (km)":"strike range (km)",
"remnant skills":"remnant skills",
"skill":"skill",
"remnant weapons":"remnant weapons",
"name":"name",
"bonus":"bonus",
"damage":"damage",
"damage bonus":"damage bonus",
"type":"type",
"assault":"assault",
"strike":"strike",
"description":"description",
"traits":"traits",
"type":"type",
"focus":"focus",
"specialty":"specialty",
"skill query":"Which skill would you like to use",
"archery / throwing":"Archery / throwing",
"athletics":"athletics",
"awareness":"awareness",
"bureaucracy":"bureaucracy",
"command":"command",
"craft":"craft",
"dodge":"dodge",
"etiquette":"etiquette",
"history":"history",
"investigation":"investigation",
"language":"language",
"larceny":"larceny",
"lore":"lore",
"medicine":"medicine",
"melee":"melee",
"perform":"perform",
"ride":"ride",
"sail":"sail",
"sciences":"sciences",
"social sciences":"social sciences",
"stealth":"stealth",
"survival":"survival",
"unarmed combat":"unarmed combat",
"drones":"drones",
"difficulty query":"What is the difficulty of the roll",
"stat query":"Which stat are you using",
"roll":"roll",
"difficulty":"difficulty",
"character":"character",
"battle remnant":"battle remnant",
"whisper rolls to gm":"whisper rolls to gm",
"setting":"setting",
"difficulty automation":"difficulty automation",
"never":"never",
"always":"always",
"ask":"ask",
"enabled":"enabled",
"disabled":"disabled",
"motion":"motion",
"remnant defence":"remnant defence",
"situation awareness abbreviation":"sa",
"image prompt":"Enter the url for the image you would like to add. Leave blank to reset.",
"remnant token":"remnant token",
"drone query":"How many drones are attacking",
"remnant-initiative":"remnant initiative",
"action penalty automation":"action penalty automation",
"use input":"use input",
"quantity abbreviation":"qty",
"action penalty query":"What is your current action penalty",
"system header":"system header",
"# active":"# active",
"lead":"lead",
"trail":"trail"
}