0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 22:52:40 +00:00

Merge branch 'naturalcrit:master' into master

This commit is contained in:
blitzher
2021-09-06 10:00:38 +02:00
committed by GitHub
22 changed files with 655 additions and 485 deletions

View File

@@ -76,7 +76,8 @@ const Editor = createClass({
const cursorPos = this.refs.codeEditor.getCursorPosition();
lines[cursorPos.line] = splice(lines[cursorPos.line], cursorPos.ch, injectText);
this.refs.codeEditor.setCursorPosition(cursorPos.line + injectText.split('\n').length, cursorPos.ch + injectText.length);
const injectLines = injectText.split('\n');
this.refs.codeEditor.setCursorPosition(cursorPos.line + injectLines.length, cursorPos.ch + injectLines[injectLines.length - 1].length);
if(this.isText()) this.props.onTextChange(lines.join('\n'));
if(this.isStyle()) this.props.onStyleChange(lines.join('\n'));

View File

@@ -2,86 +2,77 @@ const _ = require('lodash');
const features = [
'Astrological Botany',
'Astrological Chemistry',
'Biochemical Sorcery',
'Civil Alchemy',
'Consecrated Biochemistry',
'Civil Divination',
'Consecrated Augury',
'Demonic Anthropology',
'Divinatory Mineralogy',
'Genetic Banishing',
'Hermetic Geography',
'Immunological Incantations',
'Nuclear Illusionism',
'Ritual Astronomy',
'Seismological Divination',
'Spiritual Biochemistry',
'Statistical Occultism',
'Police Necromancer',
'Sixgun Poisoner',
'Pharmaceutical Gunslinger',
'Infernal Banker',
'Spell Analyst',
'Gunslinger Corruptor',
'Torque Interfacer',
'Exo Interfacer',
'Genetic Banishing',
'Gunpowder Torturer',
'Orbital Gravedigger',
'Phased Linguist',
'Mathematical Pharmacist',
'Plasma Outlaw',
'Gunslinger Corruptor',
'Hermetic Geography',
'Immunological Cultist',
'Malefic Chemist',
'Police Cultist'
'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 = ['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge'];
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 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 getFeature = (level)=>{
let res = [];
if(_.includes([4, 6, 8, 12, 14, 16, 19], level+1)){
res = ['Ability Score Improvement'];
}
res = _.union(res, _.sampleSize(features, _.sample([0, 1, 1, 1, 1, 1])));
if(!res.length) return '─';
return res.join(', ');
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(){
full : function(classes){
const classname = _.sample(classnames);
const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
const drawSlots = function(Slots){
let slots = Number(Slots);
return _.times(9, function(i){
const max = maxes[i];
if(slots < 1) return '—';
const res = _.min([max, slots]);
slots -= res;
return res;
}).join(' | ');
};
let cantrips = 3;
let spells = 1;
let slots = 2;
return `{{classTable,wide\n##### The ${classname}\n` +
`| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Level --- |||||||||\n`+
`| ^| Bonus ^| ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n`+
`|:-----:|:-----------:|:---------|:--------:|:------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${
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 = [
levelName,
`+${profBonus[level]}`,
getFeature(level),
cantrips,
spells,
drawSlots(slots)
_.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);
@@ -92,24 +83,50 @@ module.exports = {
}).join('\n')}\n}}\n\n`;
},
half : function(){
half : function(classes){
const classname = _.sample(classnames);
let featureScore = 1;
return `<div class='classTable'>\n##### The ${classname}\n` +
`| Level | Proficiency Bonus | Features | ${_.sample(features)}|\n` +
`|:---:|:---:|:---|:---:|\n${
return `{{${classes}\n##### The ${classname}\n` +
`| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` +
`|:-----:|:-----------------:|:---------|:---------------------:|\n${
_.map(levels, function(levelName, level){
const res = [
levelName,
`+${profBonus[level]}`,
getFeature(level),
`+${featureScore}`
_.pad(levelName, 5),
_.pad(`+${profBonus[level]}`, 2),
_.padEnd(_.sample(features), 23),
_.pad(`+${featureScore}`, 21),
].join(' | ');
featureScore += _.random(0, 1);
return `| ${res} |`;
}).join('\n')}\n</div>\n\n`;
}).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`;
}
};

View File

@@ -7,6 +7,7 @@ const ClassFeatureGen = require('./classfeature.gen.js');
const CoverPageGen = require('./coverpage.gen.js');
const TableOfContentsGen = require('./tableOfContents.gen.js');
const dedent = require('dedent-tabs').default;
const watercolorGen = require('./watercolor.gen.js');
module.exports = [
@@ -48,28 +49,6 @@ module.exports = [
}}
\n`
},
{
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:-100px,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:90px,right:30px
##### Homebrew Mug
[naturalcrit](https://homebrew.naturalcrit.com)
}}`
},
{
name : 'QR Code',
icon : 'fas fa-qrcode',
@@ -79,7 +58,6 @@ module.exports = [
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
`&amp;size=100x100) {width:100px;mix-blend-mode:multiply}`;
}
},
{
name : 'Page Number',
@@ -134,6 +112,48 @@ module.exports = [
]
},
/*********************** 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:-100px,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:90px,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 ********************/
@@ -237,12 +257,32 @@ module.exports = [
{
name : 'Class Table',
icon : 'fas fa-table',
gen : ClassTableGen.full,
gen : ClassTableGen.full('classTable,frame,decoration,wide'),
},
{
name : 'Half Class Table',
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,
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 : 'Table',

View 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`;
};

718
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@
"dependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-runtime": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-env": "^7.15.4",
"@babel/preset-react": "^7.14.5",
"body-parser": "^1.19.0",
"classnames": "^2.3.1",
@@ -54,11 +54,11 @@
"express-async-handler": "^1.1.4",
"express-static-gzip": "2.1.1",
"fs-extra": "10.0.0",
"googleapis": "84.0.0",
"googleapis": "85.0.0",
"jwt-simple": "^0.5.6",
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "2.1.3",
"marked": "3.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.1",
"mongoose": "^5.13.7",
@@ -69,7 +69,7 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-frame-component": "4.1.3",
"react-router-dom": "5.2.1",
"react-router-dom": "5.3.0",
"sanitize-filename": "1.6.3",
"superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"

View File

@@ -65,13 +65,13 @@ const mustacheSpans = {
raw : raw, // Text to consume from the source
text : text, // Additional custom properties
tags : tags,
tokens : this.inlineTokens(text) // inlineTokens to process **bold**, *italics*, etc.
tokens : this.lexer.inlineTokens(text) // inlineTokens to process **bold**, *italics*, etc.
};
}
}
},
renderer(token) {
return `<span class="inline-block${token.tags}>${this.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
return `<span class="inline-block${token.tags}>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
}
};
@@ -114,13 +114,13 @@ const mustacheDivs = {
raw : raw, // Text to consume from the source
text : text, // Additional custom properties
tags : tags,
tokens : this.inline(this.blockTokens(text))
tokens : this.lexer.blockTokens(text)
};
}
}
},
renderer(token) {
return `<div class="block${token.tags}>${this.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
return `<div class="block${token.tags}>${this.parser.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
}
};
@@ -149,7 +149,7 @@ const mustacheInjectInline = {
},
renderer(token) {
token.type = token.originalType;
const text = this.parseInline([token]);
const text = this.parser.parseInline([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
@@ -182,7 +182,7 @@ const mustacheInjectBlock = {
},
renderer(token) {
token.type = token.originalType;
const text = this.parse([token]);
const text = this.parser.parse([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
@@ -211,8 +211,8 @@ const definitionLists = {
const definitions = [];
while (match = regex.exec(src)) {
definitions.push({
dt : this.inlineTokens(match[1].trim()),
dd : this.inlineTokens(match[2].trim())
dt : this.lexer.inlineTokens(match[1].trim()),
dd : this.lexer.inlineTokens(match[2].trim())
});
endIndex = regex.lastIndex;
}
@@ -227,8 +227,8 @@ const definitionLists = {
renderer(token) {
return `<dl>
${token.definitions.reduce((html, def)=>{
return `${html}<dt>${this.parseInline(def.dt)}</dt>`
+ `<dd>${this.parseInline(def.dd)}</dd>\n`;
return `${html}<dt>${this.parser.parseInline(def.dt)}</dt>`
+ `<dd>${this.parser.parseInline(def.dd)}</dd>\n`;
}, '')}
</dl>`;
}
@@ -302,7 +302,7 @@ const spanTable = {
row = item.header[j];
for (k = 0; k < row.length; k++) {
row[k].tokens = [];
this.inlineTokens(row[k].text, row[k].tokens);
this.lexer.inlineTokens(row[k].text, row[k].tokens);
}
}
@@ -312,7 +312,7 @@ const spanTable = {
row = item.rows[j];
for (k = 0; k < row.length; k++) {
row[k].tokens = [];
this.inlineTokens(row[k].text, row[k].tokens);
this.lexer.inlineTokens(row[k].text, row[k].tokens);
}
}
return item;
@@ -329,7 +329,7 @@ const spanTable = {
output += `<tr>`;
for (j = 0; j < row.length; j++) {
cell = row[j];
text = this.parseInline(cell.tokens);
text = this.parser.parseInline(cell.tokens);
output += getTableCell(text, cell, 'th', token.align[col]);
col += cell.colspan;
}
@@ -344,7 +344,7 @@ const spanTable = {
output += `<tr>`;
for (j = 0; j < row.length; j++) {
cell = row[j];
text = this.parseInline(cell.tokens);
text = this.parser.parseInline(cell.tokens);
output += getTableCell(text, cell, 'td', token.align[col]);
col += cell.colspan;
}

View File

@@ -9,6 +9,7 @@
@headerText : #58180D; // Dark maroon
@monsterStatBackground : #EEDBAB; // Light orange parchment
@captionText : #766649; // Brown
@watercolorStain : #BBAD82; // Light brown
@page { margin: 0; }
body {
counter-reset : phb-page-numbers;
@@ -182,7 +183,9 @@ body {
table{
.useSansSerif();
width : 100%;
margin-bottom : 1em;
& + * {
margin-top : 1em;
}
thead{
display: table-row-group;
font-weight : 800;
@@ -272,8 +275,10 @@ body {
}
}
//*****************************
// * ARTIST CREDIT BLOCK
// * Images Snippets
// *****************************/
/* Arist Credit */
.artist {
position : absolute;
text-align : center;
@@ -298,6 +303,64 @@ body {
}
}
/* Watermark */
.watermark {
display : grid !important;
place-items : center;
justify-content : center;
position : absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
font-size : 120px;
text-transform : uppercase;
color : black;
mix-blend-mode : overlay;
opacity : 30%;
transform : rotate(-45deg);
z-index : 500;
p {
margin-bottom : none;
}
}
/* Watercolor */
[class*="watercolor"] {
position : absolute;
width : 2000px; /* dimensions need to be real big so the user can set */
height : 2000px; /* height or width and the image will maintain aspect ratio */
-webkit-mask-image : var(--wc);
-webkit-mask-size : contain;
-webkit-mask-repeat : no-repeat;
mask-image : var(--wc);
mask-size : contain;
mask-repeat : no-repeat;
background-size : cover;
background-color : @watercolorStain; /*default color*/
--wc : @watercolor1; /*default image*/
z-index : -2;
}
.watercolor1 { --wc : @watercolor1; }
.watercolor2 { --wc : @watercolor2; }
.watercolor3 { --wc : @watercolor3; }
.watercolor4 { --wc : @watercolor4; }
.watercolor5 { --wc : @watercolor5; }
.watercolor6 { --wc : @watercolor6; }
.watercolor7 { --wc : @watercolor7; }
.watercolor8 { --wc : @watercolor8; }
.watercolor9 { --wc : @watercolor9; }
.watercolor10 { --wc : @watercolor10; }
.watercolor11 { --wc : @watercolor11; }
.watercolor12 { --wc : @watercolor12; }
img {
z-index: 2 !important;
filter : drop-shadow(0px 6px 6px rgba(0,0,0,.4));
position: absolute;
}
//*****************************
// * MONSTER STAT BLOCK
// *****************************/
@@ -560,19 +623,47 @@ body {
// * CLASS TABLE
// *****************************/
.page .classTable{
margin-top : 25px;
margin-bottom : 40px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 25px 17px;
border-image-repeat : stretch;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
h5{
margin-bottom : 10px;
th[colspan]:not([rowspan]) {
white-space : nowrap;
}
&.frame {
margin-top : 0.66cm;
margin-bottom : 1.05cm;
margin-left : -0.1cm;
margin-right : -0.1cm;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 0.55cm 0.3cm;
border-image-repeat : stretch;
border-image-slice : 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
}
&.decoration {
transform-style : preserve-3d;
}
&.decoration::before {
content :'';
position : absolute;
background-image : @classTableDecoration;
background-size : contain;
background-repeat : space;
width : 7.75cm;
height : calc(100% + 3.3cm);
top : 50%;
left : 50%;
transform : translateY(-50%) translateX(-50%) translateZ(-1px);
filter : drop-shadow(0px 0px 1px #C8C5C080)
}
&.decoration.wide::before {
width : calc(100% + 3.3cm);
height : 7.75cm;
top : calc(50% + 0.4cm);
}
h5 + table{
margin-top : 0.2cm;
}
}
//*****************************

View File

@@ -1,3 +1,4 @@
// PHB
@footerAccentImage : data-uri('./themes/assets/footerAccent.png');
@frameBorderImage : data-uri('./themes/assets/frameBorder.png');
@backgroundImage : data-uri('./themes/assets/parchmentBackground.jpg');
@@ -8,3 +9,18 @@
@monsterBlockBackground : data-uri('./themes/assets/parchmentBackgroundGrayscale.jpg');
@monsterBorderImage : data-uri('./themes/assets/monsterBorderFancy.png');
@codeBorderImage : data-uri('./themes/assets/codeBorder.png');
@classTableDecoration : data-uri('./themes/assets/classTableDecoration.png');
// Watercolor Images
@watercolor1 : data-uri('./themes/assets/watercolor/watercolor1.png');
@watercolor2 : data-uri('./themes/assets/watercolor/watercolor2.png');
@watercolor3 : data-uri('./themes/assets/watercolor/watercolor3.png');
@watercolor4 : data-uri('./themes/assets/watercolor/watercolor4.png');
@watercolor5 : data-uri('./themes/assets/watercolor/watercolor5.png');
@watercolor6 : data-uri('./themes/assets/watercolor/watercolor6.png');
@watercolor7 : data-uri('./themes/assets/watercolor/watercolor7.png');
@watercolor8 : data-uri('./themes/assets/watercolor/watercolor8.png');
@watercolor9 : data-uri('./themes/assets/watercolor/watercolor9.png');
@watercolor10 : data-uri('./themes/assets/watercolor/watercolor10.png');
@watercolor11 : data-uri('./themes/assets/watercolor/watercolor11.png');
@watercolor12 : data-uri('./themes/assets/watercolor/watercolor12.png');

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB