Forum

Create merchant NPC...
 
Notifications
Clear all

Guide Create merchant NPCs with inventory pulled from rollable tables

Stendarpaval
(@stendarpaval)
Candidate

A redditor called Oberon_Blade asked for help (link) in making a macro that creates merchants based on rollable tables. They use the pf2e system, which by default supports loot sheets. I helped them out with such a macro, which should also work for the dnd5e system or any other system, really. They then suggested that I share that macro here, because it's very useful. 

So, here it is:

/*==============================================================
	 NPC Random Equipment, by Stendarpaval
//==============================================================

	 Initial set-up: 
	 1. Place items (such as weapons, equipment, consumables, etc.) into 
			folders. 
	 2. Create rollable tables from these folders by right-clicking them
			and selecting "Create Rollable Table".

//=============================================================*/

// Choose what folder to add the new actor to:
const folderName = "Merchants";

// Basic Actor data
const actorData = {
	name: "New Merchant",
	type: (game.system.id === "pf2e") ? "loot" : "npc",
	img: "icons/svg/mystery-man.svg"
}

// Choose how many roll tables can be drawn from for a single merchant:
const numTableOptions = 3;

//==============================================================

let tableNamesAndAmounts = {};
for (let table of game.tables.entities) {
	tableNamesAndAmounts[table.name] = 0;
}

let selectTable = ``;
for (let i = 0; i < numTableOptions; i++) {
	let selectTableHTML = `<label style="grid-column-start:1; align-self:center;">Roll Table:</label><select name="table-name${i}" style="grid-column-start:2; align-self:center;">`;
	for (let tableName of Object.keys(tableNamesAndAmounts)) {
		selectTableHTML += `<option value="${tableName}">${tableName}</option>`
	}
	selectTableHTML += `</select><label style="grid-column-start:1; align-self:center;">Number of items:</label><label style="grid-column-start:2; align-self:center;"><input type="number" min="1" name="num-items${i}" placeholder="1"/></label><hr>`;
	selectTable += selectTableHTML;
}


const content = `<form>
	<div style="display:grid; grid-template-columns:120px 160px; column-gap:5px;">
	<label style="grid-column-start:1; align-self:center;">Name: </label>
	<label style="grid-column-start:2; align-self:center;"><input type="text" autofocus name="merchant-name" value="${actorData.name}"/></label>
	<br>
	${selectTable}
	</div></form>`;

new Dialog({
	title: "Create Merchant",
	content: content,
	buttons: {
		one: {
			label: "Create",
			callback: (html) => {
				const merchantName = html.find(`input[name="merchant-name"]`)[0].value;
				actorData["name"] = merchantName;
				let tableName, parsedNumItems;
				for (let i = 0; i < numTableOptions; i++) {
					tableName = html.find(`[name="table-name${i}"]`)[0].value;
					parsedNumItems = parseInt(html.find(`input[name="num-items${i}"]`)[0].value);
					if (tableNamesAndAmounts[tableName]) {
						parsedNumItems += tableNamesAndAmounts[tableName];	
					}
					tableNamesAndAmounts[tableName] = parsedNumItems;
				}
				(async () => {
					await makeMerchant();
				})();
			}
		}
	},
	default: "one"
},{width:300}).render(true);


async function makeMerchant() {
	let itemData = [];

	for (let [tableName, numItem] of Object.entries(tableNamesAndAmounts)) {
		addItemFromRollTable(game.tables.entities.find(t => t.name === tableName), numItem, itemData);
	}

	// Create items on Actor and Token Actor sheets
	let lootFolder;
	if (!game.folders.find(f => f.name === folderName)) {
		lootFolder = await Folder.create({'name': folderName, 'type': "Actor", 'parent': null});
	} else {
		lootFolder = game.folders.find(f => f.name === folderName);
	}
	actorData["folder"] = lootFolder.id;
	const numTwins = lootFolder.content.filter(a => a.name.startsWith(actorData.name))?.length;
	console.log(numTwins);
	if (numTwins > 0) {
		actorData["name"] = `${actorData.name} (${numTwins + 1})`;
	}
	const actor = await Actor.create(actorData);
	const createdItemActor = await actor.createEmbeddedEntity("OwnedItem", itemData);

	ui.notifications.info(`Created ${actor.name} with ${itemData.length} items.`);
}


function addItemFromRollTable(rolltable, num, itemData) {
	for (let i = 0; i < num; i++) {
		const rollItemName = rolltable.roll().results[0].text;
		const rolledItemData = game.data.items.find(w => w.name === rollItemName);
		itemData.push(rolledItemData);
	};
}

This macro will make a folder in the actor directory called Merchants, unless it already exists. It also displays a prompt asking you for the new merchant's name, and from which roll tables it should randomly draw how many items. If a merchant with that name already exists, it adds a counter after its name (like "New Merchant (2)" and so on).

You can configure the default actor image in the macro, as well as how many roll tables you can draw from to create a single merchant.

Note that you can leave the "Number of items" field in the prompt empty, so it will draw 0 items from roll tables unless you specifically tell it to draw more than that from them.

If there are any questions about this macro, don't hesitate to aks. I'm not sure how often I'll check this new forum, so if I haven't replied for a while feel free to send me a PM on reddit. 

Cheers!

Quote
Topic starter Posted : 05/05/2021 7:24 am
Share: