random-tables-mcp
Version:
An MCP (Model Context Protocol) server for managing and rolling on random-table assets used in tabletop RPGs. Create, update, and roll on random tables with support for nested tables, weighted entries, and range-based results.
146 lines • 5.38 kB
JavaScript
import { TableEntry } from './table-entry.js';
import { RollResult } from './roll-result.js';
/**
* Represents a random table with entries that can be rolled on.
*/
export class RandomTable {
/**
* Creates a new RandomTable instance.
* @param id Unique identifier for the table.
* @param name Table name.
* @param description Optional description.
* @param entries Optional initial entries.
*/
constructor(id, name, description = '', entries = []) {
this.id = id;
this.name = name;
this.description = description;
this._entries = new Map();
if (!id) {
throw new Error('Table ID is required');
}
if (!name) {
throw new Error('Table name is required');
}
// Add initial entries
entries.forEach(entry => this.addEntry(entry));
}
/**
* Gets all entries in this table.
* @returns An array of TableEntry objects.
*/
get entries() {
return Array.from(this._entries.values());
}
/**
* Gets the total weight of all entries in this table.
* @returns The sum of all entry weights.
*/
get totalWeight() {
return this.entries.reduce((sum, entry) => sum + entry.weight, 0);
}
/**
* Adds a new entry to the table.
* @param entry The entry to add.
* @throws Error if an entry with the same ID already exists.
*/
addEntry(entry) {
if (this._entries.has(entry.id)) {
throw new Error(`Entry with ID ${entry.id} already exists`);
}
this._entries.set(entry.id, entry);
}
/**
* Removes an entry from the table.
* @param entryId The ID of the entry to remove.
* @throws Error if the entry does not exist.
*/
removeEntry(entryId) {
if (!this._entries.has(entryId)) {
throw new Error(`Entry with ID ${entryId} does not exist`);
}
this._entries.delete(entryId);
}
/**
* Updates an existing entry in the table.
* @param entryId The ID of the entry to update.
* @param updates Object containing the properties to update.
* @throws Error if the entry does not exist.
*/
updateEntry(entryId, updates) {
const entry = this._entries.get(entryId);
if (!entry) {
throw new Error(`Entry with ID ${entryId} does not exist`);
}
this._entries.set(entryId, entry.update(updates));
}
/**
* Gets an entry by ID.
* @param entryId The ID of the entry to get.
* @returns The entry, or undefined if it does not exist.
*/
getEntry(entryId) {
return this._entries.get(entryId);
}
/**
* Performs a roll on the table using a random number generator.
* @param rng A function that returns a random number between 0 and 1.
* @returns A RollResult object.
* @throws Error if the table has no entries.
*/
roll(rng = Math.random) {
if (this.entries.length === 0) {
throw new Error('Cannot roll on an empty table');
}
// Handle tables with entries that have ranges
const entriesWithRanges = this.entries.filter(entry => entry.range);
if (entriesWithRanges.length > 0) {
// Find the maximum range value across all entries
const maxRange = Math.max(...entriesWithRanges.map(entry => entry.range.max));
// Roll a value within the range
const rollValue = Math.floor(rng() * maxRange) + 1;
// Find the entry that contains this value
const foundEntry = this.entries.find(entry => entry.range && entry.isInRange(rollValue));
if (foundEntry) {
const isTemplate = foundEntry.isTemplate();
return new RollResult(this.id, foundEntry.id, foundEntry.content, isTemplate);
}
}
// For tables without ranges or if no range matched, use weighted random selection
const totalWeight = this.totalWeight;
let randomValue = rng() * totalWeight;
for (const entry of this.entries) {
randomValue -= entry.weight;
if (randomValue <= 0) {
const isTemplate = entry.isTemplate();
return new RollResult(this.id, entry.id, entry.content, isTemplate);
}
}
// Fallback (should never happen with proper weights)
const fallbackEntry = this.entries[0];
const isTemplate = fallbackEntry.isTemplate();
return new RollResult(this.id, fallbackEntry.id, fallbackEntry.content, isTemplate);
}
/**
* Creates a RandomTable from a plain object.
* @param obj The object to create the table from.
* @returns A new RandomTable instance.
*/
static fromObject(obj) {
const entries = obj.entries?.map(entry => TableEntry.fromObject(entry)) ?? [];
return new RandomTable(obj.id, obj.name, obj.description ?? '', entries);
}
/**
* Converts this table to a plain object.
* @returns A plain object representation of this table.
*/
toObject() {
return {
id: this.id,
name: this.name,
description: this.description,
entries: this.entries.map(entry => entry.toObject()),
};
}
}
//# sourceMappingURL=random-table.js.map