0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-30 04:32:38 +00:00

Merge branch 'master' of https://github.com/naturalcrit/homebrewery into quote-snippet

This commit is contained in:
Víctor Losada Hernández
2023-08-08 19:12:34 +02:00
62 changed files with 19303 additions and 16831 deletions

View File

@@ -47,8 +47,8 @@ const getTOC = (pages)=>{
return res;
};
module.exports = function(brew){
const pages = brew.text.split('\\page');
module.exports = function(props){
const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);

View File

@@ -1,3 +1,5 @@
@import (less) './themes/assets/assets.less';
:root {
//Colors
--HB_Color_Accent : #EBCEC3; // Salmon
@@ -22,3 +24,10 @@
bottom : 40px;
}
}
.page:has(.partCover) {
.partCover {
background-image: @partCoverHeaderDMG;
}
}

View File

@@ -7,6 +7,7 @@ const scriptGen = require('./snippets/script.gen.js');
const ClassFeatureGen = require('./snippets/classfeature.gen.js');
const CoverPageGen = require('./snippets/coverpage.gen.js');
const TableOfContentsGen = require('./snippets/tableOfContents.gen.js');
const indexGen = require('./snippets/index.gen.js');
const QuoteGen = require('./snippets/quote.gen.js');
const dedent = require('dedent-tabs').default;
@@ -19,20 +20,16 @@ module.exports = [
icon : 'fas fa-pencil-alt',
view : 'text',
snippets : [
{
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 : 'Table of Contents',
icon : 'fas fa-book',
gen : TableOfContentsGen
},
{
name : 'Index',
icon : 'fas fa-bars',
gen : indexGen,
experimental : true
}
]
},
@@ -187,6 +184,18 @@ module.exports = [
gen : CoverPageGen.inside,
experimental : true
},
{
name : 'Part Cover Page',
icon : 'fac book-part-cover',
gen : CoverPageGen.part,
experimental : true
},
{
name : 'Back Cover Page',
icon : 'fac book-back-cover',
gen : CoverPageGen.back,
experimental : true
},
{
name : 'Magic Item',
icon : 'fas fa-hat-wizard',
@@ -203,7 +212,7 @@ module.exports = [
}}
\n`;
},
},
}
]
},
@@ -217,34 +226,51 @@ module.exports = [
view : 'text',
snippets : [
{
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'),
name : 'Class Tables',
icon : 'fas fa-table',
gen : ClassTableGen.full('classTable,frame,decoration,wide'),
subsnippets : [
{
name : 'Martial Class Table',
icon : 'fas fa-table',
gen : ClassTableGen.non('classTable,frame,decoration'),
},
{
name : 'Martial Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.non('classTable'),
},
{
name : 'Full Caster Class Table',
icon : 'fas fa-table',
gen : ClassTableGen.full('classTable,frame,decoration,wide'),
},
{
name : 'Full Caster Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.full('classTable,wide'),
},
{
name : 'Half Caster Class Table',
icon : 'fas fa-list-alt',
gen : ClassTableGen.half('classTable,frame,decoration,wide'),
},
{
name : 'Half Caster Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.half('classTable,wide'),
},
{
name : 'Third Caster Spell Table',
icon : 'fas fa-border-all',
gen : ClassTableGen.third('classTable,frame,decoration'),
},
{
name : 'Third Caster Spell Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.third('classTable'),
}
]
},
{
name : 'Rune Table',

View File

@@ -1,132 +1,138 @@
const _ = require('lodash');
const dedent = require('dedent-tabs').default;
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'
'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'
].map((f)=>_.padEnd(f, 21)); // Pad to equal length of 21 chars long
const classnames = [
'Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger',
'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper'
];
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`;
non : function(snippetClasses){
return dedent`
{{${snippetClasses}
##### The ${_.sample(classnames)}
| Level | Proficiency Bonus | Features | ${_.sample(features)} |
|:-----:|:-----------------:|:---------|:---------------------:|
| 1st | +2 | ${_.sample(features)} | 2 |
| 2nd | +2 | ${_.sample(features)} | 2 |
| 3rd | +2 | ${_.sample(features)} | 3 |
| 4th | +2 | ${_.sample(features)} | 3 |
| 5th | +3 | ${_.sample(features)} | 3 |
| 6th | +3 | ${_.sample(features)} | 4 |
| 7th | +3 | ${_.sample(features)} | 4 |
| 8th | +3 | ${_.sample(features)} | 4 |
| 9th | +4 | ${_.sample(features)} | 4 |
| 10th | +4 | ${_.sample(features)} | 4 |
| 11th | +4 | ${_.sample(features)} | 4 |
| 12th | +4 | ${_.sample(features)} | 5 |
| 13th | +5 | ${_.sample(features)} | 5 |
| 14th | +5 | ${_.sample(features)} | 5 |
| 15th | +5 | ${_.sample(features)} | 5 |
| 16th | +5 | ${_.sample(features)} | 5 |
| 17th | +6 | ${_.sample(features)} | 6 |
| 18th | +6 | ${_.sample(features)} | 6 |
| 19th | +6 | ${_.sample(features)} | 6 |
| 20th | +6 | ${_.sample(features)} | unlimited |
}}\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`;
full : function(snippetClasses){
return dedent`
{{${snippetClasses}
##### The ${_.sample(classnames)}
| Level | Proficiency | Features | Cantrips | --- Spell Slots Per Spell Level ---|||||||||
| ^| Bonus ^| ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |
|:-----:|:-----------:|:-------------|:--------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| 1st | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — | — | — | — | — |
| 2nd | +2 | ${_.sample(features)} | 2 | 3 | — | — | — | — | — | — | — | — |
| 3rd | +2 | ${_.sample(features)} | 2 | 4 | 2 | — | — | — | — | — | — | — |
| 4th | +2 | ${_.sample(features)} | 3 | 4 | 3 | — | — | — | — | — | — | — |
| 5th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 2 | — | — | — | — | — | — |
| 6th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | — | — | — | — | — | — |
| 7th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 1 | — | — | — | — | — |
| 8th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | — | — | — | — | — |
| 9th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — |
| 10th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — |
| 11th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — |
| 12th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — |
| 13th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — |
| 14th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — |
| 15th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — |
| 16th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — |
| 17th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | 1 |
| 18th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 1 | 1 | 1 | 1 | 1 |
| 19th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 1 | 1 | 1 |
| 20th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | 1 |
}}\n\n`;
},
third : function(classes){
const classname = _.sample(classnames);
half : function(snippetClasses){
return dedent`
{{${snippetClasses}
##### The ${_.sample(classnames)}
| Level | Proficiency | Features | Spells |--- Spell Slots Per Spell Level ---|||||
| ^| Bonus ^| ^| Known ^| 1st | 2nd | 3rd | 4th | 5th |
|:-----:|:-----------:|:-------------|:------:|:-----:|:-----:|:-----:|:-----:|:-----:|
| 1st | +2 | ${_.sample(features)} | — | — | — | — | — | — |
| 2nd | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — |
| 3rd | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — |
| 4th | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — |
| 5th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — |
| 6th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — |
| 7th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — |
| 8th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — |
| 9th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — |
| 10th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — |
| 11th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — |
| 12th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — |
| 13th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — |
| 14th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — |
| 15th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — |
| 16th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — |
| 17th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 |
| 18th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 |
| 19th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 |
| 20th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 |
}}\n\n`;
},
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`;
third : function(snippetClasses){
return dedent`
{{${snippetClasses}
##### ${_.sample(classnames)} Spellcasting
| Level | Cantrips | Spells |--- Spells Slots per Spell Level ---||||
| ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |
|:-----:|:--------:|:------:|:-------:|:-------:|:-------:|:-------:|
| 3rd | 2 | 3 | 2 | — | — | — |
| 4th | 2 | 4 | 3 | — | — | — |
| 5th | 2 | 4 | 3 | — | — | — |
| 6th | 2 | 4 | 3 | — | — | — |
| 7th | 2 | 5 | 4 | 2 | — | — |
| 8th | 2 | 6 | 4 | 2 | — | — |
| 9th | 2 | 6 | 4 | 2 | — | — |
| 10th | 3 | 7 | 4 | 3 | — | — |
| 11th | 3 | 8 | 4 | 3 | — | — |
| 12th | 3 | 8 | 4 | 3 | — | — |
| 13th | 3 | 9 | 4 | 3 | 2 | — |
| 14th | 3 | 10 | 4 | 3 | 2 | — |
| 15th | 3 | 10 | 4 | 3 | 2 | — |
| 16th | 3 | 11 | 4 | 3 | 3 | — |
| 17th | 3 | 11 | 4 | 3 | 3 | — |
| 18th | 3 | 11 | 4 | 3 | 3 | — |
| 19th | 3 | 12 | 4 | 3 | 3 | 1 |
| 20th | 3 | 13 | 4 | 3 | 3 | 1 |
}}\n\n`;
}
};

View File

@@ -68,13 +68,23 @@ const footnote = [
'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.'
];
const coverText = [
'Embark on a thrilling journey across a vast and varied world, where magic and mystery await you at every turn. Encounter strange creatures and ancient secrets, and forge your own destiny with your choices. The world is yours to shape and explore.',
'Join a band of brave adventurers and set out to explore the unknown lands beyond the horizon. Along the way, youll face perilous challenges, make new friends and enemies, and uncover a plot that threatens to destroy everything you hold dear. The fate of the world rests in your hands.',
'Create your own character and enter a realm of endless possibilities, where you can be whoever you want to be. Whether you prefer to fight, sneak, charm, or craft your way through the game, youll find a style that suits you. The only limit is your imagination.',
'Experience a rich and immersive story that adapts to your actions and decisions. Every choice you make has consequences, for good or ill. Will you be a hero or a villain? A leader or a follower? A friend or a foe? The choice is yours.',
'Dive into a world of epic fantasy and adventure, where you can explore ancient civilizations, dark dungeons, and hidden secrets. Along the way, youll meet colorful characters, collect powerful items, and learn new skills. The more you play, the more youll discover.',
'Explore a vast and dynamic world that changes according to your actions. You can shape the environment, influence the politics, and alter the history of the game world. But be careful, as every change has a ripple effect that may have unforeseen consequences.',
'Enter a world of wonder and danger, where you can find allies and enemies among the various races and factions that inhabit it. You can choose to join or oppose any of them, or forge your own path. The game world is alive and responsive to your actions.'
];
module.exports = {
front : function() {
return dedent`
{{frontCover}}
{{logo ![](/assets/naturalCritLogo.svg)}}
{{logo ![](/assets/naturalCritLogoRed.svg)}}
# ${_.sample(titles)}
## ${_.sample(subtitles)}
@@ -100,10 +110,46 @@ module.exports = {
___
{{imageMaskCenter${_.random(1, 16)},--offsetX:0%,--offsetY:0%,--rotation:0
![](https://i.imgur.com/IsfUnFR.jpg){height:100%}
![background image](https://i.imgur.com/IsfUnFR.jpg){position:absolute,bottom:0,left:0,height:100%}
}}
{{logo ![](/assets/naturalCritLogo.svg)}}
{{logo ![](/assets/naturalCritLogoRed.svg)}}
\page`;
},
part : function() {
return dedent`
{{partCover}}
# PART X
## ${_.sample(subtitles)}
{{imageMaskEdge${_.random(1, 8)},--offset:10cm,--rotation:180
![Background image](https://i.imgur.com/9TU96xY.jpg){position:absolute,bottom:0,left:0,height:100%}
}}
\page`;
},
back : function() {
return dedent`
{{backCover}}
# ${_.sample(subtitles)}
${_.sampleSize(coverText, 3).join('\n:\n')}
___
For use with any fantasy roleplaying ruleset. Play the best game of your life!
![background image](https://i.imgur.com/MJ4YHu7.jpg){position:absolute,bottom:0,left:0,height:100%}
{{logo
![](/assets/naturalCritLogoWhite.svg)
Homebrewery.Naturalcrit.com
}}
\page`;
}

View File

@@ -0,0 +1,85 @@
const dedent = require('dedent-tabs').default;
module.exports = ()=>{
return dedent`
{{index,wide,columns:5;
##### Index
- Ankhesh-Bort
- city map, 7
- city watch, 12
- guilds, 19
- Cheese
- types of cheese, 8
- cheese-related magic, 14
- cheese-related quests, 26-27
- Death
- appearance, 10
- personality, 13
- hobbies, 23
- Elves
- types of elves, 15
- elvish magic, 24
- elvish curses, 28
- Footnotes
- types of footnotes, 16-17
- footnote rules, 20-21
- footnote humor, 29-30
- Gods
- types of gods, 12
- godly interventions, 25
- godly conflicts, 31
- Heroes
- class features, 11-12
- heroic deeds, 26-27
- Inns
- types of inns, 9
- inn amenities, 18
- Jokes
- types of jokes, 11-12
- joke delivery, 25
- Knives
- types of knives, 16-17
- knife skills, 22-23
- knife fights, 28-29
- Luggage
- appearance, 10
- personality, 13
- abilities, 23
- Magic
- types of magic, 15
- magic rules, 24
- magic mishaps, 28
- Socks
- types of socks, 9
- sock-related magic (yes, really), 15
- sock-related quests (no, really), 26
- Trolls
- appearance and biology, 11
- culture and language, 18
- troll rights and activism, 31
- Unknown University
- history and architecture, 12
- faculty and staff, 20
- courses and exams, 33
- Vampires
- types and origins, 13
- vampiric powers and weaknesses, 21
- vampiric etiquette and politics, 34
- Witches
- types and traditions, 14
- witchcraft and headology, 22
- witch trials and tribulations, 35
- Xylophones
- musical instruments or weapons?, 15
- xylophone-related magic and lore, 23
- xylophone-related quests and puzzles, 36
- Yetis
- appearance and behavior, 16
- yeti philosophy and religion, 24
- yeti encounters and stories, 37
- Zombies
- types and causes, 17
- zombie rights and duties, 25
- zombie survival and prevention, 38
}}`;
};

View File

@@ -29,27 +29,29 @@ const getTOC = (pages)=>{
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);
}
});
if(!page.includes("{{frontCover}}") && !page.includes("{{insideCover}}") && !page.includes("{{partCover}}") && !page.includes("{{backCover}}")) {
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');
module.exports = function(props){
const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
if(g1.title !== null) {

View File

@@ -851,6 +851,134 @@ h5 + table{
}
}
}
//*****************************
// * BACK COVER
// *****************************/
.page:has(.backCover) {
color: #fff;
columns: 1;
padding: 2.25cm 1.3cm 2cm 1.3cm;
&:after {
all: unset;
}
.columnWrapper {
width: 7.6cm;
}
.backCover {
position: absolute;
inset: 0;
width: 11cm;
background-image: @backCover;
background-repeat: no-repeat;
background-size: contain;
z-index: -1;
}
.blank {
height: 1.4em;
}
h1 {
margin-bottom: .3cm;
font-size: 1.35cm;
line-height: 0.95em;
font-family: NodestoCapsCondensed;
text-align: center;
color: #ED1C24;
}
h1+p::first-line,
h1+p::first-letter {
all: unset;
}
img {
position: absolute;
top: 0px;
height: 100%;
z-index: -2;
}
hr {
margin-left: auto;
margin-right: auto;
margin-top: 1.1cm;
height: .53cm;
width: 4.5cm;
visibility: visible;
background-image: @horizontalRule;
background-size: 100% 100%;
border: none;
}
p {
font-family: Overpass;
line-height: 1.5em;
font-size: 0.332cm;
}
hr+p {
text-align: center;
margin-top: .6cm;
}
.logo {
position: absolute;
z-index: 0;
height: 1.5cm;
left: 1.2cm;
bottom: 2cm;
width: 7.6cm;
img {
position: relative;
height : 1.5cm;
width : 100%;
z-index : 0;
}
p {
position: relative;
color: #fff;
font-family: NodestoCapsWide;
font-size: .4cm;
letter-spacing: 0.08em;
line-height: 1em;
text-align: center;
text-indent: 0;
width: 100%;
}
}
}
//*****************************
// * PART COVER
// *****************************/
.page:has(.partCover) {
columns : 1;
text-align : center;
padding-top: 0;
.partCover {
background-image: @partCoverHeaderPHB;
background-repeat: no-repeat;
position: absolute;
background-size: 100%;
top: 0;
left: 0;
height: 6cm;
width: 100%;
}
h1 {
position: relative;
text-align: center;
text-transform: uppercase;
font-size: 2.3cm;
font-family: NodestoCapsCondensed;
margin-top: .4cm;
}
h2 {
font-family: Overpass;
font-size: 0.45cm;
position: relative;
margin-top: -0.7em;
line-height: 1.1em;
margin-left : auto;
margin-right : auto;
}
}
//*****************************
// * TABLE OF CONTENTS
@@ -1009,3 +1137,26 @@ break-inside : avoid;
}
}
}
//*****************************
// * INDEX
// *****************************/
.page {
.index {
font-size : 0.218cm;
ul ul {
margin : 0;
}
ul {
padding-left : 0;
text-indent : 0;
list-style-type : none;
}
& > ul > li {
text-indent : -1.5em;
padding-left : 1.5em;
}
}
}

View File

@@ -2,6 +2,7 @@
const WatercolorGen = require('./snippets/watercolor.gen.js');
const ImageMaskGen = require('./snippets/imageMask.gen.js');
const FooterGen = require('./snippets/footer.gen.js');
const dedent = require('dedent-tabs').default;
module.exports = [
@@ -21,6 +22,53 @@ module.exports = [
icon : 'fas fa-file-alt',
gen : '\n\\page\n'
},
{
name : 'Page Number',
icon : 'fas fa-bookmark',
gen : '{{pageNumber 1}}\n'
},
{
name : 'Auto-incrementing Page Number',
icon : 'fas fa-sort-numeric-down',
gen : '{{pageNumber,auto}}\n'
},
{
name : 'Footer',
icon : 'fas fa-shoe-prints',
gen : FooterGen.createFooterFunc(),
subsnippets : [
{
name : 'Footer from H1',
icon : 'fas fa-dice-one',
gen : FooterGen.createFooterFunc(1)
},
{
name : 'Footer from H2',
icon : 'fas fa-dice-two',
gen : FooterGen.createFooterFunc(2)
},
{
name : 'Footer from H3',
icon : 'fas fa-dice-three',
gen : FooterGen.createFooterFunc(3)
},
{
name : 'Footer from H4',
icon : 'fas fa-dice-four',
gen : FooterGen.createFooterFunc(4)
},
{
name : 'Footer from H5',
icon : 'fas fa-dice-five',
gen : FooterGen.createFooterFunc(5)
},
{
name : 'Footer from H6',
icon : 'fas fa-dice-six',
gen : FooterGen.createFooterFunc(6)
}
]
},
{
name : 'Vertical Spacing',
icon : 'fas fa-arrows-alt-v',

View File

@@ -0,0 +1,17 @@
const Markdown = require('../../../../shared/naturalcrit/markdown.js');
module.exports = {
createFooterFunc : function(headerSize=1){
return (props)=>{
const cursorPos = props.cursorPos;
const markdownText = props.brew.text.split('\n').slice(0, cursorPos.line).join('\n');
const markdownTokens = Markdown.marked.lexer(markdownText);
const headerToken = markdownTokens.findLast((lexerToken)=>{ return lexerToken.type === 'heading' && lexerToken.depth === headerSize; });
const headerText = headerToken?.tokens.map((token)=>{ return token.text; }).join('');
const outputText = headerText || 'PART 1 | SECTION NAME';
return `\n{{footnote ${outputText}}}\n`;
};
}
};

View File

@@ -23,6 +23,9 @@ body {
break-inside : avoid;
display : inline-block;
width : 100%;
img {
z-index : 0;
}
}
.inline-block {
display : inline-block;
@@ -251,7 +254,6 @@ body {
background-size : 20px;
z-index : -1;
transform : translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
transition : transform 2s;
& > p:has(img) {
position : absolute;
width : 50%;
@@ -259,7 +261,6 @@ body {
bottom : 50%;
left : 50%;
transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
transition : transform 2s;
}
& img {
position : absolute;

View File

@@ -13,7 +13,10 @@
@naturalCritLogo : url('/assets/naturalCritLogo.svg');
@coverPageBanner : url('/assets/coverPageBanner.svg');
@horizontalRule : url('/assets/horizontalRule.svg');
@partCoverHeaderPHB : url('/assets/partCoverHeaderPHB.png');
@partCoverHeaderDMG : url('/assets/partCoverHeaderDMG.svg');
@insideCoverMask : url('/assets/insideCoverMask.png');
@backCover : url('/assets/backCover.png');
@scriptBorder : url('/assets/scriptBorder.png');
// Watercolor Images

BIN
themes/assets/backCover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 94.65 94.6"
version="1.1"
id="svg11"
sodipodi:docname="naturalCritLogoWhite.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview13"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="8.4989431"
inkscape:cx="38.887188"
inkscape:cy="47.35883"
inkscape:window-width="1920"
inkscape:window-height="991"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg11" />
<defs
id="defs4">
<style
id="style2">.cls-1{fill:#ed1f24;}</style>
</defs>
<title
id="title6">NaturalCritLogo</title>
<g
id="Layer_2"
data-name="Layer 2">
<g
id="base">
<path
id="D20"
class="cls-1"
d="M63.45.09s-45.91,12.4-46,12.45a.71.71,0,0,0-.15.08l-.15.1-.12.11a1.07,1.07,0,0,0-.14.16l-.09.11-.12.23,0,.06L.2,54.9a1.59,1.59,0,0,0,.11,1.69L29.36,94h0l0,0,.08.08.08.08.09.09.08.06.13.07a0,0,0,0,0,0,0,1.59,1.59,0,0,0,.27.12l.13.05.06,0a1.55,1.55,0,0,0,.37,0,1.63,1.63,0,0,0,.31,0l45.67-8.3.16,0,.11,0,.12,0,.06,0s0,0,0,0l.06,0a1.65,1.65,0,0,0,.36-.28l0-.06a1.6,1.6,0,0,0,.26-.38s0,0,0,0v0h0a.14.14,0,0,1,0-.06L94.52,43.74a1.4,1.4,0,0,0,.11-.4.41.41,0,0,0,0-.11,1.13,1.13,0,0,0,0-.26.66.66,0,0,0,0-.14,2,2,0,0,0-.06-.26l0-.11a2.68,2.68,0,0,0-.18-.33v0L65.29.6C64.77-.31,63.45.09,63.45.09ZM74.9,81.7l-28.81-18L78.5,38.49ZM44.1,61l-11-40.17L77,35.39ZM82,37.78l8.92,5.95L79,73.48Zm4.46-1.1-4.6-3.06L75.69,21.36Zm-9.26-4.8-42.07-14,28.05-14ZM30.56,16.34l-6.49-2.16L47.85,7.7Zm-11.35-.21L27.88,19,7.64,45Zm10.73,5.76L40.78,61.64,4.64,54.42Zm10.82,43.2L30.26,89.6,5.75,58.09Zm3.16,1.24L71.74,83.72l-38.26,7Z"
style="fill:#ffffff;fill-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 611.99 154.72"><defs><style>.cls-1{fill:#f4f0f1;}.cls-2{fill:#d0d4d0;stroke:#888;stroke-linecap:round;stroke-miterlimit:35;stroke-width:0.5px;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="svg"><polygon id="background" class="cls-1" points="221.58 0 221.58 86.65 305.82 136.18 389.8 86.91 389.8 0 221.58 0"/><path id="right_triangle_compound_path" data-name="right triangle compound path" class="cls-2" d="M526.31.25c0,14.18-.08,26.67-.12,40.85,0,4.47-2.72,11-11.77,11-3.25.16-13.9-1.11-23.74-6.57L410.79.25h-2.53L527.82,69.38V.25Zm-.06,66.27L498.44,50.67c1,.39,14.21,4,19.29,2.81a13.9,13.9,0,0,0,8.52-6Z"/><path id="main_shape" data-name="main shape" class="cls-2" d="M598.88,123.48l-.13-65.27a81.69,81.69,0,0,1-24,20.31,79.73,79.73,0,0,1-26.8,9L544.82,86a78.57,78.57,0,0,0,42.52-18.09c15.33-13,22.12-30.25,24.4-37.06v-7a80.45,80.45,0,0,1-15.39,31.7c.63-1,16.49-26.4-2.42-55.2h-2.37c3.37,5,12.23,19.65,9.21,37.64-4,24-27.74,43.84-58.54,46.25L471.67,43.29a3.24,3.24,0,0,0-1.37-3,3.2,3.2,0,0,0-3.55,0L447.21,29a7,7,0,0,0-2.25-2.7,7,7,0,0,0-4.73-1.19l-16.59-9.57C414.47,10.29,404,4.25,397.06.25h-3.32L436.1,25.46s-36.92.06-40.94.11c-2.79-1.71-2.7,3.24,0,1.62h41.71l-46.4.37L390.3.26h-1.57L388.79,86l-41.1,24.5c-6.35,3.73-24.5,13.91-42.86,14.1s-36.47-11.3-44-15.49C248,101.8,235.15,93.85,222.33,86.52L222.46.25h-2V28l-46.09-.37h41.7c2.71,1.62,2.79-3.33,0-1.62-4-.05-40.93-.11-40.93-.11L218.32.25H215c-7,4-18.25,10.52-27.42,15.8L171,25.62a7,7,0,0,0-4.73,1.19,7,7,0,0,0-2.25,2.7L144.46,40.72a3.19,3.19,0,0,0-3.54.05,3.24,3.24,0,0,0-1.37,3L69,84.62c-30.8-2.41-54.53-22.3-58.54-46.25-3-18,5.84-32.62,9.2-37.64H17.29c-18.91,28.8-3,54.19-2.42,55.2C2.75,41,.27,26.28.27,26.28l0,7.08a78.31,78.31,0,0,0,23.63,35A78.57,78.57,0,0,0,66.4,86.43L63.22,88a79.66,79.66,0,0,1-26.8-9,81.76,81.76,0,0,1-24-20.31L12.34,124C12.46,141,.25,147.51.25,147.51v1.72a27.5,27.5,0,0,0,8.43-8A29,29,0,0,0,13,131.51v10.41L.25,152v2.1L26,133.61v-22l20.48-12c62.61,1.71,93.58-40,99.08-46.72,21.86-20.39,70.66-22.25,74.93-22.37V87.7l85.25,49.11,85.12-49.4-.33-57.36s52.35,1.05,75.23,22.38c5.49,6.67,36.47,48.43,99.08,46.72l20.48,12v22l26.47,21.07v-2.1l-13.55-10.66V131a29,29,0,0,0,4.35,9.72,32.12,32.12,0,0,0,9.2,8.6v-1.72A27.69,27.69,0,0,1,598.88,123.48ZM139.64,46.06c-.25.47-8.48,15.47-26.11,27.94-17.91,12.67-40,10.81-40,10.81ZM24.06,110.34l.1,22.47-7.09,6.1V93.77a99.79,99.79,0,0,0,12.3,3.52,99.74,99.74,0,0,0,13.8,2ZM48.19,97A102.43,102.43,0,0,1,30,95.38a103,103,0,0,1-15-3.87l-.09,48.35L13.76,141q.22-39,.47-77.9A83.48,83.48,0,0,0,36.08,80.34a82.84,82.84,0,0,0,25.15,8.85Zm30.54-3a121.63,121.63,0,0,1-26.57,3l12-7.14a96,96,0,0,0,15.51.29A97,97,0,0,0,102.19,86,122.56,122.56,0,0,1,78.73,94ZM140,55.86C135.3,61.42,126.49,71.79,114.11,79,96,89.52,77.09,89.38,66.63,88.34l2.65-1.62A77.74,77.74,0,0,0,112,77.14c17.82-9.9,26.42-24.69,29.83-30.8.2,0,2.07.45,3.05-.86a3.75,3.75,0,0,0,.64-2.86l6.71-3.76A128.27,128.27,0,0,1,140,55.86ZM164.8,39.57a119.38,119.38,0,0,0-15.51,8.29l7-11.33,11-6.29,36.42-.38C190.52,32.15,179.34,33.15,164.8,39.57Zm140.83,95.27-23.08-13.27c3.87,1.5,15.25,4.45,22.63,4.45s18.72-2.93,23.34-4.45Zm232.1-50.51s-22.14,1.87-40-10.81c-17.63-12.47-25.85-27.47-26.1-27.94ZM446.43,39.1c-14.55-6.43-25.73-7.43-38.88-9.72l36.41.38,11,6.29,7,11.33A118,118,0,0,0,446.43,39.1ZM471.2,55.38a128.27,128.27,0,0,1-12.2-17l6.71,3.76a3.73,3.73,0,0,0,.65,2.86c1,1.31,2.84.85,3.05.86,3.4,6.11,12,20.9,29.83,30.8A77.73,77.73,0,0,0,542,86.24l2.64,1.62c-10.47,1-29.4,1.18-47.48-9.34C484.73,71.32,475.92,60.94,471.2,55.38Zm61.29,38.1A123,123,0,0,1,509,85.54a96.54,96.54,0,0,0,22.51,4.13,96,96,0,0,0,15.51-.29l12,7.14A121.63,121.63,0,0,1,532.49,93.48Zm61.67,45-7.1-6.1.09-22.47-19.1-11a99.66,99.66,0,0,0,13.81-2,100.3,100.3,0,0,0,12.3-3.52Zm2.17.95L596.23,91a102.68,102.68,0,0,1-15,3.87A102.43,102.43,0,0,1,563,96.52l-13-7.81a82.61,82.61,0,0,0,25.16-8.85A83.14,83.14,0,0,0,597,62.62q.22,39,.47,77.9Z"/><path id="left_triangle_compound_path" data-name="left triangle compound path" class="cls-2" d="M83.4.73V69.86L203.77.25h-2.53l-80.7,45.8c-9.84,5.46-20.49,6.73-23.74,6.57-9,0-11.77-6.57-11.77-11C85,27.4,85,14.91,84.91.73ZM85,48a13.87,13.87,0,0,0,8.51,6c5.08,1.18,18.26-2.42,19.29-2.81L85,67Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -107,6 +107,13 @@
font-style: italic;
}
@font-face {
font-family: NodestoCapsWide;
src: url('../../../fonts/5e/Nodesto Caps Wide.woff2');
font-weight: normal;
font-style: normal
}
@font-face {
font-family: Overpass;
src: url('../../../fonts/5e/Overpass Medium.woff2');