|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
|
|
createSnowflakes(); |
|
|
initializeStory(); |
|
|
setupNavigation(); |
|
|
|
|
|
|
|
|
document.getElementById('continue-story')?.addEventListener('click', continueStory); |
|
|
document.getElementById('ask-question')?.addEventListener('click', askQuestion); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.location-btn').forEach(btn => { |
|
|
btn.addEventListener('click', () => { |
|
|
const location = btn.dataset.location; |
|
|
showLocation(location); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('[data-back]').forEach(btn => { |
|
|
btn.addEventListener('click', showHomeView); |
|
|
}); |
|
|
}); |
|
|
|
|
|
function setupNavigation() { |
|
|
|
|
|
document.querySelectorAll('.nav-btn').forEach(btn => { |
|
|
btn.addEventListener('click', () => { |
|
|
const view = btn.dataset.view; |
|
|
showView(view); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.nav-btn').forEach(navBtn => { |
|
|
navBtn.classList.remove('active'); |
|
|
}); |
|
|
btn.classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
function showView(viewName) { |
|
|
|
|
|
document.querySelectorAll('.view').forEach(view => { |
|
|
view.classList.add('hidden'); |
|
|
view.classList.remove('active'); |
|
|
}); |
|
|
|
|
|
|
|
|
const view = document.getElementById(`${viewName}-view`); |
|
|
if (view) { |
|
|
view.classList.remove('hidden'); |
|
|
view.classList.add('active'); |
|
|
} |
|
|
} |
|
|
|
|
|
function showHomeView() { |
|
|
showView('home'); |
|
|
document.querySelector('[data-view="home"]').classList.add('active'); |
|
|
} |
|
|
|
|
|
function showLocation(location) { |
|
|
showView(location); |
|
|
} |
|
|
function initializeStory() { |
|
|
const currentDay = 1; |
|
|
document.getElementById('current-day').textContent = currentDay; |
|
|
|
|
|
const chapterTitles = [ |
|
|
"Chapter 1: The Arrival", |
|
|
"Chapter 2: The Baker's Secret", |
|
|
"Chapter 3: Market Mystery", |
|
|
"Chapter 4: Getting Around", |
|
|
"Chapter 5: Hotel Check-in", |
|
|
"Chapter 6: Café Culture", |
|
|
"Chapter 7: Weekly Review", |
|
|
"Chapter 8: Grocery Shopping", |
|
|
"Chapter 9: Post Office Visit", |
|
|
"Chapter 10: Doctor's Visit", |
|
|
"Chapter 11: Public Transport", |
|
|
"Chapter 12: Restaurant Night", |
|
|
"Chapter 13: Shopping Spree", |
|
|
"Chapter 14: Weekly Review", |
|
|
"Chapter 15: Carol Singing", |
|
|
"Chapter 16: Tree Decorating", |
|
|
"Chapter 17: Gift Wrapping", |
|
|
"Chapter 18: Winter Sports", |
|
|
"Chapter 19: Holiday Cooking", |
|
|
"Chapter 20: Town Celebration", |
|
|
"Chapter 21: Weekly Review", |
|
|
"Chapter 22: Storytelling", |
|
|
"Chapter 23: Local Legends", |
|
|
"Chapter 24: Christmas Eve", |
|
|
"Chapter 25: Farewell" |
|
|
]; |
|
|
|
|
|
if (document.getElementById('chapter-title')) { |
|
|
document.getElementById('chapter-title').textContent = chapterTitles[currentDay - 1] || "Chapter 1: The Arrival"; |
|
|
} |
|
|
} |
|
|
function generateCalendar() { |
|
|
const calendarDays = document.getElementById('calendar-days'); |
|
|
if (!calendarDays) return; |
|
|
|
|
|
calendarDays.innerHTML = ''; |
|
|
|
|
|
for (let day = 1; day <= 25; day++) { |
|
|
const dayElement = document.createElement('div'); |
|
|
dayElement.className = `text-center p-3 rounded-lg cursor-pointer transition ${day === 1 ? 'bg-primary text-white' : day <= 1 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-400'}`; |
|
|
dayElement.innerHTML = ` |
|
|
<div class="font-bold text-lg">${day}</div> |
|
|
<div class="text-xs">${day === 1 ? 'Today' : 'Day ' + day}</div> |
|
|
`; |
|
|
|
|
|
if (day <= 1) { |
|
|
dayElement.addEventListener('click', () => { |
|
|
alert(`You're on Day ${day}! ${day === 1 ? 'Continue your adventure.' : 'This day will unlock soon.'}`); |
|
|
}); |
|
|
} |
|
|
|
|
|
calendarDays.appendChild(dayElement); |
|
|
} |
|
|
} |
|
|
|
|
|
function loadCharacters() { |
|
|
const characterContainer = document.getElementById('character-profiles'); |
|
|
if (!characterContainer) return; |
|
|
|
|
|
const characters = [ |
|
|
{ |
|
|
name: "Pierre", |
|
|
role: "The Baker", |
|
|
description: "Friendly local who loves teaching phrases about food.", |
|
|
img: "http://static.photos/people/200x200/10" |
|
|
}, |
|
|
{ |
|
|
name: "Claire", |
|
|
role: "Market Vendor", |
|
|
description: "Sells Christmas ornaments and knows all the holiday vocabulary.", |
|
|
img: "http://static.photos/people/200x200/11" |
|
|
}, |
|
|
{ |
|
|
name: "Luc", |
|
|
role: "Town Musician", |
|
|
description: "Teaches French through Christmas carols.", |
|
|
img: "http://static.photos/people/200x200/12" |
|
|
}, |
|
|
{ |
|
|
name: "Monsieur Lefevre", |
|
|
role: "Mayor", |
|
|
description: "Speaks formal French - great for learning polite expressions.", |
|
|
img: "http://static.photos/people/200x200/13" |
|
|
} |
|
|
]; |
|
|
|
|
|
characterContainer.innerHTML = ''; |
|
|
|
|
|
characters.forEach(char => { |
|
|
const charElement = document.createElement('div'); |
|
|
charElement.className = 'text-center'; |
|
|
charElement.innerHTML = ` |
|
|
<img src="${char.img}" alt="${char.name}" class="w-20 h-20 rounded-full mx-auto mb-3 border-4 border-white shadow"> |
|
|
<h4 class="font-bold text-primary">${char.name}</h4> |
|
|
<p class="text-sm text-secondary font-medium">${char.role}</p> |
|
|
<p class="text-xs text-gray-600 mt-1">${char.description}</p> |
|
|
`; |
|
|
characterContainer.appendChild(charElement); |
|
|
}); |
|
|
} |
|
|
|
|
|
function continueStory() { |
|
|
const storyChat = document.getElementById('story-chat'); |
|
|
if (!storyChat) return; |
|
|
|
|
|
const storySteps = [ |
|
|
{ speaker: "ai", text: "You walk toward the town square and see a beautiful Christmas market. A vendor calls out: 'Venez voir nos décorations!' (Come see our decorations!)", character: "Market Vendor" }, |
|
|
{ speaker: "user", text: "Je voudrais acheter une décoration. (I would like to buy a decoration.)", character: "You" }, |
|
|
{ speaker: "ai", text: "'Très bien! Celle-ci coûte cinq euros,' she says with a smile. You've just completed your first transaction in French!", character: "Claire" } |
|
|
]; |
|
|
|
|
|
storySteps.forEach((step, index) => { |
|
|
setTimeout(() => { |
|
|
const messageDiv = document.createElement('div'); |
|
|
messageDiv.className = 'flex items-start space-x-3 mb-4'; |
|
|
|
|
|
if (step.speaker === 'user') { |
|
|
messageDiv.innerHTML = ` |
|
|
<div class="bg-primary rounded-full p-2"> |
|
|
<i data-feather="user" class="text-white"></i> |
|
|
</div> |
|
|
<div class="bg-gray-100 rounded-2xl px-4 py-2 max-w-xs"> |
|
|
<p class="font-medium">${step.text}</p> |
|
|
<p class="text-xs text-gray-500 mt-1">${step.character}</p> |
|
|
</div> |
|
|
`; |
|
|
} else { |
|
|
messageDiv.innerHTML = ` |
|
|
<div class="bg-secondary rounded-full p-2"> |
|
|
<i data-feather="user-check" class="text-white"></i> |
|
|
</div> |
|
|
<div class="bg-blue-50 rounded-2xl px-4 py-2 max-w-xs"> |
|
|
<p class="font-medium">${step.text}</p> |
|
|
<p class="text-xs text-gray-500 mt-1">${step.character}</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
storyChat.appendChild(messageDiv); |
|
|
storyChat.scrollTop = storyChat.scrollHeight; |
|
|
feather.replace(); |
|
|
}, (index + 1) * 1500); |
|
|
}); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
const missionElement = document.getElementById('daily-mission'); |
|
|
if (missionElement) { |
|
|
missionElement.textContent = "Visit the Christmas market and buy a decoration using French."; |
|
|
} |
|
|
}, 5000); |
|
|
} |
|
|
|
|
|
function askQuestion() { |
|
|
const storyChat = document.getElementById('story-chat'); |
|
|
if (!storyChat) return; |
|
|
|
|
|
const questionDiv = document.createElement('div'); |
|
|
questionDiv.className = 'flex items-start space-x-3 mb-4'; |
|
|
questionDiv.innerHTML = ` |
|
|
<div class="bg-accent rounded-full p-2"> |
|
|
<i data-feather="help-circle" class="text-white"></i> |
|
|
</div> |
|
|
<div class="bg-yellow-50 rounded-2xl px-4 py-2 max-w-xs"> |
|
|
<p class="font-medium">How do I say 'How much does this cost?' in French?</p> |
|
|
<p class="text-xs text-gray-500 mt-1">You asked</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
storyChat.appendChild(questionDiv); |
|
|
|
|
|
setTimeout(() => { |
|
|
const answerDiv = document.createElement('div'); |
|
|
answerDiv.className = 'flex items-start space-x-3 mb-4'; |
|
|
answerDiv.innerHTML = ` |
|
|
<div class="bg-secondary rounded-full p-2"> |
|
|
<i data-feather="user-check" class="text-white"></i> |
|
|
</div> |
|
|
<div class="bg-green-50 rounded-2xl px-4 py-2 max-w-xs"> |
|
|
<p class="font-medium">You say: 'Combien ça coûte?' (kɔm.bjɛ̃ sa kut). Try repeating it!</p> |
|
|
<p class="text-xs text-gray-500 mt-1">Pierre helps</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
storyChat.appendChild(answerDiv); |
|
|
storyChat.scrollTop = storyChat.scrollHeight; |
|
|
feather.replace(); |
|
|
}, 1000); |
|
|
} |
|
|
function createSnowflakes() { |
|
|
const snowContainer = document.getElementById('snow-overlay'); |
|
|
if (!snowContainer) return; |
|
|
|
|
|
for (let i = 0; i < 100; i++) { |
|
|
const snowflake = document.createElement('div'); |
|
|
snowflake.className = 'snowflake'; |
|
|
snowflake.innerHTML = '❄'; |
|
|
snowflake.style.left = Math.random() * 100 + 'vw'; |
|
|
snowflake.style.top = -20 + 'px'; |
|
|
snowflake.style.fontSize = (Math.random() * 10 + 8) + 'px'; |
|
|
snowflake.style.opacity = Math.random(); |
|
|
snowflake.style.animationDuration = `${Math.random() * 5 + 5}s, ${Math.random() * 5 + 5}s`; |
|
|
snowflake.style.animationDelay = `${Math.random() * 5}s`; |
|
|
|
|
|
snowContainer.appendChild(snowflake); |
|
|
} |
|
|
} |
|
|
|
|
|
function visitLocation(location) { |
|
|
const locations = { |
|
|
bakery: { |
|
|
title: "Pierre's Bakery", |
|
|
description: "The sweet smell of fresh pastries fills the air as you enter the cozy bakery. Pierre greets you warmly." |
|
|
}, |
|
|
market: { |
|
|
title: "Christmas Market", |
|
|
description: "Colorful stalls line the square, selling handmade ornaments and holiday treats. Claire waves as you approach." |
|
|
}, |
|
|
hotel: { |
|
|
title: "Town Hotel", |
|
|
description: "The grand hotel entrance welcomes you with twinkling lights. The concierge stands ready to assist." |
|
|
} |
|
|
}; |
|
|
|
|
|
const storyPanel = document.querySelector('.story-panel'); |
|
|
if (storyPanel) { |
|
|
storyPanel.querySelector('#chapter-title').textContent = locations[location].title; |
|
|
storyPanel.querySelector('#story-text').textContent = locations[location].description; |
|
|
} |
|
|
} |
|
|
}); |
|
|
|