diff --git a/ideas.md b/ideas.md new file mode 100644 index 0000000..90b7376 --- /dev/null +++ b/ideas.md @@ -0,0 +1,16 @@ + +## Search + +Full text search for quick lookup. Search both table names and table contents. + +## Modules + +Create modules for each section of the book for example see Build and NPC below. + +## Build an NPC + +It would be nice to enable a DM to create NPCs on the fly with one click and be able to change a trait using the tables either randomly or picking. + +## Additional features + +The ability to define your own random tables. diff --git a/package.json b/package.json index 0c61820..b2f7190 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ "dependencies": { "core-js": "^3.3.2", "register-service-worker": "^1.6.2", + "rxjs": "^6.5.3", "vue": "^2.6.10", "vue-class-component": "^7.0.2", - "vue-property-decorator": "^8.3.0" + "vue-property-decorator": "^8.3.0", + "vue-router": "^3.1.3", + "vue-rx": "^6.2.0" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.0.0", @@ -36,7 +39,10 @@ "eslint:recommended", "@vue/typescript" ], - "rules": {}, + "rules": { + "no-console": "off", + "no-debugger": "off" + }, "parserOptions": { "parser": "@typescript-eslint/parser" } diff --git a/public/index.html b/public/index.html index ef127f8..610f127 100644 --- a/public/index.html +++ b/public/index.html @@ -3,8 +3,12 @@ - + + + + + dungeon-master @@ -13,5 +17,8 @@
+ + + diff --git a/src/App.vue b/src/App.vue index 7633616..d0d8f0c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,17 +1,14 @@ + + diff --git a/src/components/messages.vue b/src/components/messages.vue new file mode 100644 index 0000000..fb8b76c --- /dev/null +++ b/src/components/messages.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index 839820a..dbb25f8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,13 @@ -import Vue from 'vue' -import App from './App.vue' -import './registerServiceWorker' +import Vue from 'vue'; +import App from './App.vue'; +import VueRx from 'vue-rx'; +import router from './router'; +import './registerServiceWorker'; -Vue.config.productionTip = false +Vue.use(VueRx); +Vue.config.productionTip = false; -new Vue({ +let vm = new Vue({ + router, render: h => h(App), -}).$mount('#app') +}).$mount('#app'); diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..c884570 --- /dev/null +++ b/src/router.ts @@ -0,0 +1,17 @@ +import Vue from 'vue'; +import Router from 'vue-router'; +import TestTable from './views/test-table.vue'; + +Vue.use(Router); + +export default new Router({ + mode: 'history', + base: process.env.BASE_URL, + routes: [ + { + path: '/', + name: 'home', + component: TestTable + } + ] +}); diff --git a/src/services/dice.ts b/src/services/dice.ts new file mode 100644 index 0000000..8f223ad --- /dev/null +++ b/src/services/dice.ts @@ -0,0 +1,8 @@ + +function d(faces: number) { + return Math.floor(Math.random() * faces) + 1; +} + +export { + d +} diff --git a/src/services/messageService.ts b/src/services/messageService.ts new file mode 100644 index 0000000..95800ac --- /dev/null +++ b/src/services/messageService.ts @@ -0,0 +1,30 @@ +import { Observable, Subject } from 'rxjs'; + +class MessageService { + private messageStream = new Subject(); + private messages: string[] = []; + + constructor() { + this.messageStream.next([]); + } + + get messages$(): Observable { + return this.messageStream.asObservable(); + } + + public addMessage(newMessage: string) { + this.messages.push(newMessage); + this.messageStream.next(this.messages); + } + + public clearMessages() { + this.messages = []; + this.messageStream.next([]); + } +} + +const messageService: MessageService = new MessageService(); + +export { + messageService +} diff --git a/src/services/tableData.ts b/src/services/tableData.ts new file mode 100644 index 0000000..c69ab91 --- /dev/null +++ b/src/services/tableData.ts @@ -0,0 +1,292 @@ +import { TableConfig } from './tableService'; + +let tableData: TableConfig[] = [ + { + name: 'Forms of Government', + dice: [100], + possibleResults: [ + { + keys: [1, 8], + value: 'Autocracy' + }, + { + keys: [9, 13], + value: 'Bureaucracy' + }, + { + keys: [14, 19], + value: 'Confederacy' + }, + { + keys: [20, 22], + value: 'Democracy' + }, + { + keys: [23, 27], + value: 'Dictatorship' + }, + { + keys: [28, 42], + value: 'Feudalism' + }, + { + keys: [43, 44], + value: 'Gerontocracy' + }, + { + keys: [45, 53], + value: 'Hierarchy' + }, + { + keys: [54, 56], + value: 'Magocracy' + }, + { + keys: [57, 58], + value: 'Matriarchy' + }, + { + keys: [59, 64], + value: 'Militocracy' + }, + { + keys: [65, 74], + value: 'Monarchy' + }, + { + keys: [75, 78], + value: 'Oligarchy' + }, + { + keys: [79, 80], + value: 'Patriarchy' + }, + { + keys: [81, 83], + value: 'Meritocracy' + }, + { + keys: [84, 85], + value: 'Plutocracy' + }, + { + keys: [86, 92], + value: 'Republic' + }, + { + keys: [93, 94], + value: 'Satrapy' + }, + { + keys: [95], + value: 'Kleptocracy' + }, + { + keys: [96, 100], + value: 'Theocracy' + }, + ] + }, + { + name: 'World-Shaking Events', + dice: [10], + possibleResults: [ + { + keys: [1], + value: 'Rise of a leader or an era' + }, + { + keys: [2], + value: 'Fall of a leader or an era' + }, + { + keys: [3], + value: 'Cataclysmic disaster' + }, + { + keys: [4], + value: 'Assault or invasion' + }, + { + keys: [5], + value: 'Rebellion, revolution, overthrow' + }, + { + keys: [6], + value: 'Extinction or depletion' + }, + { + keys: [7], + value: 'New organization' + }, + { + keys: [8], + value: 'Discovery, expansion, invention' + }, + { + keys: [9], + value: 'Prediction, omen, prophecy' + }, + { + keys: [10], + value: 'Myth and legend' + } + ] + }, + { + name: 'Leader Types', + dice: [6], + possibleResults: [ + { + keys: [1], + value: 'Political' + }, + { + keys: [2], + value: 'Religious' + }, + { + keys: [3], + value: 'Military' + }, + { + keys: [4], + value: 'Crime/underworld' + }, + { + keys: [5], + value: 'Art/culture' + }, + { + keys: [6], + value: 'Philosophy/learning/magic' + } + ] + }, + { + name: 'Cataclysmic Disasters', + dice: [10], + possibleResults: [ + { + keys: [1], + value: 'Earthquake' + }, + { + keys: [2], + value: 'Famine/drought' + }, + { + keys: [3], + value: 'Fire' + }, + { + keys: [4], + value: 'Flood' + }, + { + keys: [5], + value: 'Plague/disease' + }, + { + keys: [6], + value: 'Rain of fire (meteoric impact)' + }, + { + keys: [7], + value: 'Storm (hurrican, tornado, tsunami)' + }, + { + keys: [8], + value: 'Volcanic eruption' + }, + { + keys: [9], + value: 'Magic gone awry or a planar warp' + }, + { + keys: [10], + value: 'Divine judgment' + } + ] + }, + { + name: 'Invading Forces', + dice: [8], + possibleResults: [ + { + keys: [1], + value: 'A criminal enterprise' + }, + { + keys: [2], + value: 'Monsters or a unique monster' + }, + { + keys: [3], + value: 'A planar threat' + }, + { + keys: [4], + value: 'A past adversary reawakened, reborn, or resurgent' + }, + { + keys: [5], + value: 'A splinter faction' + }, + { + keys: [6], + value: 'A savage tribe' + }, + { + keys: [7], + value: 'A secret society' + }, + { + keys: [8], + value: 'A traitorous ally' + } + ] + }, + { + name: 'Extinction or Depletion', + dice: [8], + possibleResults: [ + { + keys: [1], + value: 'A kind of animal (insect, bird, fish, livestock)' + }, + { + keys: [2], + value: 'Habitable land' + }, + { + keys: [3], + value: 'Magic or magic-users (all magic, or specific kinds or schools of magic)' + }, + { + keys: [4], + value: 'A mineral resource (gems, metals, ores)' + }, + { + keys: [5], + value: 'A type of monster (unicorn, manticore, dragon)' + }, + { + keys: [6], + value: 'A people (family line, clan, culture, race)' + }, + { + keys: [7], + value: 'A kind of plant (crop, tree, herb, forest)' + }, + { + keys: [8], + value: 'A waterway (river, lake, ocean)' + } + ] + }, +]; + +export { + tableData +} diff --git a/src/services/tableService.ts b/src/services/tableService.ts new file mode 100644 index 0000000..6878920 --- /dev/null +++ b/src/services/tableService.ts @@ -0,0 +1,89 @@ +import { d } from './dice'; + +class TableService { + +} + +/* + From the DM Guide Book + Example table p.116 in DM Guide + d12 + d8 | Encounter + 2 | Sunken ship covered in barnacles (25 percent chance that the ship contains treasure; roll randomly on the treasure tables in chapter 7) + 3 | Sunken ship with reef sharks + 4 | Bed of giant oysters + 5 | Underwater steam vent (25 percent chance that the vent is a portal to the Elemental Plane of Fire) + ... + 20 | Sunken treasure chest (25 percent chance that it contains something of value; roll treasure randomly using the tables in chapter 7) + + A good index of the tables: https://slyflourish.com/random_tables_of_the_dmg.html + + Other good tables + https://www.dicegeeks.com/category/random-rpg-tables/ +*/ + +class RandomTable { + public name: string; + private dice: number[]; + private indexedResults: TableResult[] = []; + + constructor(tableConfig: TableConfig) { + this.name = tableConfig.name; + this.dice = tableConfig.dice; + this.constructIndex(tableConfig.possibleResults); + } + + 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]; + } + + 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(tableResults: TableResult[]) { + for (let resultIndex in tableResults) { + let keys: number[] = tableResults[resultIndex].keys; + for (let r = Math.min(...keys); r <= Math.max(...keys); r++) { + this.indexedResults.push(tableResults[resultIndex]); + } + } + } +} + +interface TableResult { + keys: number[]; + value: string; + nextTable?: string; + description?: string; +} + +interface TableConfig { + name: string; + dice: number[]; + possibleResults: TableResult[]; +} + +const tableService: TableService = new TableService(); + +export { + RandomTable, + TableResult, + TableConfig, + tableService +} diff --git a/src/services/test.ts b/src/services/test.ts new file mode 100644 index 0000000..36eea7a --- /dev/null +++ b/src/services/test.ts @@ -0,0 +1,2 @@ +import { tableData } from './tableData'; +import { tableService, RandomTable, TableResult, TableConfig } from './tableService'; diff --git a/src/views/test-table.vue b/src/views/test-table.vue new file mode 100644 index 0000000..506dbb4 --- /dev/null +++ b/src/views/test-table.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/yarn.lock b/yarn.lock index bf3c3dd..26c4213 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6985,7 +6985,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0: +rxjs@^6.4.0, rxjs@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== @@ -8208,6 +8208,16 @@ vue-property-decorator@^8.3.0: dependencies: vue-class-component "^7.1.0" +vue-router@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.3.tgz#e6b14fabc0c0ee9fda0e2cbbda74b350e28e412b" + integrity sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ== + +vue-rx@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/vue-rx/-/vue-rx-6.2.0.tgz#c3b86462b252626edd16417385bb9054a3a23acb" + integrity sha512-tpKUcqS5IUYS+HsdbR5TlE5LL9PK4zwlplEtmMeydnbqaUTa9ciLMplJXAtFSiQw1vuURoyEJmCXqMxaVEIloQ== + vue-style-loader@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"