Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>StoryVerse - Interactive Story Adventures</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://unpkg.com/[email protected]/dist/aos.css" rel="stylesheet"> | |
| <script src="https://unpkg.com/[email protected]/dist/aos.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/animejs/lib/anime.iife.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Inter:wght@300;400;500;600&display=swap'); | |
| :root { | |
| --primary-gold: #FFD700; | |
| --primary-purple: #6B46C1; | |
| --dark-bg: #0F0F23; | |
| --card-bg: rgba(255, 255, 255, 0.05); | |
| } | |
| .font-cinzel { font-family: 'Cinzel', serif; } | |
| .font-inter { font-family: 'Inter', sans-serif; } | |
| .story-card { | |
| background: var(--card-bg); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .story-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 40px rgba(107, 70, 193, 0.3); | |
| } | |
| .choice-btn { | |
| background: linear-gradient(135deg, var(--primary-purple), #8B5CF6); | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .choice-btn:before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
| transition: left 0.5s; | |
| } | |
| .choice-btn:hover:before { | |
| left: 100%; | |
| } | |
| .gradient-text { | |
| background: linear-gradient(135deg, var(--primary-gold), var(--primary-purple)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .floating { | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0px); } | |
| 50% { transform: translateY(-20px); } | |
| } | |
| .vanta-canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| } | |
| .story-progress { | |
| background: linear-gradient(90deg, var(--primary-purple), var(--primary-gold)); | |
| height: 4px; | |
| transition: width 0.5s ease; | |
| } | |
| .achievement-badge { | |
| background: rgba(255, 215, 0, 0.1); | |
| border: 2px solid var(--primary-gold); | |
| color: var(--primary-gold); | |
| } | |
| .modal-backdrop { | |
| backdrop-filter: blur(5px); | |
| background: rgba(0, 0, 0, 0.8); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white font-inter"> | |
| <!-- Vanta Background --> | |
| <div id="vanta-bg" class="fixed inset-0"></div> | |
| <!-- Navigation --> | |
| <nav class="relative z-50 bg-black bg-opacity-50 backdrop-blur-md sticky top-0"> | |
| <div class="container mx-auto px-4 py-4"> | |
| <div class="flex justify-between items-center"> | |
| <div class="flex items-center space-x-2"> | |
| <i data-feather="book-open" class="w-8 h-8 text-yellow-400"></i> | |
| <h1 class="text-2xl font-cinzel font-bold gradient-text">StoryVerse</h1> | |
| </div> | |
| <div class="flex items-center space-x-6"> | |
| <button id="libraryBtn" class="flex items-center space-x-2 hover:text-yellow-400 transition"> | |
| <i data-feather="library"></i> | |
| <span>Library</span> | |
| </button> | |
| <button id="achievementsBtn" class="flex items-center space-x-2 hover:text-yellow-400 transition"> | |
| <i data-feather="award"></i> | |
| <span>Achievements</span> | |
| </button> | |
| <button id="profileBtn" class="flex items-center space-x-2 hover:text-yellow-400 transition"> | |
| <i data-feather="user"></i> | |
| <span>Profile</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Content --> | |
| <main class="relative z-10 container mx-auto px-4 py-12"> | |
| <!-- Hero Section --> | |
| <section class="text-center mb-16" data-aos="fade-up"> | |
| <h2 class="text-5xl md:text-7xl font-cinzel font-bold mb-4 gradient-text floating"> | |
| Choose Your Adventure | |
| </h2> | |
| <p class="text-xl text-gray-300 mb-8">Immerse yourself in interactive stories where every choice shapes your destiny</p> | |
| <div class="flex justify-center space-x-4"> | |
| <button id="startJourney" class="px-8 py-3 bg-gradient-to-r from-purple-600 to-pink-600 rounded-full font-semibold hover:shadow-2xl transform hover:scale-105 transition"> | |
| Start Your Journey | |
| </button> | |
| <button id="createStory" class="px-8 py-3 border-2 border-yellow-400 rounded-full font-semibold hover:bg-yellow-400 hover:text-black transition"> | |
| Create Story | |
| </button> | |
| </div> | |
| </section> | |
| <!-- Stories Grid --> | |
| <section id="storiesContainer" class="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16"> | |
| <!-- Stories will be dynamically loaded here --> | |
| </section> | |
| <!-- Story Reader Modal --> | |
| <div id="storyReader" class="fixed inset-0 z-50 hidden"> | |
| <div class="modal-backdrop absolute inset-0"></div> | |
| <div class="relative z-10 container mx-auto h-full flex items-center justify-center p-4"> | |
| <div class="bg-gray-800 rounded-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden"> | |
| <div class="p-6 border-b border-gray-700"> | |
| <div class="flex justify-between items-center"> | |
| <h3 id="currentStoryTitle" class="text-2xl font-cinzel font-bold"></h3> | |
| <button id="closeReader" class="text-gray-400 hover:text-white"> | |
| <i data-feather="x" class="w-6 h-6"></i> | |
| </button> | |
| </div> | |
| <div class="mt-4"> | |
| <div class="bg-gray-700 rounded-full h-2 overflow-hidden"> | |
| <div id="progressBar" class="story-progress" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="storyContent" class="p-6 overflow-y-auto max-h-[calc(90vh-200px)]"> | |
| <!-- Story content will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Achievements Modal --> | |
| <div id="achievementsModal" class="fixed inset-0 z-50 hidden"> | |
| <div class="modal-backdrop absolute inset-0"></div> | |
| <div class="relative z-10 container mx-auto h-full flex items-center justify-center p-4"> | |
| <div class="bg-gray-800 rounded-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden"> | |
| <div class="p-6 border-b border-gray-700"> | |
| <div class="flex justify-between items-center"> | |
| <h3 class="text-2xl font-cinzel font-bold">Achievements</h3> | |
| <button id="closeAchievements" class="text-gray-400 hover:text-white"> | |
| <i data-feather="x" class="w-6 h-6"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="achievementsContent" class="p-6 overflow-y-auto"> | |
| <!-- Achievements will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Create Story Modal --> | |
| <div id="createStoryModal" class="fixed inset-0 z-50 hidden"> | |
| <div class="modal-backdrop absolute inset-0"></div> | |
| <div class="relative z-10 container mx-auto h-full flex items-center justify-center p-4"> | |
| <div class="bg-gray-800 rounded-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden"> | |
| <div class="p-6 border-b border-gray-700"> | |
| <div class="flex justify-between items-center"> | |
| <h3 class="text-2xl font-cinzel font-bold">Create Your Story</h3> | |
| <button id="closeCreateStory" class="text-gray-400 hover:text-white"> | |
| <i data-feather="x" class="w-6 h-6"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="p-6 overflow-y-auto max-h-[calc(90vh-100px)]"> | |
| <form id="storyForm" class="space-y-6"> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Story Title</label> | |
| <input type="text" id="storyTitle" class="w-full px-4 py-2 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" placeholder="Enter your story title"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Description</label> | |
| <textarea id="storyDescription" rows="3" class="w-full px-4 py-2 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" placeholder="Describe your story"></textarea> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Story Events (JSON Format)</label> | |
| <textarea id="storyEvents" rows="10" class="w-full px-4 py-2 bg-gray-700 rounded-lg font-mono text-sm focus:outline-none focus:ring-2 focus:ring-purple-500" placeholder='[{"id": 1, "text": "Your story text...", "choices": [{"text": "Choice 1", "next_event": 2}]}]'></textarea> | |
| </div> | |
| <button type="submit" class="w-full py-3 bg-gradient-to-r from-purple-600 to-pink-600 rounded-lg font-semibold hover:shadow-2xl transition"> | |
| Create Story | |
| </button> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="relative z-10 bg-black bg-opacity-50 backdrop-blur-md mt-16"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="text-center"> | |
| <p class="text-gray-400">© 2024 StoryVerse. Crafted with magic and code.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| <script> | |
| // Vanta.js Background | |
| VANTA.NET({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x6b46c1, | |
| backgroundColor: 0x0f0f23, | |
| points: 8.00, | |
| maxDistance: 25.00, | |
| spacing: 20.00 | |
| }); | |
| // Story Data | |
| const storiesData = { | |
| "stories": [ | |
| { | |
| "title": "The Enchanted Forest", | |
| "description": "Join Lily on her adventure through the enchanted forest.", | |
| "image": "http://static.photos/nature/640x360/42", | |
| "events": [ | |
| { | |
| "id": 1, | |
| "text": "Lily finds a magical key glowing softly in the moonlight. What does she do?", | |
| "choices": [ | |
| { | |
| "text": "Open the hidden door behind the ancient oak.", | |
| "next_event": 2 | |
| }, | |
| { | |
| "text": "Keep the key and continue exploring the dark path.", | |
| "next_event": 3 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 2, | |
| "text": "Behind the door, Lily discovers a friendly dragon named Ember. The dragon offers to grant her one wish. What does she do?", | |
| "choices": [ | |
| { | |
| "text": "Ask the dragon to help find her lost brother.", | |
| "next_event": 4 | |
| }, | |
| { | |
| "text": "Politely decline and ask to be friends instead.", | |
| "next_event": 5 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 3, | |
| "text": "Continuing down the path, Lily stumbles upon a hidden treasure chest filled with ancient coins. What does she do?", | |
| "choices": [ | |
| { | |
| "text": "Take just one coin as a memory and leave the rest.", | |
| "next_event": 6 | |
| }, | |
| { | |
| "text": "Leave it untouched, sensing it might be cursed.", | |
| "next_event": 7 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 4, | |
| "text": "Ember the dragon flies Lily across the kingdom. They spot her brother in a distant tower. The dragon offers to help rescue him. What now?", | |
| "choices": [ | |
| { | |
| "text": "Accept the dragon's help and plan a rescue.", | |
| "next_event": 8 | |
| }, | |
| { | |
| "text": "Thank the dragon and say she must do this alone.", | |
| "next_event": 9 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 5, | |
| "text": "Lily and Ember become best friends. The dragon teaches her ancient magic and they explore the world together. The End.", | |
| "is_ending": true, | |
| "achievement": "Dragon's Friend" | |
| }, | |
| { | |
| "id": 6, | |
| "text": "The single coin brings Lily good fortune. She finds her way home safely, forever remembering her adventure. The End.", | |
| "is_ending": true, | |
| "achievement": "Fortune's Favorite" | |
| }, | |
| { | |
| "id": 7, | |
| "text": "By showing respect for the forest's mysteries, the trees themselves guide Lily home. She becomes a guardian of the woods. The End.", | |
| "is_ending": true, | |
| "achievement": "Forest Guardian" | |
| }, | |
| { | |
| "id": 8, | |
| "text": "Together, Lily and Ember rescue her brother. The three become inseparable, protecting the kingdom from evil. The End.", | |
| "is_ending": true, | |
| "achievement": "Heroic Trio" | |
| }, | |
| { | |
| "id": 9, | |
| "text": "Lily's courage inspires her to face challenges alone. She becomes a legendary hero, admired by all. The End.", | |
| "is_ending": true, | |
| "achievement": "Solo Hero" | |
| } | |
| ] | |
| }, | |
| { | |
| "title": "The Mystery of the Old Mansion", | |
| "description": "Solve the mystery of the old mansion with Detective Max.", | |
| "image": "http://static.photos/vintage/640x360/13", | |
| "events": [ | |
| { | |
| "id": 1, | |
| "text": "Detective Max finds a mysterious note hidden in the mansion's library. The paper is old and mentions a secret room. What does he do?", | |
| "choices": [ | |
| { | |
| "text": "Follow the clues in the note to find the secret room.", | |
| "next_event": 2 | |
| }, | |
| { | |
| "text": "Ignore the note and explore the mansion's basement first.", | |
| "next_event": 3 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 2, | |
| "text": "The note leads Max to a hidden room behind the bookshelf. Inside, he finds old documents about the mansion's original owner. What next?", | |
| "choices": [ | |
| { | |
| "text": "Examine the documents carefully for clues.", | |
| "next_event": 4 | |
| }, | |
| { | |
| "text": "Search the room for hidden compartments.", | |
| "next_event": 5 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 3, | |
| "text": "In the basement, Max discovers strange symbols on the walls. They seem to be part of an ancient ritual. What does he do?", | |
| "choices": [ | |
| { | |
| "text": "Document the symbols and research them.", | |
| "next_event": 6 | |
| }, | |
| { | |
| "text": "Follow the symbols to see where they lead.", | |
| "next_event": 7 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 4, | |
| "text": "The documents reveal the mansion was built on ancient burial grounds. The owner's ghost might be trapped. What action?", | |
| "choices": [ | |
| { | |
| "text": "Perform a ritual to free the ghost.", | |
| "next_event": 8 | |
| }, | |
| { | |
| "text": "Leave the mansion and seal it forever.", | |
| "next_event": 9 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 5, | |
| "text": "Max finds a hidden safe with the original deed. It reveals a shocking truth about the mansion's dark past. The End.", | |
| "is_ending": true, | |
| "achievement": "Truth Seeker" | |
| }, | |
| { | |
| "id": 6, | |
| "text": "Research shows the symbols are protective wards. Max restores them, cleansing the mansion of evil. The End.", | |
| "is_ending": true, | |
| "achievement": "Protector" | |
| }, | |
| { | |
| "id": 7, | |
| "text": "Following the symbols leads Max to a hidden chamber with treasure. He becomes rich but keeps the mansion's secret. The End.", | |
| "is_ending": true, | |
| "achievement": "Treasure Hunter" | |
| }, | |
| { | |
| "id": 8, | |
| "text": "The ritual succeeds! The grateful ghost reveals a hidden treasure as thanks. Max solves the mystery and helps the spirit rest. The End.", | |
| "is_ending": true, | |
| "achievement": "Ghost Whisperer" | |
| }, | |
| { | |
| "id": 9, | |
| "text": "Max seals the mansion, protecting future generations from its dark influence. His caution saves many lives. The End.", | |
| "is_ending": true, | |
| "achievement": "Guardian of Safety" | |
| } | |
| ] | |
| }, | |
| { | |
| "title": "Space Explorer's Dilemma", | |
| "description": "Navigate through space and make critical decisions as Captain Sarah.", | |
| "image": "http://static.photos/technology/640x360/88", | |
| "events": [ | |
| { | |
| "id": 1, | |
| "text": "Captain Sarah receives a distress signal from a nearby planet. The signal is weak but urgent. What action?", | |
| "choices": [ | |
| { | |
| "text": "Change course to investigate immediately.", | |
| "next_event": 2 | |
| }, | |
| { | |
| "text": "Continue the mission and report it to command.", | |
| "next_event": 3 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 2, | |
| "text": "On the planet, Sarah finds an abandoned alien colony with advanced technology. What should she do?", | |
| "choices": [ | |
| { | |
| "text": "Study the technology and document findings.", | |
| "next_event": 4 | |
| }, | |
| { | |
| "text": "Leave immediately and mark the planet as dangerous.", | |
| "next_event": 5 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 3, | |
| "text": "By following protocol, Sarah's crew remains safe but the distress signal stops. She feels guilty. What now?", | |
| "choices": [ | |
| { | |
| "text": "Return to investigate despite orders.", | |
| "next_event": 6 | |
| }, | |
| { | |
| "text": "Follow orders and continue the mission.", | |
| "next_event": 7 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 4, | |
| "text": "The technology reveals a way to travel faster than light. Sarah must decide whether to share this discovery. What choice?", | |
| "choices": [ | |
| { | |
| "text": "Share it with all of humanity.", | |
| "next_event": 8 | |
| }, | |
| { | |
| "text": "Keep it secret to prevent misuse.", | |
| "next_event": 9 | |
| } | |
| ] | |
| }, | |
| { | |
| "id": 5, | |
| "text": "Sarah's caution saves her crew from an alien virus. She returns home a hero for making the right choice. The End.", | |
| "is_ending": true, | |
| "achievement": "Cautious Leader" | |
| }, | |
| { | |
| "id": 6, | |
| "text": "Returning against orders, Sarah finds survivors and becomes a legend for her compassion. The End.", | |
| "is_ending": true, | |
| "achievement": "Compassionate Captain" | |
| }, | |
| { | |
| "id": 7, | |
| "text": "Following protocol earns Sarah a promotion, but she always wonders what might have been. The End.", | |
| "is_ending": true, | |
| "achievement": "Protocol Follower" | |
| }, | |
| { | |
| "id": 8, | |
| "text": "Sharing the technology revolutionizes space travel. Sarah becomes famous but faces ethical dilemmas. The End.", | |
| "is_ending": true, | |
| "achievement": "Revolutionary" | |
| }, | |
| { | |
| "id": 9, | |
| "text": "Keeping the secret, Sarah ensures it's used responsibly. She becomes a guardian of advanced knowledge. The End.", | |
| "is_ending": true, | |
| "achievement": "Secret Keeper" | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| // User Progress | |
| let userProgress = { | |
| currentStory: null, | |
| currentEvent: null, | |
| achievements: [], | |
| completedStories: [], | |
| storyHistory: [] | |
| }; | |
| // Load user progress from localStorage | |
| function loadUserProgress() { | |
| const saved = localStorage.getItem('storyVerseProgress'); | |
| if (saved) { | |
| userProgress = JSON.parse(saved); | |
| } | |
| } | |
| // Save user progress to localStorage | |
| function saveUserProgress() { | |
| localStorage.setItem('storyVerseProgress', JSON.stringify(userProgress)); | |
| } | |
| // Render stories | |
| function renderStories() { | |
| const container = document.getElementById('storiesContainer'); | |
| container.innerHTML = ''; | |
| storiesData.stories.forEach((story, index) => { | |
| const storyCard = document.createElement('div'); | |
| storyCard.className = 'story-card rounded-xl p-6 cursor-pointer'; | |
| storyCard.setAttribute('data-aos', 'fade-up'); | |
| storyCard.setAttribute('data-aos-delay', index * 100); | |
| const isCompleted = userProgress.completedStories.includes(story.title); | |
| storyCard.innerHTML = ` | |
| <div class="relative mb-4"> | |
| <img src="${story.image}" alt="${story.title}" class="w-full h-48 object-cover rounded-lg"> | |
| ${isCompleted ? '<div class="absolute top-2 right-2 bg-green-500 text-white px-2 py-1 rounded-full text-xs">Completed</div>' : ''} | |
| </div> | |
| <h3 class="text-xl font-cinzel font-bold mb-2">${story.title}</h3> | |
| <p class="text-gray-400 mb-4">${story.description}</p> | |
| <div class="flex justify-between items-center"> | |
| <span class="text-sm text-yellow-400">${story.events.length} events</span> | |
| <button class="px-4 py-2 bg-purple-600 rounded-lg hover:bg-purple-700 transition" onclick="startStory('${story.title}')"> | |
| Begin Adventure | |
| </button> | |
| </div> | |
| `; | |
| container.appendChild(storyCard); | |
| }); | |
| } | |
| // Start a story | |
| function startStory(storyTitle) { | |
| const story = storiesData.stories.find(s => s.title === storyTitle); | |
| if (!story) return; | |
| userProgress.currentStory = storyTitle; | |
| userProgress.currentEvent = 1; | |
| userProgress.storyHistory = []; | |
| openStoryReader(); | |
| renderStoryEvent(story, 1); | |
| updateProgressBar(0); | |
| } | |
| // Render story event | |
| function renderStoryEvent(story, eventId) { | |
| const event = story.events.find(e => e.id === eventId); | |
| if (!event) return; | |
| userProgress.storyHistory.push(eventId); | |
| saveUserProgress(); | |
| const content = document.getElementById('storyContent'); | |
| const currentTitle = document.getElementById('currentStoryTitle'); | |
| currentTitle.textContent = story.title; | |
| if (event.is_ending) { | |
| // Handle story ending | |
| if (event.achievement && !userProgress.achievements.includes(event.achievement)) { | |
| userProgress.achievements.push(event.achievement); | |
| } | |
| if (!userProgress.completedStories.includes(story.title)) { | |
| userProgress.completedStories.push(story.title); | |
| } | |
| saveUserProgress(); | |
| content.innerHTML = ` | |
| <div class="text-center"> | |
| <div class="mb-8"> | |
| <i data-feather="award" class="w-24 h-24 text-yellow-400 mx-auto mb-4"></i> | |
| <h2 class="text-3xl font-cinzel font-bold mb-4">The End</h2> | |
| <p class="text-xl text-gray-300 mb-6">${event.text}</p> | |
| ${event.achievement ? ` | |
| <div class="achievement-badge inline-block px-6 py-3 rounded-full mb-6"> | |
| <i data-feather="star" class="inline w-5 h-5 mr-2"></i> | |
| Achievement Unlocked: ${event.achievement} | |
| </div> | |
| ` : ''} | |
| </div> | |
| <div class="space-y-4"> | |
| <button onclick="restartStory('${story.title}')" class="choice-btn block w-full py-3 rounded-lg font-semibold"> | |
| Play Again | |
| </button> | |
| <button onclick="closeStoryReader()" class="block w-full py-3 border border-gray-600 rounded-lg hover:bg-gray-700 transition"> | |
| Back to Library | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| updateProgressBar(100); | |
| } else { | |
| // Regular event with choices | |
| const progress = (userProgress.storyHistory.length / story.events.length) * 100; | |
| updateProgressBar(progress); | |
| content.innerHTML = ` | |
| <div class="mb-8"> | |
| <p class="text-xl leading-relaxed">${event.text}</p> | |
| </div> | |
| <div class="space-y-4"> | |
| ${event.choices.map(choice => ` | |
| <button onclick="makeChoice('${story.title}', ${choice.next_event})" | |
| class="choice-btn block w-full py-4 px-6 rounded-lg font-semibold text-left hover:shadow-lg transform hover:scale-105 transition"> | |
| ${choice.text} | |
| </button> | |
| `).join('')} | |
| </div> | |
| `; | |
| } | |
| feather.replace(); | |
| } | |
| // Make a choice | |
| function makeChoice(storyTitle, nextEventId) { | |
| const story = storiesData.stories.find(s => s.title === storyTitle); | |
| if (!story) return; | |
| userProgress.currentEvent = nextEventId; | |
| renderStoryEvent(story, nextEventId); | |
| } | |
| // Update progress bar | |
| function updateProgressBar(percentage) { | |
| const progressBar = document.getElementById('progressBar'); | |
| progressBar.style.width = `${percentage}%`; | |
| } | |
| // Restart story | |
| function restartStory(storyTitle) { | |
| startStory(storyTitle); | |
| } | |
| // Open story reader | |
| function openStoryReader() { | |
| document.getElementById('storyReader').classList.remove('hidden'); | |
| } | |
| // Close story reader | |
| function closeStoryReader() { | |
| document.getElementById('storyReader').classList.add('hidden'); | |
| renderStories(); // Refresh stories to show completed status | |
| } | |
| // Open achievements | |
| function openAchievements() { | |
| const modal = document.getElementById('achievementsModal'); | |
| const content = document.getElementById('achievementsContent'); | |
| content.innerHTML = ` | |
| <div class="grid md:grid-cols-2 gap-4"> | |
| ${userProgress.achievements.length > 0 ? userProgress.achievements.map(achievement => ` | |
| <div class="achievement-badge p-4 rounded-lg text-center"> | |
| <i data-feather="star" class="w-12 h-12 mx-auto mb-2"></i> | |
| <h4 class="font-semibold">${achievement}</h4> | |
| </div> | |
| `).join('') : ` | |
| <div class="col-span-2 text-center text-gray-400"> | |
| <i data-feather="award" class="w-16 h-16 mx-auto mb-4"></i> | |
| <p>No achievements yet. Complete stories to unlock them!</p> | |
| </div> | |
| `} | |
| </div> | |
| <div class="mt-8 text-center"> | |
| <p class="text-gray-400">Completed Stories: ${userProgress.completedStories.length}</p> | |
| <p class="text-gray-400">Total Achievements: ${userProgress.achievements.length}</p> | |
| </div> | |
| `; | |
| modal.classList.remove('hidden'); | |
| feather.replace(); | |
| } | |
| // Close achievements | |
| function closeAchievements() { | |
| document.getElementById('achievementsModal').classList.add('hidden'); | |
| } | |
| // Open create story | |
| function openCreateStory() { | |
| document.getElementById('createStoryModal').classList.remove('hidden'); | |
| } | |
| // Close create story | |
| function closeCreateStory() { | |
| document.getElementById('createStoryModal').classList.add('hidden'); | |
| } | |
| // Create new story | |
| document.getElementById('storyForm').addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const title = document.getElementById('storyTitle').value; | |
| const description = document.getElementById('storyDescription').value; | |
| const eventsText = document.getElementById('storyEvents').value; | |
| try { | |
| const events = JSON.parse(eventsText); | |
| const newStory = { | |
| title: title, | |
| description: description, | |
| image: `http://static.photos/abstract/640x360/${Math.floor(Math.random() * 100)}`, | |
| events: events | |
| }; | |
| storiesData.stories.push(newStory); | |
| renderStories(); | |
| closeCreateStory(); | |
| // Reset form | |
| document.getElementById('storyTitle').value = ''; | |
| document.getElementById('storyDescription').value = ''; | |
| document.getElementById('storyEvents').value = ''; | |
| alert('Story created successfully!'); | |
| } catch (error) { | |
| alert('Invalid JSON format. Please check your events structure.'); | |
| } | |
| }); | |
| // Event listeners | |
| document.getElementById('closeReader').addEventListener('click', closeStoryReader); | |
| document.getElementById('achievementsBtn').addEventListener('click', openAchievements); | |
| document.getElementById('closeAchievements').addEventListener('click', closeAchievements); | |
| document.getElementById('createStory').addEventListener('click', openCreateStory); | |
| document.getElementById('closeCreateStory').addEventListener('click', closeCreateStory); | |
| document.getElementById('startJourney').addEventListener('click', () => { | |
| document.getElementById('storiesContainer').scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', function() { | |
| AOS.init(); | |
| feather.replace(); | |
| loadUserProgress(); | |
| renderStories(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |