/* globals require */ // HELPERS const copyToCB = (str) => { require('child_process').spawn('clip').stdin.end(str); } const emproper = (input) => { return input.split(/[_\s]+/g).map(w=>`${w[0].toUpperCase()}${w.slice(1)}`).join(' '); } const warlockData = { careers: [ // CORE - BASIC { name: "agitator", type: "basic", skills: { small_blade: 10, intimidate: 10, persuasion: 12, streetwise: 12, }, }, { name: "beggar", type: "basic", skills: { appraise: 10, blunt: 10, spot: 12, streetwise: 12, }, }, { name: "boatman", type: "basic", skills: { navigation: 10, repair: 10, endurance: 12, swimming: 12, }, }, { name: "bodyguard", type: "basic", skills: { medicine: 10, thrown: 10, intimidate: 12, large_blade: 12, }, }, { name: "bounty_hunter", type: "basic", skills: { bargain: 10, crossbow: 10, spot: 12, streetwise: 12, }, }, { name: "entertainer", type: "basic", skills: { diplomacy: 10, history: 10, disguise: 12, persuasion: 12, }, }, { name: "footpad", type: "basic", skills: { stealth: 10, thrown: 10, intimidate: 12, streetwise: 12, }, }, { name: "gambler", type: "basic", skills: { bargain: 10, spot: 10, persuasion: 12, "sleight-of-hand": 12, }, }, { name: "grave_robber", type: "basic", skills: { intimidate: 10, ostler: 10, small_blade: 12, spot: 12, }, }, { name: "hunter", type: "basic", skills: { stealth: 10, swimming: 10, spot: 12, survival: 12, }, }, { name: "initiate", type: "basic", skills: { command: 10, ostler: 10, medicine: 12, persuasion: 12, }, }, { name: "mercenary", type: "basic", skills: { endurance: 10, streetwise: 10, dodge: 12, large_blade: 12, }, }, { name: "militiaman", type: "basic", skills: { command: 10, ostler: 10, "pole-arm": 12, thrown: 12, }, }, { name: "miner", type: "basic", skills: { survival: 10, swimming: 10, endurance: 12, navigation: 12, }, }, { name: "noble", type: "basic", skills: { language: 10, medicine: 10, diplomacy: 12, history: 12, }, }, { name: "outlaw", type: "basic", skills: { medicine: 10, "sleigh-of-hand": 10, large_blade: 12, thrown: 12, }, }, { name: "pedlar", type: "basic", skills: { ostler: 10, streetwise: 10, bargain: 12, repair: 12, }, }, { name: "raconteur", type: "basic", skills: { dodge: 10, history: 10, lie: 12, streetwise: 12, }, }, { name: "rat_catcher", type: "basic", skills: { athletics: 10, medicine: 10, stealth: 12, survival: 12, }, }, { name: "road_warden", type: "basic", skills: { crossbow: 10, dodge: 10, "pole-arm": 12, ostler: 12, }, }, { name: "soldier", type: "basic", skills: { command: 10, bow: 10, large_blade: 12, "pole-arm": 12, }, }, { name: "thief", type: "basic", skills: { lie: 10, streetwise: 10, small_blade: 12, spot: 12, }, }, { name: "tomb_robber", type: "basic", skills: { athletics: 10, intimidate: 10, blunt: 12, endurance: 12, }, }, { name: "wizard's_apprentice", type: "basic", skills: { command: 10, persuasion: 10, language: 12, incantation: 12, }, }, // CORE - ADVANCED { name: "assassin", type: "advanced", skills: { blunt: 14, dodge: 14, streetwise: 14, small_blade: 16, stealth: 16, }, }, { name: "bravo", type: "advanced", skills: { blunt: 14, bow: 14, command: 14, large_blade: 16, streetwise: 16, }, }, { name: "watch_captain", type: "advanced", skills: { crossbow: 14, endurance: 14, ostler: 14, pole_arm: 16, streetwise: 16, }, }, { name: "charlatan", type: "advanced", skills: { appraise: 14, ostler: 14, spot: 14, persuasion: 16, streetwise: 16, }, }, { name: "explorer", type: "advanced", skills: { athletics: 14, small_blade: 14, swimming: 14, navigation: 16, spot: 16, }, }, { name: "freelance", type: "advanced", skills: { diplomacy: 14, medicine: 14, thrown: 14, ostler: 16, pole_arm: 16, }, }, { name: "highwayman", type: "advanced", skills: { dodge: 14, intimidate: 14, large_blade: 14, disguise: 16, ostler: 16, }, }, { name: "mercenary_captain", type: "advanced", skills: { bargain: 14, medicine: 14, survival: 14, crossbow: 16, large_blade: 16, }, }, { name: "merchant", type: "advanced", skills: { lie: 14, persuasion: 14, sleight_of_hand: 14, appraise: 16, bargain: 16, ostler: 16, }, }, { name: "minstrel", type: "advanced", skills: { history: 14, lie: 14, repair: 14, persuasion: 16, sleight_of_hand: 16, }, }, { name: "outlaw_chief", type: "advanced", skills: { endurance: 14, ostler: 14, swimming: 14, intimidate: 16, large_blade: 16, }, }, { name: "priest", type: "advanced", skills: { diplomacy: 14, ostler: 14, thrown: 14, incantation: 16, language: 16, }, }, { name: "scholar", type: "advanced", skills: { appraise: 14, brawling: 14, repair: 14, language: 16, medicine: 16, }, }, { name: "scout", type: "advanced", skills: { athletics: 14, ostler: 14, survival: 14, spot: 16, stealth: 16, }, }, { name: "spy", type: "advanced", skills: { athletics: 14, dodge: 14, small_blade: 14, persuasion: 16, sleight_of_hand: 16, }, }, { name: "veteran_soldier", type: "advanced", skills: { dodge: 14, pole_arm: 14, survival: 14, endurance: 16, large_blade: 16, }, }, { name: "wizard", type: "advanced", skills: { brawling: 14, command: 14, medicine: 14, history: 16, incantation: 16, }, }, // COMPENDIUM BASIC { name: "dwarf_inventor", type: "basic, dwarf", skills: { brawling: 10, diplomacy: 10, repair: 12, sleight_of_hand: 12, }, }, { name: "dwarf_tunnel_fighter", type: "basic, dwarf", skills: { endurance: 10, intimidate: 10, stealth: 12, survival: 12, }, }, { name: "elf_astrologer", type: "basic, elf", skills: { appraise: 10, diplomacy: 10, navigation: 12, spot: 12, }, }, { name: "elf_kin_guard", type: "basic, elf", skills: { endurance: 10, large_blade: 10, navigation: 12, survival: 12, }, }, { name: "halfling_gong_farmer", type: "basic, halfling", skills: { bargain: 10, medicine: 10, small_blade: 12, streetwise: 12, }, }, { name: "halfling_pie_master", type: "basic, halfling", skills: { bargain: 10, small_blade: 10, persuasion: 12, streetwise: 12, }, }, // COMPENDIUM ADVANCED { name: "dwarf_battlesmith", type: "advanced, dwarf", skills: { appraise: 14, endurance: 14, history: 14, intimidate: 16, repair: 16, }, }, { name: "dwarf_slayer", type: "advanced, dwarf", skills: { athletics: 14, brawling: 14, medicine: 14, intimidate: 16, large_blade: 16, }, }, { name: "elf_agent", type: "advanced, elf", skills: { diplomacy: 16, language: 16, persuasion: 16, sleight_of_hand: 14, streetwise: 14, }, }, { name: "elf_champion", type: "advanced, elf", skills: { spot: 14, survival: 14, thrown: 14, intimidate: 16, large_blade: 16, }, }, { name: "halfling_burglar", type: "advanced, halfling", skills: { appraise: 14, athletics: 14, dodge: 14, spot: 16, stealth: 16, }, }, { name: "halfling_gaffer", type: "advanced, halfling", skills: { brawling: 14, lie: 14, intimidate: 14, command: 16, streetwise: 16, }, }, // COMPENDIUM ADVANCED SPELLCASTERS { name: "dwarf_runeforger", type: "advanced, dwarf", skills: { appraise: 14, command: 14, diplomacy: 14, incantation: 16, language: 16, }, }, { name: "halfling_conjurer", type: "advanced, halfling", skills: { disguise: 14, lie: 14, streetwise: 14, persuasion: 16, sleight_of_hand: 16, }, }, { name: "elf_druid", type: "advanced, elf", skills: { medicine: 14, small_blades: 14, spot: 14, navigation: 16, survival: 16, }, }, // KINGDOM BASIC { name: "docker", type: "basic", skills: { appraise: 10, brawling: 10, intimidate: 10, streetwise: 12, swimming: 12, }, }, { name: "fish_warden", type: "basic", skills: { blunt: 10, repair: 10, thrown: 10, navigation: 12, swimming: 12, }, }, { name: "mudlark", type: "basic", skills: { spot: 10, survival: 10, thrown: 10, endurance: 12, swimming: 12, }, }, { name: "night_watchman", type: "basic", skills: { blunt: 10, lie: 10, navigation: 10, stealth: 12, streetwise: 12, }, }, { name: "publican", type: "basic", skills: { bargain: 10, history: 10, spot: 10, blunt: 12, persuasion: 12, }, }, { name: "servant", type: "basic", skills: { bargain: 10, endurance: 10, history: 10, spot: 12, stealth: 12, }, }, // GOBLINS BASIC { name: "hedge_wizard", type: "basic, goblin", skills: { blunt: 10, navigation: 10, medicine: 12, survival: 12, }, }, { name: "goblin_pedlar", type: "basic, goblin", skills: { diplomacy: 10, sleight_of_hand: 10, blunt: 12, bargain: 12, }, }, { name: "guttersnipe", type: "basic, goblin", skills: { athletics: 10, blunt: 10, stealth: 12, sleight_of_hand: 12, }, }, { name: "mushroom_farmer", type: "basic, goblin", skills: { blunt: 10, brawling: 10, medicine: 12, navigation: 12, spot: 12, }, }, { name: "spider_rider", type: "basic, goblin", skills: { appraise: 10, bow: 10, small_blade: 12, ostler: 12, survival: 12, }, }, { name: "tinkerer", type: "basic, goblin", skills: { small_blades: 10, sleight_of_hand: 10, repair: 12, spot: 12, }, }, // GOBLINS ADVANCED { name: "emissary", type: "advanced, goblin", skills: { history: 14, languages: 14, appraise: 14, persuasion: 16, diplomacy: 16, }, }, { name: "spelunker", type: "advanced. goblin", skills: { spot: 14, swimming: 14, stealth: 14, navigation: 16, athletics: 16, }, }, { name: "sporceror", type: "advanced, goblin", skills: { stealth: 14, navigation: 14, medicine: 14, spot: 16, incantation: 16, }, }, ], } // CAREER PARSER const careerStr = `SPORCEROR Stealth 14, Navigation 14, Medicine 14 Survival 16, Spot 16, Incantation 16`; const processCareer = (inp) => { let skillObj = {}; const rxCareerName = /^[^A-Za-z]*([a-zA-Z'\s]+)\n/; let cName = inp.match(rxCareerName)?.[1]?.toLowerCase().trim().replace(/\s+/g, '_'); if (!cName) return console.error('No name found'); let skills = inp.replace(rxCareerName, '').split(/,/g).filter(v=>v); skills.map(sk => { let parts = sk.match(/([^\d]+)(\d+)/); if (!parts[2]) return console.error(`Bad skill format`); let skName = parts[1].toLowerCase().trim().replace(/(\s+|-)/g, '_'); skillObj[skName] = parseInt(parts[2]); }); let skillMax = Object.values(skillObj).reduce((a,v) => a = Math.max(a,v)); let racial = (cName.match(/(^|\s)(elf|dwarf|halfling|goblin)[\s_]+/i)||[])[2]; //console.log(racial); let tags = (skillMax > 12) ? 'advanced' : 'basic'; tags += (racial) ? `, ${racial}` : ``; return { name: cName, type: tags, skills: skillObj }; } //let aOutput = (processCareer(careerStr)); //console.log(aOutput); const skills = ['appraise', 'athletics', 'bargain', 'blunt', 'bow', 'brawling', 'command', 'crossbow', 'diplomacy', 'disguise', 'dodge', 'endurance', 'history', 'incantation', 'intimidate', 'language', 'large_blade', 'lie', 'medicine', 'navigation', 'ostler', 'persuasion', 'pole_arm', 'repair', 'sleight_of_hand', 'small_blade', 'spot', 'stealth', 'streetwise', 'survival', 'swimming', 'thrown']; const skillsMax = skills.map(sk => sk += '_max'); const rxRaces = /(\b|_)(elf|dwarf|halfling|goblin)(\b|_)/i; // ADVENTURING SKILLS HTML const generateSkillListHTML = (side='left', linebreaks) => { let reqSkills = /^l/.test(side) ? skills.slice(0, 16) : skills.slice(16); let output = ''; reqSkills.map(sk => { output += `
^
()
{
` }); return linebreaks ? output : output.replace(/\n/g, '').replace(/>\s+?<').replace(/^\s+/, ''); } copyToCB(generateSkillListHTML('r')); // CAREER SELECT const generateCareerSelectHTML = (race, advanced=false, linebreaks=false) => { let rxTier = new RegExp(advanced ? 'advanced' : 'basic', 'i'); let rxRace = (race && rxRaces.test(race)) ? new RegExp(`${race}`, 'i') : null; let genericCareers = warlockData.careers.filter(c=>(rxTier.test(c.type) && !rxRaces.test(c.type))).map(c=>c.name); console.log(genericCareers); let racialCareers = (race) ? warlockData.careers.filter(c=>(rxTier.test(c.type) && rxRace.test(c.type))).map(c=>c.name) : null; console.log(racialCareers); let advancedAttrname = (advanced) ? `advanced_` : ``; let racialHTML = ``; if (racialCareers) { racialHTML += ``; racialCareers.forEach(rc => racialHTML += ``); racialHTML += ``; } let outputHTML = `` : ``; return linebreaks ? outputHTML : outputHTML.replace(/\n/g, '').replace(/>\s+?<').replace(/^\s+/, ''); } const careerCollection = (advanced = false) => { let selectCollection = []; ['', 'dwarf', 'elf', 'halfling', 'goblin'].forEach(race => { selectCollection.push(generateCareerSelectHTML(race, advanced)); }); return selectCollection.join('\n'); }; //copyToCB(careerCollection(true)); // beast abilities const abils = `Brittle - this creature is susceptible to impact damage. If maces, clubs or hammers attack them successfully, the damage is rolled twice and the higher value is applied.; Dissolve - this being is able to dissolve matter. Any time it is successfully struck the attacker must test their luck. On a fail, their weapon is destroyed.; Ethereal - this creature does not exist in the material plane. It can only be harmed by magic and magical weapons, and can pass through solid barriers such as doors and walls with ease. Flame - the being has a flame attack that they can use once a round that ignores armour.; Flying - using wings or similar, this creature can fly. Treat this as normal movement, but ignoring barriers that can be flown over at the game master's discretion. This creature cannot be engaged in melee combat whilst flying unless it chooses to attack, in which case it can be targeted.; Formless - The body of this creature has no real form, so it may slide under doors, squeeze through narrow spaces etc. as if it were a thick, viscous liquid.; Intelligent - this being is intelligent, can speak (but perhaps not in a tongue the characters know) and use tools and weapons. It is also able to create strategies and plans.; Poisonous - this creature carries a poison or can inject a venom. If it successfully hits a target, the victim must test their luck. If they fail, the victim is poisoned, and loses 1d6 stamina at the start of the next 1d3 rounds. Poison or venom effects from the same source are not cumulative, although of course the creature can still inflict its normal damage when it hits.; Puppet - the creature has no will of its own and must be directed by another mind. In consequence, it never surrenders or flees and will fight until destroyed.; Regenerating - the being is able to recover damage. Every round the being regains 1d6 stamina, up to a maximum of its starting value.; Spell caster - the being is able to cast spells. The games master should feel free to assign the creature spells that reflect its power. It uses stamina to cast spells and requires an incantation skill check.; Tracking - the creature is excellent at tracking other creatures by scent or another sense. Treat its adventuring skill at three times the normal level for tests involving tracking.`; const beastAbilitiesObj = (input) => { let output = {}; let parts = input.split(/[\s\n]*;[\s\n]*/g).filter(v=>v); console.log(parts); parts.forEach(ab => { let abParts = ab.split(/\s*-\s*/); output[abParts[0]] = abParts[1].replace(/^[a-z]/, (match) => match.toUpperCase()).replace(/\n/g, ' '); }); return output; }; //let beast = beastAbilitiesObj(abils); const beastOutput = { brittle: "This creature is susceptible to impact damage. If maces, clubs or hammers attack them successfully, the damage is rolled twice and the higher value is applied.", dissolve: "This being is able to dissolve matter. Any time it is successfully struck the attacker must test their luck. On a fail, their weapon is destroyed.", ethereal: "This creature does not exist in the material plane. It can only be harmed by magic and magical weapons, and can pass through solid barriers such as doors and walls with ease. Flame", flying: "Using wings or similar, this creature can fly. Treat this as normal movement, but ignoring barriers that can be flown over at the game master's discretion. This creature cannot be engaged in melee combat whilst flying unless it chooses to attack, in which case it can be targeted.", formless: "The body of this creature has no real form, so it may slide under doors, squeeze through narrow spaces etc. as if it were a thick, viscous liquid.", intelligent: "This being is intelligent, can speak (but perhaps not in a tongue the characters know) and use tools and weapons. It is also able to create strategies and plans.", poisonous: "This creature carries a poison or can inject a venom. If it successfully hits a target, the victim must test their luck. If they fail, the victim is poisoned, and loses 1d6 stamina at the start of the next 1d3 rounds. Poison or venom effects from the same source are not cumulative, although of course the creature can still inflict its normal damage when it hits.", puppet: "The creature has no will of its own and must be directed by another mind. In consequence, it never surrenders or flees and will fight until destroyed.", regenerating: "The being is able to recover damage. Every round the being regains 1d6 stamina, up to a maximum of its starting value.", spell_caster: "The being is able to cast spells. The games master should feel free to assign the creature spells that reflect its power. It uses stamina to cast spells and requires an incantation skill check.", tracking: "The creature is excellent at tracking other creatures by scent or another sense. Treat its adventuring skill at three times the normal level for tests involving tracking.", }; //console.log(Object.keys(beastOutput).map(abil => ``).join('')); // WEAPON CRIT TABLES const weaponTypes = { unarmed: { damage: '1d6 - 2', type: 'crushing', threat: 'casual', skill: 'brawling' }, club: { damage: '1d6 - 1', type: 'crushing', threat: 'casual', skill: 'blunt' }, knife: { damage: '1d6 + 1', type: 'slashing', threat: 'casual', skill: 'small_blade' }, dagger: { damage: '1d6 + 2', type: 'piercing', threat: 'casual', skill: 'small_blade' }, short_sword: { damage: '1d6 + 3', type: 'slashing', threat: 'casual', skill: 'large_blade' }, arming_sword: { damage: '2d6', type: 'slashing', threat: 'casual', skill: 'large_blade' }, hammer: { damage: '2d6', type: 'crushing', threat: 'martial', skill: 'blunt', }, axe: { damage: '2d6 + 1', type: 'crushing', threat: 'martial', skill: 'pole_arm' }, mace: { damage: '2d6 + 2', type: 'crushing', threat: 'martial', skill: 'blunt' }, spear: { damage: '2d6 + 1', type: 'piercing', threat: 'martial', skill: 'pole_arm' }, pole_arm: { damage: '2d6 + 2', type: 'slashing', threat: 'martial', skill: 'pole_arm' }, ['2h_sword']: { damage: '2d6 + 3', type: 'slashing', threat: 'martial', skill: 'large_blade' }, staff: { damage: '1d6', type: 'crushing', threat: 'casual', skill: 'blunt' }, bow: { damage: '2d6 + 1', type: 'piercing', threat: 'casual', skill: 'bow', ranged: 1 }, crossbow: { damage: '2d6 + 2', type: 'piercing', threat: 'martial', skill: 'crossbow', ranged: 1 }, }; let nums = new Array(20).fill().map((x,i) => i+1); //`{{#rollTotal() optionroll 1}}
{{option1}}
{{/rollTotal() optionroll 1}}`; let rollTableHTML = nums.map(n => `{{#rollTotal() optionroll ${n}}}
{{option${n}}}
{{/rollTotal() optionroll ${n}}}`); let rollTableHTML2 = nums.map(n => `option${n}`); //copyToCB(rollTableHTML2.join(' ')); //copyToCB(Object.keys(weaponTypes).map(w=>``).join('')); let critStr = `` let arr = critStr.split(/\n\d+[+]?\s+/).filter(v=>v); let output = {}; for (let i=0; i<20; i++) { output[i+1] = `${arr[i]}`.replace(/\n/g, ' '); } //console.log(output); //console.log(skills.length) //copyToCB(generateSkillListHTML('right')) console.log('brk');