Compare commits

4 Commits

Author SHA1 Message Date
d5b91d18d6 add NPC and Villain generator writeups 2021-12-29 16:01:52 -07:00
f5101f1c24 fixed some typos 2021-12-28 14:50:03 -07:00
4598b528c4 add index (unfinished) 2021-07-18 14:17:48 -06:00
9b0d3724e7 add treasure system 2019-12-16 00:09:15 -07:00
7 changed files with 612 additions and 2 deletions

53
AdventureBuilder.md Normal file
View File

@ -0,0 +1,53 @@
# Adventure Builder
In order to build an adventure, you need to build the pieces.
## The Adventure Goals and Details
Location Based Adventures
1. Decide where the adventure will take place and establish goals.
1. Dungeon (Table Dungeon Goals p. 73)
2. Wilderness (Table Wilderness Goals p. 73)
3. City (Table Other Goals p. 73)
4. Fortress (Table Other Goals p. 73)
5. Other (Table Other Goals p. 73)
2. Identify Important NPCs and use the NPC Builder to create them (NPC Builder should be based on Chapter 4).
1. Villains (Tables Adventure Villains p. 74)
2. Allies (Tables Adventure Allies p. 74)
3. Patrons (Tables Adventure Patrons p. 74)
3. Flesh out the Location details (Chapter 5)
1. Dungeon Location (Table Dungeon Location, Exotic Location p. 99)
1. Dungeon Creator (Table Dungeon Creator, Cults and religious Groups, NPC Alignment, NPC Class p. 100)
2. Dungeon Purpose (Table Dungeon Purpose p. 101)
3. Dungeon History (Table Dungeon History p. 101)
4. Dungeon Inhabitants
1. Determine if the dungeon is large enough to support multiple factions
2. What is the ecology of the dungeon (how do the inhabitants stay alive?)
5. Suggest making a map of the dungeon or provide a map generator.
6. Dungeon Features
1. Walls (Stone, chiseled rock, wood, metal, etc.)
2. Doors (Stuck, Locked, Barred, Secret, etc.)
3. Darkness and Light
4. Air Quality
5. Sounds
7. Dungeon Hazards
1. Brown Mold, Green Slime, Webs, Yellow Mold, etc.
2. Wilderness Location (p. 106) (to be added)
3. Urban Location (p. 112) (to be added)
4. Underwater (p. 116) (to be added)
5. At Sea (p. 117) (to be added)
6. The Sky (p. 119) (to be added)
## The Adventure Structure
### The Hook
Table Adventure Introduction (p. 74)
### The Challenges
Use the encounter builder http://tools.goblinist.com/5enc to fill in appropriate monsters at a challenging level.
### The Climax
Table Adventure Climax (p. 75)

26
NPC Builder.md Normal file
View File

@ -0,0 +1,26 @@
# NPC Builder
## Quick NPCs
Use one or two qualities. Randomly choose between the qualities of the detailed NPC builder.
1. NPC Appearance p. 89
2. NPC Abilities p. 89
3. NPC Talents p. 90
4. NPC Mannerisms p. 90
5. NPC Interaction Traits p. 90
6. NPC Ideals p. 90
7. NPC Bonds p. 91
8. NPC Flaws and Secrets p. 91
## Detailed NPCs
Use all the qualities provided by the NCP Builder to flesh out a more detailed NPC. If the NPC is to be a monster then all the user to make a note of the monster and its stats.
## NPC Statistics
Suggest using one of the many free generators to make stats and copy them here to save them.
## Villains
Build a villain by adding all the NCP details and then adding the villain's schemes, methods and weaknesses.

BIN
bergx-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

12
index.html Normal file
View File

@ -0,0 +1,12 @@
<html>
<style>
body {
background: url(bergx-logo.png);
background-size: cover;
background-position: center;
}
</style>
<body>
<!-- <img src="/bergx-logo.png" /> -->
</body>
</html>

View File

@ -1561,7 +1561,7 @@ let tableData: TableConfig[] = [
}, },
{ {
keys: [2], keys: [2],
value: 'The adventurers become responsible for the safety of a noncombatant in PC. ' value: 'The adventurers become responsible for the safety of a noncombatant or PC. '
}, },
{ {
keys: [3], keys: [3],
@ -1569,7 +1569,7 @@ let tableData: TableConfig[] = [
}, },
{ {
keys: [4], keys: [4],
value: 'The adbenturers have a time limit.' value: 'The adventurers have a time limit.'
}, },
{ {
keys: [5], keys: [5],

View File

@ -0,0 +1,325 @@
import { IndividualTreasureTable, HoardTresureTable } from './treasureService';
let individualTreasureData: IndividualTreasureTable[] = [
{
name: 'Individual Treasure: Challence 0-4',
notes: '',
tags: ['Individual Treasure', 'CR0','CR1','CR2', 'CR3', 'CR4', 'Challenge 0-4', 'treasure'],
dice: [100],
possibleCoinRewards: [
{
keys: [1,30],
coinCalc: [
{
type: 'CP',
rollCount: 5,
diceType: 6,
multiplier: 1,
avg: 17
}
]
},
{
keys: [31,60],
coinCalc: [
{
type: 'SP',
rollCount: 4,
diceType: 6,
multiplier: 1,
avg: 14
}
]
},
{
keys: [61,70],
coinCalc: [
{
type: 'EP',
rollCount: 3,
diceType: 6,
multiplier: 1,
avg: 10
}
]
},
{
keys: [71,95],
coinCalc: [
{
type: 'GP',
rollCount: 3,
diceType: 6,
multiplier: 1,
avg: 10
}
]
},
{
keys: [96,100],
coinCalc: [
{
type: 'PP',
rollCount: 1,
diceType: 6,
multiplier: 1,
avg: 3
}
]
}
]
},
{
name: 'Individual Treasure: Challence 5-10',
notes: '',
tags: ['Individual Treasure', 'CR5','CR6','CR7', 'CR8', 'CR9', 'CR10', 'Challenge 5-10', 'treasure'],
dice: [100],
possibleCoinRewards: [
{
keys: [1,30],
coinCalc: [
{
type: 'CP',
rollCount: 4,
diceType: 6,
multiplier: 100,
avg: 1400
},
{
type: 'EP',
rollCount: 1,
diceType: 6,
multiplier: 10,
avg: 35
}
]
},
{
keys: [31,60],
coinCalc: [
{
type: 'SP',
rollCount: 6,
diceType: 6,
multiplier: 10,
avg: 210
},
{
type: 'GP',
rollCount: 2,
diceType: 6,
multiplier: 10,
avg: 70
}
]
},
{
keys: [61,70],
coinCalc: [
{
type: 'EP',
rollCount: 3,
diceType: 6,
multiplier: 10,
avg: 105
},
{
type: 'GP',
rollCount: 2,
diceType: 6,
multiplier: 10,
avg: 70
}
]
},
{
keys: [71,95],
coinCalc: [
{
type: 'GP',
rollCount: 4,
diceType: 6,
multiplier: 10,
avg: 140
}
]
},
{
keys: [96,100],
coinCalc: [
{
type: 'GP',
rollCount: 2,
diceType: 6,
multiplier: 10,
avg: 70
},
{
type: 'PP',
rollCount: 2,
diceType: 6,
multiplier: 1,
avg: 10
}
]
}
]
},
{
name: 'Individual Treasure: Challence 11-16',
notes: '',
tags: ['Individual Treasure', 'CR11', 'CR12', 'CR13', 'CR14', 'CR15', 'CR16', 'Challenge 11-16', 'treasure'],
dice: [100],
possibleCoinRewards: [
{
keys: [1,20],
coinCalc: [
{
type: 'SP',
rollCount: 4,
diceType: 6,
multiplier: 100,
avg: 1400
},
{
type: 'GP',
rollCount: 1,
diceType: 6,
multiplier: 100,
avg: 350
}
]
},
{
keys: [21,35],
coinCalc: [
{
type: 'EP',
rollCount: 1,
diceType: 6,
multiplier: 100,
avg: 350
},
{
type: 'GP',
rollCount: 1,
diceType: 6,
multiplier: 100,
avg: 350
}
]
},
{
keys: [36,75],
coinCalc: [
{
type: 'GP',
rollCount: 2,
diceType: 6,
multiplier: 100,
avg: 700
},
{
type: 'PP',
rollCount: 1,
diceType: 6,
multiplier: 10,
avg: 35
}
]
},
{
keys: [76,100],
coinCalc: [
{
type: 'GP',
rollCount: 2,
diceType: 6,
multiplier: 100,
avg: 700
},
{
type: 'EP',
rollCount: 2,
diceType: 6,
multiplier: 10,
avg: 70
}
]
}
]
},
{
name: 'Individual Treasure: Challence 17+',
notes: '',
tags: ['Individual Treasure', 'CR17', 'CR18', 'CR19', 'CR20', 'CR21', 'CR22', 'Challenge 17+', 'treasure'],
dice: [100],
possibleCoinRewards: [
{
keys: [1,15],
coinCalc: [
{
type: 'EP',
rollCount: 2,
diceType: 6,
multiplier: 1000,
avg: 7000
},
{
type: 'GP',
rollCount: 8,
diceType: 6,
multiplier: 100,
avg: 2800
}
]
},
{
keys: [16,55],
coinCalc: [
{
type: 'GP',
rollCount: 1,
diceType: 6,
multiplier: 1000,
avg: 3500
},
{
type: 'PP',
rollCount: 1,
diceType: 6,
multiplier: 100,
avg: 350
}
]
},
{
keys: [56,100],
coinCalc: [
{
type: 'GP',
rollCount: 1,
diceType: 6,
multiplier: 1000,
avg: 3500
},
{
type: 'PP',
rollCount: 2,
diceType: 6,
multiplier: 100,
avg: 700
}
]
}
]
},
];
let hoardTreasureData: HoardTresureTable[] = [
];
export {
individualTreasureData
}

View File

@ -0,0 +1,194 @@
import { d } from './dice';
import { individualTreasureData } from './treasureData';
class TreasureService {
private individualRewardsTables: RandomIndividualTreasureTable[] = [];
private tableByName: {[tableName: string]: RandomIndividualTreasureTable} = {};
private tablesByTags: {[tagName: string]: RandomIndividualTreasureTable[]} = {};
private tags: Set<string> = new Set();
constructor() {
console.time('TreasureServiceStartup');
for (let i in individualTreasureData) {
let table = individualTreasureData[i];
let randomTable: RandomIndividualTreasureTable = new RandomIndividualTreasureTable(table);
this.individualRewardsTables.push(randomTable);
this.tableByName[table.name] = randomTable;
if (table.tags) {
for (let j in table.tags) {
let tag: string = table.tags[j];
if (!this.tablesByTags[tag]) {
this.tablesByTags[tag] = [];
}
this.tags.add(tag);
this.tablesByTags[tag].push(randomTable);
}
}
}
console.timeEnd('TreasureServiceStartup');
}
public getTablesWithTag(tag: string) {
return this.tablesByTags[tag];
}
public getTable(name: string) {
return this.tableByName[name];
}
public getTags() {
return this.tags;
}
public searchForTable(search: string) {
let answer = this.individualRewardsTables.filter((table: RandomIndividualTreasureTable) => {
return table.name.toLowerCase().indexOf(search.toLowerCase()) !== -1 || table.getTags().join(' ').toLowerCase().indexOf(search.toLowerCase()) !== -1;
}).map((table: RandomIndividualTreasureTable) => {
let score = search.length / table.name.length;
return {
table: table,
score: score
};
}).sort((a: {table: RandomIndividualTreasureTable, score: number}, b: {table: RandomIndividualTreasureTable, score: number}) => {
return a.score - b.score;
}).map((tableScore: {table: RandomIndividualTreasureTable, score: number}) => {
return tableScore.table;
});
return answer;
}
}
class RandomIndividualTreasureTable {
public name: string;
public dice: number[];
private indexedResults: CoinRewards[] = [];
private config: IndividualTreasureTable;
constructor(config: IndividualTreasureTable) {
this.name = config.name;
this.dice = config.dice;
this.constructIndex(config.possibleCoinRewards);
this.config = config;
}
public roll(input?: number) {
let testRoll: number = this.getRoll();
if (!this.isValidInput(testRoll)) {
console.log(testRoll);
debugger;
}
if (!input || !this.isValidInput(input)) {
input = this.getRoll();
}
return this.indexedResults[input - 1];
}
public getPossibleRewards() {
return this.config.possibleCoinRewards;
}
public getTags() {
return this.config.tags || [];
}
public getNotes() {
return this.config.notes;
}
private getRoll(): number {
let answer: number = 0;
for (let i in this.dice) {
answer = answer + d(this.dice[i]);
}
return answer;
}
private isValidInput(input: number) {
return typeof this.indexedResults[input - 1] !== 'undefined';
}
private constructIndex(coinRewards: CoinRewards[]) {
for (let resultIndex in coinRewards) {
let keys: number[] = coinRewards[resultIndex].keys;
for (let r = Math.min(...keys); r <= Math.max(...keys); r++) {
this.indexedResults.push(coinRewards[resultIndex]);
}
}
}
}
type ChallengeRating = '0-4' | '5-10' | '11-16' | '17+';
interface CoinResult {
type: 'CP' | 'SP' | 'EP' | 'GP' | 'PP';
count: number;
}
interface CoinCalc {
type: 'CP' | 'SP' | 'EP' | 'GP' | 'PP';
rollCount: number;
diceType: number;
multiplier: number;
avg: number;
}
interface CoinRewards {
keys: number[];
coinCalc: CoinCalc[];
}
interface GemArtCalc {
tableName: string;
rollCount: number;
diceType: number;
avg: number;
}
interface GemArtResult {
tableName: string;
amount: number;
}
interface MagicItemCalc {
tableName: string;
rollCount: number;
diceType: number;
}
interface MagicItemResult {
tableName: string;
numberOfRoles: number;
}
interface OtherRewards {
keys: number[];
gemArt: GemArtCalc;
magicItem: MagicItemCalc[];
}
interface IndividualTreasureTable {
name: string;
notes?: string;
tags?: string[];
dice: number[];
possibleCoinRewards: CoinRewards[];
}
interface HoardTresureTable {
name: string;
notes?: string;
tags?: string[];
dice: number[];
coinRewards: CoinRewards;
possibleRewards: OtherRewards[]
}
const treasureService: TreasureService = new TreasureService();
export {
HoardTresureTable,
IndividualTreasureTable,
RandomIndividualTreasureTable,
treasureService,
}