aboutsummaryrefslogtreecommitdiffstats
path: root/converter.js
diff options
context:
space:
mode:
Diffstat (limited to 'converter.js')
-rw-r--r--converter.js216
1 files changed, 216 insertions, 0 deletions
diff --git a/converter.js b/converter.js
new file mode 100644
index 0000000..62b966f
--- /dev/null
+++ b/converter.js
@@ -0,0 +1,216 @@
+const builder = require('xmlbuilder');
+
+const convert = (jsonStr) => {
+ const inputData = JSON.parse(jsonStr);
+
+ const printableNumber = n => (n > 0) ? "+" + n : n;
+
+ const capitalize = (s) => {
+ if (typeof s !== 'string') return ''
+ return s.charAt(0).toUpperCase() + s.slice(1)
+ };
+
+ const ordinalize = (i) => {
+ const j = i % 10,
+ k = i % 100;
+ if (j == 1 && k != 11) {
+ return i + "st";
+ }
+ if (j == 2 && k != 12) {
+ return i + "nd";
+ }
+ if (j == 3 && k != 13) {
+ return i + "rd";
+ }
+ return i + "th";
+ };
+
+ const replaceSpecialValues = (text) => {
+ text = text.replace(/\{@hit (\d+?)\}/g, (match, p1) => printableNumber(p1))
+ text = text.replace(/\{@h\}(\d+?)/g, '$1');
+ text = text.replace(/\{@damage (.*?)\}/g, '$1');
+ text = text.replace(/\{@dc (\d+?)\}/g, 'DC $1');
+ text = text.replace(/\{@condition (\w+)\}/g, '$1');
+ text = text.replace(/\{@skill (\w+)\}/g, '$1');
+ text = text.replace(/\{@dice (.+?)\}/g, '$1');
+ text = text.replace(/\{@spell (.+?)\}/g, (match, p1) => capitalize(p1));
+ text = text.replace(/\{@creature (.+?)\}/g, (match, p1) => capitalize(p1));
+ text = text.replace(/\{@atk (.+?)\}/g, (match, p1) => {
+ p1 = p1.replace(/mw/g, 'Melee Weapon Attack');
+ p1 = p1.replace(/ms/g, 'Melee Spell Attack');
+ p1 = p1.replace(/rw/g, 'Ranged Weapon Attack');
+ p1 = p1.replace(/rs/g, 'Ranged Spell Attack');
+ p1 = p1.replace(/\,\s*/g, ' or ');
+ return `${p1}:`;
+ });
+ return text;
+ };
+
+ const xml = builder.create('root', { encoding: 'utf-8' })
+ .ele('compendium', { version: '5' })
+
+ const parseToXML = (obj) => {
+ const monster = xml.ele('monster');
+
+ monster.ele('name', obj.name)
+ monster.ele('size', obj.size)
+ monster.ele('type', typeof obj.type === 'object' ? obj.type.type : obj.type)
+ monster.ele('source', obj.source)
+ monster.ele('alignment', obj.alignment && obj.alignment.length ? obj.alignment.join('') : null)
+ monster.ele('ac', obj.ac && obj.ac.length ? obj.ac[0].ac : null)
+ monster.ele('hp', `${obj.hp.average} (${obj.hp.formula})`)
+ monster.ele('cr', obj.cr)
+ monster.ele('passive', obj.passive)
+ monster.ele('str', obj.str)
+ monster.ele('dex', obj.dex)
+ monster.ele('con', obj.con)
+ monster.ele('int', obj.int)
+ monster.ele('wis', obj.wis)
+ monster.ele('cha', obj.cha)
+ monster.ele('languages', obj.languages && obj.languages.length
+ ? obj.languages.join(', ')
+ : null
+ );
+ monster.ele('senses', obj.senses && obj.senses.length
+ ? obj.senses.join(', ')
+ : null
+ );
+ monster.ele('immune', obj.immune && obj.immune.length && obj.immune.map((item) => {
+ if (typeof item === 'string') {
+ return item;
+ } else {
+ let res = '';
+ if (item.preNote) {
+ res += item.preNote;
+ }
+ if (item.immune) {
+ res += item.immune.join(', ');
+ }
+ if (item.postNote) {
+ res += item.postNote;
+ }
+ return res;
+ }
+ }).join(', '));
+
+ const speedTypes = ['walk', 'burrow', 'climb', 'fly', 'swim'];
+ monster.ele('speed', obj.speed && speedTypes.map((key) => {
+ const value = obj.speed[key];
+ const type = key === 'walk' ? '' : key;
+ if (typeof value === 'object') {
+ const num = value.number;
+ const cond = value.condition || '';
+ return `${type} ${num} ft. ${cond}`.trim();
+ } else if (!!value) {
+ const num = obj.speed[key];
+ return `${type} ${num} ft.`;
+ } else {
+ return undefined;
+ }
+ }).filter(a => !!a).join(', ').trim());
+
+ monster.ele('save', obj.save && Object.keys(obj.save).map((key) => {
+ return `${key} ${obj.save[key]}`;
+ }).join(', '));
+
+ monster.ele('skill', obj.skill && Object.keys(obj.skill).map((key) => {
+ return `${key} ${obj.skill[key]}`;
+ }).join(', '));
+
+ obj.trait && obj.trait.forEach((trait) => {
+ const e = monster.ele('trait');
+ e.ele('name', trait.name);
+
+ trait.entries.forEach((entry) => {
+ const text = replaceSpecialValues(entry);
+ e.ele('text', text);
+ });
+ });
+
+ obj.spellcasting && obj.spellcasting.forEach((spellcasting) => {
+ const e = monster.ele('trait');
+ e.ele('name', spellcasting.name);
+ spellcasting.headerEntries && spellcasting.headerEntries.length && spellcasting.headerEntries.forEach((header) => {
+ e.ele('text', replaceSpecialValues(header));
+ });
+
+ spellcasting.spells && Object.keys(spellcasting.spells).forEach((level) => {
+ const spell = spellcasting.spells[level];
+ const slots = spell.slots;
+ const lst = spell.spells;
+
+ const levelNum = parseInt(level, 10);
+ const ordinal = ordinalize(levelNum);
+
+ let pre = levelNum !== 0 ? ordinal + ' level' : 'Cantrips';
+ pre += slots ? ` (${slots} slot${slots > 0 ? 's' : ''})` : '';
+ pre = pre.trim();
+ const text = `${pre}: ${lst.join(', ')}`;
+ e.ele('text', replaceSpecialValues(text));
+ });
+
+ if (spellcasting.will) {
+ const pre = 'At will'
+ const lst = spellcasting.will;
+
+ const text = `${pre}: ${lst.join(', ')}`;
+ e.ele('text', replaceSpecialValues(text));
+ }
+
+ spellcasting.daily && Object.keys(spellcasting.daily).forEach((count) => {
+ const lst = spellcasting.daily[count];
+
+ let pre = `• ${count}/day`;
+ const text = `${pre}: ${lst.join(', ')}`;
+ e.ele('text', replaceSpecialValues(text));
+ });
+ });
+
+ obj.legendary && obj.legendary.forEach((legendary) => {
+ const e = monster.ele('legendary');
+ e.ele('name', legendary.name);
+
+ legendary.entries.forEach((entry) => {
+ const text = replaceSpecialValues(entry);
+ e.ele('text', text);
+ });
+ });
+
+ obj.action && obj.action.forEach((action) => {
+ const e = monster.ele('action');
+ e.ele('name', action.name);
+
+ action.entries.forEach((entry) => {
+ const text = replaceSpecialValues(entry);
+ e.ele('text', text);
+ });
+ })
+ }
+
+ const processInputData = () => {
+ if (inputData.length) { // array
+ inputData.forEach((obj) => {
+ parseToXML(obj);
+ });
+ } else if (inputData.monster) { // .monster is an array
+ inputData.monster.forEach((obj) => {
+ parseToXML(obj);
+ });
+ } else {
+ parseToXML(inputData);
+ }
+ }
+
+ processInputData();
+
+ xml.end({
+ pretty: true,
+ newline: '\n',
+ });
+
+ return xml.toString();
+};
+
+module.exports = {
+ convert,
+};