Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>The Shadow - A Text Adventure</title> | |
<style> | |
body { | |
font-family: 'Georgia', serif; | |
background-color: #1a1a2e; /* Deep midnight blue */ | |
color: #e0e0e0; /* Soft white/grey */ | |
margin: 0; | |
padding: 20px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
min-height: 100vh; | |
box-sizing: border-box; | |
} | |
#game-container { | |
background-color: #2a2a3e; /* Slightly lighter blue/purple */ | |
border: 1px solid #4a4a6e; | |
border-radius: 8px; | |
padding: 25px; | |
width: 90%; | |
max-width: 700px; | |
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); | |
} | |
#output { | |
height: 400px; | |
overflow-y: auto; | |
border: 1px solid #4a4a6e; | |
padding: 15px; | |
margin-bottom: 15px; | |
background-color: #1c1c2c; /* Even darker for text area */ | |
border-radius: 4px; | |
white-space: pre-wrap; /* Preserves formatting and newlines */ | |
line-height: 1.6; | |
} | |
.output-line { | |
margin-bottom: 8px; | |
} | |
.narration { | |
color: #b0c4de; /* Lighter blue for narration */ | |
font-style: italic; | |
} | |
.dialogue { | |
color: #add8e6; /* Light blue for dialogue */ | |
} | |
.error { | |
color: #ff6b6b; /* Soft red for errors */ | |
} | |
.location { | |
color: #98fb98; /* Pale green for location titles */ | |
font-weight: bold; | |
} | |
.action-result { | |
color: #f0e68c; /* Khaki for action results */ | |
} | |
input[type="text"] { | |
width: calc(100% - 90px); | |
padding: 10px; | |
border: 1px solid #4a4a6e; | |
background-color: #1c1c2c; | |
color: #e0e0e0; | |
border-radius: 4px; | |
font-family: 'Georgia', serif; | |
font-size: 1em; | |
} | |
button { | |
padding: 10px 15px; | |
border: 1px solid #4a4a6e; | |
background-color: #3a3a5e; /* Darker purple for button */ | |
color: #e0e0e0; | |
cursor: pointer; | |
border-radius: 4px; | |
font-family: 'Georgia', serif; | |
font-size: 1em; | |
margin-left: 5px; | |
} | |
button:hover { | |
background-color: #4a4a6e; | |
} | |
h1 { | |
color: #c3bef0; /* Lavender for title */ | |
text-align: center; | |
margin-bottom: 20px; | |
} | |
.hint { | |
font-size: 0.9em; | |
color: #777; | |
text-align: center; | |
margin-top: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="game-container"> | |
<h1>The Shadow</h1> | |
<div id="output"></div> | |
<div> | |
<input type="text" id="commandInput" placeholder="What will you do? (e.g., 'go north', 'look')"> | |
<button onclick="processCommand()">Send</button> | |
</div> | |
<div class="hint">Common commands: look, go [direction], take [item], use [item], talk to [character], examine [object/character]. Type 'help' for more.</div> | |
</div> | |
<script> | |
const outputDiv = document.getElementById('output'); | |
const commandInput = document.getElementById('commandInput'); | |
let gameState = { | |
currentRoom: 'start', | |
inventory: [], | |
flags: { | |
metGuardian: false, | |
knowsMazeSecret: false, | |
hasCrystal: false, | |
towerDoorUnlocked: false, | |
mirrorTouched: false, | |
whisperedToEcho: false, // Easter egg related | |
easterEggFound: false | |
} | |
}; | |
const rooms = { | |
// START & EARLY GAME | |
'start': { | |
name: "The Edge of a Whispering Wood", | |
description: "You stand at the precipice of a wood where the trees themselves seem to murmur secrets. The air is cool and heavy with the scent of pine and damp earth. A faint, barely discernible path winds into the deepening shadows to the NORTH. Behind you, to the SOUTH, lies the mundane world you wish to leave behind, but its call is weak here.\nAn old, weathered signpost stands crookedly nearby. The wind whispers through the leaves, carrying faint, almost inaudible echoes.", | |
actions: { | |
'go north': 'path_entry', | |
'go south': 'mundane_return_early', | |
'examine signpost': "The signpost is ancient. Most of its carvings are eroded, but you can faintly make out one word: 'Oblivion'. It offers no other direction.", | |
'examine trees': "The trees are tall and gaunt, their branches intertwining like skeletal fingers. They seem to lean in, listening.", | |
'listen': "You hear the rustling of leaves, the distant call of an unseen bird, and something else... a soft, rhythmic sigh, like the forest breathing. Or is it an echo of your own heart?", | |
'listen to echoes': () => { // Part of the Easter Egg trigger | |
if (!gameState.flags.whisperedToEcho) { | |
gameState.flags.whisperedToEcho = true; | |
return "You strain your ears, focusing on the faintest sounds. The echoes seem to carry a sorrowful, lost quality. A peculiar feeling washes over you, a sense of being watched by something ancient and unseen. Maybe if you were quieter... or spoke to it?"; | |
} | |
return "The echoes remain elusive, sorrowful whispers on the wind."; | |
}, | |
'whisper to echoes': () => { // Easter Egg Action | |
if (gameState.flags.whisperedToEcho && !gameState.flags.easterEggFound) { | |
gameState.flags.easterEggFound = true; | |
gameState.currentRoom = 'secret_grove'; | |
return "You whisper softly into the wind, not to the trees, but to the spaces between their sounds, to the echoes themselves. A profound stillness descends. The wind dies. For a moment, all is silent. Then, the world around you shimmers, and you find yourself... elsewhere. <span class='action-result'>(EASTER EGG FOUND!)</span>"; | |
} | |
return "You whisper, but only the wind seems to respond with its usual sighs."; | |
}, | |
'whisper': "Whisper what, and to whom?", | |
} | |
}, | |
'mundane_return_early': { | |
name: "A Fleeting Glance Back", | |
description: "You turn south, but the mundane world already feels distant, its colors muted. The call of the Shadow Wood is stronger. Do you truly wish to abandon this path so soon?", | |
actions: { | |
'go north': 'start', | |
'yes': 'end_mundane', | |
'no': 'start' | |
} | |
}, | |
'path_entry': { | |
name: "Winding Forest Path", | |
description: "The path is narrow and overgrown. Sunlight struggles to penetrate the dense canopy above, casting long, dancing shadows. The air grows colder. You feel a presence, not quite malevolent, but watchful. The path continues NORTH. To the EAST, you see a faint glimmer through the trees.", | |
actions: { | |
'go north': 'crossroads', | |
'go east': 'glimmering_cave_mouth', | |
'go south': 'start', | |
'examine presence': "It's an unshakeable feeling of being observed, like eyes in the dark just beyond your sight. It's the essence of this wood.", | |
} | |
}, | |
'glimmering_cave_mouth': { | |
name: "Glimmering Cave Mouth", | |
description: "A small opening in a rocky outcrop glitters faintly. Strange, phosphorescent moss clings to the entrance. It's damp and smells of wet stone. The cave leads EAST.", | |
actions: { | |
'go east': 'crystal_cave', | |
'go west': 'path_entry', | |
'examine moss': "The moss pulses with a soft, ethereal light. It's cool to the touch.", | |
} | |
}, | |
'crystal_cave': { | |
name: "Crystal Cave", | |
description: "The cave opens into a small grotto. The walls are lined with faintly glowing crystals. In the center, a larger, fist-sized CRYSTAL rests on a natural pedestal. It pulses with a gentle, warm light. The only exit is WEST.", | |
actions: { | |
'go west': 'glimmering_cave_mouth', | |
'take crystal': () => { | |
if (!gameState.flags.hasCrystal) { | |
gameState.flags.hasCrystal = true; | |
gameState.inventory.push('glowing crystal'); | |
return "You carefully take the crystal. It feels warm in your hand and emits a comforting glow. The other crystals in the grotto dim slightly."; | |
} | |
return "You already have the crystal."; | |
}, | |
'examine crystal': "It's a beautifully formed crystal, radiating a soft, steady light. It feels strangely comforting.", | |
} | |
}, | |
// THE MAZE (Murmuring Labyrinth) | |
'crossroads': { | |
name: "Forest Crossroads", | |
description: "The path splits here. To the NORTH, it descends into a misty, tangled area. To the EAST, the forest thins slightly, revealing what looks like an old, overgrown ruin. To the WEST, the trees are particularly dense and unnerving. The way SOUTH leads back.", | |
actions: { | |
'go north': 'maze_entrance', | |
'go east': 'ruin_edge', | |
'go west': 'dark_thicket', | |
'go south': 'path_entry' | |
} | |
}, | |
'dark_thicket': { | |
name: "Dark Thicket", | |
description: "The trees here are gnarled and packed so tightly it's almost impossible to see. Strange shapes flit at the edge of your vision, and you hear unsettling skittering sounds. You feel a strong urge to leave. The only clear path is EAST.", | |
actions: { | |
'go east': 'crossroads', | |
'examine shapes': "You can't quite focus on them. They are like fragments of nightmare, always just out of sight.", | |
'use glowing crystal': () => { | |
if (gameState.inventory.includes('glowing crystal')) { | |
return "You hold up the glowing crystal. Its light pushes back the deepest shadows, and the skittering sounds retreat slightly. The shapes seem to recoil from the light, but the thicket remains impassable."; | |
} | |
return "You have nothing to use that would help here."; | |
} | |
}, | |
enemy: "Shadow Lurkers (unseen, cause unease)" | |
}, | |
'ruin_edge': { | |
name: "Edge of an Old Ruin", | |
description: "Crumbling stone walls, choked with ivy, mark the boundary of some forgotten structure. It looks ancient and desolate. You can try to enter the ruins to the EAST or return WEST to the crossroads.", | |
actions: { | |
'go east': 'ruin_courtyard', | |
'go west': 'crossroads', | |
'examine walls': "The stones are cold and worn smooth by centuries of wind and rain. Faint carvings, too eroded to decipher, hint at a lost purpose." | |
} | |
}, | |
'ruin_courtyard': { | |
name: "Ruined Courtyard", | |
description: "A small, circular courtyard, open to the sky. A shattered fountain sits in the center, dry and filled with leaves. An old, weathered chest rests against the far wall. Paths lead WEST back to the ruin's edge and NORTH deeper into the ruins.", | |
actions: { | |
'go west': 'ruin_edge', | |
'go north': 'ruin_hall', | |
'examine fountain': "The fountain is broken beyond repair. You imagine water once flowed here, a spark of life in this desolation.", | |
'open chest': "The chest is unlocked but empty, save for dust and cobwebs. Whatever treasures it once held are long gone.", | |
'examine chest': "It's made of dark, heavy wood, bound with rusted iron. It seems to have been looted long ago." | |
} | |
}, | |
'ruin_hall': { | |
name: "Ruined Hall", | |
description: "This was once a grand hall, but the roof has collapsed, and rubble litters the floor. At the far end, a stone pedestal holds a small, intricately carved wooden bird. It seems out of place. A passage leads SOUTH back to the courtyard.", | |
actions: { | |
'go south': 'ruin_courtyard', | |
'examine bird': "The wooden bird is exquisitely carved, its wings half-spread as if about to take flight. It feels light and delicate. There's a tiny inscription on its base: 'To find the way, listen to the wind's own child.'", | |
'take bird': () => { | |
if (!gameState.inventory.includes('wooden bird')) { | |
gameState.inventory.push('wooden bird'); | |
return "You take the wooden bird. It feels surprisingly significant in your hand."; | |
} | |
return "You already have the bird."; | |
} | |
} | |
}, | |
'maze_entrance': { | |
name: "Entrance to the Murmuring Labyrinth", | |
description: "Before you lies a labyrinth of mist-shrouded trees and tangled thorns. The air is filled with a constant, low murmur, like countless voices whispering just out of earshot. It's disorienting. Paths lead NORTH, EAST, and WEST into the maze. SOUTH returns to the crossroads.", | |
actions: { | |
'go north': 'maze1', | |
'go east': 'maze2', | |
'go west': 'maze3', | |
'go south': 'crossroads', | |
'listen': "The murmurs are impossible to understand, a cacophony of soft sounds that tug at your sanity. You feel a pull, a desire to understand them, but also a fear of what they might reveal.", | |
'use wooden bird': () => { | |
if (gameState.inventory.includes('wooden bird')) { | |
gameState.flags.knowsMazeSecret = true; | |
return "You hold up the wooden bird. As if sensing its presence, one of the murmuring voices seems to coalesce, becoming clearer for a fleeting moment: '...where the eldest tree weeps north... then seek the light lost in the west... then embrace the silence of the east...' The bird then trembles, and its guidance fades. You now have a clue for the maze."; | |
} | |
return "The bird remains still in your hand. Perhaps it needs a specific context?"; | |
} | |
} | |
}, | |
// Simple Maze: N, W, E from maze_true_path_start | |
// maze_true_path_start is maze1. | |
// N from maze1 -> maze_N. W from maze_N -> maze_NW. E from maze_NW -> maze_exit. | |
'maze1': { // True path start | |
name: "Labyrinth - North Path", | |
description: "The mist is thick here. Gnarled branches reach out like grasping claws. The murmurs are louder. Paths lead NORTH, SOUTH, EAST, WEST.", | |
actions: { | |
'go north': 'maze_N', // Correct | |
'go south': 'maze_entrance', | |
'go east': 'maze4', // Dead end | |
'go west': 'maze5' // Loop | |
} | |
}, | |
'maze2': { | |
name: "Labyrinth - East Path", | |
description: "A dense thicket of thorns blocks further passage to the EAST. The murmurs seem mocking. Paths lead NORTH, SOUTH, WEST.", | |
actions: { | |
'go north': 'maze4', // Loop | |
'go south': 'maze6', // Dead end | |
'go west': 'maze_entrance' | |
} | |
}, | |
'maze3': { | |
name: "Labyrinth - West Path", | |
description: "The ground here is boggy and treacherous. The air is cold. Paths lead NORTH, SOUTH, EAST.", | |
actions: { | |
'go north': 'maze5', // Loop | |
'go south': 'maze7', // Dead end | |
'go east': 'maze_entrance' | |
} | |
}, | |
'maze4': { | |
name: "Labyrinth - Dead End", | |
description: "The path ends in a dense wall of thorns. The murmurs swirl around you, disorienting. Paths lead SOUTH and WEST.", | |
actions: { | |
'go south': 'maze2', | |
'go west': 'maze1' | |
} | |
}, | |
'maze5': { | |
name: "Labyrinth - Circling Path", | |
description: "You feel like you've been here before. The trees look unnervingly familiar. Paths lead NORTH, SOUTH, EAST, WEST.", | |
actions: { | |
'go north': 'maze7', | |
'go south': 'maze3', | |
'go east': 'maze1', | |
'go west': 'maze6' | |
} | |
}, | |
'maze6': { | |
name: "Labyrinth - Hidden Nook", | |
description: "A small, relatively clear nook. The murmurs are slightly softer here. It feels like a brief respite, but offers no escape. Paths lead NORTH and EAST.", | |
actions: { | |
'go north': 'maze2', | |
'go east': 'maze5' | |
} | |
}, | |
'maze7': { | |
name: "Labyrinth - Thorny Trap", | |
description: "You are surrounded by thorny vines that seem to writhe slowly. It's a struggle to move. Paths lead SOUTH and EAST.", | |
actions: { | |
'go south': 'maze5', | |
'go east': 'maze3' | |
} | |
}, | |
'maze_N': { // Second step of true path (from maze1 go N) | |
name: "Labyrinth - Weeping Tree", | |
description: "An ancient, massive tree stands here, its branches draped like a weeping willow, though no water is visible. The mist is particularly heavy. The murmurs are sorrowful. Paths lead NORTH, SOUTH, EAST, WEST. The inscription mentioned 'where the eldest tree weeps north...'", | |
actions: { | |
'go north': 'maze4', // Wrong | |
'go south': 'maze1', | |
'go east': 'maze_dead_end_deep', | |
'go west': 'maze_NW' // Correct | |
} | |
}, | |
'maze_NW': { // Third step of true path (from maze_N go W) | |
name: "Labyrinth - Fading Light", | |
description: "A faint, almost extinguished beam of light pierces the mist from somewhere to the WEST, but the path there is blocked. The murmurs are almost silent here. Paths lead NORTH, SOUTH, EAST. The inscription mentioned '...then seek the light lost in the west... then embrace the silence of the east...'", | |
actions: { | |
'go north': 'maze_dead_end_deep', | |
'go south': 'maze5', | |
'go east': 'maze_exit', // Correct | |
'examine light': "The light is too faint and distant to reach, and thorny bushes block any direct path towards it.", | |
} | |
}, | |
'maze_dead_end_deep': { | |
name: "Labyrinth - Deep Mist", | |
description: "The mist is so thick you can barely see your hand in front of your face. The murmurs are a deafening roar of confusion. You feel utterly lost. Your only hope is to go SOUTH or WEST.", | |
actions: { | |
'go south': 'maze_N', | |
'go west': 'maze_NW' | |
} | |
}, | |
'maze_exit': { | |
name: "Labyrinth Exit - Silent Grove", | |
description: "The murmurs abruptly cease. You stand in a small, unnaturally silent grove. The mist has thinned. A towering, dark structure looms to the NORTH: The Shadow Tower. A path leads SOUTH back into the Labyrinth, if you dare.", | |
actions: { | |
'go north': 'tower_base', | |
'go south': () => { | |
if (gameState.flags.knowsMazeSecret) { | |
return "You carefully retrace your steps using the bird's wisdom: WEST from here (to Fading Light), then EAST (to Weeping Tree), then SOUTH (to North Path), then SOUTH again to the Labyrinth Entrance."; | |
// gameState.currentRoom = 'maze_NW'; // Effectively taking you back if this were a real move | |
} | |
// This is a bit of a cheat for simplicity. True retracing is complex. | |
// We'll just send them to the Labyrinth entrance. | |
gameState.currentRoom = 'maze_entrance'; | |
return "You step back into the confusing mist. The murmurs resume their disorienting chorus."; | |
}, | |
'look tower': "It's a forbidding spire of black stone, piercing the sky like a shard of night. No windows are visible from here, only a single, massive door at its base." | |
} | |
}, | |
// THE TOWER | |
'tower_base': { | |
name: "Base of the Shadow Tower", | |
description: "You stand before the colossal Shadow Tower. Its black walls are seamless and cold to the touch. A single, immense door of dark metal bars your way. There's an ornate keyhole but no handle.", | |
actions: { | |
'go south': 'maze_exit', | |
'examine door': "The door is ancient and incredibly solid. The keyhole is intricate, shaped like a coiled serpent.", | |
'use glowing crystal': () => { | |
if (gameState.inventory.includes('glowing crystal') && !gameState.flags.towerDoorUnlocked) { | |
gameState.flags.towerDoorUnlocked = true; | |
return "You hold the glowing crystal towards the keyhole. As its light touches the metal, the crystal flares brightly, and a deep groan echoes from within the tower. The serpent-shaped keyhole seems to uncoil slightly, and with a click, the massive door creaks open a sliver, just enough for you to slip through. The crystal dims significantly, its task seemingly done, but still provides a faint light."; | |
} else if (gameState.flags.towerDoorUnlocked) { | |
return "The door is already unlocked."; | |
} | |
return "The crystal's light does nothing to the door. It needs a more specific key or mechanism."; | |
}, | |
'open door': () => { | |
if (gameState.flags.towerDoorUnlocked) { | |
gameState.currentRoom = 'tower_entrance_hall'; | |
return "You slip through the narrow opening in the massive door."; | |
} | |
return "The door is sealed shut."; | |
}, | |
'knock': "Your knock makes a dull thud, absorbed by the immense door. There is no response." | |
} | |
}, | |
'tower_entrance_hall': { | |
name: "Tower Entrance Hall", | |
description: "The air inside the tower is chill and carries the scent of dust and forgotten time. Echoes dance in the vast, dark space. A grand staircase spirals UPWARDS into darkness. The only way out is SOUTH through the door.", | |
actions: { | |
'go up': 'tower_guardian_chamber', | |
'go south': 'tower_base', | |
'examine staircase': "The staircase is made of the same black stone as the tower, its steps worn smooth by time or use. It seems to ascend endlessly.", | |
'listen': "You hear the faint drip of water, your own breath, and a profound, echoing silence that seems to swallow all other sound." | |
} | |
}, | |
'tower_guardian_chamber': { | |
name: "Guardian's Chamber", | |
description: () => { | |
if (!gameState.flags.metGuardian) { | |
return "Halfway up the tower, the staircase opens into a circular chamber. In the center stands a tall, cloaked figure, its face hidden in shadow. It does not move, but you feel its ancient gaze upon you. 'So, a seeker arrives,' a voice like dry leaves rustling echoes in your mind. 'Why have you come to this place of shadow?'\nA narrow passage continues UP, and the stairs lead DOWN."; | |
} | |
return "The Guardian's chamber is still. The cloaked figure remains, watching. The passage UP and stairs DOWN are your options."; | |
}, | |
actions: { | |
'go up': 'tower_mirror_room', | |
'go down': 'tower_entrance_hall', | |
'talk to guardian': () => { | |
gameState.flags.metGuardian = true; | |
return "'I am a reflection of this tower's heart,' the Guardian's mental voice whispers. 'Many seek what lies above. Some seek power, others oblivion. Few seek understanding. What is it you truly desire from the Shadow?' You can try to 'answer desire' or ask about the 'tower' or the 'shadow'."; | |
}, | |
'examine guardian': "The figure is shrouded in dark, flowing robes. No features are discernible, only a profound sense of age and weariness. It seems less a physical being and more a concentration of the tower's essence.", | |
'answer desire knowledge': "'Knowledge is a double-edged blade,' the Guardian responds. 'It can illuminate, or it can blind. The path above may offer what you seek, or it may show you truths you are not prepared for. Proceed with caution.'", | |
'answer desire power': "'Power found in shadow is often fleeting and corrupting,' the Guardian cautions. 'This tower offers no dominion, only reflection. If power is your aim, you will likely be disappointed, or worse.'", | |
'answer desire understanding': "'Understanding... a rare and worthy goal,' the Guardian's voice softens slightly. 'To understand the shadow is to understand a part of all things, including yourself. The mirror above may aid your quest.'", | |
'answer desire oblivion': "'Oblivion is an easy path to find, but a difficult one to return from,' the Guardian warns. 'Be certain it is truly what you seek before you step further into darkness.'", | |
'ask about tower': "'This tower is a conduit, a focal point for the energies that drift between worlds, between light and dark. It has stood for eons, a silent observer.'", | |
'ask about shadow': "'The Shadow is not mere absence of light. It is potential, memory, sorrow, mystery. It is the unseen half of existence. To some, it is a threat. To others, a sanctuary.'", | |
'attack guardian': "The thought barely forms in your mind before the Guardian's voice, laced with immense sadness, fills your head: 'Violence has no place here, seeker. This is not a battle to be won, but a truth to be faced.' The air grows heavy, and you feel a crushing weariness. You cannot bring yourself to act against it." | |
}, | |
enemy: "The Guardian (non-hostile, philosophical)" | |
}, | |
'tower_mirror_room': { | |
name: "The Mirror Chamber", | |
description: "You reach the apex of the tower. This circular room is surprisingly bare, save for a single, large, ornate mirror standing in the center. Its surface is like polished obsidian, reflecting not the room, but a swirling, star-dusted void. The only way is DOWN.", | |
actions: { | |
'go down': 'tower_guardian_chamber', | |
'examine mirror': "The mirror's frame is silver, etched with strange symbols. The surface is perfectly smooth and cool to the touch. The swirling void within seems to beckon, promising... something. It doesn't reflect you, only the deep cosmos.", | |
'touch mirror': () => { | |
gameState.flags.mirrorTouched = true; | |
return "As your fingers brush the cold surface, ripples spread across the void. A soft hum emanates from the mirror. It feels... receptive. A choice hangs in the air: will you STEP INTO the mirror, or PULL AWAY?"; | |
}, | |
'step into mirror': () => { | |
if (gameState.flags.mirrorTouched) { | |
gameState.currentRoom = 'simple_place'; | |
return "You take a breath and step forward. There is no resistance, only a cool rush like plunging into deep water. The starlight envelops you, and then... serenity."; | |
} | |
return "You should probably touch the mirror first, to see if it's safe."; | |
}, | |
'pull away': () => { | |
if (gameState.flags.mirrorTouched) { | |
gameState.currentRoom = 'end_tower_hesitation'; | |
return "A wave of doubt, or perhaps self-preservation, washes over you. You pull your hand back from the mirror. The void within continues to swirl, but the invitation feels... withdrawn."; | |
} | |
return "Pull away from what?"; | |
} | |
} | |
}, | |
// ENDINGS & SPECIAL | |
'simple_place': { | |
name: "The Still Garden", | |
isEnding: true, | |
description: "You find yourself in a place of profound peace. It is a simple, circular garden under a sky of perpetual twilight. A single, luminous tree stands in the center, its leaves whispering melodies of calm. A gentle stream flows around it. The air is pure and still. You feel the burdens you carried fall away. The Shadow is not gone, but it is balanced here, understood. You have found a quiet truth within the heart of darkness.\n\n<span class='action-result'>Congratulations! You have reached a place of understanding. THE END.</span>\n\n(Type 'restart' to play again)" | |
}, | |
'end_mundane': { | |
name: "Return to the Mundane", | |
isEnding: true, | |
description: "You turn your back on the Whispering Wood and the mysteries it holds. The mundane world reclaims you, its familiar routines and predictable comforts. The Shadow fades to a distant memory, a path not taken. Perhaps it was for the best. Or perhaps, a part of you will always wonder...\n\n<span class='action-result'>You chose the familiar path. THE END.</span>\n\n(Type 'restart' to play again)" | |
}, | |
'end_tower_hesitation': { | |
name: "Hesitation at the Brink", | |
isEnding: true, | |
description: "You stepped back from the mirror, from the final mystery of the tower. The moment passed, and the tower seems to sigh around you. You are left with your choices, your questions unanswered. The path to understanding was before you, but fear or doubt held you back. The Shadow remains an enigma, and you, a wanderer on its edge.\n\n<span class='action-result'>You turned away from the final step. THE END.</span>\n\n(Type 'restart' to play again)" | |
}, | |
'secret_grove': { // Easter Egg Room | |
name: "The Weaver's Grove (Secret)", | |
description: "You are in a hidden, sun-dappled grove. Time seems to move differently here. An ancient, kindly figure with eyes like starlight sits by a loom, weaving threads of light and shadow into a beautiful tapestry. 'Ah, a listener,' they say, their voice like rustling silk. 'Few find this place, fewer still by knowing how to truly hear the world's quiet heart. This is where the stories begin and end, where echoes are born.'\n\nThey offer you a warm smile. 'You may rest here as long as you wish, or return to the path when you are ready. The wood will remember your kindness.'\n\nYou feel a profound sense of peace and connection.", | |
actions: { | |
'examine weaver': "The Weaver is ancient beyond comprehension, yet their presence is comforting. Their fingers move with impossible grace, guiding the threads of fate, or perhaps just stories.", | |
'examine tapestry': "The tapestry depicts countless worlds, moments of joy and sorrow, light and darkness, all interwoven. You see a tiny thread that looks like your own journey through the woods.", | |
'talk to weaver': "'The world is full of whispers, child,' the Weaver says softly. 'Most are too loud to hear them. The Shadow, the Light, they are but two threads in the grand design. Your journey is your own thread to weave.'", | |
'leave grove': () => { | |
gameState.currentRoom = 'start'; // Return to the start, but with the knowledge | |
return "With a gentle nod from the Weaver, the grove shimmers and you find yourself back at the Edge of the Whispering Wood, the experience a warm memory. The echoes seem friendlier now."; | |
}, | |
'stay': "You sit for a while, watching the Weaver work. The peace of the grove seeps into your bones. It is a rare and precious gift." | |
} | |
} | |
}; | |
function appendOutput(text, type = 'narration') { | |
const line = document.createElement('div'); | |
line.classList.add('output-line'); | |
if (type) { | |
line.classList.add(type); | |
} | |
// Sanitize text before setting as innerHTML to prevent XSS if user input was ever directly put here | |
// For this game, it's mostly dev-controlled strings, but good practice | |
const sanitizedText = text.replace(/</g, "<").replace(/>/g, ">"); | |
line.innerHTML = text; // Allow specific HTML for styling like spans | |
outputDiv.appendChild(line); | |
outputDiv.scrollTop = outputDiv.scrollHeight; // Auto-scroll | |
} | |
function displayCurrentRoom() { | |
const room = rooms[gameState.currentRoom]; | |
if (!room) { | |
appendOutput("Error: Room not found: " + gameState.currentRoom, 'error'); | |
return; | |
} | |
appendOutput("--- " + room.name.toUpperCase() + " ---", 'location'); | |
let description = room.description; | |
if (typeof room.description === 'function') { | |
description = room.description(); | |
} | |
appendOutput(description, 'narration'); | |
if (room.isEnding) { | |
appendOutput("Type 'RESTART' to play again.", 'hint'); | |
commandInput.disabled = true; | |
} else { | |
commandInput.disabled = false; | |
// Optional: List available actions or items | |
// appendOutput("You see: ...", 'narration'); | |
// appendOutput("Obvious exits: ...", 'narration'); | |
} | |
} | |
function normalizeCommand(cmd) { | |
cmd = cmd.toLowerCase().trim(); | |
const synonyms = { | |
'n': 'go north', 's': 'go south', 'e': 'go east', 'w': 'go west', 'u': 'go up', 'd': 'go down', | |
'north': 'go north', 'south': 'go south', 'east': 'go east', 'west': 'go west', 'up': 'go up', 'down': 'go down', | |
'look around': 'look', 'examine': 'look', 'inspect': 'look', | |
'get': 'take', 'pick up': 'take', | |
'talk': 'talk to', | |
'answer': 'answer desire' // Specific for guardian | |
}; | |
// Simple verb-noun parsing (can be expanded) | |
const parts = cmd.split(' '); | |
const verb = parts[0]; | |
const noun = parts.slice(1).join(' '); | |
if (synonyms[cmd]) return synonyms[cmd]; | |
if (synonyms[verb]) return synonyms[verb] + (noun ? ' ' + noun : ''); | |
// Specific multi-word commands that don't fit simple synonymy | |
if (cmd === "listen to the echoes" || cmd === "listen to echoes") return 'listen to echoes'; | |
if (cmd === "whisper to the echoes" || cmd === "whisper to echoes") return 'whisper to echoes'; | |
return cmd; | |
} | |
function processCommand() { | |
const commandText = commandInput.value; | |
if (!commandText.trim()) return; | |
appendOutput("> " + commandText, 'player-input'); | |
commandInput.value = ''; | |
if (commandText.toLowerCase() === 'restart') { | |
startGame(); | |
return; | |
} | |
if (commandText.toLowerCase() === 'help') { | |
showHelp(); | |
return; | |
} | |
if (commandText.toLowerCase() === 'inventory' || commandText.toLowerCase() === 'i') { | |
showInventory(); | |
return; | |
} | |
const room = rooms[gameState.currentRoom]; | |
if (room.isEnding) { // Don't process commands if game ended, except restart | |
appendOutput("The story has concluded. Type 'RESTART' to begin anew.", 'hint'); | |
return; | |
} | |
const normalized = normalizeCommand(commandText); | |
let actionFound = false; | |
if (room.actions && room.actions[normalized]) { | |
const actionResult = room.actions[normalized]; | |
if (typeof actionResult === 'string') { | |
// If it's a room ID, change room | |
if (rooms[actionResult]) { | |
gameState.currentRoom = actionResult; | |
} else { | |
// Or it's just a descriptive string for an action | |
appendOutput(actionResult, 'action-result'); | |
} | |
} else if (typeof actionResult === 'function') { | |
// Execute function, it might change room or just give text | |
const resultText = actionResult(); | |
if (resultText) { | |
appendOutput(resultText, 'action-result'); | |
} | |
} | |
actionFound = true; | |
} else { | |
// Generic commands (if not handled by specific room actions) | |
if (normalized === 'look') { | |
// Handled by displayCurrentRoom regenerating description | |
actionFound = true; | |
} else if (normalized.startsWith('examine ') || normalized.startsWith('look ')) { | |
const itemToExamine = normalized.split(' ').slice(1).join(' '); | |
// This is a fallback if a specific "examine [item]" isn't in room.actions | |
// For more detailed examine, it should be in room.actions | |
if (itemToExamine) { | |
appendOutput("You look closely at the " + itemToExamine + ". Nothing particularly noteworthy strikes you beyond what you already see, or it's not something you can examine more closely right now.", 'action-result'); | |
} else { | |
appendOutput("Examine what?", 'error'); | |
} | |
actionFound = true; | |
} | |
} | |
if (!actionFound) { | |
const goMatch = normalized.match(/^go (\w+)$/); | |
if (goMatch) { | |
appendOutput("You can't go that way.", 'error'); | |
} else { | |
appendOutput("You can't do that, or you don't know how.", 'error'); | |
} | |
} | |
displayCurrentRoom(); // Display new room state or result of action | |
} | |
function showInventory() { | |
if (gameState.inventory.length === 0) { | |
appendOutput("You are carrying nothing.", 'action-result'); | |
} else { | |
appendOutput("You are carrying: " + gameState.inventory.join(', ') + ".", 'action-result'); | |
} | |
} | |
function showHelp() { | |
appendOutput("--- HELP ---", 'location'); | |
appendOutput("Available commands often depend on your location and the situation.", 'narration'); | |
appendOutput("Common verbs: GO (north, south, east, west, up, down), LOOK (or LOOK AROUND, EXAMINE), TAKE (or GET), USE, TALK TO, OPEN, PUSH, PULL, LISTEN, WHISPER.", 'narration'); | |
appendOutput("Try to be specific, e.g., 'EXAMINE DOOR', 'TAKE CRYSTAL', 'TALK TO GUARDIAN'.", 'narration'); | |
appendOutput("You can also type 'INVENTORY' or 'I' to see what you're carrying.", 'narration'); | |
appendOutput("To find the EASTER EGG: it's very specific, involves an unusual but thematic action in an early location, hinted at subtly by the room's description. Think about the 'soulful' nature of the game and the title 'The Shadow'. It requires a bit of quiet contemplation and a non-obvious interaction.", 'narration'); | |
appendOutput("Good luck, seeker!", 'narration'); | |
} | |
function startGame() { | |
gameState = { | |
currentRoom: 'start', | |
inventory: [], | |
flags: { | |
metGuardian: false, | |
knowsMazeSecret: false, | |
hasCrystal: false, | |
towerDoorUnlocked: false, | |
mirrorTouched: false, | |
whisperedToEcho: false, | |
easterEggFound: false | |
} | |
}; | |
outputDiv.innerHTML = ''; // Clear previous game | |
commandInput.disabled = false; | |
appendOutput("Welcome to The Shadow.", 'narration'); | |
appendOutput("Your journey begins...", 'narration'); | |
displayCurrentRoom(); | |
} | |
commandInput.addEventListener('keypress', function(event) { | |
if (event.key === 'Enter') { | |
processCommand(); | |
} | |
}); | |
// Initialize game | |
startGame(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=EnovinxSchool/the-shadow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |