made a lot of things better

This commit is contained in:
RussellChamp committed 2021-07-30 19:19:03 -04:00
1 parent d5a1654b22
commit 51948299fa
21 files changed
+1124 -472

No files matched your search

+3 -2
View File
@@ -1,4 +1,5 @@
Add NPC, and Dungeons roll tables
Put the rollers in their own accordion slots
Add Steadings tables
Change steadings and mounts to not show English names by default
Make everything look nicer
Maybe put roller button in a left nav bar and the results in the middle?
Add a 3d dice rolling visualizer
+40 -20
View File
@@ -1,12 +1,15 @@
<script lang="ts">
import { Accordion, AccordionHeader, AccordionItem } from "sveltestrap";
import CreatureRoller from "./rollers/CreatureRoller.svelte";
import KingdomRoller from "./rollers/KingdomRoller.svelte";
import RegionRoller from "./rollers/RegionRoller.svelte";
import PlaceRoller from "./rollers/PlaceRoller.svelte";
import DangerRoller from "./rollers/DangerRoller.svelte";
import DiscoveryRoller from "./rollers/DiscoveryRoller.svelte";
import FollowerRoller from "./rollers/FollowerRoller.svelte";
import DetailsRoller from "./rollers/DetailsRoller.svelte";
import DiscoveryRoller from "./rollers/DiscoveryRoller.svelte";
import DungeonRoller from "./rollers/DungeonRoller.svelte";
import FollowerRoller from "./rollers/FollowerRoller.svelte";
import KingdomRoller from "./rollers/KingdomRoller.svelte";
import NpcRoller from "./rollers/NpcRoller.svelte";
import PlaceRoller from "./rollers/PlaceRoller.svelte";
import RegionRoller from "./rollers/RegionRoller.svelte";
import Footer from "./componenets/Footer.svelte";
</script>
@@ -14,21 +17,38 @@
<h1>The Perilous Wilds</h1>
<h2>A web-app for rolling on tables and getting funny results</h2>
<hr />
<KingdomRoller />
<hr />
<RegionRoller />
<hr />
<PlaceRoller />
<hr />
<DangerRoller />
<hr />
<DiscoveryRoller />
<hr />
<CreatureRoller />
<hr />
<FollowerRoller />
<hr />
<DetailsRoller />
<Accordion>
<AccordionItem active header="Creatures">
<CreatureRoller />
</AccordionItem>
<AccordionItem header="Dangers">
<DangerRoller />
</AccordionItem>
<AccordionItem header="Details">
<DetailsRoller />
</AccordionItem>
<AccordionItem header="Discoveries">
<DiscoveryRoller />
</AccordionItem>
<AccordionItem header="Dungeons">
<DungeonRoller />
</AccordionItem>
<AccordionItem header="Followers">
<FollowerRoller />
</AccordionItem>
<AccordionItem header="Kingdoms">
<KingdomRoller />
</AccordionItem>
<AccordionItem header="NPCs">
<NpcRoller />
</AccordionItem>
<AccordionItem header="Places">
<PlaceRoller />
</AccordionItem>
<AccordionItem header="Regions">
<RegionRoller />
</AccordionItem>
</Accordion>
<Footer />
</main>
+51 -10
View File
@@ -1,26 +1,67 @@
<script lang="ts">
import * as _ from "lodash";
import { capitalize } from "lodash";
import { format as formatDate } from "date-fns";
import { Button, Card, CardHeader, CardTitle, CardBody, CardSubtitle, CardText, CardFooter, Container, Row } from "sveltestrap";
import { Creature, rollCreature } from "./creatures";
export let rollResults: { value: Creature; timestamp: string }[] = [];
export let rollResults: { creature: Creature; timestamp: string }[] = [];
function roll(event: Event) {
let value = rollCreature();
console.log("creature", value);
rollResults = [{ value, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
let creature = rollCreature();
rollResults = [{ creature, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
</script>
<main>
<h2>Random Creatures</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Creature</button>
<Button color="primary" on:click={roll}>Roll Creature</Button>
{#each rollResults as result}
<h4>{result.timestamp}: {result.value.description}</h4>
{#each rollResults as { creature, timestamp }}
<Card class="mb-3">
<CardHeader>
<CardTitle>
{capitalize(creature.type)}
{#if creature.subtype !== ""}({creature.subtype}) {/if}: {capitalize(creature.description)}
</CardTitle>
</CardHeader>
<CardBody>
<CardText>
<Container>
<Row>
<strong>Details:</strong>
<ul>
{#if creature.ability}<li>Ability: {creature.ability.description}</li>{/if}
{#if creature.activity}<li>Activity: {creature.activity.description}</li>{/if}
{#if creature.adjective}<li>Adjective: {creature.adjective.description}</li>{/if}
{#if creature.age}<li>Age: {creature.age.description}</li>{/if}
{#if creature.alignment}<li>Alignment: {creature.alignment.description}</li>{/if}
{#if creature.aspect}<li>Aspect: {creature.aspect.description}</li>{/if}
{#if creature.condition}<li>Condition: {creature.condition.description}</li>{/if}
{#if creature.disposition}<li>Disposition: {creature.disposition.description}</li>{/if}
{#if creature.feature}<li>Feature: {creature.feature.description}</li>{/if}
{#if creature.npc}<li>NPC Name: {creature.npc.name}</li>{/if}
{#if creature.numAppearing}<li>Number Appearing: {creature.numAppearing.description}</li>{/if}
{#if creature.size}<li>Size: {creature.size.description}</li>{/if}
{#if creature.tags.length > 0}<li>
Tags: {#each creature.tags as tag} {tag.description}, {/each}
</li>{/if}
</ul>
</Row>
</Container>
</CardText>
</CardBody>
<CardFooter>{timestamp}</CardFooter>
</Card>
{/each}
</main>
<style>
main {
text-align: left;
}
.card-subtitle {
padding-bottom: 1em;
}
ul {
margin-left: 2em;
}
</style>
+6 -6
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { Icon } from "sveltestrap";
import { Button, Icon } from "sveltestrap";
import { Danger, rollDanger } from "./dangers";
export let rollResults: { danger: Danger; icon: string; timestamp: string }[] = [];
@@ -21,12 +20,13 @@
</script>
<main>
<h2>Random Danger</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Danger</button>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Danger</Button>
</span>
{#each rollResults as result}
{#each rollResults as { danger, icon, timestamp }}
<h4>
{result.timestamp}: {result.danger.category} ({result.danger.subcategory}) <Icon name={result.icon} />: {result.danger.description}
{timestamp}: {danger.category} ({danger.subcategory}) <Icon name={icon} />: {danger.description}
</h4>
{/each}
</main>
+22 -23
View File
@@ -1,5 +1,4 @@
<script lang="ts">
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { Button, Icon } from "sveltestrap";
import {
@@ -26,72 +25,72 @@
let rollResults: { type: string; value: string; timestamp: string }[] = [];
function addResult(item: { type: string; description: string; roll: number }) {
function roll(item: { type: string; description: string; roll: number }) {
rollResults = [{ type: item.type, value: item.description, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
</script>
<main>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollAbility())}>Roll Ability</Button>
<Button color="primary" on:click={() => roll(rollAbility())}>Roll Ability</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollActivity())}>Roll Activity</Button>
<Button color="primary" on:click={() => roll(rollActivity())}>Roll Activity</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollAdjective())}>Roll Adjective</Button>
<Button color="primary" on:click={() => roll(rollAdjective())}>Roll Adjective</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollAge())}>Roll Age</Button>
<Button color="primary" on:click={() => roll(rollAge())}>Roll Age</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollAlignment())}>Roll Alignment</Button>
<Button color="primary" on:click={() => roll(rollAlignment())}>Roll Alignment</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollAspect())}>Roll Aspect</Button>
<Button color="primary" on:click={() => roll(rollAspect())}>Roll Aspect</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollCondition())}>Roll Condition</Button>
<Button color="primary" on:click={() => roll(rollCondition())}>Roll Condition</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollDisposition())}>Roll Disposition</Button>
<Button color="primary" on:click={() => roll(rollDisposition())}>Roll Disposition</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollElement())}>Roll Element</Button>
<Button color="primary" on:click={() => roll(rollElement())}>Roll Element</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollFeature())}>Roll Feature</Button>
<Button color="primary" on:click={() => roll(rollFeature())}>Roll Feature</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollMagicType())}>Roll MagicType</Button>
<Button color="primary" on:click={() => roll(rollMagicType())}>Roll MagicType</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollNumAppearing())}>Roll NumAppearing</Button>
<Button color="primary" on:click={() => roll(rollNumAppearing())}>Roll NumAppearing</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollOddity())}>Roll Oddity</Button>
<Button color="primary" on:click={() => roll(rollOddity())}>Roll Oddity</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollOrientation())}>Roll Orientation</Button>
<Button color="primary" on:click={() => roll(rollOrientation())}>Roll Orientation</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollRuination())}>Roll Ruination</Button>
<Button color="primary" on:click={() => roll(rollRuination())}>Roll Ruination</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollSize())}>Roll Size</Button>
<Button color="primary" on:click={() => roll(rollSize())}>Roll Size</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollTag())}>Roll Tag</Button>
<Button color="primary" on:click={() => roll(rollTag())}>Roll Tag</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollTerrain())}>Roll Terrain</Button>
<Button color="primary" on:click={() => roll(rollTerrain())}>Roll Terrain</Button>
</span>
<span class="btn">
<Button color="primary" on:click={() => addResult(rollVisibility())}>Roll Visibility</Button>
<Button color="primary" on:click={() => roll(rollVisibility())}>Roll Visibility</Button>
</span>
{#each rollResults as result}
<h4>{result.timestamp}: ({result.type}): {result.value}</h4>
{#each rollResults as { type, value, timestamp }}
<h4>{timestamp}: ({type}): {value}</h4>
{/each}
</main>
+66 -9
View File
@@ -1,25 +1,82 @@
<script lang="ts">
import * as _ from "lodash";
import { Button, Card, CardHeader, CardTitle, CardBody, CardSubtitle, CardText, CardFooter, Container, Row, Icon } from "sveltestrap";
import { capitalize } from "lodash";
import { format as formatDate } from "date-fns";
import { Discovery, rollDiscovery } from "./discoveries";
export let rollResults: { value: Discovery; timestamp: string }[] = [];
export let rollResults: { discovery: Discovery; timestamp: string }[] = [];
function roll(event: Event) {
rollResults = [{ value: rollDiscovery(), timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
let discovery = rollDiscovery();
rollResults = [{ discovery, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
function getIcon(t: string, c: string): string {
return "";
}
</script>
<main>
<h2>Random Discovery</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Discovery</button>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Discovery</Button>
</span>
{#each rollResults as result}
<h4>
{result.timestamp}: {result.value.category} ({result.value.subcategory}): {result.value.description}
</h4>
{#each rollResults as { discovery, timestamp }}
<Card class="mb-3">
<CardHeader>
<CardTitle
>{capitalize(discovery.category)}{#if discovery.subcategory !== ""}&nbsp;({discovery.subcategory}){/if}: {capitalize(
discovery.description
)}</CardTitle
>
</CardHeader>
<CardBody>
<CardText>
<Container>
{#if discovery.category == "creature"}
<Row>
<p>Are you feeling lucky, punk?</p>
</Row>
{:else if discovery.category == "steading"}
<Row>
<p>TODO: format steadings</p>
</Row>
{:else}
<Row>
<strong>Details:</strong>
<ul>
{#if discovery.age}<li>Age: {discovery.age.description}</li>{/if}
{#if discovery.alignment}<li>Alignment: {discovery.alignment.description}</li>{/if}
{#if discovery.aspect}<li>Aspect: {discovery.aspect.description}</li>{/if}
{#if discovery.creature}<li>Produced/Inhabited By: {discovery.creature.description}</li>{/if}
{#if discovery.element}<li>Element: {discovery.element.description}</li>{/if}
{#if discovery.magicType}<li>MagicType: {discovery.magicType.description}</li>{/if}
{#if discovery.ruination}<li>Ruination: {discovery.ruination.description}</li>{/if}
{#if discovery.size}<li>Size: {discovery.size.description}</li>{/if}
{#if discovery.steading}<li>Steading: {discovery.steading.description}</li>{/if}
{#if discovery.visibility}<li>Visibility: {discovery.visibility.description}</li>{/if}
</ul>
</Row>
{/if}
</Container>
</CardText>
</CardBody>
<CardFooter>{timestamp}</CardFooter>
</Card>
{/each}
</main>
<style>
main {
text-align: left;
}
h2 {
text-align: center;
}
ul {
margin-left: 2em;
}
.btn {
text-align: center;
}
</style>
+103
View File
@@ -0,0 +1,103 @@
<script lang="ts">
import { Button, Card, CardHeader, CardTitle, CardBody, CardSubtitle, CardText, CardFooter, Container, Row, Icon } from "sveltestrap";
import { format as formatDate } from "date-fns";
import { Dungeon, rollDungeon } from "./dungeons";
import { capitalize } from "lodash";
export let rollResults: { dungeon: Dungeon; timestamp: string }[] = [];
function roll(event: Event) {
let dungeon = rollDungeon();
rollResults = [{ dungeon, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
function getIcon(t: string, c: string): string {
return t == "discovery"
? "eye-fill"
: t == "danger"
? "exclamation-triangle-fill"
: c == "mundane"
? "exclamation-triangle"
: c == "unusual"
? "exclamation-square"
: c == "extraordinary"
? "exclamation-octagon"
: "";
}
</script>
<main>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Dungeon</Button>
</span>
{#each rollResults as { dungeon, timestamp }}
<Card class="mb-3">
<CardHeader>
<CardTitle
>{capitalize(dungeon.size.description)} {dungeon.function.description} built by (a/an) {dungeon.builder.description}</CardTitle
>
</CardHeader>
<CardBody>
<CardSubtitle>
Fell to ruination from <em>{dungeon.ruination.description}</em>.
</CardSubtitle>
<CardText>
<Container>
<Row>
<strong>Common Themes ({dungeon.size.commonThemes}):</strong>
<ul>
{#each dungeon.commonThemes as theme}
<li><Icon name={getIcon("theme", theme.category)} /> {theme.category}: {theme.description}</li>
{/each}
</ul>
</Row>
<Row>
<strong>Unique Themes ({dungeon.size.uniqueThemes}):</strong>
<ul>
{#each dungeon.uniqueThemes as theme}
<li><Icon name={getIcon("theme", theme.category)} /> {theme.category}: {theme.description}</li>
{/each}
</ul>
</Row>
<Row>
<strong>Common Areas ({dungeon.size.commonAreas}):</strong>
<ul>
{#each dungeon.commonAreas as area}
<li><Icon name={getIcon(area.type, area.category)} /> {area.type}/{area.category}: {area.description}</li>
{/each}
</ul>
</Row>
<Row>
<strong>Unique Areas ({dungeon.size.uniqueAreas}):</strong>
<ul>
{#each dungeon.uniqueAreas as area}
<li><Icon name={getIcon(area.type, area.category)} /> {area.type}/{area.category}: {area.description}</li>
{/each}
</ul>
</Row>
</Container>
</CardText>
</CardBody>
<CardFooter>{timestamp}</CardFooter>
</Card>
{/each}
</main>
<style>
main {
text-align: left;
}
h2 {
text-align: center;
}
ul {
margin-left: 2em;
}
.btn {
text-align: center;
}
.card-subtitle {
padding-bottom: 1em;
}
</style>
+18 -20
View File
@@ -1,46 +1,44 @@
<script lang="ts">
import { Button, Card, CardHeader, CardTitle, CardBody, CardSubtitle, CardText, CardFooter, Container, Col, Row } from "sveltestrap";
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { rollFollower, Follower } from "./followers";
export let rollResults: { value: Follower; timestamp: string }[] = [];
export let rollResults: { follower: Follower; timestamp: string }[] = [];
function roll(event: Event) {
let follower = rollFollower();
rollResults = [{ value: follower, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
rollResults = [{ follower, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
</script>
<main>
<h2>Random Follower</h2>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Followers</Button>
</span>
{#each rollResults as result}
{#each rollResults as { follower, timestamp }}
<Card class="mb-3">
<CardHeader>
<CardTitle>{result.value.name} ({result.value.gender})</CardTitle>
<CardTitle>{follower.name} ({follower.gender})</CardTitle>
</CardHeader>
<CardBody>
<CardSubtitle>
From <em>{result.value.homeSteading.homesteadName}</em> in <em>{result.value.homeSteading.kingdomName}</em>
From <em>{follower.homeSteading.homesteadName}</em> in <em>{follower.homeSteading.kingdomName}</em>
</CardSubtitle>
<CardText>
<Container>
<Row>
<strong>Quality:</strong>
<p>
They are <em>{result.value.quality.description}</em> (Quality: {result.value.quality.value}). They
<em>{result.value.background.description}</em>.
They are <em>{follower.quality.description}</em> (Quality: {follower.quality.value}). They
<em>{follower.background.description}</em>.
</p>
</Row>
<Row>
<strong>Tags:</strong>
<ul>
{#each result.value.tags as tag}
{#each follower.tags as tag}
<li>{tag.name}: {tag.description}</li>
{/each}
</ul>
@@ -48,7 +46,7 @@
<Row>
<strong>Moves:</strong>
<ul>
{#each result.value.moves as move}
{#each follower.moves as move}
<li>{move.description}</li>
{/each}
</ul>
@@ -56,44 +54,44 @@
<Row>
<strong>Loyalty:</strong>
<p>
They have a loyalty score of {result.value.loyalty.value}{#if !!result.value.loyalty.description}
and <em>{result.value.loyalty.description}</em>{/if}.
They have a loyalty score of {follower.loyalty.value}{#if !!follower.loyalty.description}
and <em>{follower.loyalty.description}</em>{/if}.
</p>
</Row>
<Row>
<strong>Instinct:</strong>
<p>
When left to their own devices, they will <em>{result.value.instinct.description}</em>.
When left to their own devices, they will <em>{follower.instinct.description}</em>.
</p>
</Row>
<Row>
<strong>Cost:</strong>
<p>
They are motivated by <em>{result.value.cost.name}</em>. They will want <em>{result.value.cost.description}</em>
They are motivated by <em>{follower.cost.name}</em>. They will want <em>{follower.cost.description}</em>
</p>
</Row>
<Row>
<strong>Hit Points:</strong>
<p>
They are <em>{result.value.hitPoints.description}</em> and have {result.value.hitPoints.value} HP.
They are <em>{follower.hitPoints.description}</em> and have {follower.hitPoints.value} HP.
</p>
</Row>
<Row>
<strong>Armor & Shield:</strong>
<p>
They are equipped with <em>{result.value.armor.description}</em> ({result.value.armor.value} armor).
They are equipped with <em>{follower.armor.description}</em> ({follower.armor.value} armor).
</p>
</Row>
<Row>
<strong>Damage:</strong>
<p>
In a fight, they <em>{result.value.damage.description}</em> dealing <em>d{result.value.damage.value}</em> damage.
In a fight, they <em>{follower.damage.description}</em> dealing <em>d{follower.damage.value}</em> damage.
</p>
</Row>
<Row>
<strong>Load & Gear:</strong>
<ul>
{#each result.value.loadAndGear.gear as gear}
{#each follower.loadAndGear.gear as gear}
<li>{gear}</li>
{/each}
</ul>
@@ -101,7 +99,7 @@
</Container>
</CardText>
</CardBody>
<CardFooter>{result.timestamp}</CardFooter>
<CardFooter>{timestamp}</CardFooter>
</Card>
{/each}
</main>
+8 -12
View File
@@ -1,25 +1,21 @@
<script lang="ts">
import * as _ from "lodash";
import { capitalize } from "lodash";
import { format as formatDate } from "date-fns";
import { Button, Icon } from "sveltestrap";
import { kingdoms } from "./kingdoms";
import { kingdoms, rollCategory } from "./kingdoms";
let rollResults: { value: string; icon: string; timestamp: string }[] = [];
let kingdom = "arpad";
let categories: string[];
function addResult(value: string, category: string) {
function roll(category: string) {
let value = rollCategory(kingdom, category);
let icon = { male: "person-fill", female: "person", steading: "house-door-fill", mount: "bicycle" }[category] || "";
rollResults = [{ value, icon, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
function rollName(category: string) {
let values = kingdoms[kingdom][category];
addResult(_.sample(values), category);
}
$: categories = kingdoms[kingdom].categories;
</script>
@@ -35,12 +31,12 @@
{#each categories as category}
<span class="btn">
<Button color="primary" on:click={() => rollName(category)}>Roll {_.capitalize(category)}</Button>
<Button color="primary" on:click={() => roll(category)}>Roll {capitalize(category)}</Button>
</span>
{/each}
{#each rollResults as result}
<h4>{result.timestamp}: <Icon name={result.icon} /> {result.value}</h4>
{#each rollResults as { value, icon, timestamp }}
<h4>{timestamp}: <Icon name={icon} /> {value}</h4>
{/each}
</main>
+85
View File
@@ -0,0 +1,85 @@
<script lang="ts">
import {
Button,
Card,
CardHeader,
CardTitle,
CardBody,
CardSubtitle,
CardText,
CardFooter,
Container,
Col,
Row,
Icon,
} from "sveltestrap";
import { capitalize } from "lodash";
import { format as formatDate } from "date-fns";
import { Npc, rollNpc } from "./npcs";
export let rollResults: { npc: Npc; timestamp: string }[] = [];
function roll(event: Event) {
let npc = rollNpc();
rollResults = [{ npc, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
function getIcon(t: string, c: string): string {
return "";
}
</script>
<main>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Npc</Button>
</span>
{#each rollResults as { npc, timestamp }}
<Card class="mb-3">
<CardHeader>
<CardTitle>{npc.getDescription()}</CardTitle>
</CardHeader>
<CardBody>
<CardSubtitle>
From <em>{npc.hometown}</em>.
</CardSubtitle>
<CardText>
<Container>
<Row>
<strong>Background Context:</strong>
<ul>
<li>
They are a(n) <em>{npc.context.alignment.description}</em> <em>{npc.context.description}</em> with
<em>{npc.trait.category} ({npc.context.trait.description})</em>
encountered in a(n) <em>{npc.context.category}</em> setting while they are <em>{npc.context.activity.description}</em>
</li>
</ul>
</Row>
<Row>
<strong>Follower Stats:</strong>
<ul>
<li>{npc.follower.name}</li>
</ul>
</Row>
</Container>
</CardText>
</CardBody>
<CardFooter>{timestamp}</CardFooter>
</Card>
{/each}
</main>
<style>
main {
text-align: left;
}
ul {
margin-left: 2em;
}
.btn {
text-align: center;
}
.card-subtitle {
padding-bottom: 1em;
}
</style>
+10 -15
View File
@@ -1,28 +1,23 @@
<script lang="ts">
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { places } from "./places";
import { bodgeString } from "./utils";
import { Button } from "sveltestrap";
import { rollPlace } from "./places";
export let rollResults: { value: string; timestamp: string }[] = [];
function addResult(result: string) {
rollResults = [{ value: result, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
export let rollResults: { place: string; timestamp: string }[] = [];
function roll(event: Event) {
let template = _.sample(places.templates);
addResult(bodgeString(template, places));
let place = rollPlace();
rollResults = [{ place, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
</script>
<main>
<h2>Random Places</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Region</button>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Place</Button>
</span>
{#each rollResults as result}
<h4>{result.timestamp}: {result.value}</h4>
{#each rollResults as { place, timestamp }}
<h4>{timestamp}: {place}</h4>
{/each}
</main>
+10 -15
View File
@@ -1,28 +1,23 @@
<script lang="ts">
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { regions } from "./regions";
import { bodgeString } from "./utils";
import { Button } from "sveltestrap";
import { rollRegion } from "./regions";
export let rollResults: { value: string; timestamp: string }[] = [];
function addResult(result: string) {
rollResults = [{ value: result, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
export let rollResults: { region: string; timestamp: string }[] = [];
function roll(event: Event) {
let template = _.sample(regions.templates);
addResult(bodgeString(template, regions));
let region = rollRegion();
rollResults = [{ region, timestamp: formatDate(new Date(), "P kk:mm:ss") }, ...rollResults];
}
</script>
<main>
<h2>Random Regions</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Region</button>
<span class="btn">
<Button color="primary" on:click={roll}>Roll Region</Button>
</span>
{#each rollResults as result}
<h4>{result.timestamp}: {result.value}</h4>
{#each rollResults as { region, timestamp }}
<h4>{timestamp}: {region}</h4>
{/each}
</main>
+42 -77
View File
@@ -1,49 +1,59 @@
import { random, sample } from 'lodash';
import { rollUnnaturalEntity } from './dangers';
import { Activity, Disposition, NumAppearing, rollAbility, rollActivity, rollAlignment, rollDisposition, rollFeature, rollNumAppearing, rollOddity, rollSize, Size } from './details';
import { rollNpc } from './npcs';
import { Ability, Activity, Adjective, Age, Alignment, Aspect, Condition, Disposition, Feature, NumAppearing, rollAbility, rollActivity, rollAlignment, rollDisposition, rollFeature, rollNumAppearing, rollOddity, rollSize, Size, Tag } from './details';
import { Npc, rollNpc } from './npcs';
export class Creature {
constructor(public roll: number = 0, public subroll: number = 0, public specifcroll: number = 0, public type: string = "", public subtype: string = "", public description: string = "") { };
details: string[] = [];
ability?: Ability;
activity?: Activity;
adjective?: Adjective;
age?: Age;
alignment?: Alignment;
aspect?: Aspect;
condition?: Condition;
disposition?: Disposition;
feature?: Feature;
npc?: Npc;
numAppearing?: NumAppearing;
size?: Size;
tags: Tag[] = [];
}
export function rollCreature(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)) {
let creature = new Creature(roll, subroll, specificroll);
if (roll <= 4) {
return rollBeast(roll, subroll, specificroll);
} else if (roll <= 6) {
creature.type = "human";
let npc = rollNpc();
creature.description = `${npc.name} the ${npc.gender} ${npc.occupation.subcategory} from ${npc.hometown}`;
creature.details = [
rollActivity().description,
rollAlignment().description,
rollDisposition().description,
rollNumAppearing().description,
npc.name /* TODO: figure out how to do this */
];
let human = new Creature(roll, subroll, specificroll);
human.type = "human";
human.npc = rollNpc();
human.description = human.npc.getDescription(true);
human.activity = rollActivity();
human.alignment = rollAlignment();
human.disposition = rollDisposition();
human.numAppearing = rollNumAppearing();
return human;
} else if (roll <= 8) {
return rollHumanoid(roll, subroll, specificroll);
} else if (roll <= 12) {
return rollMonster();
}
return creature;
return new Creature(roll, subroll, specificroll);
}
export function rollBeast(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Creature {
let creature = new Creature(roll, subroll, specificroll);
creature.type = "beast";
creature.details = [
rollActivity().description,
rollDisposition().description,
rollNumAppearing().description,
rollSize().description
];
creature.activity = rollActivity();
creature.disposition = rollDisposition();
creature.numAppearing = rollNumAppearing();
creature.size = rollSize();
if (subroll <= 7) {
creature.subtype = "earthbound";
@@ -113,13 +123,11 @@ export function rollWaterGoingBeast(roll: number = random(1, 12)): string {
export function rollHumanoid(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Creature {
let creature = new Creature(roll, subroll, specificroll, "humanoid");
creature.details = [
rollActivity().description,
rollAlignment().description,
rollDisposition().description,
rollNumAppearing().description,
rollNpc().name /* figure out how to do this */
];
creature.activity = rollActivity();
creature.alignment = rollAlignment();
creature.disposition = rollDisposition();
creature.numAppearing = rollNumAppearing();
creature.npc = rollNpc(); /* figure out how to do this */
if (subroll <= 7) {
creature.subtype = "common";
@@ -190,13 +198,12 @@ export function rollHybridHumanoid(roll: number = random(1, 12)): string {
export function rollMonster(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Creature {
let creature = new Creature(roll, subroll, specificroll, "monster");
creature.details = [
rollActivity().description,
rollAlignment().description,
rollDisposition().description,
rollNumAppearing().description,
rollSize().description,
];
creature.activity = rollActivity();
creature.alignment = rollAlignment();
creature.disposition = rollDisposition();
creature.numAppearing = rollNumAppearing();
creature.size = rollSize();
if (subroll <= 7) {
creature.subtype = "unusual";
@@ -258,45 +265,3 @@ export function rollLegendaryMonster(roll: number = random(1, 12)): string {
return "";
}
// MONSTER. Give every monster life!
// I
// 'V
// 48
// UNUSUAL ......
// 1-3
// 4-6
// 7-9
// 10-12 Truai. ENTI
// 9-10
//
// ASK THE FATES
// LREATURE
// Roll 1d12 for creature type if it hasn't already been established, then 1d12 on indicat-
// ed subtable for specific prompt. Roll additional details as indicated, and/or at whim.
// 1d12 QmATuRE Aim'rtoi.. DETAILS (see pages 50-53)
// 1-4 BEAST ACTIVITY, DISPOSITION, No. APPEARING, SIZE
// 5-6 Human ACTIVITY, ALIGNMENT, DISPOSITION, No. APPEARING, NPC tables
// 7-8 HUMANOID ACTIVITY, ALIGNMENT, DISPOSITION, No. APPEARING, NPC tables 942 MONSTER ACTIVITY, ALIGNMENT, DISPOSITION, No. APPEARING, SIZE
// Optional: ABILITY, ADJECTIVE, AGE, ASPECT, CONDITION, FEATURE, TAGS
// HUMANOID . If you roll a classic fantasy species, adapt it to your setting. 1-7 COMMON 8-10 UNCOMMON 11-12 HYBRID
// MONSTER. Give every monster life!
// 1-7 UNUSUAL
// 1-3 plant/fungus
// 4-5 Undead Human
// 6 Undead HUMANOID
// 7-8 BEAST + BEAST
// 9-10 BEAST + ABILITY
// 1142 BEAST + FEATURE
// 8T10.. RARE 11-12
// 1-3 slime/ooze (4morphous) 1-3
// 4-6 creation (Construct) 4-6
// 7-9 BEAST + ODDITY 7-9
// 10-12 UNNxrua1. ENTITY 10
// II
// 12
// LEGENDARY
+75 -75
View File
@@ -8,17 +8,17 @@ export class Ability {
export function rollAbility(roll: number = random(1, 12)): Ability {
let ability = new Ability(roll);
ability.description = [
"bless/curse",
"entangle/trap/snare",
"poison/disease",
"paralyze/petrify",
"mimic/camouflage",
"seduce/hypnotize",
"dissolve/disintegrate",
sample(["bless", "curse"]),
sample(["entangle", "trap", "snare"]),
sample(["poison", "disease"]),
sample(["paralyze", "petrify"]),
sample(["mimic", "camouflage"]),
sample(["seduce", "hypnotize"]),
sample(["dissolve", "disintegrate"]),
rollMagicType().description,
"drain life/magic",
sample(["drain life", "drain magic"]),
`immunity to ${rollElement().description}`,
"read/control minds",
sample(["read minds", "control minds"]),
"[ABILITY] and [ABILITY]"
][roll - 1];
ability.description = replace(ability.description, /\[ABILITY\]/g, () => rollAbility(random(1, 11)).description);
@@ -34,16 +34,16 @@ export class Activity {
export function rollActivity(roll: number = random(1, 12)): Activity {
let activity = new Activity(roll);
activity.description = [
"laying trap/ambush",
"fighting/at war",
"prowling/on patrol",
"hunting/foraging",
"eating/resting",
"crafting/praying",
"traveling/relocating",
"exploring/lost",
sample(["laying trap", "ambush"]),
sample(["fighting", "at war"]),
sample(["prowling", "on patrol"]),
sample(["hunting", "foraging"]),
sample(["eating", "resting"]),
sample(["crafting", "praying"]),
sample(["traveling", "relocating"]),
sample(["exploring", "lost"]),
"returning home",
"building/excavating",
sample(["building", "excavating"]),
"sleeping",
"dying",
][roll - 1];
@@ -59,17 +59,17 @@ export class Adjective {
export function rollAdjective(roll: number = random(1, 12)): Adjective {
let adjective = new Adjective(roll);
adjective.description = [
"slick/slimy",
"rough/hard/sharp",
"smooth/soft/dull",
"corroded/rusty",
"rotten/decaying",
"broken/brittle",
"stinking/smelly",
"weak/thin/drained",
"strong/fat/full",
"pale/poor/shallow",
"dark/rich/deep",
sample(["slick", "slimy"]),
sample(["rough", "hard", "sharp"]),
sample(["smooth", "soft", "dull"]),
sample(["corroded", "rusty"]),
sample(["rotten", "decaying"]),
sample(["broken", "brittle"]),
sample(["stinking", "smelly"]),
sample(["weak", "thin", "drained"]),
sample(["strong", "fat", "full"]),
sample(["pale", "poor", "shallow"]),
sample(["dark", "rich", "deep"]),
"colorful",
][roll - 1];
@@ -132,16 +132,16 @@ export class Aspect {
export function rollAspect(roll: number = random(1, 12)): Aspect {
let aspect = new Aspect(roll);
aspect.description = [
"power/strength",
"trickery/dexterity",
"time/constitution",
"knowledge/intelligence",
"nature/wisdom",
"culture/charisma",
"war/lies/discord",
"peace/truth/balance",
"hate/envy",
"love/admiration",
sample(["power", "strength"]),
sample(["trickery", "dexterity"]),
sample(["time", "constitution"]),
sample(["knowledge", "intelligence"]),
sample(["nature", "wisdom"]),
sample(["culture", "charisma"]),
sample(["war", "lies", "discord"]),
sample(["peace", "truth", "balance"]),
sample(["hate", "envy"]),
sample(["love", "admiration"]),
rollElement().description,
"[ASPECT] and [ASPECT]"
][roll - 1];
@@ -159,19 +159,19 @@ export function rollCondition(roll: number = random(1, 12)): Condition {
let condition = new Condition(roll);
if (roll <= 1) {
condition.description = "being built/born";
condition.description = sample(["being built", "being born"]);
} else if (roll <= 4) {
condition.description = "intact/healthy/stable";
condition.description = sample(["intact", "healthy", "stable"]);
} else if (roll <= 7) {
condition.description = "occupied/active/alert";
condition.description = sample(["occupied", "active", "alert"]);
} else if (roll <= 9) {
condition.description = "worn/tired/weak";
condition.description = sample(["worn", "tired", "weak"]);
} else if (roll <= 10) {
condition.description = "vacant/lost";
condition.description = sample(["vacant", "lost"]);
} else if (roll <= 11) {
condition.description = "ruined/defiled/dying";
condition.description = sample(["ruined", "defiled", "dying"]);
} else if (roll <= 12) {
condition.description = "disappeared/dead";
condition.description = sample(["disappeared", "dead"]);
}
return condition;
@@ -188,15 +188,15 @@ export function rollDisposition(roll: number = random(1, 12)): Disposition {
if (roll <= 1) {
disposition.description = "attacking";
} else if (roll <= 4) {
disposition.description = "hostile/aggressive";
disposition.description = sample(["hostile", "aggressive"]);
} else if (roll <= 6) {
disposition.description = "cautious/doubtful";
disposition.description = sample(["cautious", "doubtful"]);
} else if (roll <= 7) {
disposition.description = "fearful/fleeing";
disposition.description = sample(["fearful", "fleeing"]);
} else if (roll <= 10) {
disposition.description = "neutral";
} else if (roll <= 11) {
disposition.description = "curious/hopeful";
disposition.description = sample(["curious", "hopeful"]);
} else if (roll <= 12) {
disposition.description = "friendly";
}
@@ -242,13 +242,13 @@ export function rollFeature(roll: number = random(1, 12)): Feature {
} else if (roll <= 3) {
feature.description = "winged/flying";
} else if (roll <= 4) {
feature.description = "multiple heads/headless";
feature.description = sample(["multiple heads", "headless"]);
} else if (roll <= 5) {
feature.description = "many eyes/one eye";
feature.description = sample(["many eyes", "one eye"]);
} else if (roll <= 6) {
feature.description = "many limbs/tails";
feature.description = sample(["many limbs", "tails"]);
} else if (roll <= 7) {
feature.description = "tentacles/tendrils";
feature.description = sample(["tentacles", "tendrils"]);
} else if (roll <= 8) {
feature.description = rollAspect().description;
} else if (roll <= 9) {
@@ -317,15 +317,15 @@ export class Oddity {
export function rollOddity(roll: number = random(1, 12)): Oddity {
let oddity = new Oddity(roll);
oddity.description = [
"weird color/smell/sound",
sample(["weird color", "smell", "sound"]),
"geometric",
"web/network/system",
"crystalline/glass-like",
sample(["web", "network", "system"]),
sample(["crystalline", "glass-like"]),
"fungal",
"gaseous/smokey",
"mirage/illusion",
"volcanic/explosive",
"magnetic/repellant",
sample(["gaseous", "smokey"]),
sample(["mirage", "illusion"]),
sample(["volcanic", "explosive"]),
sample(["magnetic", "repellant"]),
"devoid of life",
"unexpectedly alive",
"[ODDITY] and [ODDITY]",
@@ -379,15 +379,15 @@ export function rollRuination(roll: number = random(1, 12)): Ruination {
if (roll <= 1) {
ruination.description = "arcane disaster";
} else if (roll <= 2) {
ruination.description = "damnation/curse";
ruination.description = sample(["damnation", "curse"]);
} else if (roll <= 4) {
ruination.description = "earthquake/fire/flood";
ruination.description = sample(["earthquake", "fire", "flood"]);
} else if (roll <= 6) {
ruination.description = "plague/famine/drought";
ruination.description = sample(["plague", "famine", "drought"]);
} else if (roll <= 8) {
ruination.description = "overrun by monsters";
} else if (roll <= 10) {
ruination.description = "war/invasion";
ruination.description = sample(["war", "invasion"]);
} else if (roll <= 11) {
ruination.description = "depleted resources";
} else if (roll <= 12) {
@@ -463,19 +463,19 @@ export function rollTerrain(roll: number = random(1, 12)): Terrain {
let terrain = new Terrain(roll);
if (roll <= 1) {
terrain.description = "wasteland/desert";
terrain.description = sample(["wasteland", "desert"]);
} else if (roll <= 3) {
terrain.description = "flatland/plain";
terrain.description = sample(["flatland", "plain"]);
} else if (roll <= 4) {
terrain.description = "wetland/marsh/swamp";
terrain.description = sample(["wetland", "marsh", "swamp"]);
} else if (roll <= 7) {
terrain.description = "woodland/forest/jungle";
terrain.description = sample(["woodland", "forest", "jungle"]);
} else if (roll <= 9) {
terrain.description = "highland/hills";
terrain.description = sample(["highland", "hills"]);
} else if (roll <= 11) {
terrain.description = "mountains";
} else if (roll <= 12) {
terrain.description = rollOddity().description;
terrain.description = `${rollOddity().description} oddity`;
}
return terrain;
@@ -490,15 +490,15 @@ export function rollVisibility(roll: number = random(1, 12)): Visibility {
let visibility = new Visibility(roll);
if (roll <= 2) {
visibility.description = "buried/camouflaged/nigh invisible";
visibility.description = sample(["buried", "camouflaged", "nigh invisible"]);
} else if (roll <= 6) {
visibility.description = "partly covered/overgrown/hidden";
visibility.description = sample(["partly covered", "partly overgrown", "partly hidden"]);
} else if (roll <= 9) {
visibility.description = "obvious/in plain sight";
visibility.description = sample(["obvious", "in plain sight"]);
} else if (roll <= 11) {
visibility.description = "visible at near distance";
} else if (roll <= 12) {
visibility.description = "visible at great distance/focal point";
visibility.description = sample(["visible at great distance", "focal point"]);
}
return visibility;
+65 -30
View File
@@ -1,14 +1,23 @@
import { random, sample } from 'lodash';
import { Creature, rollCreature } from './creatures';
import { rollAlignment, rollElement, rollMagicType, rollOddity, rollTerrain } from './details';
import { Age, Alignment, Aspect, Element, MagicType, rollAge, rollAlignment, rollAspect, rollElement, rollMagicType, rollOddity, rollRuination, rollSize, rollTerrain, rollVisibility, Ruination, Size, Visibility } from './details';
import { rollDungeon } from './dungeons';
import { rollSteading, Steading } from './steadings';
import { rollTreasure } from './treasures';
export class Discovery {
constructor(public roll: number = 0, public subroll: number = 0, public specificroll: number = 0, public category: string = "", public subcategory: string = "", public description: string = "") { };
age?: Age;
alignment?: Alignment;
aspect?: Aspect;
creature?: Creature;
element?: Element;
magicType?: MagicType;
ruination?: Ruination;
size?: Size;
steading?: Steading;
visibility?: Visibility;
}
export function rollDiscovery() {
@@ -37,12 +46,18 @@ export function rollUnnaturalFeature(roll: number = random(1, 12), subroll: numb
if (subroll <= 9) {
discovery.subcategory = "arcane";
discovery.description = rollArcaneFeature(specificroll);
discovery.alignment = rollAlignment();
discovery.magicType = rollMagicType();
} else if (subroll <= 11) {
discovery.subcategory = "planar";
discovery.description = rollPlanarFeature(specificroll);
discovery.alignment = rollAlignment();
discovery.element = rollElement();
} else if (subroll <= 12) {
discovery.subcategory = "divine";
discovery.description = rollDivineFeature(specificroll);
discovery.alignment = rollAlignment();
discovery.aspect = rollAspect();
}
return discovery;
@@ -107,7 +122,9 @@ export function rollNaturalFeature(roll: number = random(1, 12), subroll: number
if (subroll <= 2) {
discovery.subcategory = "lair";
discovery.description = rollLair(specificroll)
discovery.description = rollLair(specificroll);
discovery.creature = rollCreature();
discovery.visibility = rollVisibility();
} else if (subroll <= 4) {
discovery.subcategory = "obstacle";
discovery.description = rollObstacle(specificroll);
@@ -121,8 +138,10 @@ export function rollNaturalFeature(roll: number = random(1, 12), subroll: number
discovery.subcategory = "terrain change";
discovery.description = rollLandmark(specificroll);
} else if (subroll <= 12) {
discovery.subcategory = "terrain change";
discovery.subcategory = "resource";
discovery.description = rollResource(specificroll);
discovery.size = rollSize();
discovery.visibility = rollVisibility();
}
return discovery;
@@ -148,9 +167,9 @@ export function rollObstacle(roll: number = random(1, 12)): string {
if (roll <= 5) {
return "difficult ground";
} else if (roll <= 8) {
return "cliff/crevasse/chasm";
return sample(["cliff", "crevasse", "chasm"]);
} else if (roll <= 10) {
return "ravine/gorge";
return sample(["ravine", "gorge"]);
} else if (roll <= 12) {
return rollOddity().description;
}
@@ -160,15 +179,15 @@ export function rollObstacle(roll: number = random(1, 12)): string {
export function rollTerrainChange(roll: number = random(1, 12)): string {
if (roll <= 4) {
return `limited area of ${rollTerrain()}`;
return `limited area of ${rollTerrain().description}`;
} else if (roll <= 6) {
return "crevice/hole/pit/cave";
return sample(["crevice", "hole", "pit", "cave"]);
} else if (roll <= 8) {
return "altitude change";
} else if (roll <= 10) {
return "canyon/valley";
return sample(["canyon", "valley"]);
} else if (roll <= 12) {
return "rise/peak in distance";
return sample(["rise", "peak in distance"]);
}
return "";
@@ -176,17 +195,17 @@ export function rollTerrainChange(roll: number = random(1, 12)): string {
export function rollWaterFeature(roll: number = random(1, 12)): string {
if (roll <= 1) {
return "spring/hotspring";
return sample(["spring", "hotspring"]);
} else if (roll <= 2) {
return "waterfall/geyser";
return sample(["waterfall", "geyser"]);
} else if (roll <= 6) {
return "creek/stream/brook";
return sample(["creek", "stream", "brook"]);
} else if (roll <= 8) {
return "pond/lake";
return sample(["pond", "lake"]);
} else if (roll <= 10) {
return "river";
} else if (roll <= 12) {
return "sea/ocean";
return sample(["sea", "ocean"]);
}
return "";
@@ -208,15 +227,15 @@ export function rollLandmark(roll: number = random(1, 12)): string {
export function rollResource(roll: number = random(1, 12)): string {
if (roll <= 4) {
return "game/fruit/vegetable";
return sample(["game", "fruit", "vegetable"]);
} else if (roll <= 6) {
return "herb/spice/dye source";
return sample(["herb", "spice", "dye source"]);
} else if (roll <= 9) {
return "timber/stone";
return sample(["timber", "stone"]);
} else if (roll <= 11) {
return "ore (copper, iron, etc.)";
return sample(["copper ore", "iron ore", "tin ore", "silver ore"]);
} else if (roll <= 12) {
return "precious metal/gems";
return sample(["precious metal", "precious gems"]);
}
return "";
@@ -228,9 +247,13 @@ function rollEvidence(roll: number = random(1, 12), subroll: number = random(1,
if (subroll <= 6) {
discovery.subcategory = "tracks/spoor";
discovery.description = rollTracksSpoor(specificroll);
discovery.age = rollAge();
discovery.creature = rollCreature();
} else if (subroll <= 10) {
discovery.subcategory = "remains/debris";
discovery.description = rollRemainsDebris(specificroll);
discovery.age = rollAge();
discovery.visibility = rollVisibility();
} else if (subroll <= 12) {
discovery.subcategory = "stash/cache";
discovery.description = rollStashCache(specificroll);
@@ -305,15 +328,23 @@ export function rollStructure(roll: number = random(1, 12), subroll: number = ra
if (subroll <= 1) {
discovery.subcategory = "enigmatic";
discovery.description = rollEnigmaticStructure(specificroll);
discovery.age = rollAge(random(1, 8) + 4);
discovery.size = rollSize(random(1, 8) + 4);
discovery.visibility = rollVisibility();
} else if (subroll <= 3) {
discovery.subcategory = "infrastructure";
discovery.description = rollInfrastructureStructure(specificroll);
discovery.creature = rollCreature(random(1, 4) + 4);
} else if (subroll <= 4) {
discovery.subcategory = "dwelling";
discovery.description = rollDwellingStructure(specificroll);
discovery.creature = rollCreature(random(1, 4) + 4);
} else if (subroll <= 6) {
discovery.subcategory = "burial/religious";
discovery.description = rollBurialReligiousStructure(specificroll);
discovery.creature = rollCreature(random(1, 4) + 4);
discovery.alignment = rollAlignment();
discovery.aspect = rollAspect();
} else if (subroll <= 8) {
discovery.subcategory = "steading";
discovery.steading = rollSteading();
@@ -322,6 +353,10 @@ export function rollStructure(roll: number = random(1, 12), subroll: number = ra
} else if (subroll <= 12) {
discovery.subcategory = "ruin";
discovery.description = rollRuinStructure(specificroll);
discovery.creature = rollCreature(random(1, 4) + 4);
discovery.age = rollAge(random(1, 8) + 4);
discovery.ruination = rollRuination();
discovery.visibility = rollVisibility();
}
return discovery;
@@ -343,15 +378,15 @@ export function rollEnigmaticStructure(roll: number = random(1, 12)): string {
export function rollInfrastructureStructure(roll: number = random(1, 12)): string {
if (roll <= 4) {
return "track/path";
return sample(["track", "path"]);
} else if (roll <= 8) {
return "road";
} else if (roll <= 10) {
return "bridge/ford";
return sample(["bridge", "ford"]);
} else if (roll <= 11) {
return "mine/quarry";
return sample(["mine", "quarry"]);
} else if (roll <= 12) {
return "aqueduct/canal/portal";
return sample(["aqueduct", "canal", "portal"]);
}
return "";
@@ -361,13 +396,13 @@ export function rollDwellingStructure(roll: number = random(1, 12)): string {
if (roll <= 3) {
return "campsite";
} else if (roll <= 6) {
return "hovel/hut";
return sample(["hovel", "hut"]);
} else if (roll <= 8) {
return "farm";
} else if (roll <= 10) {
return "inn/roadhouse";
return sample(["inn", "roadhouse"]);
} else if (roll <= 12) {
return "tower/keep/estate";
return sample(["tower", "keep", "estate"]);
}
return "";
@@ -375,15 +410,15 @@ export function rollDwellingStructure(roll: number = random(1, 12)): string {
export function rollBurialReligiousStructure(roll: number = random(1, 12)): string {
if (roll <= 2) {
return "grave marker/barrow";
return sample(["grave marker", "grave barrow"]);
} else if (roll <= 4) {
return "graveyard/necropolis";
return sample(["graveyard", "necropolis"]);
} else if (roll <= 6) {
return "tomb/crypt";
return sample(["tomb", "crypt"]);
} else if (roll <= 9) {
return "shrine";
} else if (roll <= 11) {
return "temple/retreat";
return sample(["temple", "retreat"]);
} else if (roll <= 12) {
return "great temple";
}
+334 -13
View File
@@ -1,27 +1,348 @@
import { random, replace, sample, times } from "lodash";
import { Creature, rollCreature, rollHumanoid, rollMonster } from "./creatures";
import { rollElement, rollOddity } from "./details";
export class Dungeon {
size: Size;
ruination: string;
builder: string;
function: string;
themes: string[];
discovery: string;
danger: string;
ruination: Ruination;
builder: Builder;
function: DunFunction;
commonThemes: Theme[] = [];
uniqueThemes: Theme[] = [];
commonAreas: (Discovery | Danger)[] = [];
uniqueAreas: (Discovery | Danger)[] = [];
printDescription() {
return `${this.size.size} ${this.builder} ${this.function}`;
return `${this.size.description} ${this.function.description} built by ${this.builder.description}`;
}
}
export class Size {
constructor(public size: string = "", public themes: string = "", public areas: string = "") { };
constructor(public roll: number = 0, public description: string = "", public commonThemes: number = 0, public uniqueThemes: number = 0, public commonAreas: number = 0, public uniqueAreas: number = 0) { };
}
export class Ruination {
constructor(public roll: number = 0, public description: string = "") { };
}
export class Builder {
constructor(public roll: number = 0, public description: string = "") { };
}
export class DunFunction {
constructor(public roll: number = 0, public description: string = "") { };
}
export class Theme {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
}
export class Discovery {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
type: string = "discovery";
}
export class Danger {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
type: string = "danger";
creature?: Creature;
}
export function rollDungeon(): Dungeon {
// TODO: Add the who dungeon thing
let dungeon = new Dungeon();
dungeon.size = new Size("huge");
dungeon.builder = "dwarves";
dungeon.function = "mine";
dungeon.size = rollSize();
dungeon.ruination = rollRuination();
dungeon.builder = rollBuilder();
dungeon.function = rollDunFunction();
times(dungeon.size.commonThemes, () => dungeon.commonThemes.push(rollTheme()));
times(dungeon.size.uniqueThemes, () => dungeon.uniqueThemes.push(rollTheme()));
times(dungeon.size.commonAreas, () => dungeon.commonAreas.push(sample([rollDiscovery, rollDanger])()));
times(dungeon.size.uniqueAreas, () => dungeon.uniqueAreas.push(sample([rollDiscovery, rollDanger])()));
return new Dungeon();
return dungeon;
}
function rollSize(roll: number = random(1, 12)): Size {
if (roll <= 3) {
return new Size(roll, "small", 2, random(1, 4), 6, random(1, 6 + 2));
} else if (roll <= 9) {
return new Size(roll, "medium", 3, random(1, 6), 12, random(1, 6) + random(1, 6) + 4);
} else if (roll <= 11) {
return new Size(roll, "large", 4, random(1, 6) + 1, 16, random(1, 6) + random(1, 6) + random(1, 6) + 6);
} else if (roll <= 12) {
return new Size(roll, "huge", 5, random(1, 6) + 2, 24, random(1, 6) + random(1, 6) + random(1, 6) + random(1, 6) + 10);
}
return new Size(roll);
}
function rollRuination(roll: number = random(1, 12)): Ruination {
if (roll <= 1) {
return new Ruination(roll, "arcane disaster");
} else if (roll <= 2) {
return new Ruination(roll, "damnation/curse");
} else if (roll <= 4) {
return new Ruination(roll, "earthquake/fire/flood");
} else if (roll <= 6) {
return new Ruination(roll, "plague/famine/drought");
} else if (roll <= 8) {
return new Ruination(roll, "overrun by monsters");
} else if (roll <= 10) {
return new Ruination(roll, "war/invasion");
} else if (roll <= 11) {
return new Ruination(roll, "depleted resources");
} else if (roll <= 12) {
return new Ruination(roll, "better prospects elsewhere");
}
return new Ruination(roll);
}
function rollBuilder(roll: number = random(1, 12)): Builder {
if (roll <= 1) {
return new Builder(roll, sample(["aliens", "precursors"]));
} else if (roll <= 2) {
return new Builder(roll, sample(["demigod", "demon"]));
} else if (roll <= 3) {
return new Builder(roll, "natural(caves, etc.)");
} else if (roll <= 5) {
return new Builder(roll, sample(["religious order", "cult"]));
} else if (roll <= 6) {
return new Builder(roll, rollHumanoid().description);
} else if (roll <= 8) {
return new Builder(roll, sample(["dwarves", "gnomes"]));
} else if (roll <= 10) {
return new Builder(roll, "elves");
} else if (roll <= 11) {
return new Builder(roll, sample(["wizard", "madman"]));
} else if (roll <= 12) {
return new Builder(roll, sample(["monarch", "warlord"]));
}
return new Builder(roll);
}
function rollDunFunction(roll: number = random(1, 12)): DunFunction {
if (roll <= 1) {
return new DunFunction(roll, sample(["source", "portal"]));
} else if (roll <= 2) {
return new DunFunction(roll, "mine");
} else if (roll <= 4) {
return new DunFunction(roll, sample(["tomb", "crypt"]));
} else if (roll <= 5) {
return new DunFunction(roll, "prison");
} else if (roll <= 7) {
return new DunFunction(roll, sample(["lair", "den", "hideout"]));
} else if (roll <= 9) {
return new DunFunction(roll, sample(["stronghold", "sanctuary"]));
} else if (roll <= 10) {
return new DunFunction(roll, sample(["shrine", "temple", "oracle"]));
} else if (roll <= 11) {
return new DunFunction(roll, sample(["archive", "library"]));
} else if (roll <= 12) {
return new DunFunction(roll, sample(["unknown", "mystery"]));
}
return new DunFunction(roll);
}
function rollTheme(roll: number = random(1, 12), subroll: number = random(1, 12)): Theme {
if (roll <= 5) {
return rollMundaneTheme(roll, subroll);
} else if (roll <= 9) {
return rollUnusualTheme(roll, subroll);
} else if (roll <= 12) {
return rollExtraordinaryTheme(roll, subroll);
}
return new Theme(roll, subroll);
}
function rollMundaneTheme(roll: number = random(1, 12), subroll: number = random(1, 12)): Theme {
return new Theme(roll, subroll, "mundane", [
"rot / decay",
"torture / agony",
"madness",
"all is lost",
"noble sacrifice",
"savage fury",
"survival",
"criminal activity",
"secrets / treachery",
"tricks and traps",
"invasion / infestation",
"factions at war",
][subroll - 1]);
}
function rollUnusualTheme(roll: number = random(1, 12), subroll: number = random(1, 12)): Theme {
return new Theme(roll, subroll, "unusual", [
"creation / invention",
`elemental ${rollElement().description}`,
"knowledge / learning",
"growth / expansion",
"deepening mystery",
"transformation / change",
"chaos and destruction",
"shadowy forces",
"forbidden knowledge",
"poison / disease 10",
"corruption / blight",
"impending disaster",
][subroll - 1]);
}
function rollExtraordinaryTheme(roll: number = random(1, 12), subroll: number = random(1, 12)): Theme {
return new Theme(roll, subroll, "extraordinary", [
"scheming evil",
"divination / scrying",
"blasphemy",
"arcane research",
"occult forces",
"an ancient curse",
"mutation",
"the unquiet dead",
"bottomless hunger",
"incredible power",
"unspeakable horrors",
"holy war",
][subroll - 1]);
}
function rollDiscovery(roll: number = random(1, 12), subroll: number = random(1, 12)): Discovery {
if (roll <= 3) {
return rollDiscoveryDressing(roll, subroll);
} else if (roll <= 9) {
return rollDiscoveryFeature(roll, subroll);
} else if (roll <= 12) {
return rollDiscoveryFind(roll, subroll);
}
return new Discovery(roll, subroll);
}
function rollDiscoveryDressing(roll: number = random(1, 12), subroll: number = random(1, 12)): Discovery {
return new Discovery(roll, subroll, "dressing", [
sample(["junk", "debris"]),
sample(["tracks", "marks"]),
"signs of battle",
sample(["writing", "carving"]),
"warning",
`dead ${rollCreature().description}`,
sample(["bones", "remains"]),
sample(["book", "scroll", "map"]),
sample(["broken door", "broken wall"]),
sample(["breeze", "wind", "smell"]),
sample(["lichen", "moss", "fungus"]),
rollOddity().description,
][subroll - 1]);
}
function rollDiscoveryFeature(roll: number = random(1, 12), subroll: number = random(1, 12)): Discovery {
return new Discovery(roll, subroll, "feature", [
sample(["cave-in", "collapse"]),
sample(["pit", "shaft", "chasm"]),
sample(["pillars", "columns"]),
sample(["locked door", "gate"]),
sample(["alcoves", "niches"]),
sample(["bridge", "stairs", "ramp"]),
sample(["fountain", "well", "pool"]),
"puzzle",
sample(["altar", "dais", "platform"]),
sample(["statue", "idol"]),
sample(["magic pool", "statue", "idol"]),
"connection to another dungeon",
][subroll - 1]);
}
function rollDiscoveryFind(roll: number = random(1, 12), subroll: number = random(1, 12)): Discovery {
var find = [
"trinkets",
"tools",
sample(["weapons", "armor"]),
sample(["supplies", "trade goods"]),
sample(["coins", "gems", "jewelry"]),
sample(["poisons", "potions"]),
sample(["adventurer", "captive"]),
"magic item",
sample(["scroll", "book"]),
sample(["magic weapon", "magic armor"]),
"artifact",
"[FIND] and [FIND]",
][subroll - 1];
find = replace(find, /\[FIND\]/g, () => rollDiscovery(random(1, 11)).description);
return new Discovery(roll, subroll, "find", find);
}
function rollDanger(roll: number = random(1, 12), subroll: number = random(1, 12)): Danger {
if (roll <= 4) {
return rollDangerTrap(roll, subroll);
} else if (roll <= 11) {
return rollDangerCreature(roll, subroll);
} else if (roll <= 12) {
return rollDangerEntity(roll, subroll);
}
return new Danger(roll, subroll);
}
function rollDangerTrap(roll: number = random(1, 12), subroll: number = random(1, 12)): Danger {
let trap = [
"alarm",
sample(["ensnaring", "paralyzing"]),
"pit",
"crushing",
sample(["piercing", "puncturing"]),
sample(["chopping", "slashing"]),
"confusing(maze, etc.)",
"gas(poison, etc.)",
rollElement().description,
"ambush",
"magical",
"[TRAP] and [TRAP]",
][subroll - 1];
trap = replace(trap, /\[TRAP\]/g, () => rollDangerTrap(random(1, 11)).description);
return new Danger(roll, subroll, "trap", trap);
}
function rollDangerCreature(roll: number = random(1, 12), subroll: number = random(1, 12)): Danger {
let creature = rollCreature();
let activity = [
"waiting in ambush",
sample(["fighting", "squabbling"]),
sample(["prowling", "on patrol"]),
"looking for food",
sample(["eating", "resting"]),
"guarding",
"on the move",
sample(["searching", "scavenging"]),
"returning to den",
"making plans",
"sleeping",
"dying",
][subroll - 1];
let danger = new Danger(roll, subroll, "creture", `${creature.description} that is ${activity}`);
danger.creature = creature;
return danger;
}
function rollDangerEntity(roll: number = random(1, 12), subroll: number = random(1, 12)): Danger {
return new Danger(roll, subroll, "entity", [
"alien interloper",
"vermin lord",
"criminal mastermind",
"warlord",
"high priest",
"oracle",
sample(["wizard", "witch", "alchemist"]),
`${rollMonster().description} lord`,
sample(["evil spirit", "ghost"]),
sample(["undead lord", "lich"]),
"demon",
"dark god",
][subroll - 1]);
}
+8 -1
View File
@@ -1,4 +1,4 @@
import { random } from 'lodash';
import { random, sample } from 'lodash';
export class Kingdom {
constructor(public description: string = "", public subtitle: string = "") { };
@@ -53,3 +53,10 @@ export function rollKingdom(roll: number = random(1, 4)): Kingdom {
return kingdoms[key];
}
export function rollCategory(kingdom: string = "", category: string = ""): string {
if (kingdoms[kingdom] && kingdoms[kingdom][category]) {
return sample(kingdoms[kingdom][category]);
}
return "";
}
+146 -132
View File
@@ -1,4 +1,5 @@
import { random, sample } from 'lodash';
import { capitalize, random, sample } from 'lodash';
import { Activity, Alignment, rollActivity, rollAlignment } from './details';
import { Follower, rollFollower } from './followers';
import { kingdoms, rollKingdom } from './kingdoms';
@@ -7,11 +8,18 @@ export class Npc {
gender: "male" | "female";
hometown: string;
context: Context;
occupation: Occupation
trait: Trait;
follower: Follower;
getDescription(fullDesc: boolean = false) {
let str = `${this.name} the ${this.gender} ${capitalize(this.occupation.category)} (${this.occupation.description})`;
str += fullDesc ? ` from ${this.hometown}` : '';
return str;
}
}
export function rollNpc(): Npc {
@@ -31,7 +39,10 @@ export function rollNpc(): Npc {
}
export class Context {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public subcategory: string = "") { };
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
activity?: Activity;
alignment?: Alignment;
trait?: Trait;
}
export function rollContext(): Context {
@@ -40,71 +51,74 @@ export function rollContext(): Context {
let context = new Context(roll, subroll);
if (roll <= 3) {
context.category = "wilderness";
context.subcategory = rollWildernessNpc(subroll);
context.description = rollWildernessNpc(subroll);
} else if (roll <= 9) {
context.category = "rural";
context.subcategory = rollRuralNpc(subroll);
context.description = rollRuralNpc(subroll);
} else if (roll <= 12) {
context.category = "urban";
context.subcategory = rollUrbanNpc(subroll);
context.description = rollUrbanNpc(subroll);
}
context.activity = rollActivity();
context.alignment = rollAlignment();
context.trait = rollTrait();
return context;
}
export function rollWildernessNpc(roll: number = random(1, 12)): string {
return [
`${rollCriminal()} -4`,
`${rollCriminal()} -4`,
"adventurer/explorer",
"adventurer/explorer",
"hunter/gatherer",
"hunter/gatherer",
rollCommoner(),
rollCommoner(),
"ranger/scout",
"ranger/scout",
"soldier/mercenary",
rollOfficial(),
`criminal ${rollCriminal()} -4`,
`criminal ${rollCriminal()} -4`,
"adventurer",
"explorer",
"hunter",
"gatherer",
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
"ranger",
"scout",
sample(["soldier", "mercenary"]),
`official ${rollOfficial()}`,
][roll - 1];
}
export function rollRuralNpc(roll: number = random(1, 12)): string {
return [
"beggar/urchin",
`${rollCriminal()} -1`,
"adventurer/explorer",
"hunter/gatherer",
rollCommoner(),
rollCommoner(),
rollCommoner(),
rollCommoner(),
rollTradeperson(),
`${rollMerchant()} -1`,
"militia/soldier/guard",
rollOfficial(),
sample(["beggar", "urchin"]),
`criminal ${rollCriminal()} -1`,
sample(["adventurer", "explorer"]),
sample(["hunter", "gatherer"]),
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`tradeperson ${rollTradeperson()}`,
`merchant ${rollMerchant()} -1`,
sample(["militia", "soldier", "guard"]),
`official ${rollOfficial()}`,
][roll - 1];
}
export function rollUrbanNpc(roll: number = random(1, 12)): string {
return [
"beggar/urchin",
"beggar/urchin",
rollCriminal(),
rollCommoner(),
rollCommoner(),
rollCommoner(),
rollCommoner(),
rollTradeperson(),
rollMerchant(),
rollSpecialist(),
"militia/soldier/guard",
rollOfficial(),
"beggar",
"urchin",
`commoner ${rollCriminal()}`,
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`commoner ${rollCommoner()}`,
`tradeperson ${rollTradeperson()}`,
`merchant ${rollMerchant()}`,
`specialist ${rollSpecialist()}`,
sample(["militia", "soldier", "guard"]),
`official ${rollOfficial()}`,
][roll - 1];
}
export class Occupation {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public subcategory: string = "") { };
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
}
export function rollOccupation(roll: number = random(1, 12), subroll: number = random(1, 12)): Occupation {
@@ -112,22 +126,22 @@ export function rollOccupation(roll: number = random(1, 12), subroll: number = r
let occupation = new Occupation(roll, subroll);
if (roll <= 1) {
occupation.category = "criminal";
occupation.subcategory = rollCriminal(subroll);
occupation.description = rollCriminal(subroll);
} else if (roll <= 6) {
occupation.category = "commoner";
occupation.subcategory = rollCommoner(subroll);
occupation.description = rollCommoner(subroll);
} else if (roll <= 8) {
occupation.category = "tradeperson";
occupation.subcategory = rollTradeperson(subroll);
occupation.description = rollTradeperson(subroll);
} else if (roll <= 10) {
occupation.category = "merchant";
occupation.subcategory = rollMerchant(subroll);
occupation.description = rollMerchant(subroll);
} else if (roll <= 11) {
occupation.category = "specialist";
occupation.subcategory = rollSpecialist(subroll);
occupation.description = rollSpecialist(subroll);
} else if (roll <= 12) {
occupation.category = "officiail";
occupation.subcategory = rollOfficial(subroll);
occupation.category = "official";
occupation.description = rollOfficial(subroll);
}
return occupation;
@@ -135,15 +149,15 @@ export function rollOccupation(roll: number = random(1, 12), subroll: number = r
function rollCriminal(roll: number = random(1, 12)): string {
return [
"bandit/brigand/thug",
"bandit/brigand/thug",
sample(["bandit", "brigand", "thug"]),
sample(["bandit", "brigand", "thug"]),
"thief",
"thief",
"bodyguard/tough",
"bodyguard/tough",
"bodyguard",
"tough",
"burglar",
"burglar",
"dealer/fence",
sample(["dealer", "fence"]),
"racketeer",
"lieutenant",
"boss"
@@ -153,66 +167,66 @@ function rollCriminal(roll: number = random(1, 12)): string {
function rollCommoner(roll: number = random(1, 12)): string {
return [
"housewife/husband",
"hunter/gatherer",
"hunter/gatherer",
"farmer/herder",
"farmer/herder",
"farmer/herder",
"laborer/servant",
"laborer/servant",
"driver/porter/guide",
"sailor/soldier/guard",
"clergy/monk",
"apprentice/adventurer"
"hunter",
"gatherer",
"farmer",
"herder",
sample(["farmer", "herder"]),
"laborer",
"servant",
sample(["driver", "porter", "guide"]),
sample(["sailor", "soldier", "guard"]),
sample(["clergy", "monk"]),
sample(["apprentice", "adventurer"]),
][roll - 1];
}
function rollTradeperson(roll: number = random(1, 12)): string {
return [
"cobbler/furrier/tailor",
"weaver/basketmaker",
"potter/carpenter",
"mason/baker/chandler",
"cooper/wheelwright",
"tanner/ropemaker",
"smith/tinker",
"stablekeeper/herbalist",
"vintner/jeweler",
"inkeeper/tavernkeeper",
"artist/actor/minstrel",
"armorer/weaponsmith",
sample(["cobbler", "furrier", "tailor"]),
sample(["weaver", "basketmaker"]),
sample(["potter", "carpenter"]),
sample(["mason", "baker", "chandler"]),
sample(["cooper", "wheelwright"]),
sample(["tanner", "ropemaker"]),
sample(["smith", "tinker"]),
sample(["stablekeeper", "herbalist"]),
sample(["vintner", "jeweler"]),
sample(["inkeeper", "tavernkeeper"]),
sample(["artist", "actor", "minstrel"]),
sample(["armorer", "weaponsmith"]),
][roll - 1];
}
function rollMerchant(roll: number = random(1, 12)): string {
return [
"general goods/outfitter",
"general goods/outfitter",
"general goods/outfitter",
"general goods",
"outfitter",
sample(["general goods", "outfitter"]),
"raw materials",
"grain/livestock",
"ale/wine/spirits",
"clothing/jewelry",
"weapons/armor",
"spices/tobacco",
"labor/slaves",
"books/scrolls",
"magic supplies/items"
sample(["grain", "livestock"]),
sample(["ale", "wine", "spirits"]),
sample(["clothing", "jewelry"]),
sample(["weapons", "armor"]),
sample(["spices", "tobacco"]),
sample(["labor", "slaves"]),
sample(["books", "scrolls"]),
sample(["magic supplies", "magic items"]),
][roll - 1];
}
function rollSpecialist(roll: number = random(1, 12)): string {
return [
"undertaker",
"sage/scholar/wizard",
"writer/illuminator",
sample(["sage", "scholar", "wizard"]),
sample(["writer", "illuminator"]),
"perfumer",
"architect/engineer",
"locksmith/clockmaker",
"physician/apothecary",
"navigator/guide",
"alchemist/astrologer",
"spy/diplomat",
sample(["architect", "engineer"]),
sample(["locksmith", "clockmaker"]),
sample(["physician", "apothecary"]),
sample(["navigator", "guide"]),
sample(["alchemist", "astrologer"]),
sample(["spy", "diplomat"]),
"cartographer",
"inventor"
][roll - 1];
@@ -222,16 +236,16 @@ function rollOfficial(roll: number = random(1, 12)): string {
return [
"town crier",
"tax collector",
"armiger/gentry",
"armiger/gentry",
"reeve/sheriff/constable",
"mayor/magistrate",
"priest/bishop/abbot",
"armiger",
"gentry",
sample(["reeve", "sheriff", "constable"]),
sample(["mayor", "magistrate"]),
sample(["priest", "bishop", "abbot"]),
"guildmaster",
"knightltemplar",
"elder/high priest",
sample(["elder", "high priest"]),
"noble (baron, etc.)",
"lord/lady/king/queen",
sample(["lord", "lady", "king", "queen"]),
][roll - 1];
}
@@ -259,15 +273,15 @@ function rollAppearance(roll: number = random(1, 12)): string {
let appearance = [
"disfigured (missing teeth, eye, etc.)",
"lasting injury (bad leg, arm, etc.)",
"tattooed/pockmarked/scarred",
"unkempt/shabby/grubby",
"big/thick/brawny",
"small/scrawny/emaciated",
sample(["tattooed", "pockmarked", "scarred"]),
sample(["unkempt", "shabby", "grubby"]),
sample(["big", "thick", "brawny"]),
sample(["small", "scrawny", "emaciated"]),
"notable hair (wild, long, none, etc.)",
"notable nose (big, hooked, etc.)",
"notable eyes (blue, bloodshot, etc.)",
"clean/well-dressed/well-groomed",
"attractive/handsome/stunning",
sample(["clean", "well-dressed", "well-groomed"]),
sample(["attractive", "handsome", "stunning"]),
"they are [APPEARANCE] despite [a contradictory detail of your choice]"
][roll - 1];
@@ -276,17 +290,17 @@ function rollAppearance(roll: number = random(1, 12)): string {
export function rollPersonality(roll: number = random(1, 12)): string {
let personality = [
"loner/alienated/antisocial",
"cruel/belligerent/bully",
"anxious/fearful/cowardly",
"envious/covetous/greedy",
"aloof/haughty/arrogant",
"awkward/shy/self-loathing",
"orderly/compulsive/controlling",
"confident/impulsive/reckless",
"kind/generous/compassionate",
"easygoing/relaxed/peaceful",
"cheerful/happy/optimistic",
sample(["loner", "alienated", "antisocial"]),
sample(["cruel", "belligerent", "bully"]),
sample(["anxious", "fearful", "cowardly"]),
sample(["envious", "covetous", "greedy"]),
sample(["aloof", "haughty", "arrogant"]),
sample(["awkward", "shy", "self-loathing"]),
sample(["orderly", "compulsive", "controlling"]),
sample(["confident", "impulsive", "reckless"]),
sample(["kind", "generous", "compassionate"]),
sample(["easygoing", "relaxed", "peaceful"]),
sample(["cheerful", "happy", "optimistic"]),
"they are [PERSONALITY] despite [a contradictory detail of your choice]",
][roll - 1];
@@ -295,17 +309,17 @@ export function rollPersonality(roll: number = random(1, 12)): string {
export function rollQuirk(roll: number = random(1, 12)): string {
let quirk = [
"insecure/racist/xenophobic",
"addict (sweets, drugs, sex, etc.)",
"phobia (spiders, fire, darkness, etc.)",
"allergic/asthmatic/chronically ill",
"skeptic/paranoid",
"superstitious/devout/fanatical",
"miser/pack-rat",
"spendthrift/wastrel",
"smart aleck/know-it-all",
"artistic/dreamer/delusional",
"naive/idealistic",
sample(["insecure", "racist", "xenophobic"]),
sample(["addict (sweets, drugs, sex, etc.)"]),
sample(["phobia (spiders, fire, darkness, etc.)"]),
sample(["allergic", "asthmatic", "chronically ill"]),
sample(["skeptic", "paranoid"]),
sample(["superstitious", "devout", "fanatical"]),
sample(["miser", "pack-rat"]),
sample(["spendthrift", "wastrel"]),
sample(["smart aleck", "know-it-all"]),
sample(["artistic", "dreamer", "delusional"]),
sample(["naive", "idealistic"]),
"they are [QUIRK] despite [a contradictory detail of your choice]",
][roll - 1];
+10 -1
View File
@@ -1,3 +1,6 @@
import { sample } from "lodash";
import { bodgeString } from "./utils";
export const places = {
templates: [
'The [PLACE]',
@@ -10,5 +13,11 @@ export const places = {
place: ['Barrier', 'Beach', 'Bowl', 'Camp', 'Cave', 'Circle', 'City', 'Cliff', 'Crater', 'Crossing', 'Crypt', 'Den', 'Ditch', 'Falls', 'Fence', 'Field', 'Fort', 'Gate', 'Grove', 'Hill', 'Hole', 'Hut', 'Keep', 'Lake', 'Marsh', 'Meadow', 'Mountain', 'Pit', 'Post', 'Ridge', 'Ring', 'Rise', 'Road', 'Rock', 'Ruin', 'Shrine', 'Spire', 'Spring', 'Stone', 'Tangle', 'Thmple', 'Throne', 'Tomb', 'Tower', 'Town', 'Tree', 'Vale', 'Valley', 'Village', 'Wall',],
adjective: ['Ancient', 'Ashen', 'Black', 'Bloody', 'Blue', 'Bright', 'Broken', 'Burning', 'Clouded', 'Copper', 'Cracked', 'Dark', 'Dead', 'Doomed', 'Endless', 'Fallen', 'Far', 'Fearsome', 'Floating', 'Forbidden', 'Frozen', 'Ghostly', 'Gloomy', 'Golden', 'Grim', 'Hidden', 'High', 'Iron', 'Jagged', 'Lonely', 'Lost', 'Low', 'Near', 'Petrified', 'Red', 'Screaming', 'Sharp', 'Shattered', 'Shifting', 'Shining', 'Shivering', 'Shrouded', 'Silver', 'Stalwart', 'Stoney', 'Sunken', 'Thorny', 'Thundering', 'White', 'Withered',],
noun: ['[Name]', 'Arm', 'Ash', 'Blood', 'Child', 'Cinder', 'Corpse', 'Crystal', 'Dagger', 'Death', 'Demon', 'Devil', 'Doom', 'Eye', 'Fear', 'Finger', 'Fire', 'Foot', 'Ghost', 'Giant', 'Goblin', 'God', 'Gold', 'Hand', 'Head', 'Heart', 'Hero', 'Hope', 'King', 'Knave', 'Knight', 'Muck', 'Mud', 'Priest', 'Queen', 'Sailor', 'Silver', 'Skull', 'Smoke', 'Souls', 'Spear', 'Spirit', 'Stone', 'Sword', 'Thief', 'Troll', 'Warrior', 'Water', 'Witch', 'Wizard',],
// * Choose a name appropriate to your setting; or, if you have a name list, roll one up.
};
export function rollPlace(): string {
let template = sample(places.templates);
let place = bodgeString(template, places);
return place;
}
+10 -1
View File
@@ -1,3 +1,6 @@
import { sample } from "lodash";
import { bodgeString } from "./utils";
export const regions = {
templates: [
'[ADJECTIVE] [TERRAIN]',
@@ -10,5 +13,11 @@ export const regions = {
terrain: ['Bay', 'Bluffs', 'Bog', 'Cliffs', 'Desert', 'Downs', 'Dunes', 'Expanse', 'Fells', 'Fen', 'Flats', 'Foothills', 'Forest', 'Groves', 'Heath', 'Heights', 'Hills', 'Hollows', 'Jungle', 'Lake', 'Lowland', 'March', 'Marsh', 'Meadows', 'Moor', 'Morass', 'Mounds', 'Mountains', 'Peaks', 'Plains', 'Prairie', 'Quagmire', 'Range', 'Reach', 'Sands', 'Savanna', 'Scarps', 'Sea', 'Slough', 'Sound', 'Steppe', 'Swamp', 'Sweep', 'Teeth', 'Thicket', 'Upland', 'Wall', 'Waste', 'Wasteland', 'Woods',],
adjective: ['Ageless', 'Ashen', 'Black', 'Blessed', 'Blighted', 'Blue', 'Broken', 'Burning', 'Cold', 'Cursed', 'Dark', 'Dead', 'Deadly', 'Deep', 'Desolate', 'Diamond', 'Dim', 'Dismal', 'Dun', 'Eerie', 'Endless', 'Fallen', 'Far', 'Fell', 'Flaming', 'Forgotten', 'Forsaken', 'Frozen', 'Glittering', 'Golden', 'Green', 'Grim', 'Holy', 'Impassable', 'Jagged', 'Light', 'Long', 'Misty', 'Perilous', 'Purple', 'Red', 'Savage', 'Shadowy', 'Shattered', 'Shifting', 'Shining', 'Silver', 'White', 'Wicked', 'Yellow',],
noun: ['[NAME]', 'Ash', 'Bone', 'Darkness', 'Dead', 'Death', 'Desolation', 'Despair', 'Devil', 'Doom', 'Dragon', 'Fate', 'Fear', 'Fire', 'Fury', 'Ghost', 'Giant', 'God', 'Gold', 'Heaven', 'Hell', 'Honor', 'Hope', 'Horror', 'King', 'Life', 'Light', 'Lord', 'Mist', 'Peril', 'Queen', 'Rain', 'Refuge', 'Regret', 'Savior', 'Shadow', 'Silver', 'Skull', 'Sky', 'Smoke', 'Snake', 'Sorrow', 'Storm', 'Sun', 'Thorn', 'Thunder', 'Traitor', 'Troll', 'Victory', 'Witch',]
// * Choose a name appropriate to your setting; or, if you have a name list, roll one up. Dw THE MAP
};
export function rollRegion(): string {
let template = sample(regions.templates);
let region = bodgeString(template, regions);
return region;
}
+12 -10
View File
@@ -1,17 +1,19 @@
import { random, sample } from 'lodash';
import { Creature, rollCreature } from './creatures';
import type { Kingdom } from './kingdoms';
export class Steading {
constructor(public roll: number = 0, public category: string = "") { };
builtBy: string;
description: string;
kingdom: string;
constructor(public roll: number = 0, public category: string = "", public description: string = "") { };
builtBy: Creature;
kingdom: Kingdom;
problem: string;
}
// This section is gonna need a lot of work
// TODO: This section is gonna need a lot of work
export function rollSteading(roll: number = random(1, 12)): Steading {
let steading = new Steading();
// STEADING Choose or roll 1d4 + 4 on the first CREATURE table(page 49) to see who builtit.
steading.builtBy = rollCreature(random(1, 4) + 4);
if (roll <= 5) {
steading = rollVillage();
} else if (roll <= 8) {
@@ -28,19 +30,19 @@ export function rollSteading(roll: number = random(1, 12)): Steading {
export function rollVillage(): Steading {
return new Steading(0, "village");
return new Steading(0, "village", "village");
}
export function rollTown(): Steading {
return new Steading(0, "town");
return new Steading(0, "town", "town");
}
export function rollKeep(): Steading {
return new Steading(0, "keep");
return new Steading(0, "keep", "keep");
}
export function rollCity(): Steading {
return new Steading(0, "city");
return new Steading(0, "city", "city");
}