mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-08 18:22:40 +00:00
DMG theme added
This commit is contained in:
@@ -23,6 +23,7 @@ const BrewRenderer = createClass({
|
|||||||
text : '',
|
text : '',
|
||||||
style : '',
|
style : '',
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
|
theme : '5ePHB',
|
||||||
errors : []
|
errors : []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -177,6 +178,8 @@ const BrewRenderer = createClass({
|
|||||||
render : function(){
|
render : function(){
|
||||||
//render in iFrame so broken code doesn't crash the site.
|
//render in iFrame so broken code doesn't crash the site.
|
||||||
//Also render dummy page while iframe is mounting.
|
//Also render dummy page while iframe is mounting.
|
||||||
|
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||||
|
const themePath = this.props.theme ?? '5ePHB';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -200,7 +203,7 @@ const BrewRenderer = createClass({
|
|||||||
<RenderWarnings />
|
<RenderWarnings />
|
||||||
<NotificationPopup />
|
<NotificationPopup />
|
||||||
</div>
|
</div>
|
||||||
<link href={`${this.props.renderer == 'legacy' ? '/themes/Legacy/5ePHB/style.css' : '/themes/V3/5ePHB/style.css'}`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
||||||
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
||||||
{this.state.isMounted
|
{this.state.isMounted
|
||||||
&&
|
&&
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||||
require('./metadataEditor.less');
|
require('./metadataEditor.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
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){
|
handleFieldChange : function(name, e){
|
||||||
this.props.onChange(_.merge({}, this.props.metadata, {
|
this.props.onChange(_.merge({}, this.props.metadata, {
|
||||||
[name] : e.target.value
|
[name] : e.target.value
|
||||||
@@ -45,6 +51,8 @@ const MetadataEditor = createClass({
|
|||||||
handleRenderer : function(renderer, e){
|
handleRenderer : function(renderer, e){
|
||||||
if(e.target.checked){
|
if(e.target.checked){
|
||||||
this.props.metadata.renderer = renderer;
|
this.props.metadata.renderer = renderer;
|
||||||
|
if(renderer == 'legacy')
|
||||||
|
this.props.metadata.theme = '5ePHB';
|
||||||
}
|
}
|
||||||
this.props.onChange(this.props.metadata);
|
this.props.onChange(this.props.metadata);
|
||||||
},
|
},
|
||||||
@@ -56,7 +64,7 @@ const MetadataEditor = createClass({
|
|||||||
|
|
||||||
handleTheme : function(theme){
|
handleTheme : function(theme){
|
||||||
this.props.metadata.renderer = theme.renderer;
|
this.props.metadata.renderer = theme.renderer;
|
||||||
this.props.metadata.theme = theme.name;
|
this.props.metadata.theme = theme.path;
|
||||||
this.props.onChange(this.props.metadata);
|
this.props.onChange(this.props.metadata);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -129,17 +137,19 @@ const MetadataEditor = createClass({
|
|||||||
renderThemeDropdown : function(){
|
renderThemeDropdown : function(){
|
||||||
const listThemes = (renderer)=>{
|
const listThemes = (renderer)=>{
|
||||||
return _.map(Themes[renderer], (theme)=>{
|
return _.map(Themes[renderer], (theme)=>{
|
||||||
return <div href={''} className='item' key={''} onClick={()=>this.handleTheme(theme)} title={''}>
|
return <div className='item' key={''} onClick={()=>this.handleTheme(theme)} title={''}>
|
||||||
{`${theme.renderer} : ${theme.name}`}
|
{`${theme.renderer} : ${theme.name}`}
|
||||||
</div>;
|
</div>;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const currentTheme = this.getThemeData(this.props.metadata.renderer, this.props.metadata.theme);
|
||||||
|
|
||||||
return <div className='field themes'>
|
return <div className='field themes'>
|
||||||
<label>theme</label>
|
<label>theme</label>
|
||||||
<Nav.dropdown trigger='click'>
|
<Nav.dropdown trigger='click'>
|
||||||
<div>
|
<div>
|
||||||
{`${this.props.metadata.renderer} : ${this.props.metadata.theme}`} <i className='fas fa-caret-down'></i>
|
{`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} <i className='fas fa-caret-down'></i>
|
||||||
</div>
|
</div>
|
||||||
{listThemes('Legacy')}
|
{listThemes('Legacy')}
|
||||||
{listThemes('V3')}
|
{listThemes('V3')}
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ const Snippetbar = createClass({
|
|||||||
componentDidMount : async function() {
|
componentDidMount : async function() {
|
||||||
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||||
const themePath = this.props.theme ?? '5ePHB';
|
const themePath = this.props.theme ?? '5ePHB';
|
||||||
console.log(Themes);
|
|
||||||
console.log(Themes[`${rendererPath}_${themePath}`]);
|
|
||||||
const snippets = Themes[`${rendererPath}_${themePath}`];
|
const snippets = Themes[`${rendererPath}_${themePath}`];
|
||||||
this.setState({
|
this.setState({
|
||||||
snippets : snippets
|
snippets : snippets
|
||||||
|
|||||||
@@ -472,7 +472,7 @@ const EditPage = createClass({
|
|||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} errors={this.state.htmlErrors} />
|
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -50,32 +50,36 @@ fs.emptyDirSync('./build');
|
|||||||
let themeFiles = fs.readdirSync('./themes/Legacy');
|
let themeFiles = fs.readdirSync('./themes/Legacy');
|
||||||
for (dir of themeFiles) {
|
for (dir of themeFiles) {
|
||||||
const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString());
|
const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString());
|
||||||
|
themeData.path = dir;
|
||||||
themes.Legacy.push(themeData);
|
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');
|
themeFiles = fs.readdirSync('./themes/V3');
|
||||||
for (dir of themeFiles) {
|
for (dir of themeFiles) {
|
||||||
const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString());
|
const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString());
|
||||||
|
themeData.path = dir;
|
||||||
themes.V3.push(themeData);
|
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));
|
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, {
|
// await less.render(lessCode, {
|
||||||
// compress : !dev,
|
// compress : !dev,
|
||||||
// sourceMap : (dev ? {
|
// sourceMap : (dev ? {
|
||||||
@@ -86,6 +90,7 @@ fs.emptyDirSync('./build');
|
|||||||
|
|
||||||
// Move assets
|
// Move assets
|
||||||
await fs.copy('./themes/fonts', './build/fonts');
|
await fs.copy('./themes/fonts', './build/fonts');
|
||||||
|
await fs.copy('./themes/assets', './build/assets');
|
||||||
|
|
||||||
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
||||||
|
|
||||||
|
|||||||
5
themes/V3/5eDMG/settings.json
Normal file
5
themes/V3/5eDMG/settings.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name" : "5e DMG",
|
||||||
|
"renderer" : "V3",
|
||||||
|
"baseTheme" : "5ePHB"
|
||||||
|
}
|
||||||
404
themes/V3/5eDMG/snippets.js
Normal file
404
themes/V3/5eDMG/snippets.js
Normal file
@@ -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 : '<!-- This is a comment that will not be rendered into your brew. Hotkey (Ctrl/Cmd + /). -->'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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`
|
||||||
|
 {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`
|
||||||
|
 {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`
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
42
themes/V3/5eDMG/snippets/classfeature.gen.js
Normal file
42
themes/V3/5eDMG/snippets/classfeature.gen.js
Normal file
@@ -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'])}
|
||||||
|
|
||||||
|
`;
|
||||||
|
};
|
||||||
132
themes/V3/5eDMG/snippets/classtable.gen.js
Normal file
132
themes/V3/5eDMG/snippets/classtable.gen.js
Normal file
@@ -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`;
|
||||||
|
}
|
||||||
|
};
|
||||||
124
themes/V3/5eDMG/snippets/coverpage.gen.js
Normal file
124
themes/V3/5eDMG/snippets/coverpage.gen.js
Normal file
@@ -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 `<style>
|
||||||
|
.page#p1{ text-align:center; counter-increment: none; }
|
||||||
|
.page#p1:after{ display:none; }
|
||||||
|
.page:nth-child(2n) .pageNumber { left: inherit !important; right: 2px !important; }
|
||||||
|
.page:nth-child(2n+1) .pageNumber { right: inherit !important; left: 2px !important; }
|
||||||
|
.page:nth-child(2n)::after { transform: scaleX(1); }
|
||||||
|
.page:nth-child(2n+1)::after { transform: scaleX(-1); }
|
||||||
|
.page:nth-child(2n) .footnote { left: inherit; text-align: right; }
|
||||||
|
.page:nth-child(2n+1) .footnote { left: 80px; text-align: left; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{{margin-top:225px}}
|
||||||
|
|
||||||
|
# ${_.sample(titles)}
|
||||||
|
|
||||||
|
{{margin-top:25px}}
|
||||||
|
|
||||||
|
{{wide
|
||||||
|
##### ${_.sample(subtitles)}
|
||||||
|
}}
|
||||||
|
|
||||||
|
\\page`;
|
||||||
|
};
|
||||||
43
themes/V3/5eDMG/snippets/fullclass.gen.js
Normal file
43
themes/V3/5eDMG/snippets/fullclass.gen.js
Normal file
@@ -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 `<img src = '${url}' style='max-width:8cm;max-height:25cm' />`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
return `${[
|
||||||
|
image,
|
||||||
|
'',
|
||||||
|
'```',
|
||||||
|
'```',
|
||||||
|
'<div style=\'margin-top:240px\'></div>\n\n',
|
||||||
|
`## ${classname}`,
|
||||||
|
'Cool intro stuff will go here',
|
||||||
|
|
||||||
|
'\\page',
|
||||||
|
ClassTableGen(classname),
|
||||||
|
ClassFeatureGen(classname),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
].join('\n')}\n\n\n`;
|
||||||
|
};
|
||||||
109
themes/V3/5eDMG/snippets/magic.gen.js
Normal file
109
themes/V3/5eDMG/snippets/magic.gen.js
Normal file
@@ -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');
|
||||||
|
}
|
||||||
|
};
|
||||||
184
themes/V3/5eDMG/snippets/monsterblock.gen.js
Normal file
184
themes/V3/5eDMG/snippets/monsterblock.gen.js
Normal file
@@ -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`;
|
||||||
|
}
|
||||||
|
};
|
||||||
84
themes/V3/5eDMG/snippets/tableOfContents.gen.js
Normal file
84
themes/V3/5eDMG/snippets/tableOfContents.gen.js
Normal file
@@ -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`;
|
||||||
|
};
|
||||||
5
themes/V3/5eDMG/snippets/watercolor.gen.js
Normal file
5
themes/V3/5eDMG/snippets/watercolor.gen.js
Normal file
@@ -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`;
|
||||||
|
};
|
||||||
25
themes/V3/5eDMG/style.less
Normal file
25
themes/V3/5eDMG/style.less
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
themes/assets/DMG_background.png
Normal file
BIN
themes/assets/DMG_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
themes/assets/DMG_footerAccent.png
Normal file
BIN
themes/assets/DMG_footerAccent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
@@ -1,5 +1,5 @@
|
|||||||
// PHB
|
// PHB
|
||||||
@footerAccentImage : data-uri('./themes/assets/footerAccent.png');
|
@footerAccentImage : data-uri('./themes/assets/PHB_footerAccent.png');
|
||||||
@frameBorderImage : data-uri('./themes/assets/frameBorder.png');
|
@frameBorderImage : data-uri('./themes/assets/frameBorder.png');
|
||||||
@backgroundImage : data-uri('./themes/assets/parchmentBackground.jpg');
|
@backgroundImage : data-uri('./themes/assets/parchmentBackground.jpg');
|
||||||
@redTriangleImage : data-uri('./themes/assets/redTriangle.png');
|
@redTriangleImage : data-uri('./themes/assets/redTriangle.png');
|
||||||
|
|||||||
@@ -3,14 +3,22 @@
|
|||||||
{
|
{
|
||||||
"name": "5e PHB",
|
"name": "5e PHB",
|
||||||
"renderer": "legacy",
|
"renderer": "legacy",
|
||||||
"baseTheme": false
|
"baseTheme": false,
|
||||||
|
"path": "5ePHB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"V3": [
|
"V3": [
|
||||||
|
{
|
||||||
|
"name": "5e DMG",
|
||||||
|
"renderer": "V3",
|
||||||
|
"baseTheme": "5ePHB",
|
||||||
|
"path": "5eDMG"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "5e PHB",
|
"name": "5e PHB",
|
||||||
"renderer": "V3",
|
"renderer": "V3",
|
||||||
"baseTheme": false
|
"baseTheme": false,
|
||||||
|
"path": "5ePHB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user