diff --git a/.prettierrc b/.prettierrc index 08409c7..a16e22e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -8,5 +8,13 @@ "endOfLine": "lf", "bracketSpacing": true, "bracketSameLine": false, - "arrowParens": "always" + "arrowParens": "always", + "overrides": [ + { + "files": ["*.mdoc"], + "options": { + "parser": "markdown" + } + } + ] } diff --git a/content/taxonomy/authors/dave-damage.yaml b/content/taxonomy/authors/dave-damage.yaml new file mode 100644 index 0000000..711a67b --- /dev/null +++ b/content/taxonomy/authors/dave-damage.yaml @@ -0,0 +1,3 @@ +name: Dave Damage +avatar: /images/authors/dave-damage/avatar.png +description: Nothing nice 2 say diff --git a/content/taxonomy/tags/homebrew.json b/content/taxonomy/tags/homebrew.json new file mode 100644 index 0000000..851a888 --- /dev/null +++ b/content/taxonomy/tags/homebrew.json @@ -0,0 +1,3 @@ +{ + "name": "Homebrew" +} diff --git a/content/taxonomy/tags/how-and-what.json b/content/taxonomy/tags/how-and-what.json new file mode 100644 index 0000000..9dbbeff --- /dev/null +++ b/content/taxonomy/tags/how-and-what.json @@ -0,0 +1,3 @@ +{ + "name": "Was ist was?" +} diff --git a/content/worldburner/elements/aether.json b/content/worldburner/elements/aether.json new file mode 100644 index 0000000..570c1a9 --- /dev/null +++ b/content/worldburner/elements/aether.json @@ -0,0 +1,15 @@ +{ + "title": "Aether", + "summary": "Represents the immaterial, magic, divine essence and spirits.", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:55", + "tags": [], + "author": "dave-damage" + }, + "category": "essence", + "glyph": "𐲯" +} diff --git a/content/worldburner/elements/aether/content.mdoc b/content/worldburner/elements/aether/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/air.json b/content/worldburner/elements/air.json new file mode 100644 index 0000000..4336abe --- /dev/null +++ b/content/worldburner/elements/air.json @@ -0,0 +1,15 @@ +{ + "title": "Air", + "summary": "Represents struggle, warfare, and violence", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:56", + "tags": [], + "author": "dave-damage" + }, + "category": "essence", + "glyph": "𐲝" +} diff --git a/content/worldburner/elements/air/content.mdoc b/content/worldburner/elements/air/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/anima.json b/content/worldburner/elements/anima.json new file mode 100644 index 0000000..3d84016 --- /dev/null +++ b/content/worldburner/elements/anima.json @@ -0,0 +1,15 @@ +{ + "title": "Anima", + "summary": "Represents sentience, spark of life, and the material world", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:58", + "tags": [], + "author": "dave-damage" + }, + "category": "essence", + "glyph": "𐲕" +} diff --git a/content/worldburner/elements/anima/content.mdoc b/content/worldburner/elements/anima/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/bloom.json b/content/worldburner/elements/bloom.json new file mode 100644 index 0000000..b385014 --- /dev/null +++ b/content/worldburner/elements/bloom.json @@ -0,0 +1,14 @@ +{ + "title": "Bloom", + "path": "elements/primal-forces", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:44", + "tags": [], + "author": "dave-damage" + }, + "category": "primal-force", + "glyph": "𐠁" +} diff --git a/content/worldburner/elements/bloom/content.mdoc b/content/worldburner/elements/bloom/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/earth.json b/content/worldburner/elements/earth.json new file mode 100644 index 0000000..25c03d8 --- /dev/null +++ b/content/worldburner/elements/earth.json @@ -0,0 +1,14 @@ +{ + "title": "Earth", + "summary": "Represents nourishment, wealth, and survival", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:59", + "tags": [] + }, + "category": "essence", + "glyph": "𐲏" +} diff --git a/content/worldburner/elements/earth/content.mdoc b/content/worldburner/elements/earth/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/fire.json b/content/worldburner/elements/fire.json new file mode 100644 index 0000000..c41f83b --- /dev/null +++ b/content/worldburner/elements/fire.json @@ -0,0 +1,15 @@ +{ + "title": "Fire", + "summary": "Represents knowledge, clarity, and skill", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T14:00", + "tags": [], + "author": "dave-damage" + }, + "category": "essence", + "glyph": "𐲠" +} diff --git a/content/worldburner/elements/fire/content.mdoc b/content/worldburner/elements/fire/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/order.json b/content/worldburner/elements/order.json new file mode 100644 index 0000000..864485c --- /dev/null +++ b/content/worldburner/elements/order.json @@ -0,0 +1,14 @@ +{ + "title": "Order", + "path": "elements/primal-forces", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:45", + "tags": [], + "author": "dave-damage" + }, + "category": "primal-force", + "glyph": "𐠞" +} diff --git a/content/worldburner/elements/order/content.mdoc b/content/worldburner/elements/order/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/void.json b/content/worldburner/elements/void.json new file mode 100644 index 0000000..f4b27be --- /dev/null +++ b/content/worldburner/elements/void.json @@ -0,0 +1,14 @@ +{ + "title": "Void", + "path": "elements/primal-forces", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T13:46", + "tags": [], + "author": "dave-damage" + }, + "category": "primal-force", + "glyph": "𐠎" +} diff --git a/content/worldburner/elements/void/content.mdoc b/content/worldburner/elements/void/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/elements/water.json b/content/worldburner/elements/water.json new file mode 100644 index 0000000..a105bf8 --- /dev/null +++ b/content/worldburner/elements/water.json @@ -0,0 +1,15 @@ +{ + "title": "Water", + "summary": "Represents intuition, passion, and sensuality", + "path": "elements/essences", + "cover": { + "showInHeader": false + }, + "meta": { + "publicationDate": "2025-11-19T14:01", + "tags": [], + "author": "dave-damage" + }, + "category": "essence", + "glyph": "𐲜" +} diff --git a/content/worldburner/elements/water/content.mdoc b/content/worldburner/elements/water/content.mdoc new file mode 100644 index 0000000..e69de29 diff --git a/content/worldburner/posts/introduction.mdoc b/content/worldburner/posts/introduction.mdoc new file mode 100644 index 0000000..147638e --- /dev/null +++ b/content/worldburner/posts/introduction.mdoc @@ -0,0 +1,146 @@ +--- +title: Introduction +summary: A short overview of the Worldburner project and it's key features +cover: + src: /image/covers/worldburner/posts/introduction/cover/src.png + alt: Landscape dominated by an active volcano + showInHeader: true +meta: + publicationDate: 2025-11-19T09:42:00.000Z + tags: + - homebrew + - how-and-what + author: dave-damage +--- +So what's this about then? »Worldburner« is a somewhat extremely opionated but system-agnostic worldbuilding tool, which provides rules and tables for creating the various moving parts of fantasy settings. It specifically focus on the cultural, mythological, and economical aspects of the fantasy genre and as such most parts of it deal with these conditions. +It is a collection of shameless theft and the ugly bastard child of other TTRPG supplements and computer games: + +- Worlds without Numbers +- AD&D +- Glorantha +- Burning Wheel +- Neoclassical Geek Revival +- Imperator: Rome +- Crusader Kings III +- Europa Universalis V +- Old World + +## Conventions + +There are visual or conceptional ideas repeatedly used throughout this document to denote options: + +### Granularity of rules + +Although the Burner Rules use the same concepts and terms, they were designed to offer three different levels of complexity: + +{% GranularityList %} +{% GranularityItem category="essential" %} +This is the quick-and-easy option. Forget the math – this one's all about storytelling or letting the dice take the wheel. Perfect for when you want to keep things moving and build big worlds fast. +{% /GranularityItem %} + +{% GranularityItem category="intermediate" %} +A set of rules that favours a balanced approach by combining a level of control with random elements. It presents a good compromise and is fitting for most worldbuilding needs. +{% /GranularityItem %} + +{% GranularityItem category="extended" %} +Here's where things get crunchy. If you love numbers, tables, and rollling a lot of die, this one's for you. Expect to make detailed choices and do some heavy lifting with calculations. +{% /GranularityItem %} +{% /GranularityList %} + +When we don't specify a granularity, expect *intermediate* to be used, and it should go without saying, that you can (and should) mix and match the various levels of granularity. + +### Elements + +Elements are part of a simple elementalist system, giving you an easy visual shortcut to express big ideas or concepts. Now whether this system is a stroke of genius, total nonsense or something in-between in the game world – that's up to you to decide. Feel free to swap out the symbols and names to better match your style. + +#### Primal Forces + +These are the raw forces that keep the universe ticking. Think of them as the cosmic building blocks – responsible for shaping reality, sparking life, and keeping the wheels of creation turning. Each one plays a role in how worlds change, form, and and sometimes break down. + +{% table %} +- Glyph +- Name +- Description +- Opposite +--- +- **𐠁** +- [Bloom](/worldburner/elements/primal-forces/bloom) +- Embodiment of change, growth, and unpredictability, where form and structure are continously reshaped +- *Order, Void* +--- +- **𐠞** +- [Order](/worldburner/elements/primal-forces/order) +- Embodies stasis, permanence, and consistency. This is the force that gives form, function, and purpose. +- *Bloom, Void* +--- +- **𐠎** +- Void +- [Void](/worldburner/elements/primal-forces/void) +- Encompasses decay, entropy, and dissolution, leading to transformation and renewal. It often serves as balance between *Bloom* and *Order* +- *Bloom, Order* +{% /table %} + +#### Essences + +The essences are the stuff the world's made of – whether it's the dirt under your feet or the spark that keeps your spirit going. Everything you see, touch, or feel is built from these. They are what makes reality work, from solid earth to mystical forces. + +{% table %} +- Glyph +- Name +- Description +- Opposite +--- +- **𐲯** +- Aether +- Represents the immaterial, magic, divine essence and spirits. +- *Anima* +--- +- **𐲝** +- Air +- Represents struggle, warfare, and violence +- *Earth* +--- +- **𐲕** +- Anima +- Represents sentience, the spark of life, and the material world +- *Aether* +--- +- 𐲏 +- Earh +- Represents nourishment, wealth, and survival +- *Air* +--- +- **𐲠** +- Fire +- Represents knowledge, clarity, and skill +- *Water* +--- +- **𐲜** +- Water +- Represents intuition, passion, and sensuality +- *Fire* +{% /table %} + +### Roleplaying Systems + +Like we said earlier, »Worldburner« works with almost any system, but that doesn't mean, we won'T sneak in a few tweaks and rules for our favourite ones, right? + +{% RPGSystem system="ngr" %} +Rules and Systems for the **Neoclassical Geek Revival** system +{% /RPGSystem %} + +{% RPGSystem system="bol" %} +Rules and systems for the **Barbarians of Lemuria** system +{% /RPGSystem %} + +{% RPGSystem system="bw" %} +Rules and systems for the **Burning Wheel** system +{% /RPGSystem %} + +{% RPGSystem system="qw" %} +Rules and systems for the **Questworlds** system +{% /RPGSystem %} + +{% RPGSystem system="sotdl" %} +Rules and systems for the **Shadow of the Daemon Lord/Weird Wizard** systems +{% /RPGSystem %} diff --git a/package.json b/package.json index 33a0839..7b94731 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "format": "prettier --write ." }, "dependencies": { + "@keystar/ui": "^0.7.19", "@keystatic/core": "^0.5.48", "@keystatic/next": "^5.0.4", "@markdoc/markdoc": "^0.5.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89b487b..711fcfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@keystar/ui': + specifier: ^0.7.19 + version: 0.7.19(next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@keystatic/core': specifier: ^0.5.48 version: 0.5.48(next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) diff --git a/public/images/authors/dave-damage/avatar.png b/public/images/authors/dave-damage/avatar.png new file mode 100644 index 0000000..13f1186 Binary files /dev/null and b/public/images/authors/dave-damage/avatar.png differ diff --git a/public/images/covers/worldburner/posts/introduction/cover/src.png b/public/images/covers/worldburner/posts/introduction/cover/src.png new file mode 100644 index 0000000..13ae8cd Binary files /dev/null and b/public/images/covers/worldburner/posts/introduction/cover/src.png differ diff --git a/src/keystatic/collections/worldburner/elements.ts b/src/keystatic/collections/worldburner/elements.ts new file mode 100644 index 0000000..ff1aee2 --- /dev/null +++ b/src/keystatic/collections/worldburner/elements.ts @@ -0,0 +1,38 @@ +import { collection, fields } from '@keystatic/core'; + +import { createArticleField } from '@/keystatic/fields/general/article'; +import { wbComponents } from '@/keystatic/components/worldburner'; + +export default collection({ + label: 'WB – Elements', + slugField: 'title', + path: 'content/worldburner/elements/*', + format: { + data: 'json', + }, + columns: ['glyph', 'title', 'category'], + schema: { + ...createArticleField('worldburner/elements', 'elements', wbComponents), + category: fields.select({ + label: 'Category', + options: [ + { + label: 'Primal Force', + value: 'primal-force', + }, + { + label: 'Essence', + value: 'essence', + }, + { + label: 'Aspect', + value: 'aspect', + }, + ], + defaultValue: 'primal-force', + }), + glyph: fields.text({ + label: 'Glyph', + }), + }, +}); diff --git a/src/keystatic/collections/worldburner/index.ts b/src/keystatic/collections/worldburner/index.ts index e498ea9..f904a51 100644 --- a/src/keystatic/collections/worldburner/index.ts +++ b/src/keystatic/collections/worldburner/index.ts @@ -1,7 +1,9 @@ import posts from '@/keystatic/collections/worldburner/posts'; +import elements from '@/keystatic/collections/worldburner/elements'; const wbCollections = { wb_posts: posts, + wb_elements: elements, }; export default wbCollections; diff --git a/src/keystatic/collections/worldburner/posts.ts b/src/keystatic/collections/worldburner/posts.ts index 1759517..546c39f 100644 --- a/src/keystatic/collections/worldburner/posts.ts +++ b/src/keystatic/collections/worldburner/posts.ts @@ -1,6 +1,7 @@ import { collection } from '@keystatic/core'; import { createArticleField } from '@/keystatic/fields/general/article'; +import { wbComponents } from '@/keystatic/components/worldburner'; export default collection({ label: 'WB – Posts', @@ -11,6 +12,6 @@ export default collection({ }, entryLayout: 'content', schema: { - ...createArticleField('worldburner/posts', ''), + ...createArticleField('worldburner/posts', '', wbComponents), }, }); diff --git a/src/keystatic/components/worldburner/granularity.ts b/src/keystatic/components/worldburner/granularity.ts new file mode 100644 index 0000000..48558c7 --- /dev/null +++ b/src/keystatic/components/worldburner/granularity.ts @@ -0,0 +1,38 @@ +import { repeating, wrapper } from '@keystatic/core/content-components'; +import { fields } from '@keystatic/core'; +import { blocksIcon } from '@keystar/ui/icon/icons/blocksIcon'; + +const granularityComponents = { + GranularityList: repeating({ + label: 'Rules Granularity', + icon: blocksIcon, + children: ['GranularityItem'], + schema: {}, + }), + GranularityItem: wrapper({ + label: 'GranularityItem', + forSpecificLocations: true, + schema: { + category: fields.select({ + label: 'Granularity', + options: [ + { + label: 'Essential', + value: 'essential', + }, + { + label: 'Intermediate', + value: 'intermediate', + }, + { + label: 'Extended', + value: 'extended', + }, + ], + defaultValue: 'intermediate', + }), + }, + }), +}; + +export default granularityComponents; diff --git a/src/keystatic/components/worldburner/index.ts b/src/keystatic/components/worldburner/index.ts new file mode 100644 index 0000000..cfcb4f0 --- /dev/null +++ b/src/keystatic/components/worldburner/index.ts @@ -0,0 +1,7 @@ +import granularityComponents from './granularity'; +import rpgsystemComponent from './rpgsystems'; + +export const wbComponents = { + ...granularityComponents, + ...rpgsystemComponent, +}; diff --git a/src/keystatic/components/worldburner/rpgsystems.ts b/src/keystatic/components/worldburner/rpgsystems.ts new file mode 100644 index 0000000..aeb3d38 --- /dev/null +++ b/src/keystatic/components/worldburner/rpgsystems.ts @@ -0,0 +1,44 @@ +import { fields } from '@keystatic/core'; +import { wrapper } from '@keystatic/core/content-components'; +import { dicesIcon } from '@keystar/ui/icon/icons/dicesIcon'; + +const rpgsystemComponent = { + RPGSystem: wrapper({ + label: 'RPG System', + icon: dicesIcon, + schema: { + system: fields.select({ + label: 'System', + options: [ + { + label: 'Non-specific', + value: 'none', + }, + { + label: 'Neoclassical Geek Revival', + value: 'ngr', + }, + { + label: 'Barbarians of Lemuria', + value: 'bol', + }, + { + label: 'The Burning Wheel', + value: 'bw', + }, + { + label: 'Questworlds', + value: 'qw', + }, + { + label: 'Shadow of the Daemon Lord', + value: 'sotdl', + }, + ], + defaultValue: 'none', + }), + }, + }), +}; + +export default rpgsystemComponent; diff --git a/src/keystatic/fields/general/article.ts b/src/keystatic/fields/general/article.ts index 4851f90..29f4b10 100644 --- a/src/keystatic/fields/general/article.ts +++ b/src/keystatic/fields/general/article.ts @@ -1,4 +1,5 @@ import { ComponentSchema, fields } from '@keystatic/core'; +import type { ContentComponent } from '@keystatic/core/content-components'; import { createContentField } from './content'; import { createPathField } from './path'; @@ -6,7 +7,8 @@ import { createMetaField } from './meta'; export const createArticleField = ( imageSubfolder: string, - defaultPath: string = '' + defaultPath: string = '', + additionalComponents?: Record ) => ({ title: fields.slug({ name: { label: 'Title' } }), summary: fields.text({ @@ -31,5 +33,5 @@ export const createArticleField = ( }), }), meta: createMetaField(), - content: createContentField(imageSubfolder), + content: createContentField(imageSubfolder, additionalComponents), }); diff --git a/src/lib/readers/worldburner/elements.ts b/src/lib/readers/worldburner/elements.ts new file mode 100644 index 0000000..4c79f05 --- /dev/null +++ b/src/lib/readers/worldburner/elements.ts @@ -0,0 +1,9 @@ +import { cache, reader } from '@/lib/readers/base'; + +export const getWBElements = cache(async () => + reader.collections['wb_elements'].all() +); + +export const getWBElementsBySlug = cache(async (slug: string) => + reader.collections['wb_elements'].read(slug) +); diff --git a/src/lib/types/worldburner.ts b/src/lib/types/worldburner.ts index dd83edb..f0a85a1 100644 --- a/src/lib/types/worldburner.ts +++ b/src/lib/types/worldburner.ts @@ -2,5 +2,8 @@ import { Entry } from '@keystatic/core/reader'; import keystaticConfig from '~/keystatic.config'; export type WBPost = Entry<(typeof keystaticConfig.collections)['wb_posts']>; +export type WBElement = Entry< + (typeof keystaticConfig.collections)['wb_elements'] +>; -export type WBContent = WBPost; +export type WBContent = WBPost | WBElement;