Icon v0.3

- Added preliminary darkmode functionality
- Added preliminary roll template / macro functionality
- Partially fixed tab impersistence
- Fixed burden column scale
- Added 'roster' tab for foes & allies
- Added camp fixtures
- Fixed collapse checkbox global
- Fixed custom action names resetting
- Added ability info boxes
This commit is contained in:
Seraaron
2023-01-07 17:31:35 +00:00
parent 1d70030c6b
commit db773a06e5
35 changed files with 1647 additions and 636 deletions
+1 -1
View File
@@ -57,4 +57,4 @@ article#details.details.character-header.paper-background
.desc-label.center
+text({name:'short desc', class:'underlined', 'data-i18n-placeholder':'brief character description', style:'width:99%;'})
.icon-logo
img(src="https://i.imgur.com/9LTydaX.png").header__image
span.icon-logo.header__image
@@ -2,7 +2,7 @@ section#advancement.advancement.span-2
h1(data-i18n='advancement')
.hide-note
h3(data-i18n='xp')
+collapse(name='xp-rules')
+checkbox({class:'collapse', value:'0', name:'collapse-xp'})
.notes.expanded
p(class='indent bullet', 'data-i18n'="level up reminder line 0")
p(class='indent bullet', 'data-i18n'="level up reminder line 1")
@@ -50,7 +50,7 @@ section#ambitions.ambitions.span-2.long-2
+button({name:'quote', type:'roll', class:'quote'})
.hide-note
p(class='bold capitalize', 'data-i18n'='ambition action roll:')
+collapse(name='ambition-rules')
+checkbox({class:'collapse', value:'0', name:'collapse-ambition'})
.notes.expanded
p(class='indent bullet', 'data-i18n'="1-3 = ambition advances by one segment")
p(class='indent bullet', 'data-i18n'="4-5 = ambition advances by two segments")
@@ -6,7 +6,7 @@ section#camp.camp.span-2.long-2
+text({name:'name', class:'h3-style-input underlined big', 'data-i18n-placeholder':'fixture'})
+textarea({name:'description', class:'serif emph small textarea-3', spellcheck:'false', 'data-i18n-placeholder':'fixture description'})
+textarea({name:'effects', class:'small textarea-4', 'data-i18n-placeholder':'fixture effects'})
+collapse(name='upgrades')
+collapse
.grid___3col.half-gap.center.notes.expanded
+text({name:'cost', class:'ability-info underlined capitalize center', 'data-i18n-placeholder':'purchase cost'})
+text({name:'upgrade', class:'ability-info underlined capitalize center', 'data-i18n-placeholder':'upgrade cost'})
@@ -1,5 +1,5 @@
section#kin-culture.kin-culture.span-2
+input({name:'kin', class:'h1-style-input', 'data-i18n-placeholder':'kin', readonly: true})
+textarea({name:'kin notes', class:'textarea-4', 'data-i18n-placeholder':'write about your kin', spellcheck:'false'})
+textarea({name:'kin notes', class:'textarea-5', 'data-i18n-placeholder':'write about your kin', spellcheck:'false'})
+input({name:'culture', class:'h1-style-input', 'data-i18n-placeholder':'culture', readonly: true})
+textarea({name:'culture notes', class:'textarea-4', 'data-i18n-placeholder':'write about your culture', spellcheck:'false'})
+textarea({name:'culture notes', class:'textarea-5', 'data-i18n-placeholder':'write about your culture', spellcheck:'false'})
@@ -17,31 +17,29 @@ section#actions.actions
h1(data-i18n='actions')
each name in actions
.flex-box.space-around
+button({name, class:'roll-d6', type:'roll', trigger:{listenerFunc:'initiateRoll'}})
+input({name:`${name}-label`, value:`${name}`, class:'action-label uppercase bigger', 'aria-level':3, data_i18n:`${name}`, type:'label', readonly:true})
//- +hidden({name, class:'math', trigger:{triggeredFuncs:['calcActionDice']}})
+roller({name, trigger:{triggeredFuncs:['iconRoll']}})
+input({name, value:`${name}`, class:'action-label uppercase bigger', 'aria-level':3, data_i18n:`${name}`, type:'label', readonly:true})
//- +number({name:`${name}_dice`, readonly:'true', class:'autocalc', value:`(@{${name}-dots}+@{${name}-knack}-@{${name}-burden})`, 'aria-level':5})
+fillLeft({
radioObj:{name:`${name}-dice`},
radioObj:{name:`${name}_dice`},
divObj:{class:'action-radio'},
valueArray:[0,1,2,3,4],
trigger:{affects:[`${name}`]}
valueArray:[0,1,2,3,4]
})
+span({style:'width:20px; height:20px'})
+checkbox({name:`${name}-knack`, class:'checkbox magenta tick'})
+checkbox({name:`${name}-knack`, value:'1', class:'checkbox magenta tick'})
+span({style:'width:20px; height:20px'})
+checkbox({name:`${name}-burden`, class:'checkbox'})
+checkbox({name:`${name}-burden`, value:'1', class:'checkbox'})
+fieldset({name:'action'})
.flex-box.space-around
+button({name:'roll', class:'roll-d6', type:'roll', trigger:{listenerFunc:'initiateRoll'}})
+input({name:'label', class:'action-label uppercase bigger', 'aria-level':3, data_i18n:'custom', type:'label', placeholder:'custom'})
//- +hidden({name:'custom', class:'math', trigger:{triggeredFuncs:['calcActionDice']}})
+roller({name:'custom', trigger:{triggeredFuncs:['iconRoll']}})
+text({name:'name', class:'action-label uppercase bigger', 'aria-level':3, 'data-i18n-placeholder':'custom'})
//- +number({name:'dice', disabled:'true', class:'autocalc', value:'(@{dots}+@{knack}-@{burden})', 'aria-level':5})
+fillLeft({
radioObj:{name:'dice'},
radioObj:{name:'dots'},
divObj:{class:'action-radio'},
valueArray:[0,1,2,3,4],
trigger:{affects:['action']}
valueArray:[0,1,2,3,4]
})
+span({style:'width:20px; height:20px'})
+checkbox({name:'knack', class:'checkbox magenta tick'})
+checkbox({name:'knack', value:'1', class:'checkbox magenta tick'})
+span({style:'width:20px; height:20px'})
+checkbox({name:'burden', class:'checkbox'})
+checkbox({name:'burden', value:'1', class:'checkbox'})
@@ -11,6 +11,6 @@ section#bond.bond
})
.hide-note
p(class='bold capitalize', 'data-i18n'='additional notes')
+collapse(name='xp-rules')
+checkbox({class:'collapse', value:'0', name:'collapse-bond'})
.notes.expanded
+textarea({name:'bond notes', class:'textarea-3', 'data-i18n-placeholder':'write anything you want here'})
@@ -39,7 +39,7 @@ section#burdens.burdens
+text({name:`penalty ${num}`, class:'burden-penalty underlined smallcaps'})
.hide-note
p(class='bold capitalize', 'data-i18n'='burdens reminder title')
+collapse(name='xp-rules')
+checkbox({class:'collapse', value:'0', name:'collapse-burdens'})
.notes.expanded
p('data-i18n'="burdens reminder line 0")
p(class='indent bullet', 'data-i18n'="burdens reminder line 1")
@@ -53,7 +53,7 @@ section#npcs.npcs.span-all
.chapter-select
+label({class:'input-label', style:'height:26px;'})
+span({class:'smallcaps input-label__text', 'data-i18n':`${name}`, role:'heading', 'aria-level':5})
+select({name:'chapter', class:'input-label__input underlined chapter-select damage-die'})
+select({name:'damage', class:'input-label__input underlined chapter-select damage-die'})
+option({value:'4'})|d4
+option({value:'6', selected:true})|d6
+option({value:'8'})|d8
@@ -72,7 +72,7 @@ section#npcs.npcs.span-all
+text({name:'info-b', class:'ability-info underlined capitalize center', placeholder:'⬧'})
+text({name:'info-c', class:'ability-info underlined capitalize left'})
+textarea({name:'abilities', class:'textarea-4', 'data-i18n-placeholder':'ability text'})
+collapse(name='additionals')
+collapse
.grid__4col.half-gap.notes.expanded
.flex-box.boxed.well-padded.center.span-3
+textarea({name:'additional', class:'textarea-4', 'data-i18n-placeholder':'additional notes or abilities'})
@@ -80,4 +80,7 @@ section#npcs.npcs.span-all
+number({name:'number', class:'h3-style-input underlined'})
p(class='colon')
+text({name:'resource', class:'h3-style-input underlined short-text', style:'min-width:40px;'})
+button({name:'quote', class:'quote span-3', type:'roll', trigger:{listenerFunc:'initiateRoll'}})
+button({name:'quote', class:'quote span-3', type:'roll', trigger:{listenerFunc:'initiateRoll'}})
+button({name:'save', type:'roll', role:'heading', 'aria-level':4,'data-i18n':'save',
value:'&{template:icon} {{name=save}} {{character_name=@{name} (Ch. @{chapter})}} {{roll=[[1d20?{Boons / Curses|NA,+0|+2D,+2d6kh1|+1D,+1d6|−1D,-1d6|−2D,-2d6kh1}]]}} {{target=@{save_target}}}'
})
@@ -16,6 +16,10 @@ section#sheet-settings.sheet-settings
inputObj:{name:'ratio_attack_hvy', class:'underlined', type:'number', value:'2'},
spanObj:{role:'heading', class:'mono smallcaps', 'aria-level':5}
})
+input-label({label:'save_target',
inputObj:{name:'save_target', class:'underlined', type:'number', value:'10'},
spanObj:{role:'heading', class:'mono smallcaps', 'aria-level':5}
})
//- //- p(data-i18n='maxhp%', class='bold mono capitalize')
//- //- +number({name:'max_hp_ratio', class:'mono', value:'4', trigger:{affects:['hp_max','vigor_max','bloodied']}})
//- p(data-i18n='bloodied%', class='bold mono capitalize')
@@ -7,7 +7,7 @@ section#ideals.ideals
+button({name:'quote', type:'roll', class:'quote'})
.hide-note
p(class='bold', 'data-i18n'='xp reminder title')
+collapse(name='xp-rules')
+checkbox({class:'collapse', value:'0', name:'collapse-ideals'})
.notes.expanded
p(class='indent bullet', 'data-i18n'="xp reminder line 0")
p(class='indent bullet', 'data-i18n'="xp reminder line 1")
@@ -1,7 +1,7 @@
section#reminders.reminders
.hide-note
h1(data-i18n='reminders')
+collapse(name='reminders')
+collapse
.notes.expanded
h3(data-i18n='interludes')
p(class='indent bullet', 'data-i18n'="clear all strain")
@@ -23,7 +23,7 @@ section#abilities.abilities.long-2
+text({name:'info-c', class:'ability-info underlined capitalize left'})
+textarea({name:'flavor', class:'serif emph small textarea-1', 'data-i18n-placeholder':'ability flavor', spellcheck:'false'})
+textarea({name:'effects', class:'small textarea-4', 'data-i18n-placeholder':'ability effects'})
+collapse(name='basic-abilities', class='ability')
+collapse
.grid__4col.half-gap.notes.expanded
span(style='width:40px;')
+checkbox({name:'talent 1', class:'talent talent-1'})
@@ -31,15 +31,14 @@ section#abilities.abilities.long-2
h5(data-i18n='talents', class='smallcaps', style='position:relative;left:-50%;')
+checkbox({name:'mastery'})
+text({name:'mastery name', class:'h3-style-input small short-text center', 'data-i18n-placeholder':'mastery name'})
+div({class:'flex-box boxed center span-2', style:'width:100% !important;'})
+textarea({name:'talent 1 effect', class:'small textarea-1', 'data-i18n-placeholder':'talent 1 effect'})
+textarea({name:'talent 2 effect', class:'small textarea-1', 'data-i18n-placeholder':'talent 2 effect'})
.flex-box.boxed.center.span-2
+textarea({name:'talent effect', class:'small textarea-1', 'data-i18n-placeholder':'talent effect'})
.flex-box.boxed.center.span-2
+textarea({name:'mastery effect', class:'small textarea-1', 'data-i18n-placeholder':'mastery effect'})
.hide-note
.flex-box.row
h5(data-i18n='basic abilities')
+collapse(name='basic-abilities')
+collapse
.notes.expanded
p(class='indent bullet', 'data-i18n'="basic abilities reminder line 0")
p(class='indent bullet', 'data-i18n'="basic abilities reminder line 1")
@@ -71,7 +70,7 @@ section#limits.limits
+text({name:'info-c', class:'ability-info underlined capitalize left'})
+textarea({name:'flavor', class:'serif emph small textarea-1', 'data-i18n-placeholder':'ability flavor', spellcheck:'false'})
+textarea({name:'effects', class:'small textarea-4', 'data-i18n-placeholder':'ability effects'})
+collapse(name='basic-abilities', class='ability')
+collapse
.grid__4col.half-gap.notes.expanded
span(style='width:40px;')
+checkbox({name:'talent 1', class:'talent talent-1'})
@@ -79,9 +78,8 @@ section#limits.limits
h5(data-i18n='talents', class='smallcaps', style='position:relative;left:-50%;')
+checkbox({name:'mastery'})
+text({name:'mastery name', class:'h3-style-input small short-text center', 'data-i18n-placeholder':'mastery name'})
+div({class:'flex-box boxed center span-2', style:'width:100% !important;'})
+textarea({name:'talent 1 effect', class:'small textarea-1', 'data-i18n-placeholder':'talent 1 effect'})
+textarea({name:'talent 2 effect', class:'small textarea-1', 'data-i18n-placeholder':'talent 2 effect'})
.flex-box.boxed.center.span-2
+textarea({name:'talent effect', class:'small textarea-1', 'data-i18n-placeholder':'talent effect'})
.flex-box.boxed.center.span-2
+textarea({name:'mastery effect', class:'small textarea-1', 'data-i18n-placeholder':'mastery effect'})
.resolve.hide-note
@@ -94,7 +92,7 @@ section#limits.limits
inputObj:{name:'party resolve', type:'number', class:'underlined'},
spanObj:{role:'heading', 'aria-level':5, class:'normal'}
})
+collapse(name='limit-break')
+checkbox({class:'collapse', value:'0', name:'collapse-limits'})
.notes.expanded
p(class='bold', 'data-i18n'="limit break reminder line 0")
p(class='indent bullet', 'data-i18n'="limit break reminder line 1")
@@ -1,10 +1,15 @@
#stats.half-gap.boxed.well-padded
span(class='center span-2')
span(class='center')
h3(data-i18n='hp')
.flex-box.center
+hidden({name:'bloodied', class:'bloodied-control', trigger:{calculation:'calcBloodied'}, type:'number'})
+input({name:'hp', class:'underlined bloodied-receiver bigger', trigger:{affects:['bloodied']}, type:'number'})
+number({name:'hp_max', class:'bigger', value:'24', readonly:true, trigger:{calculation:['calcHP']}})
span(class='center')
//- +roller({name:'saving throw', role:'heading', 'aria-level':4,'data-i18n':'saving throw', trigger:{listenerFunc:'rollGeneric'}})
+button({name:'save', type:'roll', role:'heading', 'aria-level':4,'data-i18n':'save',
value:'&{template:icon} {{name=save}} {{character_name=@{character_name}}} {{roll=[[1d20?{Boons / Curses|NA,+0|+2D,+2d6kh1|+1D,+1d6|−1D,-1d6|−2D,-2d6kh1}]]}} {{target=@{save_target}}}'
})
span(class='center span-2')
+input-label({label:'wounds',
inputObj:{name:'wounds', class:'underlined', type:'number', value:'0', readonly:true, trigger:{affects:['hp_max','vigor_max']}},
@@ -22,7 +22,7 @@ section#relics.relics.long-2
+option({value:'unequipped', 'data-i18n':'unequipped', selected:true})
+textarea({name:'flavor', class:'serif emph small textarea-1', 'data-i18n-placeholder':'relic flavor', spellcheck:'false'})
+textarea({name:'effects', class:'small textarea-4', 'data-i18n-placeholder':'relic effects'})
+collapse(name='additionals')
+collapse
.grid__3col.half-gap.notes.expanded
.flex-box.boxed.well-padded.center
+label({name:'aspected', class:'info-label emph capitalize', 'data-i18n':'aspected', 'aria-label':5})
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@@ -38,29 +38,6 @@
return returnValue;
};
k.registerFuncs({calcAttributeMod});//We need to register the function with the k-scaffold so that the k-scaffold's listeners can call it when needed.
/**
* Our function to calculate our total Defense or reduction_value rating.
*
* We are using the new `sections` parameter so that we can loop over our `repeating_armor` rows to determine what defense or reduction_value bonuses to apply.
*
* Note that the `trigger` paramter is coming last here. Because the K-scaffold calls functions via the object destructuring argument method, We can put these arguments in any order, as long as they are all part of object. Even though this is possible, I would typically put these in a standardized order of `trigger`,`attributes`,`sections` just to make it easier for me to read the code.
* @param {object} attributes - Same as in {@link calcAttributeMod}.
* @param {object} sections - An object that contains all of the repeating row information for our sheet.
* @param {string[]} sections.repeating_armor - The row IDs of our `repeating_armor` section. We'll use this to loop through and determine which defense bonuses should apply.
* @param {object} trigger - As in {@link calcAttributeMod}
* @returns {number} - Returns the new defense stat value, which the K-scaffold's infrastructure will apply to the sheet.
*/
const calcDefense = function({attributes,sections,trigger}){
let name = trigger.name.replace(/_.+/,'');//Extract the attribute name that will be used for referencing affecting attributes. e.g. `defense_total` becomes `defense` for use in referencing `defence_mod` and `repeating_armor_-oyho8987uLKJ_defense`.
let activeArmorIDs = sections.repeating_armor.filter((id)=>attributes[`repeating_armor_${id}_equipped`]);//Because the K-scaffold's attributes object takes care of ensuring a proper return value for our attributes, we can just do a simple check for whether the attribute value is truthy (e.g. `1`) or falsy (e.g. `0`) because this attribute is a checkbox with a checked state of `1` and an off state of `0`.
let finesse = name === 'defense' ?//Handles accounting for finesse in the defense calculation.
10 + attributes.finesse_mod :
0;
let activeArmorBonus = activeArmorIDs.reduce((total,id)=>total += attributes[`repeating_armor_${id}_${name}`],0);//Total up the bonuses from all our equipped gear.
return attributes[`${name}_mod`] + finesse + activeArmorBonus;//Total our defense or reduction score using the miscellaneous modifier and all of our equipped armors/shields.
};
k.registerFuncs({calcDefense});//Register the function
const boundNumber = (value, min, max) => {
[ value, min, max ] = [ value, min, max].map(input => parseFloat(input));
@@ -74,7 +51,7 @@
on('change:vigor change:vigor_max change:wounds change:vitality', () => {
getAttrs([ 'vigor', 'vigor_max' ], (attributeValues) => {
const boundValue = boundNumber(attributeValues.vigor, 0, attributeValues.vigor_max);
if (boundValue !== attributeValues.vigor) {
if (boundValue === attributeValues.vigor) {
setAttrs({ vigor: boundValue });
}
});
@@ -83,7 +60,7 @@
on('change:hp change:hp_max change:wounds change:vitality', () => {
getAttrs([ 'hp', 'hp_max' ], (attributeValues) => {
const boundValue = boundNumber(attributeValues.hp, 0, attributeValues.hp_max);
if (boundValue !== attributeValues.hp) {
if (boundValue === attributeValues.hp) {
setAttrs({ hp: boundValue });
}
});
@@ -0,0 +1,199 @@
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/**
* Function that extracts the information of which roll was clicked from the name of the button.
* @param {string} string - The triggerName to be parsed
* @returns {array} - Returns an array containing the repeating section name, the row id, and the field that is referenced.
*/
const extractRollInfo = function(string){
//Get the information on which button was clicked.
const [section,id,button] = k.parseTriggerName(string);
//Convert the button name into the name of the relevant attribute (e.g. saving-throw => saving_throw). Also removes the `-action` suffix of the action button to get the raw field name.
const field = button.replace(/\-?action/,'').replace(/\-/g,'_');
return [section,id,field];
};
/**
* Function to translate our advantage query
* @returns {string}
*/
const translateAdvantageQuery = function(){
//Iterate throught the advantage options and create the translated strings.
const options = [['advantage','2d20kh1'],['normal','1d20'],['disadvantage','2d20kl1']]
.map((arr)=>{
const translated = k.capitalize(getTranslationByKey(arr[0]));
return `${translated},${arr[1]}`;
})
.join('|');
return `?{${k.capitalize(getTranslationByKey('roll with'))}|${options}}`;
};
/**
* Our function that will actually initiate the roll.
* @param {object} attributes - Our object containing the attributes needed for the roll
* @param {object} rollObj - The object containing the fields to send with the roll
* @returns {void}
*/
const initiateRoll = async function(attributes,rollObj){
if(rollObj.roll){
rollObj.roll = rollObj.roll.replace(/@\{roll_state\}/,(match)=>{
//If roll state is set to ask the user what to do, we need to assemble a translated version of the query.
if(/\?\{/.test(attributes.roll_state)){
return translateAdvantageQuery();
}else{
//Otherwise just use the roll_state
return attributes.roll_state;
}
});
}
//Assemble our completed roll string.
const message = Object.entries(rollObj)
.reduce((text,[field,content]) => {
return text += `{{${field}=${content}}}`;
},`@{template_start}`);
//Send the completed roll string to the chat parser.
const roll = await startRoll(message);
//An object to aggregate the changes that need to be made to what is displayed in the roll output.
const computeObj = {};
//If the roll contained a result, target, and roll field with an inline roll in them, then we want to compare the roll and target values to determine if the result was a success or failure.
k.debug({roll});
if(roll.results.result && roll.results.target && roll.results.roll){
computeObj.result = roll.results.roll.result >= roll.results.target ? 1 : 0;
}
//Now we finish our roll, which tells Roll20 to actually display it in chat.
finishRoll(roll.rollId,computeObj);
};
/**
* Our generic rolling function. This will be used for our simple attribute and saving throw rolls that need very little logic.
* @param {object} event - The Roll20 event object.
* @returns {void}
*/
const rollGeneric = function(event){
const[section,id,field] = extractRollInfo(event.triggerName);
const attributeRef = attributeNames.indexOf(field) > -1 ?
`${field}_mod` :
undefined;
k.getAttrs({
props:rollGet,
callback: (attributes)=>{
//object that will aggregate all the roll template fields we will need to send to chat.
const rollObj = {
name:`^{${field.replace(/_/g,' ')}}`,
roll:attributeRef ?
`[[@{roll_state} + 0@{${attributeRef}}]]` :
`[[@{roll_state}]]`,
target:`[[0@{saving_throw}]]`,
result:`[[0[computed value]]]`
};
//Send the roll.
initiateRoll(attributes,rollObj);
}
});
};
k.registerFuncs({rollGeneric});
const rollSave = function(event){
const[section,id,field] = extractRollInfo(event.triggerName);
k.getAttrs({
props:rollGet,
callback: (attributes)=>{
//object that will aggregate all the roll template fields we will need to send to chat.
const rollObj = {
name:`^{${field.replace(/_/g,' ')}}`,
roll:`[[1d20]]`,
target:`[[0@{vitality}]]`,
result:`[[0[computed value]]]`
};
//Send the roll.
initiateRoll(attributes,rollObj);
}
});
};
k.registerFuncs({rollSave});
/**
* Our attack roll function. This won't need much logic, but has a slightly different button/attribute relationship that we need to account for.
* @param {object} event - The Roll20 event object.
* @returns {void}
*/
const rollAttack = function(event){
const[section,id,field] = extractRollInfo(event.triggerName);
const row = `${section}_${id}`;
k.getAttrs({
props:[...rollGet,`${row}_type`,`${row}_damage`],
callback:(attributes)=>{
const type = attributes[`${row}_type`];
const rollObj = {
name:`@{${row}_name}`,
roll_name:'^{attack}',
roll:`[[@{roll_state} + 0@{attack_modifier}[${getTranslationByKey('attack modifier')}] + 0@{${row}_attack_bonus}[${getTranslationByKey('bonus')}] + 0@{${type}_mod}[${getTranslationByKey(type)}]]]`,
range:`@{${row}_range}`,
traits:`@{${row}_traits}`,
aspects:`@{${row}_aspects}`,
description:`@{${row}_description}`
}
if(attributes[`${row}_damage`]){
rollObj.damage = `[[@{${row}_damage}]]`;
}
initiateRoll(attributes,rollObj);
}
});
};
k.registerFuncs({rollAttack});
// - Action CRP written by Scott C.
const assembleRollField = async (attrName,attributes,sections) => {
const dice = attributes[`${attrName}_dice`];
// Ask the user for their boons/curses and convert the response to a number
const boonCurse = +(await k.extractQueryResult('Boons / Curses|NA,0|+2D,2|+1D,1|−1D,-1|−2D,-2'));
let totalDice = Math.max(0,dice + boonCurse);
let rollString = totalDice <= 0 ?
`[[2d6kl1]]` :
`[[${totalDice}d6kh1]]`;
return rollString;
}
// Converts the rollObj into a roll string.
const assembleRollString = (rollObj,rollStart = '&{template:icon}') => {
return Object.entries(rollObj)
.reduce((str,[field,value]) => {
return str + ` {{${field}=${value}}}`;
},rollStart);
}
/**
* Function to do icon rolls
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const iconRoll = async function({trigger,attributes,sections,casc}){
// the async tag above tells javascript that this code will pause at some point (when we use the await keyword).
// extract the section, rowID, and button from the trigger's name.
const [section,rowID,button] = k.parseTriggerName(trigger.name);
// Convert your button's name to a k-scaffold attribute name
const attrName = button.replace(/-action$/,'').replace(/-/g,'_');
// I specify my rolls with an object and then use an assembly function to convert them to roll strings. It makes this much easier to debug.
const rollObj = {
roll:await assembleRollField(attrName,attributes,sections),
num6s:'[[0[computed value]]]'
};
const rollString = assembleRollString(rollObj);
const roll = await startRoll(rollString);
// Object to aggregate our modifications to the roll results
const computeObj = {};
// Count the number of 6s rolled by the dice
computeObj.num6s = roll.results.roll.dice.reduce((total,die) => {
if(die === 6){
total++;
}
return total;
},0);
// Tell roll20 to release the roll to the chat with our changes.
finishRoll(roll.rollId,computeObj);
};
k.registerFuncs({iconRoll});
@@ -6,20 +6,20 @@
* @param {object} attributes - The attributes object as returned from `getAllAttrs` and used in the default K-scaffold event cascade.
* @returns {void}
*/
const navigateSheet = function({trigger,attributes}){
let name = trigger ?
trigger.name :
attributes.sheet_state;//The trigger property is not passed when this function is called on sheet:opened. So, we need to get the current sheet state out of memory in that case.
let [,,page] = k.parseTriggerName(name);//Parse out the name of the navigation button that was clicked.
page = page.replace(/^nav-|-action$/g,'');//Remove the navigation and action button specific pieces of the button name to leave us with just the name of the button, which ideally is the same as a CSS class that we want to apply `.active` to.
navButtons.forEach((button)=>{//iterate through all the navigation buttons. navButtons is exported to our JS by the K-scaffold.
let element = button.replace(/-action/,'');//sanitize the button name down until we get just the target class name.
$20(`.${element}`).removeClass('active');//Remove the active class from all elements with the indicated class (e.g. .character)
});
$20(`.${page}`).addClass('active');//Add .active back to the elements that we are actually navigating to. We do this outside of the loop so that we can properly handle elements that may have more than one navigation class applied to them, although we don't have any of these in this sheet setup.
attributes.sheet_state = page;//Store the new page selection in our sheet_state attribute so that it will be remembered when the sheet is opened again.
};
k.registerFuncs({navigateSheet},{type:['opener']});//Register the function. Note that we are using a new argument with registerFuncs. The second object here is the options object where we can define what type of function this is. See the K-scaffold documentation for more information on the available types.
// const navigateSheet = function({trigger,attributes}){
// let name = trigger ?
// trigger.name :
// attributes.sheet_state;//The trigger property is not passed when this function is called on sheet:opened. So, we need to get the current sheet state out of memory in that case.
// let [,,page] = k.parseTriggerName(name);//Parse out the name of the navigation button that was clicked.
// page = page.replace(/^nav-|-action$/g,'');//Remove the navigation and action button specific pieces of the button name to leave us with just the name of the button, which ideally is the same as a CSS class that we want to apply `.active` to.
// navButtons.forEach((button)=>{//iterate through all the navigation buttons. navButtons is exported to our JS by the K-scaffold.
// let element = button.replace(/-action/,'');//sanitize the button name down until we get just the target class name.
// $20(`.${element}`).removeClass('active');//Remove the active class from all elements with the indicated class (e.g. .character)
// });
// $20(`.${page}`).addClass('active');//Add .active back to the elements that we are actually navigating to. We do this outside of the loop so that we can properly handle elements that may have more than one navigation class applied to them, although we don't have any of these in this sheet setup.
// attributes.sheet_state = page;//Store the new page selection in our sheet_state attribute so that it will be remembered when the sheet is opened again.
// };
// k.registerFuncs({navigateSheet},{type:['opener']});//Register the function. Note that we are using a new argument with registerFuncs. The second object here is the options object where we can define what type of function this is. See the K-scaffold documentation for more information on the available types.
/**
* Function to control what sections of the sheet are accessible based on the type of sheet that is selected (npc vs. character). This is done by application of the `inactive` class to the element.
@@ -35,4 +35,15 @@ const displayArticles = function({attributes}){
$20(`.${type}`)[`${action}Class`]('inactive');//Actually do the class manipulation.
});
};
k.registerFuncs({displayArticles},{type:['opener']});//This function is registered as an opener so that it will be fired every time the sheet is opened. This will ensure that the sheet is displayed always matches the selections. It will also be called in reaction to changes to the `sheet_type` attribute.
k.registerFuncs({displayArticles},{type:['opener']});//This function is registered as an opener so that it will be fired every time the sheet is opened. This will ensure that the sheet is displayed always matches the selections. It will also be called in reaction to changes to the `sheet_type` attribute.
// tab script
const buttonlist = ["narrative","tactics","journal","roster","settings"];
buttonlist.forEach(button => {
on(`clicked:${button}`, function() {
setAttrs({
sheet_state: button
});
});
});
@@ -58,10 +58,10 @@ section {
}
}
#settings {
display: grid;
grid-template-columns: auto;
}
// #settings {
// display: grid;
// grid-template-columns: auto;
// }
div.burdens {
min-width: 120px;
@@ -8,6 +8,8 @@ html{
}
.ui-dialog {
@extend %universalVariables;
--paper-background:url("https://i.imgur.com/Arep50S.jpg"); // light mode paper background
--icon-logo:url("https://i.imgur.com/9LTydaX.png"); // light mode logo
box-sizing:border-box;
.tab-content .charsheet{
@extend %defaultStyles;
@@ -21,23 +23,29 @@ html{
//- Spaceholder darkmode colors. We'll want to adjust these to a better dark mode pallette before release.
.sheet-darkmode .ui-dialog,
.sheet-rolltemplate-darkmode{
--backColor:var(--dark-surface1);
--fontColor:var(--darkModeText);
--borderColor:var(--dark-primarytext);
--subHeadBackColor:var(--dark-surface1);
--selectedColor:var(--darkModeText);
--disableBackground:var(--dark-surface1);
--borderColor: var(--off-white); //Our dark mode border color
--fontColor: var(--white); //Our dark mode font color
--invertedFontColor: var(--black); //Our dark mode inverted font color
--placeholderFontColor: var(--grey-lt); //Our dark mode placeholder font color
--backColor: var(--half-black); //Our dark mode background color
--invertedBackColor: var(--half-white); //Our dark mode inverted background color
--subHeadBackColor: var(--grey-lt); //Our dark mode sub header background color
--unselectedColor: var(--grey-dk); //Our dark mode font color for items that are currently inactive
--selectedColor: var(--orange); //Our dark mode font color for items that are currently active
--disableBackground: var(--grey-dk);
--buttonColor: var(--magenta);
--hoverColor: var(--magenta-lt);
--clickColor: var(--orange);
//--shadowColor:
--disableShadow:0px 0 7px 0px #161616 inset;
--buttonColor:var(--dark-surface1);
--buttonColor:#2e2e2e;
--hoverColor:#2e2e2e;
--clickColor:black;
//Images
--paper-background:url("https://i.imgur.com/Arep50S.jpg");
--star1Image:url(https://s3.amazonaws.com/files.d20.io/images/277891541/3QIvaKp_cOc6B8DAAXM6CA/original.png);
--star2Image:url(https://s3.amazonaws.com/files.d20.io/images/277891540/WAPK5ypyf235723n0GcVMQ/original.png);
--star3Image:url(https://s3.amazonaws.com/files.d20.io/images/277891539/iR6cOrzXq28bpGfsqSrK0Q/original.png);
--paper-background:url("https://i.imgur.com/vb8bLma.jpg"); // dark mode paper background
--icon-logo:url("https://i.imgur.com/2klK40m.png"); // dark mode logo
.sheetform{
background-color:inherit;
@@ -57,7 +65,7 @@ html{
display:grid;
font-family: var(--basicFont);
.paper-background{
background: url("https://i.imgur.com/Arep50S.jpg") repeat top left;
background: var(--paper-background) repeat top left;
// border:var(--half-gap) solid transparent;
// border-image:{
// source:var(--paper-background);
@@ -148,22 +156,22 @@ html{
[readonly]{
background:transparent;
border:0px solid transparent;
box-shadow:var(--disableShadow);
// box-shadow:var(--disableShadow);
cursor: default;
}
/*nav button styling*/
.sheet-nav__tab{
color:white;
font-style:normal;
border:1px solid transparent;
margin:0 0 -1px;
background-color:rgb(136, 57, 162);
border-radius:4px 4px 0 0;
box-shadow:none;
font-size:13px;
background-image:none;
padding:9px 12px;
.sheet-nav__tab{
color:white;
font-style:normal;
border:1px solid transparent;
margin:0 0 -1px;
background-color:rgb(136, 57, 162);
border-radius:4px 4px 0 0;
box-shadow:none;
font-size:13px;
background-image:none;
padding:9px 12px;
height:16px;
text-transform: uppercase;
font-family: var(--serifFont);
@@ -173,13 +181,13 @@ html{
&:not(.active):active{
background-color:rgb(242, 130, 255);
}
&.active{
&.active, &:focus{
background-color:rgb(156, 70, 255);
}
&.inactive{
display:none !important;
}
}
}
}
// debugging borders
// div {
@@ -46,6 +46,12 @@
-ms-transform: scale(1.1); /* IE 9 */
-webkit-transform: scale(1.1); /* Chrome, Safari, Opera */
transform: scale(1.1);
span.icon-logo {
content: var(--icon-logo);
width: 100%;
height: 100%;
}
}
.desc-label {
grid-area: desc;
@@ -76,18 +82,4 @@
font-family: var(--serifFont);
z-index: 0;
}
}
div.chapter-select {
width: 90px !important;
// position: relative;
// top: 2px;
}
select.chapter-select {
background-color: transparent !important;
font-family: var(--serifFont);
position: relative;
top: 3px;
height: 26px;
}
@@ -112,11 +112,10 @@
//- Some styling to highlight which input is currently being interacted with.
%inputHighlight {
border-width: 1px 3px;
border-style: solid;
border-color: black;
border: 1px solid var(--borderColor);
border-radius: 5px;
box-sizing: border-box;
background-color: var(--invertedFontColor);
}
//- Placeholder that allows us to easily set a square aspect ratio
@@ -141,7 +140,7 @@
//- Creates our basic border for inputs
%base-border {
border-width: 1px 3px;
border-width: 1px 1px;
border-style: solid;
border-radius: 5px;
border-color: transparent;
@@ -261,7 +260,7 @@
//- Basic styling for headers.
%baseHeader {
@extend %baseText;
color: black;
color: var(--fontColor);
display: block;
white-space: nowrap;
margin-top: 0px;
@@ -296,7 +295,7 @@
font-family: var(--titleFont);
text-transform: uppercase;
background-color: var(--magenta);
color: white !important;
color: var(--invertedFontColor) !important;
&:not(input) {
text-align: center;
@@ -416,7 +415,7 @@
//- Styling for elements that should have a box around them
%boxed {
border: 1px solid black;
border: 1px solid var(--borderColor);
border-radius: 0;
box-sizing: border-box;
}
@@ -430,7 +429,6 @@
%universalVariables {
@extend %font-vars;
@extend %color-palette;
--darkModeText: var(--orange);
--shadowColor1:rgba(0, 0, 0, 0.16);
--shadowColor2:rgba(0, 0, 0, 0.24);
--grid-gap: 1rem; //Variable to define our basic gap between layout elements. Followed by several gaps that are mathematically related to it.
@@ -446,9 +444,12 @@
--spanSectionColumn: 1/-1; //How a section should span when it is meant to span large numbers of columns (default span all)
--displayBorderWidth: 2px; //Our default border width
--borderColor: var(--black); //Our basic border color
--borderColor: var(--off-black); //Our basic border color
--fontColor: var(--black); //Our basic font color
--backColor: var(--white); //Our basic background color
--invertedFontColor: var(--white); //Our basic inverted font color
--placeholderFontColor: var(--grey-dk); //Our basic placeholder font color
--backColor: var(--half-white); //Our basic background color
--invertedBackColor: var(--half-black); //Our basic inverted background color
--subHeadBackColor: var(--grey); //Our sub header background color
--unselectedColor: var(--grey-lt); //Our default font color for items that are currently inactive
--selectedColor: var(--orange); //Our default font color for items that are currently active
@@ -645,7 +646,7 @@
width: auto;
&:placeholder {
color: var(--grey-dk);
color: var(--placeholderFontColor);
}
&.plus-control:not([value*="-"])+span:before {
@@ -669,6 +670,8 @@
--fontWght: 700;
font-weight: var(--fontWght);
font-variation-settings: "wght"var(--fontWght);
background-color: var(--backColor);
color: var(--fontColor);
}
.sheet-hidden,
@@ -735,7 +738,7 @@
input{
&:where([type='checkbox'],[type='radio']){
align-self: center;
border: 1px solid black;
border: 1px solid var(--borderColor);
cursor: pointer;
width: 1rem;
display:inline-block;
@@ -770,14 +773,14 @@
display:grid;
gap:var(--half-gap);
}
.nav-display{
&:not(.active){
display:none !important;
}
}
.inactive{
display:none !important;
}
// .nav-display{
// &:not(.active){
// display:none !important;
// }
// }
// .inactive{
// display:none !important;
// }
.sheet-pictos,
.pictos {
@@ -896,7 +899,7 @@
.underlined,
.sheet-underlined {
border-radius: 0;
border-bottom: 1px solid black;
border-bottom: 1px solid var(--borderColor);
transition: var(--focusTrans);
}
@@ -906,7 +909,7 @@
}
&:not([readonly]):is(:focus, :focus-within) {
background-color: var(--off-white);
background-color: var(--invertedFontColor);
transition: var(--focusTrans);
}
}
@@ -949,7 +952,7 @@
position: absolute;
right: 1px;
top: 0px;
border: 0px solid black;
border: 0px solid var(--borderColor);
border-radius: 0;
color: var(--selectedColor);
text-transform: none;
@@ -1059,7 +1062,7 @@
appearance:none;
-webkit-appearance:none;
order: 1;
background-color:black;
background-color:var(--backColor);
width:20px;
height:20px;
cursor:pointer;
@@ -1076,6 +1079,7 @@
p {
font-size: 1.0rem;
width: 96%;
color: var(--invertedBackColor);
&.bullet::before {
font-family: pictos;
content: ']';
+36 -15
View File
@@ -1,3 +1,5 @@
@use '_mixinsandplaceholders';
ul.nav-tabs.nav{
position:sticky;
top:0;
@@ -20,19 +22,38 @@ ul.nav-tabs.nav{
box-shadow: 30px 0 var(--backColor),-30px 0 var(--backColor);
color: var(--fontColor);
}
#settings{
display:flex;
flex-wrap:wrap;
gap:var(--half-gap);
h2{
width:100%;
}
.input-label *{
width:auto;
}
// #settings{
// display:flex;
// flex-wrap:wrap;
// gap:var(--half-gap);
// h2{
// width:100%;
// }
// .input-label *{
// width:auto;
// }
// }
// .nav-display{
// &:not(.active){
// display:none !important;
// }
// }
.narrative,
.tactics,
.journal,
.roster,
.settings {
display: none;
}
/* show the selected tab */
.tabs__toggle[value="narrative"] ~ article.narrative,
.tabs__toggle[value="tactics"] ~ article.tactics,
.tabs__toggle[value="journal"] ~ article.journal,
.tabs__toggle[value="roster"] ~ article.roster,
.tabs__toggle[value="settings"] ~ article.settings {
display: grid;
}
.nav-display{
&:not(.active){
display:none !important;
}
}
@@ -45,6 +45,10 @@
}
}
input[type=checkbox].magenta {
accent-color: var(--magenta);
}
// .resource-radio {
// // -webkit-appearance: none;
@@ -1,2 +1,168 @@
//Styling for our rolltemplate. This won't be written yet.
@use '_mixinsandplaceholders';
//- One of the effects of Roll20's aggressive sanitization is that we can't define a default font-size
//- in the html element for roll templates like we did for the character sheet.
//- This means, that unless we want to base all our sizes off of the default Roll20 font size of 10px,
//- we'll want to define a font-size in our roll template, and then use the em unit to size our text.
//- Roll templates can have hyperlinks in them, which means we need to style <a> tags.
//- Additionally, ability and API command buttons are also hyperlinks themselves,
//- so we'll want to style these specifically as well.
//- Any 'inlinerollresults' should be styled as well
@use '_mixinsandplaceholders';
.sheet-rolltemplate-icon {
@extend %universalVariables;
@extend %defaultStyles;
--templateFontSize: 16px;
--critColor: var(--magenta);
--fumbleColor: var(--orange);
--importantColor: var(--purple);
--headerBackColor: var(--grey-lt);
}
.sheet-rolltemplate-darkmode {
--critColor:green;
--fumbleColor:red;
--importantColor:#00ADA2;
--headerBackColor:#2e2e2e;
}
.sheet-rolltemplate-icon {
padding-bottom:var(--grid-gap);
//If the template is output with the chat avatar being displayed, give the chat avatar some more room so that we don't overlap it.
&:not(:first-child){
margin-top:15px;
}
.sheet-header{
display:grid;
background-image: linear-gradient(90deg, transparent, var(--headerBackColor) var(--big-gap) calc(100% - var(--big-gap)), transparent);
padding: var(--half-gap);
margin-inline: calc(var(--half-gap) * -1);
h3{
justify-self:start;
}
h4{
justify-self:end;
}
}
// CSS to show/hide our success/failure message
.sheet-result-controller{
&.sheet-result-true ~ .sheet-result-display-false,
&.sheet-result-false ~ .sheet-result-display-true{
display:none
}
}
.sheet-result-display-false{
color:var(--fumbleColor);
}
.sheet-result-display-true{
color:var(--critColor);
}
//Our content wrapper
.sheet-template{
//We can't set the default font size in the html element in rolltemplates like we did in the sheet. This means that the rem unit uses the default Roll20 font-size of 10px. Because of this we're going to set the font-size on the overall container, and then use the em unit for out font sizes in the roll template.
font-size:var(--templateFontSize);
display:grid;
gap:var(--half-gap);
//applying the same paper background that we used for our sections
border:var(--half-gap) solid transparent;
//a fall back background-color for if our image doesn't load.
background-color: var(--backColor);
filter:drop-shadow(var(--shadowColor1) 0px 3px 6px) drop-shadow(var(--shadowColor2) 0px 3px 6px);
padding:var(--half-gap);
span,
span *,
h5,
h5 *{
font-size:1em;
}
h5{
font-weight:bold;
text-align:left;
}
h3,
h3 *{
font-size:1.25em;
}
h4,
h4 *{
font-size:1em;
}
//Our basic rows of the template are going to be laid out with flex-box so that if the content of the field is short, it will be displayed on a single line. If the content is long, the row will display as a header over the content.
.sheet-template-row{
display:flex;
flex-wrap:wrap;
gap:var(--half-gap);
justify-content: space-between;
align-items:center;
}
//Our areas that we expect longer content to exist in will have a border on the top to help visually separate them.
.sheet-template-row--text{
border-top:2px solid var(--fontColor);
}
.sheet-template-row--summary{
justify-content:center;
}
//The content of our fields will be height restricted so that a chat output has less of a chance of taking up too much space in the chat. Note that this limits the ability of the template to take up space, it does not wholly prevent it from becoming unwieldably large.
.sheet-description{
max-height:20rem;
overflow-y:auto;
white-space: pre-wrap;
}
//Styling for links in our roll template. This will override the in your face styling of the default Roll20 ability and API command buttons.
a{
padding:0;
//For links that are not ability/API command buttons, we want to decorate them as a standard link.
&:not([href^="!"]),
&:not([href^="~"]){
color:var(--fontColor);
border:none;
text-decoration: underline;
}
//For our ability/API command buttons, we want to make them visually distinct, but keep them displaying inline with the rest of the text around them.
&[href^="!"],
&[href^="~"]{
// @extend %buttonInteraction;
border-radius:5px;
color:var(--backColor);
background-color:var(--fontColor);
text-decoration:none;
padding-inline:var(--tiny-gap);
}
}
//Styling our inline roll result
.inlinerollresult{
//We want our inline rolls to visually pop so that they are easily identifiable
font-size:1.25em;
position: relative;
left:-10px;
//We'll be looking at computed values in the javascript, but if we send a placeholder value to chat, we don't want to display the hover tooltip for it because it will not give any useful information.
&[title*="[computed value]"],
&[original-title*="[computed value]"]{
pointer-events: none;
}
//The default Roll20 inline roll styling does not flow as well inside text. We want to get rid of the border, background and padding.
&,
&.fullcrit,
&.fullfail,
&.importantroll{
border: none;
background-color: transparent;
padding:0;
}
//And then apply roll state specific styling, in this case, just coloring the rolls.
&.fullcrit{
color: var(--critColor);
}
&.fullfail{
color: var(--fumbleColor);
}
&.importantroll{
color: var(--importantColor);
}
}
}
}
@@ -8,12 +8,29 @@ label.input-label .numb-label {
width: 80px;
}
input.action-label {
width: 50% !important;
}
input.burden {
width: 98% !important;
text-align: center;
align-items: center;
}
div.chapter-select {
width: 90px !important;
// position: relative;
// top: 2px;
}
select.chapter-select {
background-color: var(--backColor);
font-family: var(--serifFont);
position: relative;
top: 3px;
height: 26px;
}
input.burden-penalty {
position: relative;
@@ -43,7 +60,7 @@ input.dust {
height: 40px !important;
width: 100% !important;
font-size: 1.4rem !important;
color: var(--off-black) !important;
color: var(--fontColor) !important;
text-shadow: 0 0 2px var(--orange);
&.ability-info {
height: 26px !important;
@@ -57,7 +74,7 @@ input.dust {
}
select.color-receiver {
color: var(--off-white);
color: var(--invertedFontColor) !important;
border-radius: 0px !important;
}
@@ -1 +1,62 @@
//- Note that rolletemplate classes still need the 'sheet-' prefix, even with CSE
//- Note that rolletemplate classes still need the 'sheet-' prefix, even with CSE
+templateWrapper(templateName)
.header
+templateConditionalDisplay('name')
h3 {{name}}
+characterLink
+templateConditionalDisplay('roll')
.template-row
+templateConditionalDisplay('roll_name')
h5 {{roll_name}}
+templateConditionalDisplay('roll_name','invert')
h5(data-i18n='roll')
span.description
|{{roll}}
+templateConditionalDisplay('target')
span(data-i18n='vs')
|{{target}}
+templateConditionalDisplay('target')
+rollBetween({values:'computed::result 1 1'})
.template-row.template-row--summary
span(data-i18n='success').capitalize.result-display-true
+rollBetween({values:'computed::result 0 0'})
.template-row.template-row--summary
span(data-i18n='failure').capitalize.result-display-false
+templateConditionalDisplay('damage')
.template-row
+templateConditionalDisplay('damage_type')
h5 {{roll_name}}
+templateConditionalDisplay('damage_type','invert')
h5(data-i18n='damage')
span.description {{damage}}
+templateConditionalDisplay('range')
.template-row
h5(data-i18n='range')
span.description {{range}}
+templateConditionalDisplay('traits')
.template-row
h5(data-i18n='traits')
span.description {{traits}}
+templateConditionalDisplay('aspects')
.template-row
h5(data-i18n='aspects')
span.description {{aspects}}
+templateConditionalDisplay('description')
.template-row.template-row--text
h5(data-i18n='description')
span.description {{description}}
+templateConditionalDisplay('effect')
.template-row.template-row--text
h5(data-i18n='effect')
span.description {{effect}}
each num in [1,2,3]
+templateConditionalDisplay(`effect_${num}`)
.template-row.template-row--text
h5(data-i18n=`effect ${num}`)
span.description {{effect_#{num}}}
//- This template will be user extensible. User defined field/value combos will be displayed using the same format as other rows
+allProps({values:'character_name character_id name roll_name roll damage damage_type effect effect_1 effect_2 effect_3 description range traits aspects target result'})
.template-row.template-row--text
h5 {{key}}
span.description {{value}}
@@ -1,7 +1,7 @@
.tabs {
.tabs__body {
.tabs__container:not(.k-active-tab) {
display: none ! important;
}
}
}
// .tabs {
// .tabs__body {
// .tabs__container:not(.k-active-tab) {
// display: none ! important;
// }
// }
// }
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+13 -11
View File
@@ -1,26 +1,27 @@
include scaffold/_kpug.pug
include mixins/_sheetmixins.pug
//- additional includes should be below this point
- let gameName = 'icon';
- let templateName = 'icon';
- varObjects.sheetTypes = ['narrative','tactics','journal','roster'];
+hidden({name:'template start', value:`&{template:${gameName}} {{character_name=@{character_name}}} {{character_id=@{character_id}}}`})
+hidden({name:'sheet state', value:'settings'})
+hidden({name:'collapsed', value:''})
main#main
nav#main-nav.sheet-nav
each val in varObjects.sheetTypes
+navButton({
name:val,
class:`sheet-nav__tab ${val}`,
'data-i18n':`${val} sheet`,
'data-i18n-title':`${val} sheet button title`,
role:'heading',
'aria-level':5,
trigger:{triggeredFuncs:['navigateSheet']}})
+button({name:val, type:'action', class:`sheet-nav__tab ${val}`, 'data-i18n':`${val} sheet`, 'data-i18n-title':`navigate to the ${val} tab`, role:'heading', 'aria-level':5})
//- +navButton({
//- name:val,
//- class:`sheet-nav__tab ${val}`,
//- 'data-i18n':`${val} sheet`,
//- 'data-i18n-title':`navigate to the ${val} tab`,
//- role:'heading',
//- 'aria-level':5})
//- //- trigger:{triggeredFuncs:['navigateSheet']}})
//- This is our button to access the settings page. it's written separately because it has some different requirements
+navButton({name:'settings', class:'pictos active sheet-nav__tab settings','data-i18n-title':'open the settings page', role:'heading', 'aria-level':5, trigger:{triggeredFuncs:['navigateSheet']}})
+button({name:'settings', type:'action', class:'pictos sheet-nav__tab settings','data-i18n-title':'navigate to the settings tab', role:'heading', 'aria-level':5})
|y
+hidden({name:'sheet state', class:'tabs__toggle', value:'settings'})
include articles/_details.pug
include articles/_narrative.pug
include articles/_tactics.pug
@@ -34,4 +35,5 @@ include rolltemplates/_icontemplate.pug
include Javascript/calculators.js
include Javascript/updaters.js
include Javascript/triggeredfuncs.js
include javascript/rollfunctions.js
//- include Javascript/demoworkers.js
+3 -1
View File
@@ -104,5 +104,7 @@
"limit break reminder line 1":"Party Resolve increases by 1 at the start of EACH ROUND in combat, and depletes to 0 when combat ends.",
"limit break reminder line 2":"Personal Resolve is gained at a rate of 1 after EVERY combat, and resets to 0 when you camp.",
"limit break reminder line 3":"When you spend Resolve to use a Limit Break, you can use any combination of party or personal, but you MUST get your group's permission to use the party pool.",
"rules reminders info line 0":"Click the ⓘ symbol to show / hide ALL rules reminders."
"rules reminders info line 0":"Click the ⓘ symbol to show / hide ALL rules reminders.",
"roll":"roll",
"vs":"vs."
}