Compare commits
4 Commits
master
...
treasureBu
Author | SHA1 | Date | |
---|---|---|---|
d5b91d18d6 | |||
f5101f1c24 | |||
4598b528c4 | |||
9b0d3724e7 |
53
AdventureBuilder.md
Normal file
53
AdventureBuilder.md
Normal 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
26
NPC Builder.md
Normal 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
BIN
bergx-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
12
index.html
Normal file
12
index.html
Normal 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>
|
@ -1561,7 +1561,7 @@ let tableData: TableConfig[] = [
|
||||
},
|
||||
{
|
||||
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],
|
||||
@ -1569,7 +1569,7 @@ let tableData: TableConfig[] = [
|
||||
},
|
||||
{
|
||||
keys: [4],
|
||||
value: 'The adbenturers have a time limit.'
|
||||
value: 'The adventurers have a time limit.'
|
||||
},
|
||||
{
|
||||
keys: [5],
|
||||
|
325
src/services/treasureData.ts
Normal file
325
src/services/treasureData.ts
Normal 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
|
||||
}
|
194
src/services/treasureService.ts
Normal file
194
src/services/treasureService.ts
Normal 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,
|
||||
}
|
Reference in New Issue
Block a user