Fantasy Map Story Companion

Fantasy Map Story Companion

Bring your world to life by generating stories for map locations.

🌲 ⛰️ 🏰

Select a Location

Click a location on the map to generate its story...

${storyData.story}

`; worldLogContainer.appendChild(logEntry); }); }; mapLocations.forEach(location => { location.addEventListener('click', () => { const locationId = location.id; const locationName = location.dataset.name; const tone = storyToneSelect.value; const focus = storyFocusSelect.value; const story = generateStory(locationName, tone, focus); // Update main display locationNameOutput.textContent = locationName; locationStoryOutput.textContent = story; // Save story generatedStories[locationId] = { name: locationName, story: story }; // Update log and enable PDF button updateWorldLog(); downloadPdfButton.disabled = false; }); }); const downloadPDF = () => { if (Object.keys(generatedStories).length === 0) return; const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pageMargin = 20; const pageWidth = doc.internal.pageSize.getWidth(); const pageHeight = doc.internal.pageSize.getHeight(); const contentWidth = pageWidth - (pageMargin * 2); let y = 0; const realm = realmNameInput.value.trim() || 'This Realm'; // --- PDF Template: Fantasy Chronicle --- doc.addPage(); doc.deletePage(1); // Start with a fresh page // Title Page doc.setFont('times', 'bold'); doc.setFontSize(28); doc.text(`The Chronicles of ${realm}`, pageWidth / 2, pageHeight / 2 - 10, { align: 'center' }); doc.setFont('times', 'normal'); doc.setFontSize(14); doc.text('A Companion to its Lands and Legends', pageWidth / 2, pageHeight / 2, { align: 'center' }); // Content Pages Object.values(generatedStories).forEach(storyData => { doc.addPage(); y = pageMargin + 10; // Location Header doc.setFont('times', 'bold'); doc.setFontSize(18); doc.text(storyData.name, pageWidth / 2, y, { align: 'center' }); y += 5; doc.setLineWidth(0.5); doc.line(pageMargin + 20, y, pageWidth - pageMargin - 20, y); y += 15; // Story Text doc.setFont('times', 'normal'); doc.setFontSize(12); const textLines = doc.splitTextToSize(storyData.story, contentWidth); doc.text(textLines, pageMargin, y); }); // Page Footers const pageCount = doc.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); doc.setFontSize(10); doc.setTextColor(150); // Don't put a page number on the title page if (i > 1) { doc.text(`Page ${i-1}`, pageWidth / 2, pageHeight - 10, { align: 'center' }); } } doc.save(`${realm.replace(/\s+/g, '_')}-Chronicles.pdf`); }; // Event Listeners for buttons nextButton.addEventListener('click', () => (currentTab === '1') && switchTab('2')); prevButton.addEventListener('click', () => (currentTab === '2') && switchTab('1')); tabs['1'].button.addEventListener('click', () => switchTab('1')); tabs['2'].button.addEventListener('click', () => switchTab('2')); downloadPdfButton.addEventListener('click', downloadPDF); updateNavButtons(); // Initial setup });
Scroll to Top