Files
MonsterCards/rulesets/sr6/helpers.js

108 lines
3.8 KiB
JavaScript

/**
* SR6 Ruleset Handlebars Helpers
* Auto-loaded by preview server for ruleset 'sr6'.
*/
module.exports = function (handlebars) {
// Compute maximum physical damage boxes: 8 + ceil(body / 2)
handlebars.registerHelper('calcPhysicalBoxes', function (body) {
const b = typeof body === 'object' && body !== null ? (body.value || body.base || 0) : Number(body || 0);
return 8 + Math.ceil(b / 2);
});
// Compute maximum stun damage boxes: 8 + ceil(willpower / 2)
handlebars.registerHelper('calcStunBoxes', function (willpower) {
const w = typeof willpower === 'object' && willpower !== null ? (willpower.value || willpower.base || 0) : Number(willpower || 0);
return 8 + Math.ceil(w / 2);
});
// Format currency with nuyen symbol
handlebars.registerHelper('formatNuyen', function (amount) {
if (amount === undefined || amount === null) return '0¥';
const num = typeof amount === 'number' ? amount : parseInt(String(amount).replace(/[^0-9]/g, ''), 10) || 0;
return `${num.toLocaleString('en-US')}¥`;
});
// Format dice pool helper: (skill rating + linked attribute value)
handlebars.registerHelper('calcDicePool', function (skill, attributes) {
if (!skill) return 0;
const rating = typeof skill.rating === 'number' ? skill.rating : (parseInt(skill.rating, 10) || 0);
const attrName = (skill.attribute || '').toLowerCase();
let attrVal = 0;
if (attributes && attrName && attributes[attrName] !== undefined) {
const a = attributes[attrName];
attrVal = typeof a === 'object' && a !== null ? (a.value || a.base || 0) : (Number(a) || 0);
}
const bonus = skill.expertise ? 3 : (skill.specialization ? 2 : 0);
return rating + attrVal + bonus;
});
// Safe attribute value extractor
handlebars.registerHelper('attr', function (attributes, name) {
if (!attributes || !name) return '—';
const val = attributes[name.toLowerCase()];
if (val === undefined || val === null) return '—';
return typeof val === 'object' ? (val.value ?? val.base ?? '—') : val;
});
// Helper to build 3-box rows for condition monitor
handlebars.registerHelper('conditionMonitorRows', function (totalBoxes, currentDamage, maxGridBoxes, options) {
const total = Number(totalBoxes) || 8;
const damage = Number(currentDamage) || 0;
const maxBoxes = Number(maxGridBoxes) || 18;
const rowsCount = Math.ceil(maxBoxes / 3);
let output = '';
for (let r = 0; r < rowsCount; r++) {
const boxes = [];
for (let c = 1; c <= 3; c++) {
const boxIdx = r * 3 + c;
if (boxIdx <= maxBoxes) {
boxes.push({
index: boxIdx,
active: boxIdx <= total,
filled: boxIdx <= damage,
blackedOut: boxIdx > total,
isThreshold: c === 3,
penalty: -(r + 1)
});
}
}
output += options.fn({
rowNumber: r + 1,
penalty: -(r + 1),
boxes: boxes
});
}
return output;
});
// Matrix condition monitor boxes generator (1 to 12)
handlebars.registerHelper('matrixMonitorBoxes', function (maxBoxes, currentDamage, options) {
const total = Number(maxBoxes) || 12;
const damage = Number(currentDamage) || 0;
let output = '';
for (let i = 1; i <= total; i++) {
output += options.fn({
index: i,
filled: i <= damage
});
}
return output;
});
// Edge points tracker generator (1 to maxEdge)
handlebars.registerHelper('edgePointsBoxes', function (maxEdge, currentEdge, options) {
const max = Number(maxEdge) || 7;
const curr = Number(currentEdge) || 0;
let output = '';
for (let i = 1; i <= Math.max(7, max); i++) {
output += options.fn({
index: i,
filled: i <= curr,
disabled: i > max
});
}
return output;
});
};