Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Earth Explorer Adventure</title> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet"> | |
<script src="env.js"></script> | |
<script src="https://cesium.com/downloads/cesiumjs/releases/1.128/Build/Cesium/Cesium.js"></script> | |
<link href="https://cesium.com/downloads/cesiumjs/releases/1.128/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> | |
<style> | |
body { | |
margin: 0; | |
font-family: 'Inter', Arial, sans-serif; | |
overflow: hidden; | |
background-color: #f7fafd; | |
} | |
.overlay-title { | |
position: absolute; | |
top: 18px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: fit-content; | |
min-width: 220px; | |
max-width: 700px; | |
text-align: center; | |
font-size: 2.1em; | |
font-family: 'Inter', Arial, sans-serif; | |
font-weight: 900; | |
letter-spacing: 0.03em; | |
color: #fff; | |
background: linear-gradient(90deg, #651fff 0%, #00e6ff 100%); | |
border-radius: 36px; | |
box-shadow: none; | |
padding: 10px 36px; | |
z-index: 20; | |
pointer-events: none; | |
} | |
.overlay-reset { | |
position: absolute; | |
left: 50%; | |
bottom: 16px; | |
transform: translateX(-50%); | |
z-index: 20; | |
pointer-events: auto; | |
box-shadow: 0 2px 8px rgba(26,35,126,0.10); | |
} | |
.modern-btn { | |
font-family: 'Inter', Arial, sans-serif; | |
font-size: 1.2em; | |
font-weight: 900; | |
color: #fff; | |
background: linear-gradient(90deg, #651fff 0%, #00e6ff 100%); | |
border: none; | |
border-radius: 36px; | |
padding: 14px 34px; | |
cursor: pointer; | |
} | |
#cesiumContainer { | |
width: 100%; | |
height: 100vh; | |
min-height: 420px; | |
background: #e3eafc; | |
box-shadow: 0 4px 24px rgba(26,35,126,0.07); | |
position: relative; | |
overflow: hidden; | |
padding-top: 0; | |
padding-bottom: 0; | |
} | |
#place-info { | |
position: absolute; | |
top: 32px; | |
left: 32px; | |
background: #fff; | |
padding: 22px 28px 18px 28px; | |
border-radius: 16px; | |
box-shadow: 0 4px 24px rgba(26,35,126,0.08); | |
max-width: 360px; | |
min-width: 220px; | |
text-align: left; | |
border: 1.5px solid #c5cae9; | |
z-index: 13; | |
font-family: 'Inter', Arial, sans-serif; | |
font-size: 1.08em; | |
display: none; | |
color: #263238; | |
} | |
#place-name { | |
font-size: 1.3em; | |
font-weight: 600; | |
color: #283593; | |
margin-bottom: 8px; | |
} | |
#place-fact { | |
margin-bottom: 12px; | |
color: #1565c0; | |
} | |
#place-quiz label { | |
font-weight: 600; | |
color: #3949ab; | |
} | |
#place-quiz select { | |
font-size: 1em; | |
margin: 5px; | |
border-radius: 6px; | |
border: 1.5px solid #c5cae9; | |
background: #f7fafd; | |
} | |
#animal-quiz button { | |
background: #ffd700; | |
border: none; | |
border-radius: 7px; | |
padding: 6px 16px; | |
font-size: 1em; | |
color: #0288d1; | |
font-weight: bold; | |
margin-left: 8px; | |
cursor: pointer; | |
} | |
#animal-treasure { | |
font-size: 2em; | |
margin-top: 16px; | |
display: none; | |
} | |
#ui { | |
position: absolute; | |
right: 24px; | |
bottom: 24px; | |
background: linear-gradient(135deg, #ffe082 0%, #b388ff 100%); | |
padding: 14px 18px 12px 18px; | |
border-radius: 14px; | |
box-shadow: 0 4px 18px rgba(80,60,180,0.15); | |
max-width: 260px; | |
min-width: 140px; | |
text-align: left; | |
border: 2px solid #ff7eb3; | |
z-index: 12; | |
font-family: 'Inter', Arial, sans-serif; | |
font-size: 1.07em; | |
overflow: hidden; | |
color: #3d155f; | |
word-break: break-word; | |
line-height: 1.35; | |
} | |
#ui .sparkle { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
left: 0; top: 0; | |
pointer-events: none; | |
z-index: 1; | |
background: url('data:image/svg+xml;utf8,<svg width="100" height="60" xmlns="http://www.w3.org/2000/svg"><circle cx="10" cy="10" r="2" fill="%23fff" opacity=".7"/><circle cx="80" cy="40" r="1.5" fill="%23fff" opacity=".5"/><circle cx="50" cy="20" r="1.2" fill="%23fff" opacity=".6"/><circle cx="30" cy="50" r="1.7" fill="%23fff" opacity=".4"/></svg>'); | |
animation: sparkle-move 3s linear infinite; | |
} | |
@keyframes sparkle-move { | |
0% { background-position: 0 0; } | |
100% { background-position: 100px 60px; } | |
} | |
#ui .ui-heading { | |
font-size: 1.25em; | |
font-weight: 700; | |
color: #5e35b1; | |
margin-bottom: 8px; | |
letter-spacing: 0.02em; | |
} | |
#score { | |
font-size: 1.35em; | |
font-weight: 900; | |
color: #ff4081; | |
background: #fffde7; | |
border-radius: 14px; | |
padding: 10px 20px 10px 18px; | |
margin-bottom: 15px; | |
box-shadow: 0 2px 10px #ffecb3aa; | |
display: inline-block; | |
letter-spacing: 0.01em; | |
} | |
#instructions { | |
font-size: 1.18em; | |
color: #ffd600; | |
font-weight: 900; | |
margin-bottom: 10px; | |
letter-spacing: 0.02em; | |
} | |
#ui ul { | |
list-style: none; | |
padding: 0 0 0 2px; | |
margin: 0 0 0 0; | |
} | |
#ui ul li { | |
font-size: 1.13em; | |
margin-bottom: 11px; | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
color: #651fff; | |
font-weight: 800; | |
text-shadow: 0 2px 10px #fff7, 0 1px 2px #0002; | |
} | |
#ui ul li .icon { | |
font-size: 1.3em; | |
display: inline-block; | |
margin-right: 3px; | |
} | |
#ui ul { | |
margin: 0 0 0 18px; | |
padding: 0; | |
} | |
#ui li { | |
font-size: 1.05em; | |
color: #0288d1; | |
margin-bottom: 7px; | |
font-family: 'Comic Sans MS', 'Comic Sans', cursive, Arial, sans-serif; | |
} | |
#ui #instructions { | |
font-size: 1.1em; | |
color: #ff7043; | |
margin-bottom: 10px; | |
font-family: 'Comic Sans MS', 'Comic Sans', cursive, Arial, sans-serif; | |
font-weight: bold; | |
background: none; | |
border: none; | |
padding: 0; | |
box-shadow: none; | |
display: block; | |
} | |
#score { | |
font-size: 22px; | |
color: #d81b60; | |
margin-bottom: 10px; | |
} | |
#instructions { | |
font-size: 1.3em; | |
color: #ff7043; | |
margin-bottom: 18px; | |
font-family: 'Comic Sans MS', 'Comic Sans', cursive, Arial, sans-serif; | |
font-weight: bold; | |
background: #fffbe7; | |
border-radius: 12px; | |
border: 2px dashed #ffd700; | |
padding: 10px 14px; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.07); | |
display: inline-block; | |
} | |
#fact { | |
font-size: 13px; | |
color: #388e3c; | |
margin-top: 10px; | |
font-style: italic; | |
} | |
#loading { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 24px; | |
color: #ffffff; | |
background: rgba(0, 0, 0, 0.7); | |
padding: 10px; | |
border-radius: 5px; | |
} | |
#error { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 18px; | |
color: #d32f2f; | |
background: rgba(255, 255, 255, 0.9); | |
padding: 15px; | |
border-radius: 5px; | |
max-width: 80%; | |
text-align: center; | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="cesiumContainer"> | |
<div id="game-title" class="overlay-title">Earth Explorer</div> | |
<button id="resetViewBtn" class="modern-btn overlay-reset">Reset View</button> | |
</div> | |
<div id="loading">Loading Globe Explorer...</div> | |
<div id="error"></div> | |
<div id="place-info"> | |
<div id="place-name"></div> | |
<div id="place-fact"></div> | |
<div id="place-quiz"> | |
<div id="place-clue"></div> | |
<input id="place-answer-input" type="text" placeholder="Type your answer here..." autocomplete="off" style="margin-top:8px;padding:6px 10px;border-radius:6px;border:1.5px solid #c5cae9;font-size:1em;"> | |
<button id="place-answer-submit">Submit</button> | |
<div id="place-feedback" style="margin-top:7px;min-height:22px;font-weight:bold;"></div> | |
</div> | |
<div id="place-treasure"></div> | |
</div> | |
<div id="ui"> | |
<div id="score">Places Discovered: 0/9</div> | |
<div id="instructions">π§ <b>How to Play:</b></div> | |
<ul> | |
<li>π <b>Spin the globe to explore</li> | |
<li>π <b>Find the red pins on the map</li> | |
<li>π <b>Click a pin to get a clue about a famous place</li> | |
<li>π <b>Zoom in to discover the place and learn a fun fact</li> | |
<li>π <b>Collect all places to win!</li> | |
</ul> | |
</div> | |
<script> | |
// Add Reset View button functionality (attached after viewer is created) | |
function setupResetButton(viewer) { | |
document.getElementById('resetViewBtn').onclick = function() { | |
viewer.camera.flyTo({ | |
destination: Cesium.Cartesian3.fromDegrees(0, 0, 20000000), | |
orientation: { | |
heading: Cesium.Math.toRadians(0), | |
pitch: Cesium.Math.toRadians(-90), | |
roll: 0.0 | |
}, | |
duration: 1.5 | |
}); | |
// Hide the quiz/info box | |
document.getElementById('place-info').style.display = 'none'; | |
}; | |
} | |
async function initializeCesium() { | |
try { | |
// INSERT YOUR CESIUM ION API KEY HERE | |
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3NGVhMWEzOC1jNmUyLTQxYzUtYmZlYS0xY2IyOGE0Mjk1MzMiLCJpZCI6Mjk0NjEzLCJpYXQiOjE3NDQ3OTk2MTB9.8BBF1u-znrldQsC74qgPbMSJpN5cc5wcaUP48oLmA20'; | |
// Only keep Bing for satellite view (no OSM) | |
window.currentImagery = 'default'; | |
// Only use Cesium Ion Imagery Provider (assetId: 3) as the sole imagery layer | |
const viewer = new Cesium.Viewer('cesiumContainer', { | |
baseLayerPicker: false, | |
timeline: false, | |
animation: false, | |
geocoder: false, | |
homeButton: false, | |
sceneModePicker: true, | |
navigationHelpButton: true | |
}); | |
window.viewer = viewer; | |
setupResetButton(viewer); | |
// Add imagery provider as a layer (do not zoom to it, use default Cesium view) | |
try { | |
const imageryLayer = viewer.imageryLayers.addImageryProvider( | |
await Cesium.IonImageryProvider.fromAssetId(3) | |
); | |
// Do not zoom to the layer | |
} catch (error) { | |
console.log(error); | |
} | |
// Fallback settings to ensure globe visibility | |
viewer.scene.globe.enableLighting = true; | |
viewer.scene.globe.depthTestAgainstTerrain = true; | |
// Hide loading text | |
document.getElementById('loading').style.display = 'none'; | |
// --- INTERACTIVE GAME LOGIC --- | |
const scoreDisplay = document.getElementById('score'); | |
let placesFound = 0; | |
const totalPlaces = 9; | |
const places = [ | |
{ name: "Sahara Desert", longitude: 13.0, latitude: 23.5, clue: "This is the largest hot desert in the world, stretching across North Africa. Zoom in to see endless sand dunes and almost no vegetation. What is this place?", fact: "The Sahara Desert covers over 9 million square kilometers!" }, | |
{ name: "Great Barrier Reef", longitude: 147.7, latitude: -18.3, clue: "This natural wonder is the worldβs largest coral reef system, found off the coast of Australia. Zoom in to see turquoise waters and coral islands. What is this place?", fact: "The Great Barrier Reef is home to thousands of species of marine life." }, | |
{ name: "Amazon Rainforest", longitude: -60.0, latitude: -3.0, clue: "This is the worldβs largest tropical rainforest, covering much of northern South America. Zoom in to see a thick green jungle and winding rivers. What is this place?", fact: "The Amazon Rainforest produces 20% of the worldβs oxygen." }, | |
{ name: "Mount Everest", longitude: 86.9250, latitude: 27.9881, clue: "This is the highest mountain on Earth, located in the Himalayas. Zoom in to see snow-capped peaks towering above the clouds. What is this place?", fact: "Mount Everest is 8,848 meters (29,029 ft) tall!" }, | |
{ name: "Antarctica", longitude: 0.0, latitude: -82.8628, clue: "This is the coldest, driest, and windiest, continent, covered almost entirely by ice. Zoom in to see a vast white landscape. What is this place?", fact: "Antarctica holds 60% of the worldβs fresh water in its ice." }, | |
{ name: "Grand Canyon", longitude: -112.1401, latitude: 36.0544, clue: "This steep-sided gorge in Arizona, USA, is carved by a river over millions of years. Zoom in to see colorful rock layers and a deep canyon. What is this place?", fact: "The Grand Canyon is 446 km long and up to 1,857 meters deep." }, | |
{ name: "Great Wall of China", longitude: 117.236, latitude: 40.6769, clue: "This is a man-made structure stretching thousands of kilometers across northern China. Zoom in to spot a long wall winding over hills. What is this place?", fact: "The Great Wall of China is over 21,000 km (13,000 miles) long!" }, | |
{ name: "Niagara Falls", longitude: -79.074, latitude: 43.0799, clue: "These are three massive waterfalls on the border of Canada and the USA. Zoom in to see roaring water and mist. What is this place?", fact: "More than 3,000 tons of water flow over Niagara Falls every second." }, | |
{ name: "Uluru (Ayers Rock)", longitude: 131.0318, latitude: -25.3475, clue: "This giant red sandstone rock is found in the heart of Australiaβs Outback. Zoom in to see a huge, isolated rock rising from flat land. What is this place?", fact: "Uluru is sacred to the local Anangu people and is 348 meters tall." } | |
]; | |
scoreDisplay.textContent = `Places Discovered: 0/${totalPlaces}`; | |
// Add place markers as billboards | |
const pinDataUrl = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><text x="0" y="52" font-size="56">π</text></svg>'; | |
const entities = places.map(place => { | |
return viewer.entities.add({ | |
position: Cesium.Cartesian3.fromDegrees(place.longitude, place.latitude, 1000), | |
billboard: { | |
image: pinDataUrl, | |
width: 48, | |
height: 48, | |
verticalOrigin: Cesium.VerticalOrigin.BOTTOM | |
}, | |
name: "Zoom", | |
properties: { | |
clue: place.clue, | |
placename: place.name, | |
fact: place.fact | |
} | |
}); | |
}); | |
// Pin click handler | |
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); | |
handler.setInputAction((click) => { | |
const pickedObject = viewer.scene.pick(click.position); | |
if (Cesium.defined(pickedObject) && pickedObject.id) { | |
const entity = pickedObject.id; | |
// Show place info box on left | |
const clue = entity.properties.clue.getValue(); | |
const placename = entity.properties.placename.getValue(); | |
const fact = entity.properties.fact.getValue(); | |
const placeInfoDiv = document.getElementById('place-info'); | |
const placeNameDiv = document.getElementById('place-name'); | |
const placeFactDiv = document.getElementById('place-fact'); | |
const placeQuizDiv = document.getElementById('place-quiz'); | |
const placeTreasureDiv = document.getElementById('place-treasure'); | |
if (placeInfoDiv) placeInfoDiv.style.display = 'block'; | |
if (placeNameDiv) placeNameDiv.textContent = `π Mystery Place`; | |
if (placeFactDiv) placeFactDiv.textContent = ''; | |
if (placeTreasureDiv) placeTreasureDiv.style.display = 'none'; | |
if (placeQuizDiv) { | |
placeQuizDiv.innerHTML = ` | |
<div id="place-clue"><label>${clue}</label><br><span style='color:#0288d1;font-size:1em;'>Type your answer or zoom in for help!</span></div> | |
<input id="place-answer-input" type="text" placeholder="Type your answer here..." autocomplete="off" style="margin-top:8px;padding:6px 10px;border-radius:6px;border:1.5px solid #c5cae9;font-size:1em;"> | |
<button id="place-answer-submit">Submit</button> | |
<div id="place-feedback" style="margin-top:7px;min-height:22px;font-weight:bold;"></div> | |
`; | |
} | |
document.getElementById('place-answer-input').value = ''; | |
document.getElementById('place-answer-input').disabled = false; | |
document.getElementById('place-answer-submit').disabled = false; | |
document.getElementById('place-feedback').textContent = ''; | |
if (placeFactDiv) placeFactDiv.textContent = ''; | |
if (placeTreasureDiv) placeTreasureDiv.style.display = 'none'; | |
let tries = 0; | |
let answered = false; | |
let points = 0; | |
let zoomRevealed = false; | |
let allowZoomAttempt = false; | |
// Enhanced answer checking logic | |
function revealPlace(correct, revealByZoom = false) { | |
answered = true; | |
document.getElementById('place-answer-input').disabled = true; | |
document.getElementById('place-answer-submit').disabled = true; | |
if (placeNameDiv) placeNameDiv.textContent = `π ${placename}`; | |
if (placeFactDiv) placeFactDiv.textContent = fact; | |
if (placeTreasureDiv) { | |
placeTreasureDiv.textContent = correct ? 'π You found a hidden treasure!' : ''; | |
placeTreasureDiv.style.display = 'block'; | |
} | |
if (correct) { | |
placesFound++; | |
if (scoreDisplay) scoreDisplay.textContent = `Places Discovered: ${placesFound}/${totalPlaces}`; | |
setTimeout(() => { | |
viewer.entities.remove(entity); | |
if (placeInfoDiv) placeInfoDiv.style.display = 'none'; | |
if (placesFound === totalPlaces) { | |
setTimeout(() => { | |
if (placeInfoDiv) placeInfoDiv.style.display = 'block'; | |
if (placeNameDiv) placeNameDiv.textContent = 'π Globe Explorer!'; | |
if (placeFactDiv) placeFactDiv.textContent = "You found all the hidden places!"; | |
document.getElementById('place-clue').innerHTML = ''; | |
if (placeTreasureDiv) { | |
placeTreasureDiv.textContent = 'π'; | |
placeTreasureDiv.style.display = 'block'; | |
} | |
}, 800); | |
} | |
}, 1800); | |
} else if (revealByZoom) { | |
document.getElementById('place-feedback').textContent = `The answer is: ${placename}. ${fact}`; | |
} else { | |
document.getElementById('place-feedback').textContent = `The answer is: ${placename}. ${fact}`; | |
} | |
} | |
function checkAnswer() { | |
if (answered) return; | |
const userAns = document.getElementById('place-answer-input').value.trim().toLowerCase(); | |
const correctAns = placename.trim().toLowerCase(); | |
if (!zoomRevealed) tries++; | |
if (userAns === correctAns) { | |
if (!zoomRevealed) { | |
points = tries === 1 ? 40 : tries === 2 ? 30 : tries === 3 ? 20 : 0; | |
} else { | |
points = 10; | |
} | |
document.getElementById('place-feedback').textContent = `π Correct! You earned ${points} points!`; | |
if (placeNameDiv) placeNameDiv.textContent = `π ${placename}`; | |
revealPlace(true); | |
} else { | |
if (!zoomRevealed && tries < 3) { | |
document.getElementById('place-feedback').textContent = `β Try again!`; | |
document.getElementById('place-answer-input').value = ''; | |
} else if (!zoomRevealed && tries === 3) { | |
// Show Zoom for Hint button | |
allowZoomAttempt = true; | |
document.getElementById('place-feedback').innerHTML = `β Out of tries! <button id='zoom-hint-btn' style='margin-left:8px;padding:5px 12px;border-radius:8px;background:#00e6ff;color:#fff;font-weight:bold;border:none;cursor:pointer;'>Zoom in for a hint!</button>`; | |
document.getElementById('place-answer-input').disabled = true; | |
document.getElementById('place-answer-submit').disabled = true; | |
document.getElementById('zoom-hint-btn').onclick = function() { | |
document.getElementById('place-feedback').textContent = 'π Zoom in to the location to reveal the answer!'; | |
document.getElementById('place-answer-input').value = ''; | |
// Wait for zoom event | |
}; | |
} else if (zoomRevealed) { | |
// Final attempt after zoom | |
document.getElementById('place-answer-input').disabled = false; | |
document.getElementById('place-answer-submit').disabled = false; | |
allowZoomAttempt = false; | |
if (tries > 3) { | |
// This is the final attempt after zoom | |
if (userAns === correctAns) { | |
points = 10; | |
document.getElementById('place-feedback').textContent = `π Correct! You earned ${points} points!`; | |
if (placeNameDiv) placeNameDiv.textContent = `π ${placename}`; | |
revealPlace(true); | |
} else { | |
document.getElementById('place-feedback').textContent = `The answer is: ${placename}. ${fact}`; | |
if (placeFactDiv) placeFactDiv.textContent = `${placename}: ${fact}`; | |
document.getElementById('place-answer-input').disabled = true; | |
document.getElementById('place-answer-submit').disabled = true; | |
points = 0; | |
// Keep info box visible until Reset View is pressed | |
// Do NOT call revealPlace(false) here to avoid overwriting the answer | |
} | |
} | |
} | |
} | |
} | |
document.getElementById('place-answer-submit').onclick = checkAnswer; | |
document.getElementById('place-answer-input').onkeydown = function(e){ if(e.key==='Enter') checkAnswer(); }; | |
// Zoom-to-reveal for help (if not answered) | |
function checkZoom() { | |
try { | |
const cameraHeight = viewer.camera.positionCartographic.height; | |
if (!answered && allowZoomAttempt && !zoomRevealed && cameraHeight < 2000000) { | |
zoomRevealed = true; | |
document.getElementById('place-feedback').textContent = `π You unlocked a final try! Type your answer and press Submit.`; | |
document.getElementById('place-answer-input').value = ''; | |
document.getElementById('place-answer-input').disabled = false; | |
document.getElementById('place-answer-submit').disabled = false; | |
document.getElementById('place-answer-submit').style.pointerEvents = 'auto'; | |
document.getElementById('place-answer-submit').style.opacity = '1'; | |
document.getElementById('place-answer-input').focus(); | |
} | |
} catch (err) { console.error(err); } | |
} | |
viewer.camera.moveEnd.addEventListener(checkZoom); | |
} | |
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); | |
// --- END INTERACTIVE GAME LOGIC --- | |
} catch (e) { | |
console.error('Error initializing Cesium:', e); | |
document.getElementById('loading').style.display = 'none'; | |
document.getElementById('error').innerText = 'An error occurred while loading Cesium.'; | |
document.getElementById('error').style.display = 'block'; | |
} | |
} | |
window.onload = initializeCesium; | |
</script> | |
</body> | |
</html> |