mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 19:52:29 +00:00
347 lines
19 KiB
Plaintext
347 lines
19 KiB
Plaintext
//- Changelog v1.1
|
|
- Stat bonuses now apply (e.g body_mod)
|
|
- Infrastructure changed for improved maintainability
|
|
- Fixed bug with drone structure handling (drone attribute values ported)
|
|
include scaffold/_htmlelements.pug
|
|
include scaffold/_rolltemplate mixins.pug
|
|
include Mixins/_Sheet Mixins.pug
|
|
- let templateName = 'rapidfire';
|
|
+hidden({name:'template_start',value:`&{template:${templateName}} {{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,trigger:{listenerFunc:'navigateSheet'}})
|
|
+navButton({name:'settings',class:'active nav__button nav__button--settings settings',trigger:{listenerFunc:'navigateSheet'}})
|
|
|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,trigger:{listenerFunc:'navigateSheet'}})
|
|
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:'',trigger:{triggeredFuncs:['setupSystem']}})
|
|
+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
|
|
- let inputObj = {name:field,type:'text',class:`underlined ${replaceSpaces(field)}`};
|
|
if field === 'character name'
|
|
- inputObj.trigger = {listener:true,triggeredFuncs:['setActionCalls']};
|
|
+input-label(field,inputObj,{},{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 = '';
|
|
- optObj.trigger = statTriggers[name];
|
|
+option(optObj)
|
|
!=value
|
|
+number({name:`${name} mod`,class:'boxed',trigger:statTriggers[name]})
|
|
.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'})
|
|
+text({name:'name',class:'underlined',role:'heading','aria-level':3})
|
|
+number({name:'quantity',class:'underlined'})
|
|
.headed-textarea.notes.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
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',trigger:{calculation:'calcInitiative'}})
|
|
+number({name:`initiative mod`,class:`boxed initiative_mod`,trigger:{affects:['initiative']}})
|
|
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],trigger:secondaryTriggers[name]})
|
|
+number({name:`${name} mod`,class:`boxed ${name}_mod`,id:`${name}-mod`,trigger:{affects:[`${name}`]}})
|
|
label(for='health' class='health-label' data-i18n='health',role='heading' aria-level=4)
|
|
.boxed.span-2
|
|
+number({name:'health',id:'health',trigger:{affects:['defence'],calculation:'totalHealth',initialFunc:'syncHealth'}})
|
|
span.slash /
|
|
+number(
|
|
{name:'health max',readonly:'',trigger:{affects:['defence'],triggeredFuncs:['calcHealth']}})
|
|
+number({name:`health mod`,class:`boxed health_mod`,trigger:{affects:['health_max']}})
|
|
.combat-gear.stat-grid
|
|
+h2({'data-i18n':'combat gear'})
|
|
each name in combatGear
|
|
- let inputObj = {name,class:'boxed',type:'number',trigger:combatGearTriggers[name]};
|
|
+input-label(name,inputObj,{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})
|
|
+roller({name:'roll',class:'raw'})
|
|
+span({name:'name','data-i18n-dynamic':'',role:'heading','aria-level':5})
|
|
+roller({name:'roll',class:'custom'})
|
|
+text({name:'name',role:'heading','aria-level':5,class:'custom underlined'})
|
|
+number({name:'level',class:'underlined',trigger:{triggeredFuncs:['skillEffects']}})
|
|
+select({name:'stat',class:'underlined'})
|
|
+option({value:'query','data-i18n':'ask',selected:'',trigger:{triggeredFuncs:['skillEffects']}})
|
|
each value in stats
|
|
+option({value,'data-i18n':value})
|
|
+number({name:'xp',class:'underlined'})
|
|
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'})
|
|
+text({name:'name',class:'underlined',role:'heading','aria-level':3})
|
|
.headed-textarea.notes.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
.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'})
|
|
+text({name:'name',class:'underlined',role:'heading','aria-level':3})
|
|
.headed-textarea.notes.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
.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'})
|
|
+text({name:'name',class:'underlined',role:'heading','aria-level':3})
|
|
.headed-textarea.mod.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
//-
|
|
.headed-textarea.mods.expanded
|
|
+h5({'data-i18n':'mods'})
|
|
+adaptiveTextarea({name:'mods',class:'underlined'})
|
|
+repeating_section('weapon','weapons',['weapon','skill','damage'])
|
|
+checkbox({name:'collapse',class:'collapse'})
|
|
+roller({name:'roll'})
|
|
+text({name:'name',class:'underlined'})
|
|
+hidden({name:'skill',value:'select'})
|
|
+actionQuery({name:'skill action',trigger:{listenerFunc:'dynamicSelect'}})
|
|
+checkbox({name:'translate skill',hidden:'',value:'1',class:'translate-control',checked:''})
|
|
+span({name:'skill',class:'underlined translated','data-i18n-dynamic':''})
|
|
+span({name:'skill',class:'underlined untranslated'})
|
|
.roll-plus
|
|
+span({'data-i18n':'lead'})
|
|
span.plus +
|
|
+number({name:'damage',class:'underlined'})
|
|
.headed-textarea.notes.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
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',trigger:{triggeredFuncs:['imageInput']}},{class:'image-container__input stacked'},{role:'heading','aria-level':5})
|
|
+action({name:'remnant token',class:'image-container__button',trigger:{listenerFunc:'toggleImageInput'}})
|
|
+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;
|
|
- let inputObj = {name,readonly:'',class:'boxed',trigger:remnantRatingTriggers[name]};
|
|
- let bonusObj = {name:`${name} mod`,class:'boxed',trigger:remnantRatingBonusTriggers[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(inputObj)
|
|
+number(bonusObj)
|
|
+roller({name:'remnant initiative','data-i18n':'initiative',role:'heading','aria-level':4})
|
|
+number({name:'remnant initiative',readonly:'',class:'boxed',trigger:{calculation:'calcRemnantInitiative'}})
|
|
+number({name:'remnant initiative mod',class:'boxed',trigger:{affects:['remnant initiative']}})
|
|
.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})
|
|
- let levelObj = {name:`${name} level`,class:'underlined',trigger:remnantSkillTriggers[name]};
|
|
+number(levelObj)
|
|
+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',trigger:{affects:['remnant_defence'],calculation:'totalHealth',initialFunc:'syncHealth'}})
|
|
span.slash /
|
|
+hidden({name:'structure max',value:5,trigger:{affects:['remnant_defence'],triggeredFuncs:['calcHealth']}})
|
|
+number({name:'structure base',value:5,trigger:{affects:['structure_max']}})
|
|
else
|
|
label(data-i18n=name for=replaceSpaces(name) role='heading' aria-level=4).span-2
|
|
- let inputObj = {name,class:'boxed',type:'number',id:replaceSpaces(name),trigger:remnantStatTriggers[name]};
|
|
+number(inputObj)
|
|
each name in remnantCombatStats
|
|
- let inputObj = {name,class:`boxed ${replaceSpaces(name)}_input`,type:'number',id:replaceSpaces(name),trigger:remnantCombatStatTriggers[name]};
|
|
label(data-i18n=name role='heading' aria-level=4 for=replaceSpaces(name) class=`${replaceSpaces(name)}_label`)
|
|
!=`<!-- inputObj: ${JSON.stringify(inputObj)}-->`
|
|
+input(inputObj)
|
|
+repeating_section('specialty','specialty',['name'],'remnant-specialty')
|
|
+checkbox({name:'collapse',class:'collapse'})
|
|
+text({name:'name',class:'underlined'})
|
|
.headed-textarea.description.expanded
|
|
+h5({'data-i18n':'description'})
|
|
+adaptiveTextarea({name:'description',class:'underlined'})
|
|
+repeating_section('trait','traits',['name','duress','focus'],'remnant-trait')
|
|
+checkbox({name:'collapse',class:'collapse'})
|
|
+text({name:'name',class:'underlined'})
|
|
+number({name:'duress',class:'underlined'})
|
|
+number({name:'focus',class:'underlined'})
|
|
.headed-textarea.description.expanded
|
|
+h5({'data-i18n':'description'})
|
|
+adaptiveTextarea({name:'description',class:'underlined'})
|
|
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})
|
|
+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)
|
|
- let inputObj = {name,type:'number',class:`boxed`};
|
|
if name === 'strike'
|
|
- inputObj.trigger = {affects:['repeating_drone_$x_strikerange']};
|
|
+roller-label(inputObj,{name,role:'heading','aria-level':5,'data-i18n':label},{class:`${replaceSpaces(name)}`})
|
|
else if name === 'struct'
|
|
.structure
|
|
+span({role:'heading','aria-level':5,'data-i18n':label})
|
|
.boxed.span-2
|
|
+number({name:'struct'})
|
|
span.slash /
|
|
+number({name:'struct max'})
|
|
else if name=== 'strikerange'
|
|
+input-label(label,{name,type:'number',class:`boxed`,readonly:'',trigger:{calculation:'calcStrikeRange'}},{class:replaceSpaces(name)},{role:'heading','aria-level':5,'data-i18n':label})
|
|
else
|
|
+input-label(label,{name,type:'number',class:`boxed`},{class:replaceSpaces(name)},{role:'heading','aria-level':5,'data-i18n':label})
|
|
.headed-textarea.description.expanded
|
|
+h5({'data-i18n':'description'})
|
|
+adaptiveTextarea({name:'description',class:'underlined'})
|
|
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'})
|
|
+text({name:'name',class:'underlined',role:'heading','aria-level':4})
|
|
+select({name:'skill',class:'underlined'})
|
|
+option({value:' ','data-i18n':'select',selected:'',trigger:{affects:['repeating_remnant-weapon_$x_damage']}})
|
|
+option({value:'assault','data-i18n':'assault'})
|
|
+option({value:'strike','data-i18n':'strike'})
|
|
+number({name:'bonus',class:'underlined'})
|
|
.roll-plus
|
|
+span({'data-i18n':'lead'})
|
|
span.plus +
|
|
+number({name:'damage',class:'underlined',readonly:'',trigger:{calculation:'calcRemnantDamage'}})
|
|
+number({name:'damage bonus',class:'underlined damage-bonus',trigger:{affects:['repeating_remnant-weapon_$x_damage']}})
|
|
+text({name:'type',class:'underlined'})
|
|
.headed-textarea.description.expanded
|
|
+h5({'data-i18n':'notes'})
|
|
+adaptiveTextarea({name:'notes',class:'underlined'})
|
|
//-Rolltemplate definitions
|
|
+rolltemplate(templateName)
|
|
.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 scaffold/utility.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
|
|
include scaffold/attribute_proxy.js
|
|
include scaffold/accessSheet.js
|
|
include scaffold/parse_cascade.js
|
|
include scaffold/sheetworker_aliases.js
|
|
//- All additional javascript files should start here
|
|
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/updaters.js
|
|
//- All additional javascript files should end here
|
|
include scaffold/listeners.js |