mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 11:42:29 +00:00
193 lines
8.3 KiB
JavaScript
193 lines
8.3 KiB
JavaScript
/*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}]]`;
|
|
};
|
|
|
|
//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';
|
|
};
|
|
|
|
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] = parseTriggerName(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);
|
|
}});
|
|
};
|
|
funcs.initiateRoll = initiateRoll;
|
|
|
|
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;
|
|
}; |