Fantasy World Generator

Fantasy World Generator

Fantasy World Generator

Define core concepts and generate random elements for your next campaign.

1. Foundational Theme

2. Core Elements (Races, Deities, Factions)

Key Races/Factions

    Deities/Organizations

      3. Random Element Generation

      Click a button above to generate a random element!

      Add races/factions.

      '; } races.forEach((item, index) => { const li = document.createElement('li'); li.className = 'fwg-list-item'; li.innerHTML = `${item}`; raceList.appendChild(li); }); if (deities.length === 0) { deityList.innerHTML = '

      Add deities/orgs.

      '; } deities.forEach((item, index) => { const li = document.createElement('li'); li.className = 'fwg-list-item'; li.innerHTML = `${item}`; deityList.appendChild(li); }); } function addItem(inputType, listState) { const item = inputType.value.trim(); if (item && !listState.includes(item)) { listState.push(item); inputType.value = ''; renderLists(); } } function removeItem(index, listState, listType) { listState.splice(index, 1); renderLists(); } // Event delegation for removal container.addEventListener('click', (e) => { const target = e.target; if (target.classList.contains('fwg-btn-red') && target.dataset.index) { const index = parseInt(target.dataset.index); if (target.dataset.type === 'race') { removeItem(index, races, 'race'); } else if (target.dataset.type === 'deity') { removeItem(index, deities, 'deity'); } } }); // --- Generation Handlers --- function handleGenerateCity() { const prefix = getRandom(RANDOM_DATA.CityPrefix); const suffix = getRandom(RANDOM_DATA.CitySuffix); outputPlaceholder.classList.add('hidden'); outputCity.innerHTML = `City Name: ${prefix}${suffix}`; } function handleGenerateNpc() { const title = getRandom(RANDOM_DATA.NpcTitles); const role = getRandom(RANDOM_DATA.NpcRoles); outputPlaceholder.classList.add('hidden'); outputNpc.innerHTML = `NPC Concept: ${role}, known as "${title}"`; } function handleGenerateArtifact() { const adj = getRandom(RANDOM_DATA.ArtifactAdjective); const noun = getRandom(RANDOM_DATA.ArtifactNoun); outputPlaceholder.classList.add('hidden'); outputArtifact.innerHTML = `Key Artifact: The ${adj} ${noun} of ${getRandom(races) || 'The Ancients'}`; } // --- PDF Generation (Ensured functionality) --- function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('portrait', 'pt', 'a4'); const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); let currentY = margin; // --- Header --- doc.setFontSize(24); doc.setFont('helvetica', 'bold'); doc.setTextColor(124, 58, 237); doc.text(worldNameInput.value || 'Untitled Fantasy World', pageWidth / 2, currentY, { align: 'center' }); currentY += 15; doc.setFontSize(14); doc.text("World Lore Document", pageWidth / 2, currentY, { align: 'center' }); currentY += 30; // --- Section 1: Core Theme --- doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.setTextColor(124, 58, 237); doc.text("1. Foundational Theme", margin, currentY); currentY += 5; const themeData = [ ["Magic System:", magicTypeSelect.value], ["Technology Level:", techLevelSelect.value], ["Primary Conflict:", conflictTypeSelect.value], ]; doc.autoTable({ startY: currentY, body: themeData, theme: 'striped', styles: { cellPadding: 3, fontSize: 10 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [245, 243, 255] } } }); currentY = doc.autoTable.previous.finalY + 10; doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.text("Key World Hook/Pitch:", margin, currentY); currentY += 12; doc.setFont('helvetica', 'normal'); const hookLines = doc.splitTextToSize(keyHookTextarea.value || 'N/A', pageWidth - margin * 2); doc.text(hookLines, margin, currentY); currentY += hookLines.length * 12 + 15; // --- Section 2: Core Elements (Tables) --- doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text("2. Core World Elements", margin, currentY); currentY += 15; const elementData = [ ["Key Races/Factions", races.join('\n')], ["Deities/Organizations", deities.join('\n')] ]; doc.autoTable({ startY: currentY, body: elementData, theme: 'grid', styles: { cellPadding: 4, fontSize: 10, minCellHeight: 20 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [245, 243, 255], cellWidth: 100 } } }); currentY = doc.autoTable.previous.finalY + 20; // --- Section 3: Generated Elements --- doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text("3. Generated Elements (for reference)", margin, currentY); currentY += 15; const generatedData = [ ["City Name:", outputCity.textContent.replace('City Name:', '').trim() || 'N/A'], ["NPC Concept:", outputNpc.textContent.replace('NPC Concept:', '').trim() || 'N/A'], ["Key Artifact:", outputArtifact.textContent.replace('Key Artifact:', '').trim() || 'N/A'] ]; doc.autoTable({ startY: currentY, body: generatedData, theme: 'striped', styles: { cellPadding: 4, fontSize: 10, minCellHeight: 15 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [255, 247, 237], textColor: [180, 83, 9] } } }); doc.save('fantasy-world-document.pdf'); } // --- Event Listeners and Initial Load --- addRaceBtn.addEventListener('click', () => addItem(raceInput, races)); addDeityBtn.addEventListener('click', () => addItem(deityInput, deities)); generateCityBtn.addEventListener('click', handleGenerateCity); generateNpcBtn.addEventListener('click', handleGenerateNpc); generateArtifactBtn.addEventListener('click', handleGenerateArtifact); pdfBtn.addEventListener('click', downloadPDF); renderLists(); });
      Scroll to Top