Files
roll20-character-sheets/backbone/source/scaffold/scripts/mock20.js
T

224 lines
7.5 KiB
JavaScript

// This code adapted from Nic Bradley's R20 test framework from the WFRP4e official sheet.
const _ = require('underscore');
const environment = {
attributes: {},
triggers: [],
};
global.environment = environment;
const on = jest.fn((trigger, func) => {
environment.triggers.push({ trigger, func });
});
global.on = on;
const getAttrs = jest.fn((query, callback) => {
let values = {};
for (const attr of query) {
if (attr in environment.attributes) values[attr] = environment.attributes[attr];
}
if (typeof callback === "function") callback(values);
});
global.getAttrs = getAttrs;
const setAttrs = jest.fn((submit, params, callback) => {
if (!callback && typeof params === "function") callback = params;
for (const attr in submit) {
environment.attributes[attr] = submit[attr];
}
if (typeof callback === "function") callback();
});
global.setAttrs = setAttrs;
const getSectionIDs = jest.fn((section, callback) => {
const ids = [];
const sectionName = section.indexOf("repeating_") === 0 ? section : `repeating_${section}`;
const attributes = environment.attributes;
for (const attr in attributes) {
if (attr.indexOf(sectionName) === 0) ids.push(attr.split("_")[2]);
}
const idMap = [...new Set(ids)];
if (typeof callback === "function") callback(idMap);
});
global.getSectionIDs = getSectionIDs;
const getSectionIDsSync = jest.fn((section) => {
const ids = [];
const sectionName = section.indexOf("repeating_") === 0 ? section : `repeating_${section}`;
const attributes = environment.attributes;
for (const attr in attributes) {
if (attr.indexOf(sectionName) === 0) ids.push(attr.split("_")[2]);
}
const idMap = [...new Set(ids)];
return idMap;
});
global.getSectionIDsSync = getSectionIDsSync;
const removeRepeatingRow = jest.fn((id) => {
const attributes = environment.attributes;
for (const attr in attributes) {
if (attr.indexOf(id) > -1) delete environment.attributes[attr];
}
});
global.removeRepeatingRow = removeRepeatingRow;
const getCompendiumPage = jest.fn((request, callback) => {
const pages = compendiumData;
if (!pages)
throw new Error(
"Tried to use getCompendiumPage, but testing environment does not contain compendiumData."
);
if (typeof request === "string") {
const [category, pageName] = request.split(":");
const response = {
Name: pageName,
Category: category,
data: {},
};
if (pages[request]) response.data = pages[request].data;
if (typeof callback === "function") callback(response);
} else if (Array.isArray(request)) {
const pageArray = [];
for (const page of request) {
if (pages[request] && pages[request].Category === category) pageArray.push(pages[pageName]);
}
if (typeof callback === "function") callback(pageArray);
}
});
global.getCompendiumPage = getCompendiumPage;
const generateUUID = jest.fn(() => {
var a = 0,
b = [];
return (function () {
var c = new Date().getTime() + 0,
d = c === a;
a = c;
for (var e = Array(8), f = 7; 0 <= f; f--)
(e[f] = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(c % 64)),
(c = Math.floor(c / 64));
c = e.join("");
if (d) {
for (f = 11; 0 <= f && 63 === b[f]; f--) b[f] = 0;
b[f]++;
} else for (f = 0; 12 > f; f++) b[f] = Math.floor(64 * Math.random());
for (f = 0; 12 > f; f++)
c += "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(b[f]);
return c.replace(/_/g, "z");
})();
});
global.generateUUID = generateUUID;
const generateRowID = jest.fn(() => {
return generateUUID().replace(/_/g, "Z");
});
global.generateRowID = generateRowID;
const simulateEvent = jest.fn((event) => {
environment.triggers.forEach((trigger) => {
const splitTriggers = trigger.trigger.split(" ") || [trigger.trigger];
splitTriggers.forEach((singleTrigger) => {
if (event === singleTrigger) {
trigger.func({
sourceAttribute: "test",
});
}
});
});
});
global.simulateEvent = simulateEvent;
const getTranslationByKey = jest.fn((key) => key);
global.getTranslationByKey = getTranslationByKey;
// Roll Handlingglobal.getTranslationByKey = getTranslationByKey;
const extractRollTemplate = (rollString) => {
const rollTemplate = rollString.match(/&\{template:(.*?)\}/)?.[1];
environment.attributes.__rolltemplate = rollTemplate;
};
const cleanRollElements = (value) => value.replace(/(\{\{|\}})/gi, "").split("=");
const extractRollElements = (rollString) => {
const rollElements = rollString.match(/\{\{(.*?)\}{2,}/g);
if (!rollElements || rollElements.length < 1) throw new Error("No roll elements provided in roll string");
return Object.fromEntries(rollElements.map(cleanRollElements));
};
const getExpression = (element) => element.replace(/(\[\[|\]\])/gi, "");
const getDiceOrHalf = (size) => {
const diceStack = environment.diceStack;
if (!diceStack?.[size] || diceStack[size].length < 0) return size / 2;
return environment.diceStack[size].pop();
};
const getDiceRolls = (expression) => {
const rolls = expression.match(/([0-9]+)?d([0-9]+)/gi);
if (!rolls) return [];
const allRolls = [];
rolls.forEach((roll) => {
const [number, size] = roll.split(/d/i);
for (let i = 1; i <= number; i++) {
const dice = getDiceOrHalf(size);
allRolls.push(dice);
}
});
return allRolls;
};
const calculateResult = (expression, dice) => {
const rolls = expression.match(/([0-9]+)?d([0-9]+)/gi);
if (!rolls) return eval(expression);
rolls.forEach((roll, index) => {
const [number, size] = roll.split(/d/i);
let total = 0;
for (let i = 1; i <= number; i++) {
total += +dice.shift();
}
expression = expression.replace(/([0-9]+d[0-9]+([+\-*/][0-9]+)?)(.*?)$/gi, "$1");
const regex = new RegExp(roll, "gi");
expression = expression.replace(regex, total);
});
return eval(expression);
};
const replaceAttributes = (element) => {
const test = /@\{(.*?)\}/i;
while (test.test(element)) {
element = element.replace(/@\{(.*?)\}/gi, (sub, ...args) => {
const attributeName = args[0];
const attributeValue = environment.attributes[attributeName];
const attributeExists = typeof attributeValue !== "undefined";
const possibleAttributes = Object.keys(environment.attributes);
if (attributeExists) return attributeValue;
else
throw new Error(
`Roll called ${sub} but no corresponding attribute "${attributeName}" was found. Attributes are: ${possibleAttributes.join(
", "
)}`
);
});
}
return element;
};
const calculateRollResult = (rollElements) => {
const results = {};
for (const key in rollElements) {
const element = rollElements[key];
if (element.indexOf("[[") === -1) continue;
const attributeFilled = replaceAttributes(element);
const expression = getExpression(attributeFilled);
const dice = getDiceRolls(expression);
const result = calculateResult(expression, [...dice]);
results[key] = {
result,
dice,
expression,
};
}
return results;
};
const startRoll = jest.fn(async (rollString) => {
if (!rollString) throw new Error("startRoll expected a Roll String but none was provided.");
const rollResult = { results: {} };
extractRollTemplate(rollString);
const rollElements = extractRollElements(rollString);
rollResult.results = calculateRollResult(rollElements);
rollResult.rollId = generateUUID();
return rollResult;
});
global.startRoll = startRoll;
const finishRoll = jest.fn(() => {});
global.finishRoll = finishRoll;