Middle Ages Daily Life Simulator

Embark on Your Medieval Journey

Character Creation

Your Medieval Chronicle

Customize Your Chronicle

My Medieval Story

Your simulation log will appear here once you complete a simulation.

${summaryText}

`; } function saveLastChronicle() { const chronicleData = { character: character, simulationLog: simulationLog, title: chronicleTitleInput.value, lastScenarioId: currentScenarioId // To get the end summary }; localStorage.setItem(LS_CHRONICLE_KEY, JSON.stringify(chronicleData)); } function loadLastChronicle() { const saved = localStorage.getItem(LS_CHRONICLE_KEY); if (saved) { const data = JSON.parse(saved); character = data.character || {}; // Ensure character is an object simulationLog = data.simulationLog || []; currentScenarioId = data.lastScenarioId; // Used for summary text if (data.title) chronicleTitleInput.value = data.title; // Important: Ensure character.stats exists if character is loaded if (character && !character.stats && character.role) { character.stats = JSON.parse(JSON.stringify(initialStats[character.role] || initialStats.Peasant)); } else if (!character.stats) { // Absolute fallback character.stats = JSON.parse(JSON.stringify(initialStats.Peasant)); } renderChronicle(); } const savedCustom = localStorage.getItem(LS_CUSTOM_KEY); if (savedCustom) { const custom = JSON.parse(savedCustom); chronicleTitleInput.value = custom.title || "My Medieval Story"; primaryColorInput.value = custom.primaryColor || "#5D4037"; textColorInput.value = custom.textColor || "#212121"; bgColorInput.value = custom.bgColor || "#F5F5F5"; logEntryBgColorInput.value = custom.logEntryBgColor || "#FFFFFF"; updateCssVariables(); // Apply loaded custom colors } } function saveCustomization() { const customData = { title: chronicleTitleInput.value, primaryColor: primaryColorInput.value, textColor: textColorInput.value, bgColor: bgColorInput.value, logEntryBgColor: logEntryBgColorInput.value }; localStorage.setItem(LS_CUSTOM_KEY, JSON.stringify(customData)); } function handlePdfDownload() { if (!jsPDF_constructor_for_tool) { alert("PDF library is not available."); return; } if (simulationLog.length === 0) { alert("No simulation log to export. Please run a simulation first."); return; } const pdf = new jsPDF_constructor_for_tool({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfTitle = chronicleTitleInput.value; const primaryColor = primaryColorInput.value; const textColor = textColorInput.value; const pageBgColor = bgColorInput.value; // PDF page background pdf.setFillColor(pageBgColor); pdf.rect(0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight(), 'F'); const margin = 15; const contentWidth = pdf.internal.pageSize.getWidth() - 2 * margin; let currentY = margin; // PDF Title pdf.setFontSize(18); pdf.setTextColor(primaryColor); const titleLines = pdf.splitTextToSize(pdfTitle, contentWidth); pdf.text(titleLines, pdf.internal.pageSize.getWidth() / 2, currentY, { align: 'center' }); currentY += (titleLines.length * 18 * 0.35) + 10; // Character Summary pdf.setFontSize(12); pdf.setTextColor(textColor); const charSummary = `Character: ${character.name}, the ${character.role}`; pdf.text(charSummary, margin, currentY); currentY += 8; // Log Entries simulationLog.forEach(entry => { let logBlockHeight = 0; // Estimate height for page break pdf.setFontSize(10); logBlockHeight += (pdf.splitTextToSize(`Scenario: ${entry.scenarioText}`, contentWidth).length * 3.5); pdf.setFontSize(9); logBlockHeight += (pdf.splitTextToSize(`Choice: ${entry.choiceText}`, contentWidth).length * 3); logBlockHeight += (pdf.splitTextToSize(`Outcome: ${entry.outcomeText}`, contentWidth).length * 3); logBlockHeight += 7; // Spacing if (currentY + logBlockHeight > pdf.internal.pageSize.getHeight() - margin) { pdf.addPage(); currentY = margin; pdf.setFillColor(pageBgColor); pdf.rect(0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight(), 'F'); } pdf.setFontSize(10); pdf.setTextColor(primaryColor); pdf.setFont("helvetica", "bold"); const scenarioLines = pdf.splitTextToSize(`Scenario: ${entry.scenarioText}`, contentWidth); pdf.text(scenarioLines, margin, currentY); currentY += (scenarioLines.length * 10 * 0.35) + 1; pdf.setFontSize(9); pdf.setTextColor(textColor); pdf.setFont("helvetica", "italic"); const choiceLines = pdf.splitTextToSize(`Your Choice: ${entry.choiceText}`, contentWidth - 5); // Indent pdf.text(choiceLines, margin + 5, currentY); currentY += (choiceLines.length * 9 * 0.35) + 1; pdf.setFont("helvetica", "normal"); const outcomeLines = pdf.splitTextToSize(`Outcome: ${entry.outcomeText}`, contentWidth - 5); pdf.text(outcomeLines, margin + 5, currentY); currentY += (outcomeLines.length * 9 * 0.35) + 5; }); // Final Stats if (currentY + 20 > pdf.internal.pageSize.getHeight() - margin) { // Check for space for stats & summary pdf.addPage(); currentY = margin; pdf.setFillColor(pageBgColor); pdf.rect(0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight(), 'F'); } pdf.setFontSize(12); pdf.setTextColor(primaryColor); pdf.setFont("helvetica", "bold"); pdf.text("Final Stats:", margin, currentY); currentY += 6; pdf.setFontSize(10); pdf.setTextColor(textColor); pdf.setFont("helvetica", "normal"); pdf.text(`Health: ${character.stats.health}, Wealth: ${character.stats.wealth}, Happiness/Reputation: ${character.stats.happiness}`, margin, currentY); currentY += 8; // Narrative Summary const lastEventId = currentScenarioId; const lastScenario = scenarios[lastEventId] || SIM_END_DEFAULT; let narrative = `${character.name} the ${character.role} ${lastScenario.summary || "completed their simulated journey."}`; if (character.stats.health <= 2) narrative += " It was a physically tolling experience."; if (character.stats.wealth >= 7) narrative += " They showed some knack for improving their material standing."; if (character.stats.happiness <= 3) narrative += " Their spirits were often low."; else if (character.stats.happiness >= 7) narrative += " They found moments of joy or earned respect."; if (currentY + (pdf.splitTextToSize(narrative, contentWidth).length * 10 * 0.35) + 10 > pdf.internal.pageSize.getHeight() - margin) { pdf.addPage(); currentY = margin; pdf.setFillColor(pageBgColor); pdf.rect(0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight(), 'F'); } pdf.setFontSize(12); pdf.setTextColor(primaryColor); pdf.setFont("helvetica", "bold"); pdf.text("Concluding Thoughts:", margin, currentY); currentY += 6; pdf.setFontSize(10); pdf.setTextColor(textColor); pdf.setFont("helvetica", "normal"); const narrativeLines = pdf.splitTextToSize(narrative, contentWidth); pdf.text(narrativeLines, margin, currentY); let safePdfTitle = pdfTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase() || "medieval_chronicle"; if (safePdfTitle.length > 50) safePdfTitle = safePdfTitle.substring(0, 50); pdf.save(safePdfTitle + '.pdf'); } // Event Listeners tabs.forEach(tab => { tab.addEventListener('click', (e) => switchTab(e.currentTarget.dataset.tab)); }); document.querySelectorAll('.msa-next-tab, .msa-prev-tab').forEach(button => { button.addEventListener('click', (e) => { const targetTabId = e.currentTarget.dataset.nexttab || e.currentTarget.dataset.prevtab; if (targetTabId) switchTab(targetTabId); }); }); if(startSimButton) startSimButton.addEventListener('click', startSimulation); [chronicleTitleInput, primaryColorInput, textColorInput, bgColorInput, logEntryBgColorInput].forEach(input => { if (input) { input.addEventListener('input', () => { updateCssVariables(); renderChronicle(); // Re-render chronicle if colors change saveCustomization(); }); if (input.type === 'color') input.addEventListener('change', () => { updateCssVariables(); renderChronicle(); saveCustomization(); }); } }); if (downloadPdfButton && !downloadPdfButton.disabled) { downloadPdfButton.addEventListener('click', handlePdfDownload); } // Initialization loadLastChronicle(); // Load saved chronicle and customization if available switchTab('simulationSetup'); // Default to first tab // updateCssVariables is called by switchTab and loadLastChronicle // renderChronicle is called by loadLastChronicle });
Scroll to Top