From f84e2c316f2ef5c532980ffd76929242af93eed8 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 21 Jan 2022 00:27:52 -0500 Subject: [PATCH] DMG theme added --- client/homebrew/brewRenderer/brewRenderer.jsx | 5 +- .../editor/metadataEditor/metadataEditor.jsx | 16 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 2 - client/homebrew/pages/editPage/editPage.jsx | 2 +- scripts/buildHomebrew.js | 35 +- themes/V3/5eDMG/settings.json | 5 + themes/V3/5eDMG/snippets.js | 404 ++++++++++++++++++ themes/V3/5eDMG/snippets/classfeature.gen.js | 42 ++ themes/V3/5eDMG/snippets/classtable.gen.js | 132 ++++++ themes/V3/5eDMG/snippets/coverpage.gen.js | 124 ++++++ themes/V3/5eDMG/snippets/fullclass.gen.js | 43 ++ themes/V3/5eDMG/snippets/magic.gen.js | 109 +++++ themes/V3/5eDMG/snippets/monsterblock.gen.js | 184 ++++++++ .../V3/5eDMG/snippets/tableOfContents.gen.js | 84 ++++ themes/V3/5eDMG/snippets/watercolor.gen.js | 5 + themes/V3/5eDMG/style.less | 25 ++ themes/assets/DMG_background.png | Bin 0 -> 38966 bytes themes/assets/DMG_footerAccent.png | Bin 0 -> 13202 bytes ...{footerAccent.png => PHB_footerAccent.png} | Bin themes/assets/assets.less | 2 +- themes/themes.json | 12 +- 21 files changed, 1206 insertions(+), 25 deletions(-) create mode 100644 themes/V3/5eDMG/settings.json create mode 100644 themes/V3/5eDMG/snippets.js create mode 100644 themes/V3/5eDMG/snippets/classfeature.gen.js create mode 100644 themes/V3/5eDMG/snippets/classtable.gen.js create mode 100644 themes/V3/5eDMG/snippets/coverpage.gen.js create mode 100644 themes/V3/5eDMG/snippets/fullclass.gen.js create mode 100644 themes/V3/5eDMG/snippets/magic.gen.js create mode 100644 themes/V3/5eDMG/snippets/monsterblock.gen.js create mode 100644 themes/V3/5eDMG/snippets/tableOfContents.gen.js create mode 100644 themes/V3/5eDMG/snippets/watercolor.gen.js create mode 100644 themes/V3/5eDMG/style.less create mode 100644 themes/assets/DMG_background.png create mode 100644 themes/assets/DMG_footerAccent.png rename themes/assets/{footerAccent.png => PHB_footerAccent.png} (100%) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 2cdac284c..24c48b12b 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -23,6 +23,7 @@ const BrewRenderer = createClass({ text : '', style : '', renderer : 'legacy', + theme : '5ePHB', errors : [] }; }, @@ -177,6 +178,8 @@ const BrewRenderer = createClass({ render : function(){ //render in iFrame so broken code doesn't crash the site. //Also render dummy page while iframe is mounting. + const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; + const themePath = this.props.theme ?? '5ePHB'; return ( @@ -200,7 +203,7 @@ const BrewRenderer = createClass({ - + {/* Apply CSS from Style tab and render pages from Markdown tab */} {this.state.isMounted && diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 75d489087..2768597da 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./metadataEditor.less'); const React = require('react'); const createClass = require('create-react-class'); @@ -29,6 +30,11 @@ const MetadataEditor = createClass({ }; }, + getThemeData : function(renderer, theme){ + + return Themes[_.upperFirst(renderer)].find((x)=>x.path == theme); + }, + handleFieldChange : function(name, e){ this.props.onChange(_.merge({}, this.props.metadata, { [name] : e.target.value @@ -45,6 +51,8 @@ const MetadataEditor = createClass({ handleRenderer : function(renderer, e){ if(e.target.checked){ this.props.metadata.renderer = renderer; + if(renderer == 'legacy') + this.props.metadata.theme = '5ePHB'; } this.props.onChange(this.props.metadata); }, @@ -56,7 +64,7 @@ const MetadataEditor = createClass({ handleTheme : function(theme){ this.props.metadata.renderer = theme.renderer; - this.props.metadata.theme = theme.name; + this.props.metadata.theme = theme.path; this.props.onChange(this.props.metadata); }, @@ -129,17 +137,19 @@ const MetadataEditor = createClass({ renderThemeDropdown : function(){ const listThemes = (renderer)=>{ return _.map(Themes[renderer], (theme)=>{ - return
this.handleTheme(theme)} title={''}> + return
this.handleTheme(theme)} title={''}> {`${theme.renderer} : ${theme.name}`}
; }); }; + const currentTheme = this.getThemeData(this.props.metadata.renderer, this.props.metadata.theme); + return
- {`${this.props.metadata.renderer} : ${this.props.metadata.theme}`} + {`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`}
{listThemes('Legacy')} {listThemes('V3')} diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index cf9dd1166..1dc0b1231 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -42,8 +42,6 @@ const Snippetbar = createClass({ componentDidMount : async function() { const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; const themePath = this.props.theme ?? '5ePHB'; - console.log(Themes); - console.log(Themes[`${rendererPath}_${themePath}`]); const snippets = Themes[`${rendererPath}_${themePath}`]; this.setState({ snippets : snippets diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index d2f21481d..17dfd865b 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -472,7 +472,7 @@ const EditPage = createClass({ onMetaChange={this.handleMetaChange} renderer={this.state.brew.renderer} /> - +
; diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js index 7ae28ddf5..17605e2a1 100644 --- a/scripts/buildHomebrew.js +++ b/scripts/buildHomebrew.js @@ -50,32 +50,36 @@ fs.emptyDirSync('./build'); let themeFiles = fs.readdirSync('./themes/Legacy'); for (dir of themeFiles) { const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString()); + themeData.path = dir; themes.Legacy.push(themeData); + const src = `./themes/Legacy/${dir}/style.less`; + ((outputDirectory)=>{ + less.render(fs.readFileSync(src).toString(), { + compress : !isDev + }, function(e, output) { + fs.outputFile(`./build/themes/Legacy/${dir}/style.css`, output.css); + }); + })(`./build/themes/Legacy/${dir}/style.css`); + } themeFiles = fs.readdirSync('./themes/V3'); for (dir of themeFiles) { const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString()); + themeData.path = dir; themes.V3.push(themeData); + const src = `./themes/V3/${dir}/style.less`; + ((outputDirectory)=>{ + less.render(fs.readFileSync(src).toString(), { + compress : !isDev + }, function(e, output) { + fs.outputFile(outputDirectory, output.css); + }); + })(`./build/themes/V3/${dir}/style.css`); } await fs.outputFile('./themes/themes.json', JSON.stringify(themes, null, 2)); - // Compile Less files TODO: MOVE INTO ABOVE SECTION TO PROGRAMATICALLY GRAB ALL LESS FILES - let src = './themes/Legacy/5ePHB/style.less'; - - less.render(fs.readFileSync(src).toString(), { - compress : !isDev - }, function(e, output) { - fs.outputFile('./build/themes/Legacy/5ePHB/style.css', output.css); - }); - src = './themes/V3/5ePHB/style.less'; - less.render(fs.readFileSync(src).toString(), { - compress : !isDev - }, function(e, output) { - fs.outputFile('./build/themes/V3/5ePHB/style.css', output.css); - }); - // await less.render(lessCode, { // compress : !dev, // sourceMap : (dev ? { @@ -86,6 +90,7 @@ fs.emptyDirSync('./build'); // Move assets await fs.copy('./themes/fonts', './build/fonts'); + await fs.copy('./themes/assets', './build/assets'); //v==----------------------------- BUNDLE PACKAGES --------------------------------==v// diff --git a/themes/V3/5eDMG/settings.json b/themes/V3/5eDMG/settings.json new file mode 100644 index 000000000..99ea7f54a --- /dev/null +++ b/themes/V3/5eDMG/settings.json @@ -0,0 +1,5 @@ +{ + "name" : "5e DMG", + "renderer" : "V3", + "baseTheme" : "5ePHB" +} diff --git a/themes/V3/5eDMG/snippets.js b/themes/V3/5eDMG/snippets.js new file mode 100644 index 000000000..727e17359 --- /dev/null +++ b/themes/V3/5eDMG/snippets.js @@ -0,0 +1,404 @@ +/* eslint-disable max-lines */ + +const MagicGen = require('./snippets/magic.gen.js'); +const ClassTableGen = require('./snippets/classtable.gen.js'); +const MonsterBlockGen = require('./snippets/monsterblock.gen.js'); +const ClassFeatureGen = require('./snippets/classfeature.gen.js'); +const CoverPageGen = require('./snippets/coverpage.gen.js'); +const TableOfContentsGen = require('./snippets/tableOfContents.gen.js'); +const WatercolorGen = require('./snippets/watercolor.gen.js'); +const dedent = require('dedent-tabs').default; + + + +module.exports = [ + + { + groupName : 'Text Editor', + icon : 'fas fa-pencil-alt', + view : 'text', + snippets : [ + { + name : 'Column Break', + icon : 'fas fa-columns', + gen : '\n\\column\n' + }, + { + name : 'New Page', + icon : 'fas fa-file-alt', + gen : '\n\\page\n' + }, + { + name : 'Vertical Spacing', + icon : 'fas fa-arrows-alt-v', + gen : '\n::::\n' + }, + { + name : 'Horizontal Spacing', + icon : 'fas fa-arrows-alt-h', + gen : ' {{width:100px}} ' + }, + { + name : 'Wide Block', + icon : 'fas fa-window-maximize', + gen : dedent`\n + {{wide + Everything in here will be extra wide. Tables, text, everything! + Beware though, CSS columns can behave a bit weird sometimes. You may + have to manually place column breaks with \`\column\` to make the + surrounding text flow with this wide block the way you want. + }} + \n` + }, + { + name : 'QR Code', + icon : 'fas fa-qrcode', + gen : (brew)=>{ + return `![]` + + `(https://api.qrserver.com/v1/create-qr-code/?data=` + + `https://homebrewery.naturalcrit.com${brew.shareId ? `/share/${brew.shareId}` : ''}` + + `&size=100x100) {width:100px;mix-blend-mode:multiply}`; + } + }, + { + name : 'Page Number', + icon : 'fas fa-bookmark', + gen : '{{pageNumber 1}}\n{{footnote PART 1 | SECTION NAME}}\n\n' + }, + { + name : 'Auto-incrementing Page Number', + icon : 'fas fa-sort-numeric-down', + gen : '{{pageNumber,auto}}\n{{footnote PART 1 | SECTION NAME}}\n\n' + }, + { + name : 'Link to page', + icon : 'fas fa-link', + gen : '[Click here](#p3) to go to page 3\n' + }, + { + name : 'Table of Contents', + icon : 'fas fa-book', + gen : TableOfContentsGen + }, + { + name : 'Add Comment', + icon : 'fas fa-code', + gen : '' + }, + ] + }, + { + groupName : 'Style Editor', + icon : 'fas fa-pencil-alt', + view : 'style', + snippets : [ + { + name : 'Remove Drop Cap', + icon : 'fas fa-remove-format', + gen : dedent`/* Removes Drop Caps */ + .page h1+p:first-letter { + all: unset; + }\n\n` + }, + { + name : 'Tweak Drop Cap', + icon : 'fas fa-sliders-h', + gen : dedent`/* Drop Cap settings */ + .page h1 + p::first-letter { + font-family: SolberaImitationRemake; + font-size: 3.5cm; + background-image: linear-gradient(-45deg, #322814, #998250, #322814); + line-height: 1em; + }\n\n` + }, + { + name : 'Add Comment', + icon : 'fas fa-code', + gen : '/* This is a comment that will not be rendered into your brew. */' + }, + ] + }, + + /*********************** IMAGES *******************/ + { + groupName : 'Images', + icon : 'fas fa-images', + view : 'text', + snippets : [ + { + name : 'Image', + icon : 'fas fa-image', + gen : dedent` + ![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px,mix-blend-mode:multiply} + + {{artist,position:relative,top:-230px,left:10px,margin-bottom:-30px + ##### Cat Warrior + [Kyoung Hwan Kim](https://www.artstation.com/tahra) + }}` + }, + { + name : 'Background Image', + icon : 'fas fa-tree', + gen : dedent` + ![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,top:50px,right:30px,width:280px} + + {{artist,top:80px,right:30px + ##### Homebrew Mug + [naturalcrit](https://homebrew.naturalcrit.com) + }}` + }, + { + name : 'Watercolor Splatter', + icon : 'fas fa-fill-drip', + gen : WatercolorGen, + }, + { + name : 'Watermark', + icon : 'fas fa-id-card', + gen : dedent` + {{watermark Homebrewery}}\n` + }, + ] + }, + + + /************************* PHB ********************/ + + { + groupName : 'PHB', + icon : 'fas fa-book', + view : 'text', + snippets : [ + { + name : 'Spell', + icon : 'fas fa-magic', + gen : MagicGen.spell, + }, + { + name : 'Spell List', + icon : 'fas fa-scroll', + gen : MagicGen.spellList, + }, + { + name : 'Class Feature', + icon : 'fas fa-mask', + gen : ClassFeatureGen, + }, + { + name : 'Note', + icon : 'fas fa-sticky-note', + gen : function(){ + return dedent` + {{note + ##### Time to Drop Knowledge + Use notes to point out some interesting information. + + **Tables and lists** both work within a note. + }} + \n`; + }, + }, + { + name : 'Descriptive Text Box', + icon : 'fas fa-comment-alt', + gen : function(){ + return dedent` + {{descriptive + ##### Time to Drop Knowledge + Use descriptive boxes to highlight text that should be read aloud. + + **Tables and lists** both work within a descriptive box. + }} + \n`; + }, + }, + { + name : 'Monster Stat Block (unframed)', + icon : 'fas fa-paw', + gen : MonsterBlockGen.monster('monster', 2), + }, + { + name : 'Monster Stat Block', + icon : 'fas fa-spider', + gen : MonsterBlockGen.monster('monster,frame', 2), + }, + { + name : 'Wide Monster Stat Block', + icon : 'fas fa-dragon', + gen : MonsterBlockGen.monster('monster,frame,wide', 4), + }, + { + name : 'Cover Page', + icon : 'fas fa-file-word', + gen : CoverPageGen, + }, + { + name : 'Magic Item', + icon : 'fas fa-hat-wizard', + gen : MagicGen.item, + }, + { + name : 'Artist Credit', + icon : 'fas fa-signature', + gen : function(){ + return dedent` + {{artist,top:90px,right:30px + ##### Starry Night + [Van Gogh](https://www.vangoghmuseum.nl/en) + }} + \n`; + }, + }, + ] + }, + + + + /********************* TABLES *********************/ + + { + groupName : 'Tables', + icon : 'fas fa-table', + view : 'text', + snippets : [ + { + name : 'Table', + icon : 'fas fa-th-list', + gen : function(){ + return dedent` + ##### Character Advancement + | Experience Points | Level | Proficiency Bonus | + |:------------------|:-----:|:-----------------:| + | 0 | 1 | +2 | + | 300 | 2 | +2 | + | 900 | 3 | +2 | + | 2,700 | 4 | +2 | + | 6,500 | 5 | +3 | + | 14,000 | 6 | +3 | + \n`; + } + }, + { + name : 'Wide Table', + icon : 'fas fa-list', + gen : function(){ + return dedent` + {{wide + ##### Weapons + | Name | Cost | Damage | Weight | Properties | + |:------------------------|:-----:|:----------------|--------:|:-----------| + | *Simple Melee Weapons* | | | | | + |   Club | 1 sp | 1d4 bludgeoning | 2 lb. | Light | + |   Dagger | 2 gp | 1d4 piercing | 1 lb. | Finesse | + |   Spear | 1 gp | 1d6 piercing | 3 lb. | Thrown | + | *Simple Ranged Weapons* | | | | | + |   Dart | 5 cp | 1d4 piercig | 1/4 lb. | Finesse | + |   Shortbow | 25 gp | 1d6 piercing | 2 lb. | Ammunition | + |   Sling | 1 sp | 1d4 bludgeoning | — | Ammunition | + }} + \n`; + } + }, + { + name : 'Split Table', + icon : 'fas fa-th-large', + gen : function(){ + return dedent` + ##### Typical Difficulty Classes + {{column-count:2 + | Task Difficulty | DC | + |:----------------|:--:| + | Very easy | 5 | + | Easy | 10 | + | Medium | 15 | + + | Task Difficulty | DC | + |:------------------|:--:| + | Hard | 20 | + | Very hard | 25 | + | Nearly impossible | 30 | + }} + \n`; + } + }, + { + name : 'Class Table', + icon : 'fas fa-table', + gen : ClassTableGen.full('classTable,frame,decoration,wide'), + }, + { + name : 'Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.full('classTable,wide'), + }, + { + name : '1/2 Class Table', + icon : 'fas fa-list-alt', + gen : ClassTableGen.half('classTable,decoration,frame'), + }, + { + name : '1/2 Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.half('classTable'), + }, + { + name : '1/3 Class Table', + icon : 'fas fa-border-all', + gen : ClassTableGen.third('classTable,frame'), + }, + { + name : '1/3 Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.third('classTable'), + } + ] + }, + + + + + /**************** PAGE *************/ + + { + groupName : 'Print', + icon : 'fas fa-print', + view : 'style', + snippets : [ + { + name : 'A4 Page Size', + icon : 'far fa-file', + gen : dedent`/* A4 Page Size */ + .page{ + width : 210mm; + height : 296.8mm; + }\n\n` + }, + { + name : 'Square Page Size', + icon : 'far fa-file', + gen : dedent`/* Square Page Size */ + .page { + width : 125mm; + height : 125mm; + padding : 12.5mm; + columns : unset; + }\n\n` + }, + { + name : 'Ink Friendly', + icon : 'fas fa-tint', + gen : dedent` + /* Ink Friendly */ + *:is(.page,.monster,.note,.descriptive) { + background : white !important; + filter : drop-shadow(0px 0px 3px #888) !important; + } + + .page img { + visibility : hidden; + }\n\n` + }, + ] + }, + +]; diff --git a/themes/V3/5eDMG/snippets/classfeature.gen.js b/themes/V3/5eDMG/snippets/classfeature.gen.js new file mode 100644 index 000000000..7e2e7e858 --- /dev/null +++ b/themes/V3/5eDMG/snippets/classfeature.gen.js @@ -0,0 +1,42 @@ +const _ = require('lodash'); +const dedent = require('dedent-tabs').default; + +module.exports = function(classname){ + + classname = _.sample(['archivist', 'fancyman', 'linguist', 'fletcher', + 'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge']); + + classname = classname.toLowerCase(); + + const hitDie = _.sample([4, 6, 8, 10, 12]); + + const abilityList = ['Strength', 'Dexerity', 'Constitution', 'Wisdom', 'Charisma', 'Intelligence']; + const skillList = ['Acrobatics', 'Animal Handling', 'Arcana', 'Athletics', 'Deception', 'History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival']; + + + return dedent` + ## Class Features + As a ${classname}, you gain the following class features + #### Hit Points + + **Hit Dice:** :: 1d${hitDie} per ${classname} level + **Hit Points at 1st Level:** :: ${hitDie} + your Constitution modifier + **Hit Points at Higher Levels:** :: 1d${hitDie} (or ${hitDie/2 + 1}) + your Constitution modifier per ${classname} level after 1st + + #### Proficiencies + + **Armor:** :: ${_.sampleSize(['Light armor', 'Medium armor', 'Heavy armor', 'Shields'], _.random(0, 3)).join(', ') || 'None'} + **Weapons:** :: ${_.sampleSize(['Squeegee', 'Rubber Chicken', 'Simple weapons', 'Martial weapons'], _.random(0, 2)).join(', ') || 'None'} + **Tools:** :: ${_.sampleSize(['Artian\'s tools', 'one musical instrument', 'Thieve\'s tools'], _.random(0, 2)).join(', ') || 'None'} + + **Saving Throws:** :: ${_.sampleSize(abilityList, 2).join(', ')} + **Skills:** :: Choose two from ${_.sampleSize(skillList, _.random(4, 6)).join(', ')} + + #### Equipment + You start with the following equipment, in addition to the equipment granted by your background: + - *(a)* a martial weapon and a shield or *(b)* two martial weapons + - *(a)* five javelins or *(b)* any simple melee weapon + - ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])} + + `; +}; diff --git a/themes/V3/5eDMG/snippets/classtable.gen.js b/themes/V3/5eDMG/snippets/classtable.gen.js new file mode 100644 index 000000000..c1f6254f9 --- /dev/null +++ b/themes/V3/5eDMG/snippets/classtable.gen.js @@ -0,0 +1,132 @@ +const _ = require('lodash'); + +const features = [ + 'Astrological Botany', + 'Biochemical Sorcery', + 'Civil Divination', + 'Consecrated Augury', + 'Demonic Anthropology', + 'Divinatory Mineralogy', + 'Exo Interfacer', + 'Genetic Banishing', + 'Gunpowder Torturer', + 'Gunslinger Corruptor', + 'Hermetic Geography', + 'Immunological Cultist', + 'Malefic Chemist', + 'Mathematical Pharmacy', + 'Nuclear Biochemistry', + 'Orbital Gravedigger', + 'Pharmaceutical Outlaw', + 'Phased Linguist', + 'Plasma Gunslinger', + 'Police Necromancer', + 'Ritual Astronomy', + 'Sixgun Poisoner', + 'Seismological Alchemy', + 'Spiritual Illusionism', + 'Statistical Occultism', + 'Spell Analyst', + 'Torque Interfacer' +]; + +const classnames = ['Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger', + 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper']; + +const levels = ['1st', '2nd', '3rd', '4th', '5th', + '6th', '7th', '8th', '9th', '10th', + '11th', '12th', '13th', '14th', '15th', + '16th', '17th', '18th', '19th', '20th']; + +const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]; + +const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1]; + +const drawSlots = function(Slots, rows, padding){ + let slots = Number(Slots); + return _.times(rows, function(i){ + const max = maxes[i]; + if(slots < 1) return _.pad('—', padding); + const res = _.min([max, slots]); + slots -= res; + return _.pad(res.toString(), padding); + }).join(' | '); +}; + +module.exports = { + full : function(classes){ + const classname = _.sample(classnames); + + + let cantrips = 3; + let spells = 1; + let slots = 2; + return `{{${classes}\n##### The ${classname}\n` + + `| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Spell Level ---|||||||||\n`+ + `| ^| Bonus ^| ^| Known ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |\n`+ + `|:-----:|:-----------:|:-------------|:--------:|:------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|\n${ + _.map(levels, function(levelName, level){ + const res = [ + _.pad(levelName, 5), + _.pad(`+${profBonus[level]}`, 2), + _.padEnd(_.sample(features), 21), + _.pad(cantrips.toString(), 8), + _.pad(spells.toString(), 6), + drawSlots(slots, 9, 2), + ].join(' | '); + + cantrips += _.random(0, 1); + spells += _.random(0, 1); + slots += _.random(0, 2); + + return `| ${res} |`; + }).join('\n')}\n}}\n\n`; + }, + + half : function(classes){ + const classname = _.sample(classnames); + + let featureScore = 1; + return `{{${classes}\n##### The ${classname}\n` + + `| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` + + `|:-----:|:-----------------:|:---------|:---------------------:|\n${ + _.map(levels, function(levelName, level){ + const res = [ + _.pad(levelName, 5), + _.pad(`+${profBonus[level]}`, 2), + _.padEnd(_.sample(features), 23), + _.pad(`+${featureScore}`, 21), + ].join(' | '); + + featureScore += _.random(0, 1); + + return `| ${res} |`; + }).join('\n')}\n}}\n\n`; + }, + + third : function(classes){ + const classname = _.sample(classnames); + + let cantrips = 3; + let spells = 1; + let slots = 2; + return `{{${classes}\n##### ${classname} Spellcasting\n` + + `| Class | Cantrips | Spells |--- Spells Slots per Spell Level ---||||\n` + + `| Level ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |\n` + + `|:------:|:--------:|:-------:|:-------:|:-------:|:-------:|:-------:|\n${ + _.map(levels, function(levelName, level){ + const res = [ + _.pad(levelName, 6), + _.pad(cantrips.toString(), 8), + _.pad(spells.toString(), 7), + drawSlots(slots, 4, 7), + ].join(' | '); + + cantrips += _.random(0, 1); + spells += _.random(0, 1); + slots += _.random(0, 1); + + return `| ${res} |`; + }).join('\n')}\n}}\n\n`; + } +}; diff --git a/themes/V3/5eDMG/snippets/coverpage.gen.js b/themes/V3/5eDMG/snippets/coverpage.gen.js new file mode 100644 index 000000000..0fb8ba7a4 --- /dev/null +++ b/themes/V3/5eDMG/snippets/coverpage.gen.js @@ -0,0 +1,124 @@ +const _ = require('lodash'); + +const titles = [ + 'The Burning Gallows', + 'The Ring of Nenlast', + 'Below the Blind Tavern', + 'Below the Hungering River', + 'Before Bahamut\'s Land', + 'The Cruel Grave from Within', + 'The Strength of Trade Road', + 'Through The Raven Queen\'s Worlds', + 'Within the Settlement', + 'The Crown from Within', + 'The Merchant Within the Battlefield', + 'Ioun\'s Fading Traveler', + 'The Legion Ingredient', + 'The Explorer Lure', + 'Before the Charming Badlands', + 'The Living Dead Above the Fearful Cage', + 'Vecna\'s Hidden Sage', + 'Bahamut\'s Demonspawn', + 'Across Gruumsh\'s Elemental Chaos', + 'The Blade of Orcus', + 'Beyond Revenge', + 'Brain of Insanity', + 'Breed Battle!, A New Beginning', + 'Evil Lake, A New Beginning', + 'Invasion of the Gigantic Cat, Part II', + 'Kraken War 2020', + 'The Body Whisperers', + 'The Diabolical Tales of the Ape-Women', + 'The Doctor Immortal', + 'The Doctor from Heaven', + 'The Graveyard', + 'Azure Core', + 'Core Battle', + 'Core of Heaven: The Guardian of Amazement', + 'Deadly Amazement III', + 'Dry Chaos IX', + 'Gate Thunder', + 'Guardian: Skies of the Dark Wizard', + 'Lute of Eternity', + 'Mercury\'s Planet: Brave Evolution', + 'Ruby of Atlantis: The Quake of Peace', + 'Sky of Zelda: The Thunder of Force', + 'Vyse\'s Skies', + 'White Greatness III', + 'Yellow Divinity', + 'Zidane\'s Ghost' +]; + +const subtitles = [ + 'In an ominous universe, a botanist opposes terrorism.', + 'In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.', + 'In a land of corruption, two cyberneticists and a dungeon delver search for freedom.', + 'In an evil empire of horror, two rangers battle the forces of hell.', + 'In a lost city, in an age of sorcery, a librarian quests for revenge.', + 'In a universe of illusions and danger, three time travellers and an adventurer search for justice.', + 'In a forgotten universe of barbarism, in an era of terror and mysticism, a virtual reality programmer and a spy try to find vengance and battle crime.', + 'In a universe of demons, in an era of insanity and ghosts, three bodyguards and a bodyguard try to find vengance.', + 'In a kingdom of corruption and battle, seven artificial intelligences try to save the last living fertile woman.', + 'In a universe of virutal reality and agony, in an age of ghosts and ghosts, a fortune-teller and a wanderer try to avert the apocalypse.', + 'In a crime-infested kingdom, three martial artists quest for the truth and oppose evil.', + 'In a terrifying universe of lost souls, in an era of lost souls, eight dancers fight evil.', + 'In a galaxy of confusion and insanity, three martial artists and a duke battle a mob of psychics.', + 'In an amazing kingdom, a wizard and a secretary hope to prevent the destruction of mankind.', + 'In a kingdom of deception, a reporter searches for fame.', + 'In a hellish empire, a swordswoman and a duke try to find the ultimate weapon and battle a conspiracy.', + 'In an evil galaxy of illusion, in a time of technology and misery, seven psychiatrists battle crime.', + 'In a dark city of confusion, three swordswomen and a singer battle lawlessness.', + 'In an ominous empire, in an age of hate, two philosophers and a student try to find justice and battle a mob of mages intent on stealing the souls of the innocent.', + 'In a kingdom of panic, six adventurers oppose lawlessness.', + 'In a land of dreams and hopelessness, three hackers and a cyborg search for justice.', + 'On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.', + 'In a wicked universe, five seers fight lawlessness.', + 'In a kingdom of death, in an era of illusion and blood, four colonists search for fame.', + 'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.', + 'In a cursed empire, five inventors oppose terrorism.', + 'On a crime-ridden planet of conspiracy, a watchman and an artificial intelligence try to find love and oppose lawlessness.', + 'In a forgotten land, a reporter and a spy try to stop the apocalypse.', + 'In a forbidden land of prophecy, a scientist and an archivist oppose a cabal of barbarians intent on stealing the souls of the innocent.', + 'On an infernal world of illusion, a grave robber and a watchman try to find revenge and combat a syndicate of mages intent on stealing the source of all magic.', + 'In a galaxy of dark magic, four fighters seek freedom.', + 'In an empire of deception, six tomb-robbers quest for the ultimate weapon and combat an army of raiders.', + 'In a kingdom of corruption and lost souls, in an age of panic, eight planetologists oppose evil.', + 'In a galaxy of misery and hopelessness, in a time of agony and pain, five planetologists search for vengance.', + 'In a universe of technology and insanity, in a time of sorcery, a computer techician quests for hope.', + 'On a planet of dark magic and barbarism, in an age of horror and blasphemy, seven librarians search for fame.', + 'In an empire of dark magic, in a time of blood and illusions, four monks try to find the ultimate weapon and combat terrorism.', + 'In a forgotten empire of dark magic, six kings try to prevent the destruction of mankind.', + 'In a galaxy of dark magic and horror, in an age of hopelessness, four marines and an outlaw combat evil.', + 'In a mysterious city of illusion, in an age of computerization, a witch-hunter tries to find the ultimate weapon and opposes an evil corporation.', + 'In a damned kingdom of technology, a virtual reality programmer and a fighter seek fame.', + 'In a hellish kingdom, in an age of blasphemy and blasphemy, an astrologer searches for fame.', + 'In a damned world of devils, an alien and a ranger quest for love and oppose a syndicate of demons.', + 'In a cursed galaxy, in a time of pain, seven librarians hope to avert the apocalypse.', + 'In a crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime.' +]; + + +module.exports = ()=>{ + return ` + +{{margin-top:225px}} + +# ${_.sample(titles)} + +{{margin-top:25px}} + +{{wide +##### ${_.sample(subtitles)} +}} + +\\page`; +}; \ No newline at end of file diff --git a/themes/V3/5eDMG/snippets/fullclass.gen.js b/themes/V3/5eDMG/snippets/fullclass.gen.js new file mode 100644 index 000000000..5ede9e501 --- /dev/null +++ b/themes/V3/5eDMG/snippets/fullclass.gen.js @@ -0,0 +1,43 @@ +const _ = require('lodash'); + +const ClassFeatureGen = require('./classfeature.gen.js'); + +const ClassTableGen = require('./classtable.gen.js'); + +module.exports = function(){ + + const classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher', + 'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']); + + + const image = _.sample(_.map([ + 'http://orig01.deviantart.net/4682/f/2007/099/f/c/bard_stick_figure_by_wrpigeek.png', + 'http://img07.deviantart.net/a3c9/i/2007/099/3/a/archer_stick_figure_by_wrpigeek.png', + 'http://pre04.deviantart.net/d596/th/pre/f/2007/099/5/2/adventurer_stick_figure_by_wrpigeek.png', + 'http://img13.deviantart.net/d501/i/2007/099/d/4/black_mage_stick_figure_by_wrpigeek.png', + 'http://img09.deviantart.net/5cf3/i/2007/099/d/d/dark_knight_stick_figure_by_wrpigeek.png', + 'http://pre01.deviantart.net/7a34/th/pre/f/2007/099/6/3/monk_stick_figure_by_wrpigeek.png', + 'http://img11.deviantart.net/5dcc/i/2007/099/d/1/mystic_knight_stick_figure_by_wrpigeek.png', + 'http://pre08.deviantart.net/ad45/th/pre/f/2007/099/a/0/thief_stick_figure_by_wrpigeek.png', + ], function(url){ + return ``; + })); + + + return `${[ + image, + '', + '```', + '```', + '
\n\n', + `## ${classname}`, + 'Cool intro stuff will go here', + + '\\page', + ClassTableGen(classname), + ClassFeatureGen(classname), + + + + ].join('\n')}\n\n\n`; +}; \ No newline at end of file diff --git a/themes/V3/5eDMG/snippets/magic.gen.js b/themes/V3/5eDMG/snippets/magic.gen.js new file mode 100644 index 000000000..33ce8406d --- /dev/null +++ b/themes/V3/5eDMG/snippets/magic.gen.js @@ -0,0 +1,109 @@ +const _ = require('lodash'); + +const spellNames = [ + 'Astral Rite of Acne', + 'Create Acne', + 'Cursed Ramen Erruption', + 'Dark Chant of the Dentists', + 'Erruption of Immaturity', + 'Flaming Disc of Inconvenience', + 'Heal Bad Hygene', + 'Heavenly Transfiguration of the Cream Devil', + 'Hellish Cage of Mucus', + 'Irritate Peanut Butter Fairy', + 'Luminous Erruption of Tea', + 'Mystic Spell of the Poser', + 'Sorcerous Enchantment of the Chimneysweep', + 'Steak Sauce Ray', + 'Talk to Groupie', + 'Astonishing Chant of Chocolate', + 'Astounding Pasta Puddle', + 'Ball of Annoyance', + 'Cage of Yarn', + 'Control Noodles Elemental', + 'Create Nervousness', + 'Cure Baldness', + 'Cursed Ritual of Bad Hair', + 'Dispell Piles in Dentist', + 'Eliminate Florists', + 'Illusionary Transfiguration of the Babysitter', + 'Necromantic Armor of Salad Dressing', + 'Occult Transfiguration of Foot Fetish', + 'Protection from Mucus Giant', + 'Tinsel Blast', + 'Alchemical Evocation of the Goths', + 'Call Fangirl', + 'Divine Spell of Crossdressing', + 'Dominate Ramen Giant', + 'Eliminate Vindictiveness in Gym Teacher', + 'Extra-Planar Spell of Irritation', + 'Induce Whining in Babysitter', + 'Invoke Complaining', + 'Magical Enchantment of Arrogance', + 'Occult Globe of Salad Dressing', + 'Overwhelming Enchantment of the Chocolate Fairy', + 'Sorcerous Dandruff Globe', + 'Spiritual Invocation of the Costumers', + 'Ultimate Rite of the Confetti Angel', + 'Ultimate Ritual of Mouthwash', +]; +const itemNames = [ + 'Doorknob of Niceness', + 'Paper Armor of Folding', + 'Mixtape of Sadness', + 'Staff of Endless Confetti', +]; + +module.exports = { + + spellList : function(){ + const levels = ['Cantrips (0 Level)', '1st Level', '2nd Level', '3rd Level', '4th Level', '5th Level', '6th Level', '7th Level', '8th Level', '9th Level']; + + const content = _.map(levels, (level)=>{ + const spells = _.map(_.sampleSize(spellNames, _.random(4, 10)), (spell)=>{ + return `- ${spell}`; + }).join('\n'); + return `##### ${level} \n${spells} \n`; + }).join('\n'); + + return `{{spellList,wide\n${content}\n}}`; + }, + + spell : function(){ + const level = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th']; + const spellSchools = ['abjuration', 'conjuration', 'divination', 'enchantment', 'evocation', 'illusion', 'necromancy', 'transmutation']; + + + let components = _.sampleSize(['V', 'S', 'M'], _.random(1, 3)).join(', '); + if(components.indexOf('M') !== -1){ + components += ` (${_.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1, 3)).join(', ')})`; + } + + return [ + `#### ${_.sample(spellNames)}`, + `*${_.sample(level)}-level ${_.sample(spellSchools)}*`, + '', + '**Casting Time:** :: 1 action', + `**Range:** :: ${_.sample(['Self', 'Touch', '30 feet', '60 feet'])}`, + `**Components:** :: ${components}`, + `**Duration:** :: ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`, + '', + 'A flame, equivalent in brightness to a torch, springs from an object that you touch. ', + 'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ', + 'A *continual flame* can be covered or hidden but not smothered or quenched.', + '\n\n\n' + ].join('\n'); + }, + + item : function() { + return [ + `#### ${_.sample(itemNames)}`, + `*${_.sample(['Wondrous item', 'Armor', 'Weapon'])}, ${_.sample(['Common', 'Uncommon', 'Rare', 'Very Rare', 'Legendary', 'Artifact'])} (requires attunement)*`, + `:`, + `This knob is pretty nice. When attached to a door, it allows a user to`, + `open that door with the strength of the nearest animal. For example, if`, + `there is a cow nearby, the user will have the "strength of a cow" while`, + `opening this door.` + ].join('\n'); + } +}; diff --git a/themes/V3/5eDMG/snippets/monsterblock.gen.js b/themes/V3/5eDMG/snippets/monsterblock.gen.js new file mode 100644 index 000000000..dddf9b78c --- /dev/null +++ b/themes/V3/5eDMG/snippets/monsterblock.gen.js @@ -0,0 +1,184 @@ +const _ = require('lodash'); +const dedent = require('dedent-tabs').default; + +const genList = function(list, max){ + return _.sampleSize(list, _.random(0, max)).join(', ') || 'None'; +}; + +const getMonsterName = function(){ + return _.sample([ + 'All-devouring Baseball Imp', + 'All-devouring Gumdrop Wraith', + 'Chocolate Hydra', + 'Devouring Peacock', + 'Economy-sized Colossus of the Lemonade Stand', + 'Ghost Pigeon', + 'Gibbering Duck', + 'Sparklemuffin Peacock Spider', + 'Gum Elemental', + 'Illiterate Construct of the Candy Store', + 'Ineffable Chihuahua', + 'Irritating Death Hamster', + 'Irritating Gold Mouse', + 'Juggernaut Snail', + 'Juggernaut of the Sock Drawer', + 'Koala of the Cosmos', + 'Mad Koala of the West', + 'Milk Djinni of the Lemonade Stand', + 'Mind Ferret', + 'Mystic Salt Spider', + 'Necrotic Halitosis Angel', + 'Pinstriped Famine Sheep', + 'Ritalin Leech', + 'Shocker Kangaroo', + 'Stellar Tennis Juggernaut', + 'Wailing Quail of the Sun', + 'Angel Pigeon', + 'Anime Sphinx', + 'Bored Avalanche Sheep of the Wasteland', + 'Devouring Nougat Sphinx of the Sock Drawer', + 'Djinni of the Footlocker', + 'Ectoplasmic Jazz Devil', + 'Flatuent Angel', + 'Gelatinous Duck of the Dream-Lands', + 'Gelatinous Mouse', + 'Golem of the Footlocker', + 'Lich Wombat', + 'Mechanical Sloth of the Past', + 'Milkshake Succubus', + 'Puffy Bone Peacock of the East', + 'Rainbow Manatee', + 'Rune Parrot', + 'Sand Cow', + 'Sinister Vanilla Dragon', + 'Snail of the North', + 'Spider of the Sewer', + 'Stellar Sawdust Leech', + 'Storm Anteater of Hell', + 'Stupid Spirit of the Brewery', + 'Time Kangaroo', + 'Tomb Poodle', + ]); +}; + +const getType = function(){ + return `${_.sample(['Tiny', 'Small', 'Medium', 'Large', 'Gargantuan', 'Stupidly vast'])} ${_.sample(['beast', 'fiend', 'annoyance', 'guy', 'cutie'])}`; +}; + +const getAlignment = function(){ + return _.sample([ + 'annoying evil', + 'chaotic gossipy', + 'chaotic sloppy', + 'depressed neutral', + 'lawful bogus', + 'lawful coy', + 'manic-depressive evil', + 'narrow-minded neutral', + 'neutral annoying', + 'neutral ignorant', + 'oedpipal neutral', + 'silly neutral', + 'unoriginal neutral', + 'weird neutral', + 'wordy evil', + 'unaligned' + ]); +}; + +const getStats = function(){ + return `|${_.times(6, function(){ + const num = _.random(1, 20); + const mod = Math.ceil(num/2 - 5); + return `${num} (${mod >= 0 ? `+${mod}` : mod})`; + }).join('|')}|`; +}; + +const genAbilities = function(){ + return _.sample([ + '***Pack Tactics.*** These guys work together like peanut butter and jelly.', + '***Fowl Appearance.*** While the creature remains motionless, it is indistinguishable from a normal chicken.', + '***Onion Stench.*** Any creatures within 5 feet of this thing develops an irrational craving for onion rings.', + '***Enormous Nose.*** This creature gains advantage on any check involving putting things in its nose.', + '***Sassiness.*** When questioned, this creature will talk back instead of answering.', + '***Big Jerk.*** Whenever this creature makes an attack, it starts telling you how much cooler it is than you.', + ]); +}; + +const genLongAbilities = function(){ + return _.sample([ + dedent`***Pack Tactics.*** These guys work together like peanut butter and jelly. Jelly and peanut butter. + + When one of these guys attacks, the target is covered with, well, peanut butter and jelly.`, + dedent`***Hangriness.*** This creature is angry, and hungry. It will refuse to do anything with you until its hunger is satisfied. + + When in visual contact with this creature, you must purchase an extra order of fries, even if they say they aren't hungry.`, + dedent`***Full of Detergent.*** This creature has swallowed an entire bottle of dish detergent and is actually having a pretty good time. + + While walking near this creature, you must make a dexterity check or become "a soapy mess" for three hours, after which your skin will get all dry and itchy.` + ]); +}; + +const genAction = function(){ + const name = _.sample([ + 'Abdominal Drop', + 'Airplane Hammer', + 'Atomic Death Throw', + 'Bulldog Rake', + 'Corkscrew Strike', + 'Crossed Splash', + 'Crossface Suplex', + 'DDT Powerbomb', + 'Dual Cobra Wristlock', + 'Dual Throw', + 'Elbow Hold', + 'Gory Body Sweep', + 'Heel Jawbreaker', + 'Jumping Driver', + 'Open Chin Choke', + 'Scorpion Flurry', + 'Somersault Stump Fists', + 'Suffering Wringer', + 'Super Hip Submission', + 'Super Spin', + 'Team Elbow', + 'Team Foot', + 'Tilt-a-whirl Chin Sleeper', + 'Tilt-a-whirl Eye Takedown', + 'Turnbuckle Roll' + ]); + + return `***${name}.*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) `; +}; + + +module.exports = { + + monster : function(classes, genLines){ + return dedent` + {{${classes} + ## ${getMonsterName()} + *${getType()}, ${getAlignment()}* + ___ + **Armor Class** :: ${_.random(10, 20)} (chain mail, shield) + **Hit Points** :: ${_.random(1, 150)}(1d4 + 5) + **Speed** :: ${_.random(0, 50)}ft. + ___ + | STR | DEX | CON | INT | WIS | CHA | + |:-----:|:-----:|:-----:|:-----:|:-----:|:-----:| + ${getStats()} + ___ + **Condition Immunities** :: ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)} + **Senses** :: darkvision 60 ft., passive Perception ${_.random(3, 20)} + **Languages** :: ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)} + **Challenge** :: ${_.random(0, 15)} (${_.random(10, 10000)} XP) + ___ + ${_.times(_.random(genLines, genLines + 2), function(){return genAbilities();}).join('\n:\n')} + : + ${genLongAbilities()} + ### Actions + ${_.times(_.random(genLines, genLines + 2), function(){return genAction();}).join('\n:\n')} + }} + \n`; + } +}; diff --git a/themes/V3/5eDMG/snippets/tableOfContents.gen.js b/themes/V3/5eDMG/snippets/tableOfContents.gen.js new file mode 100644 index 000000000..1c52d5cf7 --- /dev/null +++ b/themes/V3/5eDMG/snippets/tableOfContents.gen.js @@ -0,0 +1,84 @@ +const _ = require('lodash'); +const dedent = require('dedent-tabs').default; + +const getTOC = (pages)=>{ + const add1 = (title, page)=>{ + res.push({ + title : title, + page : page + 1, + children : [] + }); + }; + const add2 = (title, page)=>{ + if(!_.last(res)) add1(null, page); + _.last(res).children.push({ + title : title, + page : page + 1, + children : [] + }); + }; + const add3 = (title, page)=>{ + if(!_.last(res)) add1(null, page); + if(!_.last(_.last(res).children)) add2(null, page); + _.last(_.last(res).children).children.push({ + title : title, + page : page + 1, + children : [] + }); + }; + + const res = []; + _.each(pages, (page, pageNum)=>{ + const lines = page.split('\n'); + _.each(lines, (line)=>{ + if(_.startsWith(line, '# ')){ + const title = line.replace('# ', ''); + add1(title, pageNum); + } + if(_.startsWith(line, '## ')){ + const title = line.replace('## ', ''); + add2(title, pageNum); + } + if(_.startsWith(line, '### ')){ + const title = line.replace('### ', ''); + add3(title, pageNum); + } + }); + }); + return res; +}; + +module.exports = function(brew){ + const pages = brew.text.split('\\page'); + const TOC = getTOC(pages); + const markdown = _.reduce(TOC, (r, g1, idx1)=>{ + if(g1.title !== null) { + r.push(`- ### [{{ ${g1.title}}}{{ ${g1.page}}}](#p${g1.page})`); + } + if(g1.children.length){ + _.each(g1.children, (g2, idx2)=>{ + if(g2.title !== null) { + r.push(` - #### [{{ ${g2.title}}}{{ ${g2.page}}}](#p${g2.page})`); + } + if(g2.children.length){ + _.each(g2.children, (g3, idx3)=>{ + if(g2.title !== null) { + r.push(` - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`); + } else { // Don't over-indent if no level-2 parent entry + r.push(` - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`); + } + }); + } + }); + } + return r; + }, []).join('\n'); + + return dedent` + {{toc,wide + # Table Of Contents + + ${markdown} + }} + \n`; +}; diff --git a/themes/V3/5eDMG/snippets/watercolor.gen.js b/themes/V3/5eDMG/snippets/watercolor.gen.js new file mode 100644 index 000000000..735a35602 --- /dev/null +++ b/themes/V3/5eDMG/snippets/watercolor.gen.js @@ -0,0 +1,5 @@ +const _ = require('lodash'); + +module.exports = ()=>{ + return `{{watercolor${_.random(1, 12)},top:20px,left:30px,width:300px,background-color:#BBAD82,opacity:80%}}\n\n`; +}; diff --git a/themes/V3/5eDMG/style.less b/themes/V3/5eDMG/style.less new file mode 100644 index 000000000..4657828dd --- /dev/null +++ b/themes/V3/5eDMG/style.less @@ -0,0 +1,25 @@ +@import (less) './themes/fonts/5e/fonts.less'; +@import (less) './themes/assets/assets.less'; +@import (less) './themes/V3/5ePHB/style.less'; + +@noteGreen : #ebcec3; // Salmon +@footnotes : #5c5c5c; // Dark gray + +.page { + background-image : url(/assets/DMG_background.png); + background-size : cover; + + &:after { + background-image : url(/assets/DMG_footerAccent.png); + height: 58px; + } + + .pageNumber { + color : @footnotes; + } + + .footnote { + color : @footnotes; + bottom : 40px; + } +} diff --git a/themes/assets/DMG_background.png b/themes/assets/DMG_background.png new file mode 100644 index 0000000000000000000000000000000000000000..5fbccf9a8578a89d81d835672b22a51b8db76673 GIT binary patch literal 38966 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%0DW+E_ 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 literal 0 HcmV?d00001 diff --git a/themes/assets/DMG_footerAccent.png b/themes/assets/DMG_footerAccent.png new file mode 100644 index 0000000000000000000000000000000000000000..cc115709a136f1b00475710fd9b4b44f007118e0 GIT binary patch literal 13202 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