Skip to content

Commit

Permalink
WIP: Obsidian output with fixed links
Browse files Browse the repository at this point in the history
  • Loading branch information
dprothero committed Mar 10, 2024
1 parent 5737c6a commit 4533858
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 59 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ LGMRD is officially pronounced "Lymm Grid" and LGMMBRD "Lymm Bard".
- [HTML](https://github.com/crit-tech/LGMRD/blob/main/LGMRD.html) **Updated: <span id="html-last-updated">Thu, 08 Feb 2024 02:14:22 GMT</span>**
- [All-in-one Markdown](https://github.com/crit-tech/LGMRD/blob/main/LGMRD.md) **Updated: <span id="markdown-last-updated">Sun, 10 Mar 2024 23:21:26 GMT</span>**
- [Separate Markdown](https://github.com/crit-tech/LGMRD/tree/main/markdown_separate) files **Updated: <span id="markdown_separate-last-updated">Sun, 10 Mar 2024 23:21:26 GMT</span>**
- [Markdown for Obsidian](https://github.com/crit-tech/LGMRD/tree/main/markdown_obsidian) **Updated: <span id="markdown_obsidian-last-updated">Sun, 10 Mar 2024 23:21:53 GMT</span>**
- [Markdown for Obsidian](https://github.com/crit-tech/LGMRD/tree/main/markdown_obsidian) **Updated: <span id="markdown_obsidian-last-updated">Sun, 10 Mar 2024 23:31:19 GMT</span>**
- [JSON](https://github.com/crit-tech/LGMRD/blob/main/LGMRD.json) **Updated: <span id="json-last-updated">Thu, 08 Feb 2024 02:14:27 GMT</span>**
- [NPM](https://www.npmjs.com/package/@crit-tech/lgmrd) **Updated: <span id="json-last-updated">Thu, 08 Feb 2024 02:14:27 GMT</span>**
- [PDF](https://github.com/crit-tech/LGMRD/blob/main/LGMRD.pdf) **Updated: <span id="pdf-last-updated">Thu, 08 Feb 2024 02:14:24 GMT</span>**
Expand Down
14 changes: 7 additions & 7 deletions markdown_obsidian/Lazy Solo 5e.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ This scenario begins at 1st level, and your character gains a level after each s

## Building the Quest

First, build a quest-giving NPC by rolling for behavior and ancestry in the "[Core Adventure Generators](coreadventuregenerators.md)" section of this document. Choose a name and any other details from the "[NPC Generator](npcgenerator.md)" section.
First, build a quest-giving NPC by rolling for behavior and ancestry in the "[Core Adventure Generators](<Core Adventure Generators.md>)" section of this document. Choose a name and any other details from the "[NPC Generator](<NPC Generator.md>)" section.

Next, roll on the Quests in "[Core Adventure Generators](coreadventuregenerators.md)". Determine the location by rolling on the Condition, Description, Origin, and Location table.
Next, roll on the Quests in "[Core Adventure Generators](<Core Adventure Generators.md>)". Determine the location by rolling on the Condition, Description, Origin, and Location table.

Choose a map for the location from your favorite online source of maps or any other maps you have on hand. Choose a map that fits the location. Maps with fifteen or more rooms work best for adventures created with these guidelines.

You can then fill in additional quest details from the other tables in "[Core Adventure Generators](coreadventuregenerators.md)". Examples include required keys, monuments, villains, lieutenants, and other quest goals.
You can then fill in additional quest details from the other tables in "[Core Adventure Generators](<Core Adventure Generators.md>)". Examples include required keys, monuments, villains, lieutenants, and other quest goals.

Choose a starting room on the map that makes sense.

Expand All @@ -37,17 +37,17 @@ On your fourth roll of quest progress, you reach the final challenge of the ques

## Traps and Hazards

Roll for the trap type on the Traps and Hazards table in the "[Core Adventure Generators](coreadventuregenerators.md)" section. Then make a DC 12 Wisdom (Perception) check to locate the trap or hazard. Roll the most applicable ability check or saving throw vs DC 12 to avoid the trap or hazard. Failure on either roll results in 1d6 damage per character level of a type appropriate for the trap.
Roll for the trap type on the Traps and Hazards table in the "[Core Adventure Generators](<Core Adventure Generators.md>)" section. Then make a DC 12 Wisdom (Perception) check to locate the trap or hazard. Roll the most applicable ability check or saving throw vs DC 12 to avoid the trap or hazard. Failure on either roll results in 1d6 damage per character level of a type appropriate for the trap.

## Monster Encounter

Roll for a random monster on the Monster table in the "[Random Dungeon Monsters](randomdungeonmonsters.md)" section, selecting a dungeon level equal to your character level.
Roll for a random monster on the Monster table in the "[Random Dungeon Monsters](<Random Dungeon Monsters.md>)" section, selecting a dungeon level equal to your character level.

Assume the monster starts 25 feet away from you. Roll for initiative, with the monster using a static initiative score of 10 + the monster's Dexterity bonus. Adjudicate combat as you desire, assuming the monster acts as they would given their fiction.

## Monuments

Each monster encounter includes a helpful, neutral, or harmful monument. Generate monuments using the Locations, Monuments, and Items table, and the Condition, Description, and Origin table in the "[Core Adventure Generators](coreadventuregenerators.md)" section.
Each monster encounter includes a helpful, neutral, or harmful monument. Generate monuments using the Locations, Monuments, and Items table, and the Condition, Description, and Origin table in the "[Core Adventure Generators](<Core Adventure Generators.md>)" section.

Roll a d6 to determine the effect of the monument.

Expand Down Expand Up @@ -75,4 +75,4 @@ When you defeat a monster or enter a chamber with unguarded treasure, roll on th
7. Consumable item
8. Permanent item

For consumable and permanent items, roll on the tables in the "[Treasure Generator](treasuregenerator.md)" section or a random treasure table of your choice. You can replace any consumable magic item with a *potion of healing*. You can replace any permanent magic item with a *+1 weapon* of your choice.
For consumable and permanent items, roll on the tables in the "[Treasure Generator](<Treasure Generator.md>)" section or a random treasure table of your choice. You can replace any consumable magic item with a *potion of healing*. You can replace any permanent magic item with a *+1 weapon* of your choice.
2 changes: 1 addition & 1 deletion markdown_obsidian/Random Dungeon Monsters.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Once you have a dungeon level selected, roll a d20 and look across to see which

There are a few ways to choose the number of monsters in an encounter. First, think about what makes sense. Ghouls travel in packs, but a rug of smothering is probably found alone. You might roll dice to determine the number of monsters-for example, 3d6 ghouls. You might also choose to have one monster leading others-a pack of ghouls led by a ghast, for example.

Before the number of monsters is set, you can gauge whether your intended encounter is deadly or not by using the "[5e Quick Encounter Building](quickencounterbuilding.md)" section of this document.
Before the number of monsters is set, you can gauge whether your intended encounter is deadly or not by using the "[5e Quick Encounter Building](<5e Quick Encounter Building.md>)" section of this document.

| | Monster List | | | | | | | | | |
| ------------- | ------------ | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
Expand Down
2 changes: 1 addition & 1 deletion markdown_obsidian/The Eight Steps of Lazy RPG Prep.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ During our preparation, we'll outline those NPCs (nonplayer characters) most cri

What monsters are the characters most likely to face? What monsters make sense for a specific location and situation? We're using the term "monster" loosely here, so as to include enemy NPCs as well as truly monstrous foes. Whatever type of enemy you need, reading through books of monsters can give you the fuel to choose the right creatures for the right situation.

Additionally, understanding the loose relationship between monster challenge rating and character level can help you understand how a battle might go. Most of the time, you can just list a number of monsters and improvise encounters based on what's happening in the adventure. For boss battles, you might have to do more work. See [Lazy Combat Encounter Building](lazycombatencounterbuilding.md) for more information.
Additionally, understanding the loose relationship between monster challenge rating and character level can help you understand how a battle might go. Most of the time, you can just list a number of monsters and improvise encounters based on what's happening in the adventure. For boss battles, you might have to do more work. See [Lazy Combat Encounter Building](<Lazy Combat Encounter Building for 5e.md>) for more information.

## Select Magic Item Rewards

Expand Down
64 changes: 32 additions & 32 deletions markdown_obsidian/The Lazy GM's Resource Document.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,35 @@ You can find several versions of this document including EPUB, Markdown, and JSO

## Table of Contents

* [The Eight Steps of Lazy RPG Prep](eightsteps.md)
* [The Lazy RPG Preparation Process](prepprocess.md)
* [Example Strong Starts](strongstarts.md)
* [Creating Secrets and Clues](creatingsecrets.md)
* [Building an RPG Group](buildingagroup.md)
* [Session Zero Checklist](sessionzerochecklist.md)
* [Safety Tools](safetytools.md)
* [Connecting Characters](connectingcharacters.md)
* [Spiral Campaign Development](spiralcampaigns.md)
* [Quest Templates](questtemplates.md)
* [Tools for 5e Improvisation](toolsforimprov.md)
* [Quick Tricks for Lazier 5e Games](quicktricks.md)
* [5e Quick Encounter Building](quickencounterbuilding.md)
* [Wilderness Travel and Exploration](wildernesstravel.md)
* [Lazy Combat Encounter Building for 5e](lazycombatencounterbuilding.md)
* [Theater of the Mind Guidelines (Extended)](totmguidelines1.md)
* [Theater of the Mind Guidelines (Abbreviated)](totm2.md)
* [Zone-Based Combat](zonebasedcombat.md)
* [Monster Difficulty Dials](monsterdifficultydials.md)
* [Monster Templates](monstertemplates.md)
* [Undead Templates](undeadtemplates.md)
* [Running Hordes](runninghordes.md)
* [Stress Effects](stresseffects.md)
* [Core Adventure Generators](coreadventuregenerators.md)
* [NPC Generator](npcgenerator.md)
* [Treasure Generator](treasuregenerator.md)
* [Random Traps](randomtraps.md)
* [Random Monuments](randommonuments.md)
* [Random Items](randomitems.md)
* [Random Town Events](randomtownevents.md)
* [Random Dungeon Monsters](randomdungeonmonsters.md)
* [Lazy Solo 5e](lazysolo5e.md)
* [The Eight Steps of Lazy RPG Prep](<The Eight Steps of Lazy RPG Prep.md>)
* [The Lazy RPG Preparation Process](<The Lazy RPG Preparation Process.md>)
* [Example Strong Starts](<Example Strong Starts.md>)
* [Creating Secrets and Clues](<Creating Secrets and Clues.md>)
* [Building an RPG Group](<Building an RPG Group.md>)
* [Session Zero Checklist](<Session Zero Checklist.md>)
* [Safety Tools](<Safety Tools.md>)
* [Connecting Characters](<Connecting Characters.md>)
* [Spiral Campaign Development](<Spiral Campaign Development.md>)
* [Quest Templates](<Quest Templates.md>)
* [Tools for 5e Improvisation](<Tools for 5e Improvisation.md>)
* [Quick Tricks for Lazier 5e Games](<Quick Tricks for Lazier 5e Games.md>)
* [5e Quick Encounter Building](<5e Quick Encounter Building.md>)
* [Wilderness Travel and Exploration](<Wilderness Travel and Exploration.md>)
* [Lazy Combat Encounter Building for 5e](<Lazy Combat Encounter Building for 5e.md>)
* [Theater of the Mind Guidelines (Extended)](<Theater of the Mind Guidelines (Extended).md>)
* [Theater of the Mind Guidelines (Abbreviated)](<Theater of the Mind Guidelines (Abbreviated).md>)
* [Zone-Based Combat](<Zone-Based Combat.md>)
* [Monster Difficulty Dials](<Monster Difficulty Dials.md>)
* [Monster Templates](<Monster Templates.md>)
* [Undead Templates](<Undead Templates.md>)
* [Running Hordes](<Running Hordes.md>)
* [Stress Effects](<Stress Effects.md>)
* [Core Adventure Generators](<Core Adventure Generators.md>)
* [NPC Generator](<NPC Generator.md>)
* [Treasure Generator](<Treasure Generator.md>)
* [Random Traps](<Random Traps.md>)
* [Random Monuments](<Random Monuments.md>)
* [Random Items](<Random Items.md>)
* [Random Town Events](<Random Town Events.md>)
* [Random Dungeon Monsters](<Random Dungeon Monsters.md>)
* [Lazy Solo 5e](<Lazy Solo 5e.md>)
6 changes: 3 additions & 3 deletions markdown_obsidian/Tools for 5e Improvisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ The following tricks can also help make it easier for you to improvise during yo

Choose monsters that make sense for the location, the situation, and the story. Don't worry about whether an encounter is "balanced" — except to determine if it might be deadly. An encounter might be deadly if the total of all the monsters' challenge ratings is greater than one quarter of the total of all the characters' levels, or one half of the characters' levels if the characters are 5th level or higher.

If an encounter might be deadly, warn the players — and make sure the characters have a chance to escape. A more detailed version of the benchmark can be found in [Lazy Combat Encounter Building for 5e](lazycombatencounterbuilding.md) later in this document.
If an encounter might be deadly, warn the players — and make sure the characters have a chance to escape. A more detailed version of the benchmark can be found in [Lazy Combat Encounter Building for 5e](<Lazy Combat Encounter Building for 5e.md>) later in this document.

## Running Hordes

When running large numbers of creatures, instead of rolling independent attack rolls or saving throws, assume that one-quarter of those rolls succeed. Increase or decrease that number depending on the situation (for example, if many creatures in the horde have advantage or disadvantage). Additionally, instead of tracking individual hit points for a horde, you can tally the damage done to the entire horde when any of its creatures are hit. Every time the tally becomes equal to or higher than the hit points of any individual creature in the horde, remove a creature from the horde and reset the tally. Round monster hit points to the nearest 5 or 10 to make things easier. A more detailed version of these guidelines can be found in "[Running Hordes](runninghordes.md)" later in this document.
When running large numbers of creatures, instead of rolling independent attack rolls or saving throws, assume that one-quarter of those rolls succeed. Increase or decrease that number depending on the situation (for example, if many creatures in the horde have advantage or disadvantage). Additionally, instead of tracking individual hit points for a horde, you can tally the damage done to the entire horde when any of its creatures are hit. Every time the tally becomes equal to or higher than the hit points of any individual creature in the horde, remove a creature from the horde and reset the tally. Round monster hit points to the nearest 5 or 10 to make things easier. A more detailed version of these guidelines can be found in "[Running Hordes](<Running Hordes.md>)" later in this document.

## Names

You can never have enough names on hand while improvising your game. Here are a few you can use whenever an NPC, location, business, or other part of your game needs a name.

First Names: Shum, Agtos, Edbert, Josiane, Olaugh, Rosaline, Pearson, Boyle, Typhon, Satyros, Ronald, Brice, Wilford, Circe, Surbag, Kayla, Latona, Cecily, Shuzug, Moth, Dolly, Minerva, Prutha, Esmour, Tristan, Lake, Stewart, Hebub, Lanos, Ingram, Orvist, Daud, Metope

Last Names/Organization Names: Lionstone, Treeson, Oakhelm, Gentleheart, Whitesong, Starharp, Nightchaser, Shadowstinger, Catclaw, Faeriebound, Leafwing, Goldrock, Darkslicer, Gravewalker, Rainbright, Needleflinger, Goosechaser, Steelclaw, Scalerazor, Glasscutter, Ironhouse, Eboncloud. More names can be found in the "[NPC Generator](npcgenerator.md)" section later in this document.
Last Names/Organization Names: Lionstone, Treeson, Oakhelm, Gentleheart, Whitesong, Starharp, Nightchaser, Shadowstinger, Catclaw, Faeriebound, Leafwing, Goldrock, Darkslicer, Gravewalker, Rainbright, Needleflinger, Goosechaser, Steelclaw, Scalerazor, Glasscutter, Ironhouse, Eboncloud. More names can be found in the "[NPC Generator](<NPC Generator.md>)" section later in this document.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ You can find several versions of this document including EPUB, Markdown, and JSO

### Table of Contents

* [Building a Quick Monster](buildingaquickmonster.md)
* [General-Use Combat Stat Blocks](generalusestatblocks.md)
* [Lazy Tricks for Running Monsters](lazytricksforrunningmonsters.md)
* [Lightning Rods](lightingrods.md)
* [Monster Roles](monsterroles.md)
* [Bosses and Minions](bossesandminions.md)
* [The Combat Encounter Checklist](combatencounterchecklist.md)
* [Monster Combinations for a Hard Challenge](monstercombinationsforahardchallenge.md)
* [The Lazy Encounter Benchmark](lazyencounterbenchmark.md)
* [Monsters by Adventure Location](monstersbyadventurelocation.md)
* [Monsters and the Tiers of Play](monstersandthetiersofplay.md)
* [Building a Quick Monster](<Building a Quick Monster.md>)
* [General-Use Combat Stat Blocks](<General-Use Combat Stat Blocks.md>)
* [Lazy Tricks for Running Monsters](<Lazy Tricks for Running Monsters.md>)
* [Lightning Rods](<Lightning Rods.md>)
* [Monster Roles](<Monster Roles.md>)
* [Bosses and Minions](<Bosses and Minions.md>)
* [The Combat Encounter Checklist](<The Combat Encounter Checklist.md>)
* [Monster Combinations for a Hard Challenge](<Monster Combinations for a Hard Challenge.md>)
* [The Lazy Encounter Benchmark](<The Lazy Encounter Benchmark.md>)
* [Monsters by Adventure Location](<Monsters by Adventure Location.md>)
* [Monsters and the Tiers of Play](<Monsters and the Tiers of Play.md>)
4 changes: 2 additions & 2 deletions metadata/updates.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
"mb-markdown": "2024-03-10T23:21:27.774Z",
"mb-markdown_separate": "2024-03-10T23:21:27.918Z",
"mb-json": "2024-03-10T23:21:54.715Z",
"markdown_obsidian": "2024-03-10T23:21:53.352Z",
"mb-markdown_obsidian": "2024-03-10T23:21:54.425Z"
"markdown_obsidian": "2024-03-10T23:31:19.690Z",
"mb-markdown_obsidian": "2024-03-10T23:31:20.859Z"
}
42 changes: 41 additions & 1 deletion src/formats/markdownObsidian.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ export async function convertToMarkdownObsidian(
);

let newMarkdown = "";
const titleMap = new Map<string, string>();
const obsidianMarkdownFiles: string[] = [];

// First pass over files to get titles
for (const file of separateMarkdownFiles) {
const markdownFileContent = fs.readFileSync(file, "utf8");

let title = path.basename(file, ".md");
const oldTitle = path.basename(file, ".md");
let title = oldTitle;
const titleFinderPlugin = () => {
return (tree: Root) => {
visit(tree, "heading", (node) => {
Expand All @@ -55,14 +60,49 @@ export async function convertToMarkdownObsidian(
.use(remarkStringify)
.process(markdownFileContent);

titleMap.set(oldTitle, title);

const markdownFilePath = path.join(
MARKDOWN_OBSIDIAN_PATHS[docType],
`${title}.md`
);
fs.writeFileSync(markdownFilePath, data.toString());
obsidianMarkdownFiles.push(markdownFilePath);
}

// Second pass over files to update links
for (const file of obsidianMarkdownFiles) {
const markdownFileContent = fs.readFileSync(file, "utf8");

const linkUpdaterPlugin = () => {
return (tree: Root) => {
visit(tree, "link", (node) => {
const link = node.url as string;
if (link.startsWith("http")) {
return;
}

const oldTitle = path.basename(link, ".md");
const newTitle = titleMap.get(oldTitle);
if (newTitle) {
node.url = newTitle + ".md";
}
});
};
};

const data = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(linkUpdaterPlugin)
.use(remarkStringify)
.process(markdownFileContent);

fs.writeFileSync(file, data.toString());
newMarkdown += "\n" + data.toString();
}

process.stdout.write("Done\n");

return previousMarkdown !== newMarkdown;
}

0 comments on commit 4533858

Please sign in to comment.