touch all the things

This commit is contained in:
RussellChamp committed 2021-07-25 22:48:37 -04:00
1 parent b82a976e1a
commit c896a4b9d9
19 files changed
+1935 -81

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
Add Discovery, Danger, Details, NPC, and Dungeons roll tables
Put the rollers in their own accordion slots
Make everything look nicer
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>TPW Roller App</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css' />
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
+3
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import CreatureRoller from "./rollers/CreatureRoller.svelte";
import KingdomRoller from "./rollers/KingdomRoller.svelte";
import RegionRoller from "./rollers/RegionRoller.svelte";
import PlaceRoller from "./rollers/PlaceRoller.svelte";
@@ -16,6 +17,8 @@
<hr />
<PlaceRoller />
<hr />
<CreatureRoller />
<hr />
<FollowerRoller />
<hr />
<Footer />
+26
View File
@@ -0,0 +1,26 @@
<script lang="ts">
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { Creature, rollCreature } from "./creatures";
export let rollResults: { value: 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];
}
</script>
<main>
<h2>Random Creatures</h2>
<button type="button" class="btn btn-primary" on:click={roll}>Roll Creature</button>
{#each rollResults as result}
<h4>{result.timestamp}: {result.value.description}</h4>
{/each}
</main>
<style>
</style>
View File
Whitespace-only changes.
+1 -1
View File
@@ -3,7 +3,7 @@
import { format as formatDate } from "date-fns";
import { Button, Icon } from "sveltestrap";
import { kingdoms } from "./data";
import { kingdoms } from "./kingdoms";
let rollResults: { value: string; icon: string; timestamp: string }[] = [];
let kingdom = "arpad";
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { places } from "./data";
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { places } from "./places";
import { bodgeString } from "./utils";
export let rollResults: { value: string; timestamp: string }[] = [];
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { regions } from "./data";
import * as _ from "lodash";
import { format as formatDate } from "date-fns";
import { regions } from "./regions";
import { bodgeString } from "./utils";
export let rollResults: { value: string; timestamp: string }[] = [];
+302
View File
@@ -0,0 +1,302 @@
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';
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[] = [];
}
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 */
];
} else if (roll <= 8) {
return rollHumanoid(roll, subroll, specificroll);
} else if (roll <= 12) {
return rollMonster();
}
return creature;
}
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
];
if (subroll <= 7) {
creature.subtype = "earthbound";
creature.description = rollEarthboundBeast(specificroll);
} else if (subroll <= 10) {
creature.subtype = "airborne";
creature.description = rollAirborneBeast(specificroll);
} else if (subroll <= 12) {
creature.subtype = "water-going";
creature.description = rollWaterGoingBeast(specificroll);
}
return creature;
}
export function rollEarthboundBeast(roll: number = random(1, 12)): string {
return [
sample(["termite", "tick", "louse"]),
sample(["snail", "slug", "worm"]),
sample(["ant", "centipede", "scorpion"]),
sample(["snake", "lizard"]),
sample(["vole", "rat", "weasel"]),
sample(["boar", "pig"]),
sample(["dog", "fox", "wolf"]),
sample(["cat", "lion", "panther"]),
sample(["deer", "horse", "camel"]),
sample(["ox", "rhino"]),
sample(["bear", "ape", "gorilla"]),
sample(["mammoth", "dinosaur"])
][roll - 1];
}
export function rollAirborneBeast(roll: number = random(1, 12)): string {
return [
sample(["mosquito", "firefly"]),
sample(["locust", "dragonfly", "moth"]),
sample(["bee", "wasp"]),
sample(["chicken", "duck", "goose"]),
sample(["songbird", "parrot"]),
sample(["gull", "waterbird"]),
sample(["heron", "crane", "stork"]),
sample(["crow", "raven"]),
sample(["hawk", "falcon"]),
sample(["eagle", "owl"]),
"condor",
"pteranodon",
][roll - 1];
}
export function rollWaterGoingBeast(roll: number = random(1, 12)): string {
return [
"insect",
sample(["jelly", "anemone"]),
sample(["clam", "oyster", "snail"]),
sample(["eel", "snake"]),
sample(["frog", "toad"]),
"fish",
sample(["crab", "lobster"]),
"turtle",
sample(["alligator", "crocodile"]),
sample(["dolphin", "shark"]),
sample(["squid", "octopus"]),
"whale",
][roll - 1];
}
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 */
];
if (subroll <= 7) {
creature.subtype = "common";
creature.description = rollCommonHumanoid(specificroll);
} else if (subroll <= 10) {
creature.subtype = "uncommon";
creature.description = rollUncommonHumanoid(specificroll);
} else if (subroll <= 12) {
creature.subtype = "hybrid";
creature.description = rollHybridHumanoid(specificroll);
}
return creature;
}
export function rollCommonHumanoid(roll: number = random(1, 12)): string {
if (roll <= 3) {
return "halfling (small)";
} else if (roll <= 5) {
return sample(["goblin", "kobold"]) + " (small)";
} else if (roll <= 7) {
return sample(["dwarf", "gnome"]) + " (small)";
} else if (roll <= 9) {
return sample(["orc", "hobgoblin", "gnoll"]);
} else if (roll <= 11) {
return "elf";
} else if (roll <= 12) {
return "half-" + rollCommonHumanoid(random(1, 11));
}
return "";
}
export function rollUncommonHumanoid(roll: number = random(1, 12)): string {
if (roll <= 1) {
return "fey (tiny)";
} else if (roll <= 3) {
return sample(["catfolk", "dogfolk"]);
} else if (roll <= 6) {
return sample(["lizardfolk", "merfolk"]);
} else if (roll <= 7) {
return "birdfolk";
} else if (roll <= 10) {
return sample(["ogre", "troll"]) + " (large)";
} else if (roll <= 12) {
return sample(["cyclops", "giant"]) + " (large)";
}
return "";
}
export function rollHybridHumanoid(roll: number = random(1, 12)): string {
if (roll <= 2) {
return "centaur";
} else if (roll <= 5) {
return sample(["werewolf", "werebear"]);
} else if (roll <= 6) {
return `werecreature (human + ${rollBeast().description})`;
} else if (roll <= 10) {
return `human ${rollBeast().description}`;
}
else if (roll <= 12) {
return `human ${rollBeast().description}-${rollBeast().description}`;
}
return "";
}
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,
];
if (subroll <= 7) {
creature.subtype = "unusual";
creature.description = rollUnusualMonster(specificroll);
} else if (roll <= 10) {
creature.subtype = "rare";
creature.description = rollRareMonster(specificroll);
} else if (roll <= 12) {
creature.subtype = "legendary";
creature.description = rollLegendaryMonster(specificroll);
}
return creature;
}
export function rollUnusualMonster(roll: number = random(1, 12)): string {
if (roll <= 3) {
return "plant/fungus";
} else if (roll <= 5) {
return "undead human";
} else if (roll <= 6) {
return `undead ${rollHumanoid().description}`;
} else if (roll <= 8) {
return `${rollBeast().description}-${rollBeast().description}`;
} else if (roll <= 10) {
return `${rollBeast().description} with ${rollAbility().description}`;
} else if (roll <= 12) {
return `${rollBeast().description} with ${rollFeature().description}`;
}
return "";
}
export function rollRareMonster(roll: number = random(1, 12)): string {
if (roll <= 3) {
return sample(["slime", "ooze"]) + " (amorphous)";
} else if (roll <= 6) {
return "creation (construct)";
} else if (roll <= 9) {
return `${rollOddity().description} ${rollBeast().description}`;
} else if (roll <= 12) {
return rollUnnaturalEntity().description;
}
}
export function rollLegendaryMonster(roll: number = random(1, 12)): string {
if (roll <= 3) {
return `${sample(["dragon", "colossus"])} (huge)`;
} else if (roll <= 6) {
return `${rollUnusualMonster()} (huge)`;
} else if (roll <= 9) {
return `${rollRareMonster()} (huge)`;
} else if (roll <= 10) {
return `${rollBeast().description} dragon`;
} else if (roll <= 11) {
return `${rollUnusualMonster()} dragon`;
} else if (roll <= 12) {
return `${rollRareMonster()} dragon`;
}
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
+160
View File
@@ -0,0 +1,160 @@
import { random, sample } from 'lodash';
import { Creature, rollCreature } from './creatures';
import { rollOddity } from './details';
export class Danger {
constructor(public roll: number = 0, public subroll: number = 0, public specificroll: number = 0, public category: string = "", public subcategory: string = "", public description: string = "") { };
creature?: Creature;
}
export function rollDanger(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Danger {
if (roll <= 1) {
return rollUnnaturalEntity(roll, subroll, specificroll);
} else if (roll <= 6) {
return rollHazard(roll, subroll, specificroll);
} else if (roll <= 12) {
return rollDangerCreature(roll);
}
return new Danger();
}
export function rollUnnaturalEntity(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Danger {
let danger = new Danger(roll, subroll, specificroll, "unnatural entity");
if (subroll <= 8) {
danger.subcategory = "undead";
danger.description = rollUndeadEntity(specificroll);
} else if (subroll <= 11) {
danger.subcategory = "planar";
danger.description = rollPlanarEntity(specificroll);
} else if (subroll <= 12) {
danger.subcategory = "divine";
danger.description = rollDivineEntity(specificroll);
}
return danger;
}
export function rollUndeadEntity(roll: number = random(1, 12)): string {
if (roll <= 4) {
return sample(["haunt", "wisp"]);
} else if (roll <= 8) {
return sample(["ghost", "spectre"]);
} else if (roll <= 9) {
return "banshee";
} else if (roll <= 11) {
return sample(["wraith", "wight"]);
} else if (roll <= 12) {
return `spirit ${sample(["lord", "master"])}`;
}
}
export function rollPlanarEntity(roll: number = random(1, 12)): string {
if (roll <= 3) {
return "imp (small)";
} else if (roll <= 6) {
return "lesser elemental";
} else if (roll <= 9) {
return `lesser ${sample(["demon", "horror"])}`;
} else if (roll <= 10) {
return "greater elemental";
} else if (roll <= 11) {
return `greater ${sample(["demon", "horror"])}`
} else if (roll <= 12) {
return `${sample(["devil", "elemental"])} lord`;
}
}
export function rollDivineEntity(roll: number = random(1, 12)): string {
if (roll <= 5) {
return "agent";
} else if (roll <= 9) {
return "champion";
} else if (roll <= 11) {
return "army (horde)";
} else if (roll <= 12) {
return "avatar";
}
}
export function rollHazard(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Danger {
let danger = new Danger(roll, subroll, specificroll, "hazard");
if (subroll <= 2) {
danger.subcategory = "unnatural";
danger.description = rollUnnaturalHazard(specificroll);
} else if (subroll <= 10) {
danger.subcategory = "natural";
danger.description = rollNaturalHazard(specificroll);
} else if (subroll <= 12) {
danger.subcategory = "trap";
danger.description = rollTrapHazard(specificroll);
}
return danger;
}
export function rollUnnaturalHazard(roll: number = random(1, 12)): string {
if (roll <= 3) {
return sample(["taint", "blight", "curse"]);
} else if (roll <= 8) {
return `arcane ${sample(["trap", "effect"])}`;
} else if (roll <= 11) {
return `planar ${sample(["trap", "effect"])}`;
} else if (roll <= 12) {
return "divine";
}
return "";
}
export function rollNaturalHazard(roll: number = random(1, 12)): string {
if (roll <= 2) {
return `blinding ${sample(["mist", "fog"])}`;
} else if (roll <= 4) {
return sample(["bog", "mire", "quicksand"]);
} else if (roll <= 7) {
return sample(["pitfall", "sinkhole", "chasm"]);
} else if (roll <= 9) {
return sample(["poison", "disease"]);
} else if (roll <= 11) {
return sample(["flood", "fire", "tornado"]);
} else if (roll <= 12) {
return rollOddity().description;
}
return "";
}
export function rollTrapHazard(roll: number = random(1, 12)): string {
if (roll <= 2) {
return "alarm";
} else if (roll <= 5) {
return sample(["ensnaring", "paralyzing"]);
} else if (roll <= 8) {
return "injurious (pit, etc.)";
} else if (roll <= 9) {
return sample(["gas", "fire", "poison"]);
} else if (roll <= 12) {
return "ambush";
}
return "";
}
// 1-3 1-2 1-2
// 4-8 34 3-5
// 9-11 5-7 6-8
// 12 divine 8-9 9
// 10-11 10-12 ambush
// ASPECT, VISIBILITY 12 ODDITY CREATURE responsible,
// ASPECT, VISIBILITY
export function rollDangerCreature(roll: number = random(1, 12)): Danger {
let danger = new Danger(roll, 0, 0, "creature");
danger.creature = rollCreature();
return danger;
}
-77
View File
@@ -1,77 +0,0 @@
export class Kingdom {
constructor(public description: string = "", public subtitle: string = "") { };
categories: string[];
male: string[];
female: string[];
steading: string[];
mount: string[];
}
export const kingdoms: { [kingdom: string]: Kingdom } = {
arpad: {
description: 'The Kingdom of Arpad',
subtitle: 'Based loosely on Hungarian',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Agoston', 'Arpad', 'Attila', 'Bognar', 'Denes', 'Edmond', 'Erno', 'Etele', 'Ferdinand', 'Florian', 'Geza', 'Gyula', 'Hugo', 'Karcsi', 'Konrad', 'Lazlo', 'Lukas', 'Marko', 'Miklos', 'Peti', 'Robi', 'Tamas', 'Ronold', 'Viktor', 'Zoltan',],
female: ['Abigel', 'Aliz', 'Amalia', 'Andrea', 'Aranka', 'Csilla', 'Edit', 'Erzebet', 'Gertrud', 'Greta', 'Iren', 'Kamilla', 'Lara', 'Ua', 'Lujza', 'Matild', 'Olga', 'Otilia', 'Parma', 'Roza', 'Terez', 'Tunda', 'Valeria', 'Vilma', 'Viola',],
steading: ['Aldott (Blessed)', 'Almahid (Applebridge)', 'Elesett (Fallen)', 'Feketz (Black Rock)', 'Godor (Pit)', 'Kelegaz (Eastford)', 'Kigyov (Snake Swamp)', 'Kiralokas (Queen\'s Castle)', 'Kiralsir (King\'s Grave)', 'Magziklar (Highcliff)', 'Mocsar (Fen)', 'Nagyvros (Hightown)', 'Okorm (Oxfield)', 'Orkfal (Orcwail)', 'Perov (Redwater)', 'Soterdo (Dark Wood)', 'Tehenvar (Cow Town)', 'Toron (Tower)', 'Torott (Ironhold)', 'Utolszer (Last Stand)', 'Valavolg (Greendale)', 'Vastar (Dwarf Watch)', 'Viz (Oxfield)', 'Volgyom (Valley)', 'Zoldom (Green Hill)',],
mount: ['Barat (Friend)', 'Barsony (Velvet)', 'Eclesem (Sweetheart)', 'Egatz (Skyfire)', 'Eso (Rain)', 'Fakyla (Torch)', 'Feketsor (Black Mane)', 'Felho (Cloud)', 'Fiotta (Fleet)', 'Gazda (Master)', 'Hold (Moon)', 'Igaslo (Dobbin)', 'Ijeda (Skittish)', 'Koszalo (Rambler)', 'Megmento (Savior)', 'Napfen (Sunshine)', 'Rozsa (Rose)', 'Szamla (Bill)', 'Szellem (Spritied)', 'Szeplok (Freckles)', 'Szerence (Lucky)', 'Tusko (Stumps)', 'Vad (Wild One)', 'Vihar (Storm)', 'Villam (Lightning)',]
},
oloru: {
description: 'Oloru, Sky-God\'s Home',
subtitle: 'Based loosely on Yoruba',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Mibemi', 'Aboye', 'Megoke', 'Ayokunle', 'Babajide', 'Babatunde', 'Enitan', 'Femi', 'Kayin', 'Kayode', 'Lanre', 'Lekan', 'Mongo', 'Nwachukwu', 'Oban', 'Ogun', 'Olukayode', 'Oluwalanni', 'Oluwatoke', 'Onipede', 'Sijuade', 'Toben', 'Utiba', 'Zaki', 'Zoputan',],
female: ['Abeni', 'Me', 'Alaba', 'Bolanle', 'Bosade', 'Daraja', 'Fan', 'Gbemisola', 'Ife', 'Ige', 'Lewa', 'Mojisola', 'Monifa', 'Olufemi', 'Omolara', 'Oni', 'Orisa', 'Osa', 'Ronke', 'Shanum', 'Simisola', 'Titlayo', 'Yejide', 'Yewande', 'Zauna',],
steading: ['Asala Ilu (Desert Town)', 'Atijo ma (Old Fire)', 'Bajesia (Broken Banner)', 'Dudu Olomi (Blackmarsh)', 'Ebutte Meta (Three Ports)', 'Eodo (Snake River)', 'Esukale (Devil\'s Dinner)', 'Fadormi (Silver Spring)', 'Funfumi (Whitewater)', 'Gooluna (Gold Road)', 'Ijisofo (Storm Hollow)', 'Ikukenu (Dearth\'s Door)', 'Twin Ago (Faery Watch)', 'Jinibi (Far Place)', 'Oba Ile (King\'s Home)', 'Oduroke (Prayer Hill)', 'Ogbinibi (Farming Place)', 'Ogunibi (Battle Place)', 'Okanigi (One Tree)', 'Okutasibo (Stone Marker)', 'Olorusura (God\'s Treasure)', 'Olusajeki (Wizard\'s Keep)', 'Oluwakaji (Lord\'s Tomb)', 'Opolokuta (Many Stones)', 'Opoligi (Many Trees)'],
mount: ['Adiitu (Mystery)', 'Alayo (Happy)', 'Atale (Ginger)', 'Bilu (Bill)', 'Dudupatak (Dark Hoof)', 'Egun (Bramble)', 'Eniyan (Ember)', 'Esirun (Longshanks)', 'Fenuko (Kiss)', 'Funfungo (White Hair)', 'Gunirun (Longhair)', 'Imole (Bright)', 'Imole Uju (Bright Eye)', 'Ira (Rambler)', 'Itan (Story)', 'Iyebiye (Precious)', 'Lulu (Powder)', 'Nilera (Healthy)', 'Ogbo (Spotted)', 'Ogboju (Brave Heart)', 'Ojiji (Shadow)', 'Olooto (Faithful)', 'Orisa (Spring)', 'Orun (Heaven)', 'Sare (Flash)'],
},
valkoina: {
description: 'Valkoina, Land of White Iron',
subtitle: 'Based loosely on Finnish',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Aatami', 'Armas', 'Arsi', 'Arvi', 'Eetu', 'Hannu', 'Heimo', 'Ilkka', 'Jorma', 'Kaapo', 'Kain', 'Kauko', 'Lari', 'Manu', 'Nuutti', 'Petri', 'Rairno', 'Reima', 'Risto', 'Sakari', 'Sampsa', 'Seppo', 'Taito', 'Terho', 'Vilppu',],
female: ['Aija', 'Ama', 'Ainikki', 'Heini', 'Ilona', 'Irja', 'Jaana', 'Kirsi', 'Maija', 'Marita', 'Miina', 'Mimmi', 'Minja', 'Mira', 'Naemi', 'Outi', 'Pirjo', 'Paivikki', 'Riikka', 'Saimi', 'Suoma', 'Suvi', 'Tuula', 'Vellamo', 'Virpi',],
steading: ['Etuvartio (Outpost)', 'Hopea Kaivos (Silver Mine)', 'Kalapunki (Fish Town)', 'Kivimurri (Stone Wall)', 'Maaginen (Magic)', 'Maki Linna (Hill Castle)', 'Merenranta (Seaside)', 'Metsastysniaat (Hunting Ground)', 'Mustakota (Black Hut)', 'Maenrinne (Hillside)', 'Paja (Forge)', 'Pienni Paikka (Low Place)', 'Pyha Paikka (Holy Place)', 'Rantakallio (Cliff)', 'Rikki (Broken)', 'Suo (Swamp)', 'Suosi (Favored)', 'Torni (Tower)', 'Turvapaikka (Refuge)', 'Uusipunki (New Town)', 'Valkoinen Kivi (Whitestone)', 'Valtaistuin (Throne)', 'Vapaanki (Free Town)', 'Vihrea Paikka (Green Place)', 'Viimeinen Koti (Last Home)',],
mount: ['Aave (Ghost)', 'Enkeli (Angel)', 'Haiva (Shadow)', 'Hopea (Silver)', 'Ilmavirta (Current)', 'Kesi (Tame)', 'Kestaba (Durable)', 'Kiukuinnen (Angry)', 'Lansiviima (West Wind)', 'Luotettava (Trusty)', 'Myrskyisa (Stormy)', 'Nokka (Bill)', 'Noyra (Humble)', 'Pitkanena (Longnose)', 'Saikki (Skittish)', 'Salama (Thunderbolt)', 'Sankani (Hero)', 'Sisko (sister)', 'Upea (Magnificent)', 'Vahva (Strong)', 'Valkoinen (White)', 'Varmaotteinen (Surefoot)', 'Vesuri (Billhook)', 'Vinha (Fast)', 'Ystava (Friend)',]
},
tamanarugan: {
description: 'The Tamanarugan Empire',
subtitle: 'Based loosely on Indonesian',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Me', 'Mi', 'Amaziah', 'Ayokunle', 'Ary', 'Bambang', 'Bima', 'Budi', 'Darma', 'Dian', 'Eli', 'Gunardi', 'Hartono', 'Irwan', 'Lauwita', 'Manusama', 'Okan', 'Onesimus', 'Sammin', 'Tameem', 'Tanaya', 'Tirto', 'Wiryono', 'Yandi', 'Zebulun',],
female: ['Mah', 'Bulan', 'Candrakusuma', 'Devi', 'Hanjojo', 'Iman', 'Intan', 'Laksniini', 'Lestari', 'Limijanto', 'Marah', 'Megawati', 'Melati', 'Nadiyya', 'Ophrah', 'Ramza', 'Sapphiral', 'Selah', 'Suminten', 'Tamar', 'Tanjaya', 'Tjokro', 'Tri', 'Wangi', 'Zenze',],
steading: ['Airdib (Blessed Waters)', 'Airjinh (Clearwater)', 'Akhir Jalan (Road\'s End)', 'Berdarah (Bloody)', 'Bidang Bera (Fallow Field)', 'Candibula (Moon Temple)', 'Ditingga (Forsaken)', 'Emasungai (Gold Creek)', 'Gunung (Mountain)', 'Kayu (Timber)', 'Kuil (Temple)', 'Ladang Hijau (Greenfield)', 'Lembah (Valley)', 'Menjau (Far Away)', 'Ngarai (Canyon)', 'Persimpangan (Crossroads)', 'Puncakit (Hilltop)', 'Sungairac (}bison River)', 'Teibing (Cliffside)', 'Tempat Aman (Safe Place)', 'Tempat Istir (Rest Place)', 'Terkutuk (Cursed)', 'Tersentu (Touched by God)', 'Wahah (Oasis)', 'Yangtinggi (High Tower)',],
mount: ['Anginu (North Wind)', 'Api (Blaze)', 'Bakat (Felicity)', 'Bakti (Loyal)', 'Beruntung (Lucky)', 'Bilah (Blade)', 'Biru (Blue)', 'Gemetar (Quiver)', 'Guntur (Thunder)', 'Hidungi (Black Nose)', 'Janda (Widow)', 'Kakicerah (Bright Foot)', 'Kakiring (Light Foot)', 'Keriangan (Sunshine)', 'Kunang (Firefly)', 'Lapar (Hungry)', 'Murni (Pure)', 'Paruh (Bill)', 'Penebus (Avenger)', 'Penyelemat (Savior)', 'Prajurit (Warrior)', 'Rusak (Broken)', 'Satucep (Fast One)', 'Setia (True Heart)', 'Tahanla (Durable)',]
},
};
export const regions = {
templates: [
'[ADJECTIVE] [TERRAIN]',
'[TERRAIN] of (the) [NOUN]',
'The [TERRAIN] [ADJECTIVE]',
'[NOUN] [TERRAIN]',
'[NOUN]\'s [ADJECTIVE] [TERRAIN]',
'[ADJECTIVE] [TERRAIN] of (the) [NOUN]'
],
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 const places = {
templates: [
'The [PLACE]',
'The [ADJECTIVE] [PLACE]',
'The [PLACE] of (the) [NOUN]',
'(The) [NOUN]\'s [PLACE]',
'[PLACE] of the [ADJECTIVE] [NOUN]',
'The [ADJECTIVE] [NOUN]'
],
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.
};
+489
View File
@@ -0,0 +1,489 @@
import { random, replace, sample, sampleSize } from "lodash";
export class Ability {
constructor(public roll: number = 0, public description = "") { };
}
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",
rollMagicType().description,
"drain life/magic",
`immunity to ${rollElement().description}`,
"read/control minds",
"[ABILITY] and [ABILITY]"
][roll - 1];
ability.description = replace(ability.description, /\[ABILITY\]/g, () => rollAbility(random(1, 11)).description);
return ability;
}
export class Activity {
constructor(public roll: number = 0, public description: string = "") { };
}
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",
"returning home",
"building/excavating",
"sleeping",
"dying",
][roll - 1];
return activity;
}
export class Adjective {
constructor(public roll: number = 0, public description: string = "") { };
}
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",
"colorful",
][roll - 1];
return adjective;
}
export class Age {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollAge(roll: number = random(1, 12)): Age {
let age = new Age(roll);
if (roll <= 1) {
age.description = "being born/built";
} else if (roll <= 4) {
age.description = "young/recent";
} else if (roll <= 7) {
age.description = "middle-aged";
} else if (roll <= 9) {
age.description = "old";
} else if (roll <= 11) {
age.description = "ancient";
} else if (roll <= 12) {
age.description = "pre-historic";
}
return age;
}
export class Alignment {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollAlignment(roll: number = random(1, 12)): Alignment {
let alignment = new Alignment(roll);
if (roll <= 2) {
alignment.description = "chaotic";
} else if (roll <= 4) {
alignment.description = "evil";
} else if (roll <= 8) {
alignment.description = "neutral";
} else if (roll <= 10) {
alignment.description = "good";
} else if (roll <= 12) {
alignment.description = "lawful";
}
return alignment;
}
export class Aspect {
constructor(public roll: number = 0, public description: string = "") { };
}
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",
rollElement().description,
"[ASPECT] and [ASPECT]"
][roll - 1];
aspect.description = replace(aspect.description, /\[ASPECT\]/g, () => rollAspect(random(1, 11)).description);
return aspect;
}
export class Condition {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollCondition(roll: number = random(1, 12)): Condition {
let condition = new Condition(roll);
if (roll <= 1) {
condition.description = "being built/born";
} else if (roll <= 4) {
condition.description = "intact/healthy/stable";
} else if (roll <= 7) {
condition.description = "occupied/active/alert";
} else if (roll <= 9) {
condition.description = "worn/tired/weak";
} else if (roll <= 10) {
condition.description = "vacant/lost";
} else if (roll <= 11) {
condition.description = "ruined/defiled/dying";
} else if (roll <= 12) {
condition.description = "disappeared/dead";
}
return condition;
}
export class Disposition {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollDisposition(roll: number = random(1, 12)): Disposition {
let disposition = new Disposition(roll);
if (roll <= 1) {
disposition.description = "attacking";
} else if (roll <= 4) {
disposition.description = "hostile/aggressive";
} else if (roll <= 6) {
disposition.description = "cautious/doubtful";
} else if (roll <= 7) {
disposition.description = "fearful/fleeing";
} else if (roll <= 10) {
disposition.description = "neutral";
} else if (roll <= 11) {
disposition.description = "curious/hopeful";
} else if (roll <= 12) {
disposition.description = "friendly";
}
return disposition;
}
export class Element {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollElement(roll: number = random(1, 12)): Element {
let element = new Element(roll);
if (roll <= 2) {
element.description = "air";
} else if (roll <= 4) {
element.description = "earth";
} else if (roll <= 6) {
element.description = "fire";
} else if (roll <= 8) {
element.description = "water";
} else if (roll <= 10) {
element.description = "life";
} else if (roll <= 12) {
element.description = "death";
}
return element;
}
export class Feature {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollFeature(roll: number = random(1, 12)): Feature {
let feature = new Feature(roll);
if (roll <= 1) {
feature.description = "heavily armored";
} else if (roll <= 3) {
feature.description = "winged/flying";
} else if (roll <= 4) {
feature.description = "multiple heads/headless";
} else if (roll <= 5) {
feature.description = "many eyes/one eye";
} else if (roll <= 6) {
feature.description = "many limbs/tails";
} else if (roll <= 7) {
feature.description = "tentacles/tendrils";
} else if (roll <= 8) {
feature.description = rollAspect().description;
} else if (roll <= 9) {
feature.description = rollElement().description;
} else if (roll <= 10) {
feature.description = rollMagicType().description;
} else if (roll <= 11) {
feature.description = rollOddity().description;
} else if (roll <= 12) {
feature.description = `${rollFeature(random(1, 11)).description} and ${rollFeature(random(1, 11)).description}`;
}
return feature;
}
export class MagicType {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollMagicType(roll: number = random(1, 12)): MagicType {
let magicType = new MagicType(roll);
if (roll <= 2) {
magicType.description = "divination";
} else if (roll <= 4) {
magicType.description = "enchantment";
} else if (roll <= 6) {
magicType.description = "evocation";
} else if (roll <= 8) {
magicType.description = "illusion";
} else if (roll <= 10) {
magicType.description = "necromancy";
} else if (roll <= 12) {
magicType.description = "summoning";
}
return magicType;
}
export class NumAppearing {
constructor(public roll: number = 0, public description: string = "", public count: string = "") { };
}
export function rollNumAppearing(roll: number = random(1, 12)): NumAppearing {
let numAppearing = new NumAppearing(roll);
if (roll <= 4) {
numAppearing.description = "solitary";
numAppearing.count = "1";
} else if (roll <= 9) {
numAppearing.description = "group";
numAppearing.count = "1d6+2";
} else if (roll <= 12) {
numAppearing.description = "horde";
numAppearing.count = "4d6 per wave";
}
return numAppearing;
}
export class Oddity {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollOddity(roll: number = random(1, 12)): Oddity {
let oddity = new Oddity(roll);
oddity.description = [
"weird color/smell/sound",
"geometric",
"web/network/system",
"crystalline/glass-like",
"fungal",
"gaseous/smokey",
"mirage/illusion",
"volcanic/explosive",
"magnetic/repellant",
"devoid of life",
"unexpectedly alive",
"[ODDITY] and [ODDITY]",
][roll - 1];
oddity.description = replace(oddity.description, /\[ODDITY\]/g, () => rollOddity(random(1, 11)).description);
return oddity;
}
export class Orientation {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollOrientation(roll: number = random(1, 12)): Orientation {
let orientation = new Orientation(roll);
if (roll <= 2) {
orientation.description = "down/earthward";
} else if (roll <= 3) {
orientation.description = "north";
} else if (roll <= 4) {
orientation.description = "northeast";
} else if (roll <= 5) {
orientation.description = "east";
} else if (roll <= 6) {
orientation.description = "southeast";
} else if (roll <= 7) {
orientation.description = "south";
} else if (roll <= 8) {
orientation.description = "southwest";
} else if (roll <= 9) {
orientation.description = "west";
} else if (roll <= 10) {
orientation.description = "northwest";
} else if (roll <= 12) {
orientation.description = "up/skyward ";
}
return orientation;
}
export class Ruination {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollRuination(roll: number = random(1, 12)): Ruination {
let ruination = new Ruination(roll);
if (roll <= 1) {
ruination.description = "arcane disaster";
} else if (roll <= 2) {
ruination.description = "damnation/curse";
} else if (roll <= 4) {
ruination.description = "earthquake/fire/flood";
} else if (roll <= 6) {
ruination.description = "plague/famine/drought";
} else if (roll <= 8) {
ruination.description = "overrun by monsters";
} else if (roll <= 10) {
ruination.description = "war/invasion";
} else if (roll <= 11) {
ruination.description = "depleted resources";
} else if (roll <= 12) {
ruination.description = "better prospects elsewhere";
}
return ruination;
}
export class Size {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollSize(roll: number = random(1, 12)): Size {
let size = new Size(roll);
if (roll <= 1) {
size.description = "tiny";
} else if (roll <= 3) {
size.description = "small";
} else if (roll <= 9) {
size.description = "medium-sized";
} else if (roll <= 11) {
size.description = "large";
} else if (roll <= 12) {
size.description = "huge";
}
return size;
}
export class Tag {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollTag(roll: number = random(1, 12)) {
let tag = new Tag(roll);
if (roll <= 1) {
tag.description = "amorphous";
} else if (roll <= 2) {
tag.description = "cautious";
} else if (roll <= 3) {
tag.description = "construct";
} else if (roll <= 4) {
tag.description = "devious";
} else if (roll <= 5) {
tag.description = "intelligent";
} else if (roll <= 6) {
tag.description = "magical";
} else if (roll <= 8) {
tag.description = "organized";
} else if (roll <= 9) {
tag.description = "planar";
} else if (roll <= 10) {
tag.description = "stealthy";
} else if (roll <= 11) {
tag.description = "terrifying";
} else if (roll <= 12) {
tag.description = `${rollTag(random(1, 11)).description} and ${rollTag(random(1, 11)).description}`;
}
return tag;
}
export class Terrain {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollTerrain(roll: number = random(1, 12)): Terrain {
let terrain = new Terrain(roll);
if (roll <= 1) {
terrain.description = "wasteland/desert";
} else if (roll <= 3) {
terrain.description = "flatland/plain";
} else if (roll <= 4) {
terrain.description = "wetland/marsh/swamp";
} else if (roll <= 7) {
terrain.description = "woodland/forest/jungle";
} else if (roll <= 9) {
terrain.description = "highland/hills";
} else if (roll <= 11) {
terrain.description = "mountains";
} else if (roll <= 12) {
terrain.description = rollOddity().description;
}
return terrain;
}
export class Visibility {
constructor(public roll: number = 0, public description: string = "") { };
}
export function rollVisibility(roll: number = random(1, 12)): Visibility {
let visibility = new Visibility(roll);
if (roll <= 2) {
visibility.description = "buried/camouflaged/nigh invisible";
} else if (roll <= 6) {
visibility.description = "partly covered/overgrown/hidden";
} else if (roll <= 9) {
visibility.description = "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";
}
return visibility;
}
+354
View File
@@ -0,0 +1,354 @@
import { random, sample } from 'lodash';
import { Creature, rollCreature } from './creatures';
import { rollAlignment, rollElement, rollMagicType } from './details';
import { rollSteading, Steading } from './steadings';
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 = "") { };
creature?: Creature;
steading?: Steading;
}
export function rollDiscovery() {
let roll = random(1, 12);
let subroll = random(1, 12);
let specificroll = random(1, 12);
if (roll <= 1) {
return rollUnnaturalFeature(roll, subroll, specificroll);
} else if (roll <= 4) {
return rollNaturalFeature(roll, subroll, specificroll);
} else if (roll <= 6) {
return rollEvidence(roll, subroll, specificroll);
} else if (roll <= 8) {
return rollDiscoveryCreature(roll);
} else if (roll <= 12) {
return rollStructure(roll, subroll, specificroll);
}
return new Discovery(roll, subroll, specificroll);
}
export function rollUnnaturalFeature(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)): Discovery {
var discovery = new Discovery(roll, subroll, specificroll, "unnatural feature");
if (subroll <= 9) {
discovery.subcategory = "arcane";
discovery.description = rollArcaneFeature(specificroll);
} else if (subroll <= 11) {
discovery.subcategory = "planar";
discovery.description = rollPlanarFeature(specificroll);
} else if (subroll <= 12) {
discovery.subcategory = "divine";
discovery.description = rollDivineFeature(specificroll);
}
return discovery;
}
export function rollArcaneFeature(roll: number = random(1, 12)): string {
let feature = random(1, 3) == 3 ? sample([rollAlignment(), rollMagicType()]) + " " : "";
if (roll <= 2) {
feature += "residue";
} else if (roll <= 5) {
feature += "blight";
} else if (roll <= 7) {
feature += "alteration/mutation";
} else if (roll <= 10) {
feature += "enchantment";
} else if (roll <= 12) {
feature += "source/repository";
}
return feature;
}
export function rollPlanarFeature(roll: number = random(1, 12)): string {
let feature = random(1, 3) == 3 ? sample([rollAlignment(), rollElement()]) + " " : "";
if (roll <= 4) {
feature += "distortion/warp";
} else if (roll <= 8) {
feature += "portal/gate";
} else if (roll <= 10) {
feature += "rift/tear";
} else if (roll <= 12) {
feature += "outpost";
}
return feature;
}
export function rollDivineFeature(roll: number = random(1, 12)) {
let feature = random(1, 3) == 3 ? sample([rollAlignment(), rollElement()]) + " " : "";
if (roll <= 3) {
feature += "mark/sign";
} else if (roll <= 6) {
feature += "cursed place";
} else if (roll <= 9) {
feature += "hallowed place";
} else if (roll <= 11) {
feature += "watched place";
} else if (roll <= 12) {
feature += "presence";
}
return feature;
}
export function rollNaturalFeature(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)) {
var discovery = new Discovery(roll, subroll, specificroll, "natural feature");
if (subroll <= 2) {
discovery.subcategory = "lair";
discovery.description = rollLair(specificroll)
} else if (subroll <= 4) {
discovery.subcategory = "obstacle";
discovery.description = rollObstacle(specificroll);
} else if (subroll <= 7) {
discovery.subcategory = "terrain change";
discovery.description = rollTerrainChange(specificroll);
} else if (subroll <= 9) {
discovery.subcategory = "water feature";
discovery.description = rollWaterFeature(specificroll);
} else if (subroll <= 11) {
discovery.subcategory = "terrain change";
discovery.description = rollLandmark(specificroll);
} else if (subroll <= 12) {
discovery.subcategory = "terrain change";
discovery.description = rollResource(specificroll);
}
return discovery;
}
export function rollLair(roll: number = random(1, 12)): string {
// 1-2 LAIR
// 1-3 burrow
// 4-7 cave/tunnels
// 8-9 nest/aerie
// 10 hive
// 1142 ruins (see STRUCTURE)
return "";
}
export function rollObstacle(roll: number = random(1, 12)): string {
// 3-4 OBSTACLE
// 1-5 difficult ground
// (specific to terrain)
// 6-8 cliff/crevasse/chasm
// 9-10 ravine/gorge
// 11-12 ODDITY
return "";
}
export function rollTerrainChange(roll: number = random(1, 12)): string {
// 5-7 TERRAIN CHANGE
// 14 limited area of an
// another TERRAIN type
// 5-6 crevice/hole/pit/cave
// 7-8 altitude change
// 9-10 canyon/valley
// 11-12 rise/peak in distance
return "";
}
export function rollWaterFeature(roll: number = random(1, 12)): string {
// 8-9 WATER FEATURE 10-11 LANDMARK 12 RESOURCE
// I spring/hotspring 1-3 water-based (water- 1-4 game/fruit/vegetable
// 2 waterfall/geyser fall, geyser, etc.) 5-6 herb/spice/dye source
// 3-6 creek/stream/brook 4-6 plant-based (ancient 7-9 timber/stone
// 7-8 pond/lake tree, giant flowers, etc.) 10-11 ore (copper, iron, etc.)
// 9-10 river 7-10 earth-based (peak, 12 precious metal/gems
// 11-12 sea/ocean formation, crater, etc.)
// SIZE, ............................... VISIBILITY.
return "";
}
export function rollLandmark(roll: number = random(1, 12)): string {
// 8-9 WATER FEATURE 10-11 LANDMARK 12 RESOURCE
// I spring/hotspring 1-3 water-based (water- 1-4 game/fruit/vegetable
// 2 waterfall/geyser fall, geyser, etc.) 5-6 herb/spice/dye source
// 3-6 creek/stream/brook 4-6 plant-based (ancient 7-9 timber/stone
// 7-8 pond/lake tree, giant flowers, etc.) 10-11 ore (copper, iron, etc.)
// 9-10 river 7-10 earth-based (peak, 12 precious metal/gems
// 11-12 sea/ocean formation, crater, etc.)
// SIZE, ............................... VISIBILITY.
return "";
}
export function rollResource(roll: number = random(1, 12)): string {
// 8-9 WATER FEATURE 10-11 LANDMARK 12 RESOURCE
// I spring/hotspring 1-3 water-based (water- 1-4 game/fruit/vegetable
// 2 waterfall/geyser fall, geyser, etc.) 5-6 herb/spice/dye source
// 3-6 creek/stream/brook 4-6 plant-based (ancient 7-9 timber/stone
// 7-8 pond/lake tree, giant flowers, etc.) 10-11 ore (copper, iron, etc.)
// 9-10 river 7-10 earth-based (peak, 12 precious metal/gems
// 11-12 sea/ocean formation, crater, etc.)
// SIZE, ............................... VISIBILITY.
return "";
}
function rollEvidence(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)) {
let discovery = new Discovery(roll, subroll, specificroll, "evidence");
if (subroll <= 6) {
discovery.subcategory = "tracks/spoor";
discovery.description = rollTracksSpoor(specificroll);
} else if (subroll <= 10) {
discovery.subcategory = "remains/debris";
discovery.description = rollRemainsDebris(specificroll);
} else if (subroll <= 12) {
discovery.subcategory = "stash/cache";
discovery.description = rollStashCache(specificroll);
}
return discovery;
}
export function rollTracksSpoor(roll: number = random(1, 12)): string {
// 1-6 TRACKS/SPOOR
// 1-3 faint/unclear
// 4-6 definite/clear
// 7-8 multiple
// 9-10 signs of violence
// 11-12 trail of blood/other
// AGE, CREATURE responsible
// 44
return "";
}
export function rollRemainsDebris(roll: number = random(1, 12)): string {
// 7-10 REMAINS/DEBRIS
// 1-4 bones
// 5-7 corpse/carcass
// 8-9 site of violence
// 10 junk/refuse
// 11 lost supplies/cargo
// 12 tools/weapons/armor
// AGE, VISIBILITY
return "";
}
export function rollStashCache(roll: number = random(1, 12)): string {
// 1142 STASH/CACHE
// 1-3 trinkets/coins
// 4-5 tools/weapons/armor
// 6-7 map
// 8-9 food/supplies
// 10-12 treasure (p43)
return "";
}
export function rollDiscoveryCreature(roll: number = random(1, 12)): Discovery {
let discovery = new Discovery(roll, 0, 0, "creature");
discovery.creature = rollCreature();
return discovery;
}
export function rollStructure(roll: number = random(1, 12), subroll: number = random(1, 12), specificroll: number = random(1, 12)) {
let discovery = new Discovery(roll, subroll, specificroll, "structure");
if (subroll <= 1) {
discovery.subcategory = "enigmatic";
discovery.description = rollEnigmaticStructure(specificroll);
} else if (subroll <= 3) {
discovery.subcategory = "infrastructure";
discovery.description = rollInfrastructureStructure(specificroll);
} else if (subroll <= 4) {
discovery.subcategory = "dwelling";
discovery.description = rollDwellingStructure(specificroll);
} else if (subroll <= 6) {
discovery.subcategory = "burial/religious";
discovery.description = rollBurialReligiousStructure(specificroll);
} else if (subroll <= 8) {
discovery.subcategory = "steading";
discovery.description = "steading";
discovery.specificroll = 0;
discovery.steading = rollSteading();
} else if (subroll <= 12) {
discovery.subcategory = "ruin";
discovery.description = rollRuinStructure(specificroll);
}
return discovery;
}
export function rollEnigmaticStructure(roll: number = random(1, 12)): string {
// 912 STRUCTURE. Who built it? Is it connected to anything else they made nearby?
// 1 ENIGMATIC 2-3 INFRASTRUCTURE 4 DWELLING
// 1-4 earthworks 14 track/path 1-3 campsite
// 5-8 megalith 5-8 road 4-6 hovel/hut
// 941 statue/idol/totem 9-10 bridge/ford 7-8 farm
// 12 ODDITY 11 mine/quarry 9-10 inn/roadhouse
// AGE (1d844) SIZE (1d844) 12 aqueduct/canal/portal 1112 tower/keep/estate
// VISIBILITY CREATURE responsible (1d4.4) CREATURE responsible (1d4+4)
return "";
}
export function rollInfrastructureStructure(roll: number = random(1, 12)): string {
// 912 STRUCTURE. Who built it? Is it connected to anything else they made nearby?
// 1 ENIGMATIC 2-3 INFRASTRUCTURE 4 DWELLING
// 1-4 earthworks 14 track/path 1-3 campsite
// 5-8 megalith 5-8 road 4-6 hovel/hut
// 941 statue/idol/totem 9-10 bridge/ford 7-8 farm
// 12 ODDITY 11 mine/quarry 9-10 inn/roadhouse
// AGE (1d844) SIZE (1d844) 12 aqueduct/canal/portal 1112 tower/keep/estate
// VISIBILITY CREATURE responsible (1d4.4) CREATURE responsible (1d4+4)
return "";
}
export function rollDwellingStructure(roll: number = random(1, 12)): string {
// 912 STRUCTURE. Who built it? Is it connected to anything else they made nearby?
// 1 ENIGMATIC 2-3 INFRASTRUCTURE 4 DWELLING
// 1-4 earthworks 14 track/path 1-3 campsite
// 5-8 megalith 5-8 road 4-6 hovel/hut
// 941 statue/idol/totem 9-10 bridge/ford 7-8 farm
// 12 ODDITY 11 mine/quarry 9-10 inn/roadhouse
// AGE (1d844) SIZE (1d844) 12 aqueduct/canal/portal 1112 tower/keep/estate
// VISIBILITY CREATURE responsible (1d4.4) CREATURE responsible (1d4+4)
return "";
}
export function rollBurialReligiousStructure(roll: number = random(1, 12)): string {
// 5-6 BURIAL/RELIGIOUS 7-8 STEADING 9-12 RUIN
// 1-2 grave marker/barrow Roll on STEADING table 1-2 Ias'raucruRE (1d646)
// 3-4 graveyard/necropolis 3-4 DWELLING (1d844)
// 5-6 tomb/crypt 5-6 BURIAIJREIJGI0us
// 7-9 shrine (1d8+4)
// 1041 temple/retreat 7-8 STEADING (00+2)
// 12 great temple 9-12 DUNGEON (pp60-61)
// CREATURE responsible (1d4+4), CREATURE responsible (1d444),
// ALIGNMENT, ASPECT AGE (1d8.4), RUINATION,
// VISIBILITY
return "";
}
export function rollRuinStructure(roll: number = random(1, 12)): string {
// 5-6 BURIAL/RELIGIOUS 7-8 STEADING 9-12 RUIN
// 1-2 grave marker/barrow Roll on STEADING table 1-2 Ias'raucruRE (1d646)
// 3-4 graveyard/necropolis 3-4 DWELLING (1d844)
// 5-6 tomb/crypt 5-6 BURIAIJREIJGI0us
// 7-9 shrine (1d8+4)
// 1041 temple/retreat 7-8 STEADING (00+2)
// 12 great temple 9-12 DUNGEON (pp60-61)
// CREATURE responsible (1d4+4), CREATURE responsible (1d444),
// ALIGNMENT, ASPECT AGE (1d8.4), RUINATION,
// VISIBILITY
return "";
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { random, sample, sampleSize, times } from 'lodash';
import { Kingdom, kingdoms } from './data';
import { Kingdom, kingdoms } from './kingdoms';
export class Follower {
constructor(public name: string = "") { };
+55
View File
@@ -0,0 +1,55 @@
import { random } from 'lodash';
export class Kingdom {
constructor(public description: string = "", public subtitle: string = "") { };
categories: string[];
male: string[];
female: string[];
steading: string[];
mount: string[];
}
export const kingdoms: { [kingdom: string]: Kingdom } = {
arpad: {
description: 'The Kingdom of Arpad',
subtitle: 'Based loosely on Hungarian',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Agoston', 'Arpad', 'Attila', 'Bognar', 'Denes', 'Edmond', 'Erno', 'Etele', 'Ferdinand', 'Florian', 'Geza', 'Gyula', 'Hugo', 'Karcsi', 'Konrad', 'Lazlo', 'Lukas', 'Marko', 'Miklos', 'Peti', 'Robi', 'Tamas', 'Ronold', 'Viktor', 'Zoltan',],
female: ['Abigel', 'Aliz', 'Amalia', 'Andrea', 'Aranka', 'Csilla', 'Edit', 'Erzebet', 'Gertrud', 'Greta', 'Iren', 'Kamilla', 'Lara', 'Ua', 'Lujza', 'Matild', 'Olga', 'Otilia', 'Parma', 'Roza', 'Terez', 'Tunda', 'Valeria', 'Vilma', 'Viola',],
steading: ['Aldott (Blessed)', 'Almahid (Applebridge)', 'Elesett (Fallen)', 'Feketz (Black Rock)', 'Godor (Pit)', 'Kelegaz (Eastford)', 'Kigyov (Snake Swamp)', 'Kiralokas (Queen\'s Castle)', 'Kiralsir (King\'s Grave)', 'Magziklar (Highcliff)', 'Mocsar (Fen)', 'Nagyvros (Hightown)', 'Okorm (Oxfield)', 'Orkfal (Orcwail)', 'Perov (Redwater)', 'Soterdo (Dark Wood)', 'Tehenvar (Cow Town)', 'Toron (Tower)', 'Torott (Ironhold)', 'Utolszer (Last Stand)', 'Valavolg (Greendale)', 'Vastar (Dwarf Watch)', 'Viz (Oxfield)', 'Volgyom (Valley)', 'Zoldom (Green Hill)',],
mount: ['Barat (Friend)', 'Barsony (Velvet)', 'Eclesem (Sweetheart)', 'Egatz (Skyfire)', 'Eso (Rain)', 'Fakyla (Torch)', 'Feketsor (Black Mane)', 'Felho (Cloud)', 'Fiotta (Fleet)', 'Gazda (Master)', 'Hold (Moon)', 'Igaslo (Dobbin)', 'Ijeda (Skittish)', 'Koszalo (Rambler)', 'Megmento (Savior)', 'Napfen (Sunshine)', 'Rozsa (Rose)', 'Szamla (Bill)', 'Szellem (Spritied)', 'Szeplok (Freckles)', 'Szerence (Lucky)', 'Tusko (Stumps)', 'Vad (Wild One)', 'Vihar (Storm)', 'Villam (Lightning)',]
},
oloru: {
description: 'Oloru, Sky-God\'s Home',
subtitle: 'Based loosely on Yoruba',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Mibemi', 'Aboye', 'Megoke', 'Ayokunle', 'Babajide', 'Babatunde', 'Enitan', 'Femi', 'Kayin', 'Kayode', 'Lanre', 'Lekan', 'Mongo', 'Nwachukwu', 'Oban', 'Ogun', 'Olukayode', 'Oluwalanni', 'Oluwatoke', 'Onipede', 'Sijuade', 'Toben', 'Utiba', 'Zaki', 'Zoputan',],
female: ['Abeni', 'Me', 'Alaba', 'Bolanle', 'Bosade', 'Daraja', 'Fan', 'Gbemisola', 'Ife', 'Ige', 'Lewa', 'Mojisola', 'Monifa', 'Olufemi', 'Omolara', 'Oni', 'Orisa', 'Osa', 'Ronke', 'Shanum', 'Simisola', 'Titlayo', 'Yejide', 'Yewande', 'Zauna',],
steading: ['Asala Ilu (Desert Town)', 'Atijo ma (Old Fire)', 'Bajesia (Broken Banner)', 'Dudu Olomi (Blackmarsh)', 'Ebutte Meta (Three Ports)', 'Eodo (Snake River)', 'Esukale (Devil\'s Dinner)', 'Fadormi (Silver Spring)', 'Funfumi (Whitewater)', 'Gooluna (Gold Road)', 'Ijisofo (Storm Hollow)', 'Ikukenu (Dearth\'s Door)', 'Twin Ago (Faery Watch)', 'Jinibi (Far Place)', 'Oba Ile (King\'s Home)', 'Oduroke (Prayer Hill)', 'Ogbinibi (Farming Place)', 'Ogunibi (Battle Place)', 'Okanigi (One Tree)', 'Okutasibo (Stone Marker)', 'Olorusura (God\'s Treasure)', 'Olusajeki (Wizard\'s Keep)', 'Oluwakaji (Lord\'s Tomb)', 'Opolokuta (Many Stones)', 'Opoligi (Many Trees)'],
mount: ['Adiitu (Mystery)', 'Alayo (Happy)', 'Atale (Ginger)', 'Bilu (Bill)', 'Dudupatak (Dark Hoof)', 'Egun (Bramble)', 'Eniyan (Ember)', 'Esirun (Longshanks)', 'Fenuko (Kiss)', 'Funfungo (White Hair)', 'Gunirun (Longhair)', 'Imole (Bright)', 'Imole Uju (Bright Eye)', 'Ira (Rambler)', 'Itan (Story)', 'Iyebiye (Precious)', 'Lulu (Powder)', 'Nilera (Healthy)', 'Ogbo (Spotted)', 'Ogboju (Brave Heart)', 'Ojiji (Shadow)', 'Olooto (Faithful)', 'Orisa (Spring)', 'Orun (Heaven)', 'Sare (Flash)'],
},
valkoina: {
description: 'Valkoina, Land of White Iron',
subtitle: 'Based loosely on Finnish',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Aatami', 'Armas', 'Arsi', 'Arvi', 'Eetu', 'Hannu', 'Heimo', 'Ilkka', 'Jorma', 'Kaapo', 'Kain', 'Kauko', 'Lari', 'Manu', 'Nuutti', 'Petri', 'Rairno', 'Reima', 'Risto', 'Sakari', 'Sampsa', 'Seppo', 'Taito', 'Terho', 'Vilppu',],
female: ['Aija', 'Ama', 'Ainikki', 'Heini', 'Ilona', 'Irja', 'Jaana', 'Kirsi', 'Maija', 'Marita', 'Miina', 'Mimmi', 'Minja', 'Mira', 'Naemi', 'Outi', 'Pirjo', 'Paivikki', 'Riikka', 'Saimi', 'Suoma', 'Suvi', 'Tuula', 'Vellamo', 'Virpi',],
steading: ['Etuvartio (Outpost)', 'Hopea Kaivos (Silver Mine)', 'Kalapunki (Fish Town)', 'Kivimurri (Stone Wall)', 'Maaginen (Magic)', 'Maki Linna (Hill Castle)', 'Merenranta (Seaside)', 'Metsastysniaat (Hunting Ground)', 'Mustakota (Black Hut)', 'Maenrinne (Hillside)', 'Paja (Forge)', 'Pienni Paikka (Low Place)', 'Pyha Paikka (Holy Place)', 'Rantakallio (Cliff)', 'Rikki (Broken)', 'Suo (Swamp)', 'Suosi (Favored)', 'Torni (Tower)', 'Turvapaikka (Refuge)', 'Uusipunki (New Town)', 'Valkoinen Kivi (Whitestone)', 'Valtaistuin (Throne)', 'Vapaanki (Free Town)', 'Vihrea Paikka (Green Place)', 'Viimeinen Koti (Last Home)',],
mount: ['Aave (Ghost)', 'Enkeli (Angel)', 'Haiva (Shadow)', 'Hopea (Silver)', 'Ilmavirta (Current)', 'Kesi (Tame)', 'Kestaba (Durable)', 'Kiukuinnen (Angry)', 'Lansiviima (West Wind)', 'Luotettava (Trusty)', 'Myrskyisa (Stormy)', 'Nokka (Bill)', 'Noyra (Humble)', 'Pitkanena (Longnose)', 'Saikki (Skittish)', 'Salama (Thunderbolt)', 'Sankani (Hero)', 'Sisko (sister)', 'Upea (Magnificent)', 'Vahva (Strong)', 'Valkoinen (White)', 'Varmaotteinen (Surefoot)', 'Vesuri (Billhook)', 'Vinha (Fast)', 'Ystava (Friend)',]
},
tamanarugan: {
description: 'The Tamanarugan Empire',
subtitle: 'Based loosely on Indonesian',
categories: ['male', 'female', 'steading', 'mount'],
male: ['Me', 'Mi', 'Amaziah', 'Ayokunle', 'Ary', 'Bambang', 'Bima', 'Budi', 'Darma', 'Dian', 'Eli', 'Gunardi', 'Hartono', 'Irwan', 'Lauwita', 'Manusama', 'Okan', 'Onesimus', 'Sammin', 'Tameem', 'Tanaya', 'Tirto', 'Wiryono', 'Yandi', 'Zebulun',],
female: ['Mah', 'Bulan', 'Candrakusuma', 'Devi', 'Hanjojo', 'Iman', 'Intan', 'Laksniini', 'Lestari', 'Limijanto', 'Marah', 'Megawati', 'Melati', 'Nadiyya', 'Ophrah', 'Ramza', 'Sapphiral', 'Selah', 'Suminten', 'Tamar', 'Tanjaya', 'Tjokro', 'Tri', 'Wangi', 'Zenze',],
steading: ['Airdib (Blessed Waters)', 'Airjinh (Clearwater)', 'Akhir Jalan (Road\'s End)', 'Berdarah (Bloody)', 'Bidang Bera (Fallow Field)', 'Candibula (Moon Temple)', 'Ditingga (Forsaken)', 'Emasungai (Gold Creek)', 'Gunung (Mountain)', 'Kayu (Timber)', 'Kuil (Temple)', 'Ladang Hijau (Greenfield)', 'Lembah (Valley)', 'Menjau (Far Away)', 'Ngarai (Canyon)', 'Persimpangan (Crossroads)', 'Puncakit (Hilltop)', 'Sungairac (}bison River)', 'Teibing (Cliffside)', 'Tempat Aman (Safe Place)', 'Tempat Istir (Rest Place)', 'Terkutuk (Cursed)', 'Tersentu (Touched by God)', 'Wahah (Oasis)', 'Yangtinggi (High Tower)',],
mount: ['Anginu (North Wind)', 'Api (Blaze)', 'Bakat (Felicity)', 'Bakti (Loyal)', 'Beruntung (Lucky)', 'Bilah (Blade)', 'Biru (Blue)', 'Gemetar (Quiver)', 'Guntur (Thunder)', 'Hidungi (Black Nose)', 'Janda (Widow)', 'Kakicerah (Bright Foot)', 'Kakiring (Light Foot)', 'Keriangan (Sunshine)', 'Kunang (Firefly)', 'Lapar (Hungry)', 'Murni (Pure)', 'Paruh (Bill)', 'Penebus (Avenger)', 'Penyelemat (Savior)', 'Prajurit (Warrior)', 'Rusak (Broken)', 'Satucep (Fast One)', 'Setia (True Heart)', 'Tahanla (Durable)',]
},
};
export function rollKingdom(roll: number = random(1, 4)): Kingdom {
let key = Object.keys(kingdoms)[roll - 1];
return kingdoms[key];
}
+363
View File
@@ -0,0 +1,363 @@
import { random, sample } from 'lodash';
import { Follower, rollFollower } from './followers';
import { kingdoms, rollKingdom } from './kingdoms';
export class Npc {
constructor(public name: string = "") { };
gender: "male" | "female";
hometown: string;
context: Context;
occupation: Occupation
trait: Trait;
follower: Follower;
}
export function rollNpc(): Npc {
let kingdom = rollKingdom();
let npc = new Npc();
npc.gender = sample(["male", "female"]);
npc.name = sample(kingdom[npc.gender]);
npc.hometown = sample(kingdom.steading);
npc.context = rollContext();
npc.occupation = rollOccupation();
npc.trait = rollTrait();
npc.follower = rollFollower();
return npc;
}
export class Context {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public subcategory: string = "") { };
}
export function rollContext(): Context {
let roll = random(1, 12);
let subroll = random(1, 12);
let context = new Context(roll, subroll);
if (roll <= 3) {
context.category = "wilderness";
context.subcategory = rollWildernessNpc(subroll);
} else if (roll <= 9) {
context.category = "rural";
context.subcategory = rollRuralNpc(subroll);
} else if (roll <= 12) {
context.category = "urban";
context.subcategory = rollUrbanNpc(subroll);
}
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(),
][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(),
][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(),
][roll - 1];
}
export class Occupation {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public subcategory: string = "") { };
}
export function rollOccupation(roll: number = random(1, 12), subroll: number = random(1, 12)): Occupation {
let occupation = new Occupation(roll, subroll);
if (roll <= 1) {
occupation.category = "criminal";
occupation.subcategory = rollCriminal(subroll);
} else if (roll <= 6) {
occupation.category = "commoner";
occupation.subcategory = rollCommoner(subroll);
} else if (roll <= 8) {
occupation.category = "tradeperson";
occupation.subcategory = rollTradeperson(subroll);
} else if (roll <= 10) {
occupation.category = "merchant";
occupation.subcategory = rollMerchant(subroll);
} else if (roll <= 11) {
occupation.category = "specialist";
occupation.subcategory = rollSpecialist(subroll);
} else if (roll <= 12) {
occupation.category = "officiail";
occupation.subcategory = rollOfficial(subroll);
}
return occupation;
}
function rollCriminal(roll: number = random(1, 12)): string {
return [
"bandit/brigand/thug",
"bandit/brigand/thug",
"thief",
"thief",
"bodyguard/tough",
"bodyguard/tough",
"burglar",
"burglar",
"dealer/fence",
"racketeer",
"lieutenant",
"boss"
][roll - 1]
}
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"
][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",
][roll - 1];
}
function rollMerchant(roll: number = random(1, 12)): string {
return [
"general goods/outfitter",
"general goods/outfitter",
"general goods/outfitter",
"raw materials",
"grain/livestock",
"ale/wine/spirits",
"clothing/jewelry",
"weapons/armor",
"spices/tobacco",
"labor/slaves",
"books/scrolls",
"magic supplies/items"
][roll - 1];
}
function rollSpecialist(roll: number = random(1, 12)): string {
return [
"undertaker",
"sage/scholar/wizard",
"writer/illuminator",
"perfumer",
"architect/engineer",
"locksmith/clockmaker",
"physician/apothecary",
"navigator/guide",
"alchemist/astrologer",
"spy/diplomat",
"cartographer",
"inventor"
][roll - 1];
}
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",
"guildmaster",
"knightltemplar",
"elder/high priest",
"noble (baron, etc.)",
"lord/lady/king/queen",
][roll - 1];
}
class Trait {
constructor(public roll: number = 0, public subroll: number = 0, public category: string = "", public description: string = "") { };
}
function rollTrait(roll: number = random(1, 12), subroll: number = random(1, 12)): Trait {
let trait = new Trait(roll, subroll);
if (roll <= 6) {
trait.category = "physical appearance";
trait.description = rollAppearance(subroll);
} else if (roll <= 9) {
trait.category = "personality";
trait.description = rollPersonality(subroll);
} else if (roll <= 12) {
trait.category = "quirk";
trait.description = rollQuirk(subroll);
}
return trait;
}
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",
"notable hair (wild, long, none, etc.)",
"notable nose (big, hooked, etc.)",
"notable eyes (blue, bloodshot, etc.)",
"clean/well-dressed/well-groomed",
"attractive/handsome/stunning",
"they are [APPEARANCE] despite [a contradictory detail of your choice]"
][roll - 1];
return appearance.replace("[APPEARANCE]", () => rollAppearance(random(1, 11)));
}
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",
"they are [PERSONALITY] despite [a contradictory detail of your choice]",
][roll - 1];
return personality.replace("[PERSONALITY]", () => rollPersonality(random(1, 11)));
};
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",
"they are [QUIRK] despite [a contradictory detail of your choice]",
][roll - 1];
return quirk.replace("[QUIRK]", () => rollQuirk(random(1, 11)));
}
// *** THESE TABLES OVERLAP HEAVILY WITH THE ONES IN THE FOLLOWER CREATION SECTION *** //
// Right now we'll use the roller from that section and maybe mash them together later
// NPC FOLLOWER
// Roll 1d12 for each category; reconcile to the fiction, tag, and equip as you see fit.
// COMPETENCE
// 1-3 A liability: Quality -1, +0 tags
// 4-9 Competent: Quality .0, +1 tags
// 10-11 Fully capable: Quality +1, +2 tags
// 12 Exceptional: Quality +2, +4 tags
// BACKGROUND
// 1-2 Life of servitude/oppression: #Meek
// 3 Past their prime: -1 to Quality, +1 Wise
// 4-5 Has lived a life of dangec +2 tags
// 6-9 Unremarkable
// 10 Has lived a life of privilege: +1 tag
// 11 Specialist: .1 to Quality, -2 tags
// 12 Roll twice on this table
// LOYALTY
// 1-2 0 (little faith in leader's cause)
// 3-10 +1
// 11-12 +2 (great faith in leader's cause)
// INSTINCT
// 1 Loot, pillage, and burn
// 2 Hold a grudge and seek payback
// 3 Question leadership or authority
// 4-5 Lord over others
// 6-7 Act impulsively
// 8-9 Give in to temptation
// 10-11 Slack off
// 12 Avoid danger or punishment
// COST
// I Debauchery
// 2 Vengeance
// 3-5 Lucre
// 6-7 Renown
// 8-9 Glory
// 10 Affection
// 11 Knowledge
// 12 Good
// HIT POINTS DAMAGE DIE
// 1-3 3 H d4
// 4-9 6 H d6
// 10-12 9 HP d8
+14
View File
@@ -0,0 +1,14 @@
export const places = {
templates: [
'The [PLACE]',
'The [ADJECTIVE] [PLACE]',
'The [PLACE] of (the) [NOUN]',
'(The) [NOUN]\'s [PLACE]',
'[PLACE] of the [ADJECTIVE] [NOUN]',
'The [ADJECTIVE] [NOUN]'
],
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.
};
+14
View File
@@ -0,0 +1,14 @@
export const regions = {
templates: [
'[ADJECTIVE] [TERRAIN]',
'[TERRAIN] of (the) [NOUN]',
'The [TERRAIN] [ADJECTIVE]',
'[NOUN] [TERRAIN]',
'[NOUN]\'s [ADJECTIVE] [TERRAIN]',
'[ADJECTIVE] [TERRAIN] of (the) [NOUN]'
],
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
};
+126
View File
@@ -0,0 +1,126 @@
import { random, sample } from 'lodash';
export class Steading {
constructor(public roll: number = 0, public category: string = "") { };
builtBy: string;
description: string;
kingdom: string;
problem: string;
}
// 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.
if (roll <= 5) {
steading = rollVillage();
} else if (roll <= 8) {
steading = rollTown();
} else if (roll <= 11) {
steading = rollKeep();
} else if (roll <= 12) {
steading = rollCity();
}
steading.roll = roll;
return steading;
}
export function rollVillage(): Steading {
return new Steading(0, "village");
}
export function rollTown(): Steading {
return new Steading(0, "town");
}
export function rollKeep(): Steading {
return new Steading(0, "keep");
}
export function rollCity(): Steading {
return new Steading(0, "city");
}
// 1 - 5 VILLAGE
// Poor, Steady, Militia, Resource(GM choice) and has an Oath(steading of GM's choice). If the village is part of a kingdom or empire, choose I or roll 1d12:
// 1 - 3 Natural defenses: Safe, -Defenses
// 4 - 6 Abundant resources: +Prosperity, Resource(GM choice), Enmity(GM choice) 7 - 8 Protected by another steading: Oath(that steading), .Defenses
// 9 - 10 On a major road: Trade(GM choice), +Prosperity
// 11 Built around a wizard's tower: Personage (the wizard), Blight (arcane creatures)
// 12 Built on a site of religious significance: Divine, History(GM choice)
// Then, choose I problem or roll 1d12:
// 1 - 2 Surrounded by and or uncultivable land: Need(food)
// 3 - 4 Dedicated to a deity: Religious(that deity), Enmity(steading of opposing deity)
// 5 - 6 Recently at war: -Population, -Prosperity if they fought to the end, -Defenses if they lost 7 - 8 Monster problem: Blight(that monster), Need(adventurers)
// 9 - 10 Absorbed another village: +Population, Lawless
// 11 - 12 Remote or unwelcoming: -Prosperity, Dwarven or Elven or other non - human
// 6 - 8 TowN
// Moderate, Steady, Watch, and Trade(with 2 places of GM's choice).
// If the town is listed as Trade by another steading, choose I or roll 1d12:
// 1 Booming: Booming, Lawless
// 2 - 3 At a crossroads: Market, +Prosperity
// 4 - 5 Defended by another steading: Oath(that steading), +Defenses
// 6 - 7 Built around a church: Power(divine)
// 8 - 10 Built around a craft: Craft(your choice), Resource(something required for that craft) 11 - 12 Built around a military post.Defenses
// Then, choose 1 problem or roll 1d12:
// 1 - 2 Outgrowing a vital resource: Need(that resource), Trade(a steading with that resource) 3 - 4 Offers defense to others: Oath(GM choice), -Defenses
// 5 - 6 Outlaw rumored to live there: Personage(the outlaw), Enmity(steading preyed upon) 7 - 8 Controls a good / service: Exotic(that good / service), Enmity(steading with ambition) 9 - 10 Suffers from disease: -Population
// 11 - 12 lkpular meeting place: +Population, Lawless
// Then, choose I problem or roll 1d12:
// 9 - 11 KEEP
// Poor, Shrinking, Guard, Need(supplies), Trade(somei
// If the keep is owed fealty by at least one other stea, E
// 1 - 2 Belongs to a noble family: +Prosperity, Tozer(p 3 - 4 Run by a skilled commander: Personage(the
// 5 - 6 Stands watch over a trade road: .Prosperity, GNI 7 - 8 Used to train special troops: Arcane, -Population 9 - 10 Surrounded by fertile land: remove Need(Supçifr 11 - 12 Stands on a border: .Defenses, Enmity(steadinga
// Then, choose I problem or roll 1d12:
// 1 - 3 Built on a naturally defensible position: Safe, -fl, p.
// 4 Formerly occupied by another power: Enmity(Sim
// 5 Safe haven for brigands: Lawless
// 6 Built to defend from a specific threat: Blight(tbat
// 7 Has seen horrible bloody war: History(battle),
// 8 Is given the worst of the worst: Need(skilled recrn
// 9 - 10 Suffers from disease: -Population
// 11 - 12 Ik)pular meeting place: * Population, -Law
// 12 QTY
// Moderate, Steady, Guard, Market, Guild(GM's choice), 2. If the city has trade with and fealty from at least 1 steadin
// 1 - 3 Permanent defenses, such as walls: +Defenses, Oath 4 - 6 Ruled by a single individual: Personage(the ruler). 7 Diverse: Dwarven or Elven or both
// 8 - 10 Trade hub: Trade(every nearby steading), +Prosperi
// 11 Ancient, built on top of its own ruins: History(yoa
// 12 Center of learning: Arcane, Craft(your choice), Fbs
// 1 - 3 Outgrown its resources: +Population, Need(food) 4 - 6 Designs on nearby territory: Enmity(nearby stead 7 - 8 Ruled by a theocracy: -Defenses, Power(divine) 9 - 10 Ruled by the people: -Defenses, +Population
// 11 Supernatural defenses: .Defenses, Blight(related sti
// 12 Occupies a place of power: Arcane, Personage(who
// Blight(arcane creatures)
// 9 - 11 KEEP
// Poor, Shrinking, Guard, Need(supplies), Trade(someplace with supplies), Oath(GM's choice). If the keep is owed fealty by at least one other steading, choose 1 or roll 1d12:
// 1 - 2 Belongs to a noble family: +Prosperity, Power(political)
// 3 - 4 Run by a skilled commander: Personage(the commander), .Defenses 5 - 6 Stands watch over a trade road: +Prosperity, Guild(trade)
// 7 - 8 Used to train special troops: Arcane, -Population
// 9 - 10 Surrounded by fertile land: remove Need(Supplies)
// 11 - 12 Stands on a border: .Defenses, Enmity(steading on the other side of the border)
// Then, choose I problem or roll 1d12:
// 1 - 3 Built on a naturally defensible position: Safe, -Population
// 4 Formerly occupied by another power: Enmity(steadings of that power)
// 5 Safe haven for brigands: Lawless
// 6 Built to defend from a specific threat: Blight(that threat)
// 7 Has seen horrible bloody war: History(battle), Blight(restless spirits)
// 8 Is given the worst of the worst: Need(skilled recruits)
// 9 - 10 Suffers from disease: -Population
// 11 - 12 Popular meeting place: .Population, -Law
// 12 QTY
// Moderate, Steady, Guard, Market, Guild(GM's choice), 2+ Oaths (steadings of GM's choice) If the city has trade with and fealty from at least 1 steading, choose I or roll 1d12:
// 1 - 3 Permanent defenses, such as walls: +Defenses, Oath((M's choice) 4-6 Ruled by a single individual: Personage (the ruler), Power (political) 7 Diverse: Dwarven or Elven or both
// 8 - 10 Trade hub: Trade(every nearby steading), +Prosperity
// 11 Ancient, built on top of its own ruins: History(your choice), Divine
// 12 Center of learning: Arcane, Craft(your choice), Power(arcane)
// Then, choose I problem or roll 1d12:
// 1 - 3 Outgrown its resources: +Population, Need(food)
// 4 - 6 Designs on nearby territory: Enmity(nearby steadings), .Defenses 7 - 8 Ruled by a theocracy: -Defenses, Power(divine)
// 9 - 10 Ruled by the people: -Defenses, +Population
// 11 Supernatural defenses: +Defenses, Blight(related supernatural creatures)
// 12 Occupies a place of power: Arcane, Personage(whoever watches the place of power),
// Blight(arcane creatures)