-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
783 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# EldritchEchoes: A Cosmic Horror Adventure | ||
|
||
## Game Overview | ||
EldritchEchoes is a text-based adventure game set in the nightmarish city of R'lyeh, a place of cosmic horror and mind-bending impossibilities. Players explore this alien landscape, facing sanity-threatening events and encounters while trying to uncover the mysteries of the city and survive the presence of the Great Old One. | ||
|
||
## Setting: The City of R'lyeh | ||
|
||
R'lyeh is a nightmarish metropolis of impossible geometry, rising from an endless, dark ocean. Colossal spires of alien architecture pierce the sky, their surfaces slick with an otherworldly sheen. The city seems to shift and breathe, defying the laws of physics and sanity alike. | ||
|
||
### Key Locations | ||
|
||
1. The Dreaming Spires: Impossibly tall towers that sway and whisper in an unfelt wind. | ||
2. The Sunken Plaza: A vast square where the laws of gravity fluctuate unpredictably. | ||
3. The Cyclopean Gate: A massive archway that sometimes opens to other dimensions. | ||
4. The Whispering Library: A maze-like collection of alien tomes and artifacts. | ||
5. The Abyss: The dark waters surrounding the city, home to unspeakable horrors. | ||
|
||
### Atmospheric Elements | ||
|
||
1. Perpetual Eclipse: A sickly green moon, eternally eclipsed, casts an eerie glow over the landscape. | ||
2. Reality Distortions: Objects and buildings occasionally phase in and out of existence. | ||
3. Psychic Whispers: Visitors to the city are plagued by maddening whispers and visions. | ||
4. Flying Horrors: Bat-like creatures with too many wings circle the spires endlessly. | ||
|
||
## The Great Old One | ||
|
||
Looming over the city is a colossal entity, part octopus, part dragon, and part human, yet wholly alien. Its wings block out the sky, its tentacles weave through the city's architecture. Its presence alone is enough to shatter minds and warp reality. | ||
|
||
## Game Mechanics | ||
|
||
### Player Statistics | ||
|
||
1. Sanity: Represents the player's mental health. Decreases when encountering horrors or making certain choices. If it reaches zero, the player's mind shatters. | ||
2. Reality Anchor: Represents the player's connection to their own reality. As it decreases, they risk being lost in the alien dimension. | ||
3. Inventory: Players can collect and use items found throughout the city. | ||
4. Skills: Players can develop skills like "Eldritch Knowledge" and "Mental Fortitude" as they progress. | ||
|
||
### Gameplay Elements | ||
|
||
1. Exploration: Players navigate between different locations in R'lyeh, each with its unique descriptions and events. | ||
2. Events: Each location has random events that the player must respond to, with choices affecting their stats and progression. | ||
3. Item Usage: Players can find and use items that affect their stats or provide special abilities. | ||
4. Skill Development: As players make certain choices or use certain items, their skills improve, providing benefits in future encounters. | ||
5. Encounters with the Great Old One: Random encounters with the cosmic entity can cause significant sanity and reality anchor loss. | ||
|
||
### Items | ||
|
||
1. Ethereal Feather: Found in the Dreaming Spires, restores Reality Anchor when used. | ||
2. Gravity Crystal: Found in the Sunken Plaza, restores Sanity when used. | ||
3. Dimensional Key: Found at the Cyclopean Gate, increases Eldritch Knowledge when used. | ||
4. Tome of Madness: Found in the Whispering Library, decreases Sanity but increases Mental Fortitude when used. | ||
|
||
## Narrative Elements | ||
|
||
1. The Cult of the Dreaming God: A group of humans working to fully awaken the Great Old One. | ||
2. Ancient Artifacts: Scattered items that can grant cosmic power or insight at the cost of sanity. | ||
3. Time Loops: Areas where time behaves erratically, trapping explorers in repeating cycles. | ||
|
||
## Game Progression | ||
|
||
As players explore R'lyeh, they will: | ||
1. Discover the nature of the city and its connection to the Great Old One. | ||
2. Uncover the plans of the Cult of the Dreaming God. | ||
3. Find and use ancient artifacts to gain power and knowledge. | ||
4. Navigate the dangers of the city while managing their sanity and reality anchor. | ||
5. Eventually face a final confrontation or revelation that determines their fate. | ||
|
||
## End Game Scenarios | ||
|
||
1. Banishment: Find a way to send the Great Old One back to its slumber. | ||
2. Ascension: Join with the cosmic forces and transcend humanity. | ||
3. Escape: Discover a way to leave R'lyeh and return to the normal world. | ||
4. Madness: Lose all sanity and become one with the horrors of R'lyeh. | ||
5. Untethered: Lose all reality anchor and fade from existence. | ||
|
||
This text-based adventure combines elements of exploration, resource management, and decision-making, all set against a backdrop of cosmic horror inspired by H.P. Lovecraft's works. The game challenges players to navigate the thin line between enlightenment and madness as they uncover the secrets of R'lyeh and confront forces beyond human comprehension. | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
# File: assets.py | ||
|
||
import pygame | ||
|
||
def load_assets(): | ||
return { | ||
'player_image': pygame.Surface((32, 32)), # Placeholder, replace with actual image loading | ||
'npc_image': pygame.Surface((32, 32)), # Placeholder, replace with actual image loading | ||
'dialogue_bg': pygame.Surface((700, 150)) # Placeholder, replace with actual image loading | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# File: dialogue.py | ||
|
||
import pygame | ||
|
||
class DialogueSystem: | ||
def __init__(self, background_image): | ||
self.background = background_image | ||
self.dialogues = { | ||
"Detective": [ | ||
"Welcome to Shadowbrook. I'm Detective Johnson.", | ||
"We've been experiencing some strange occurrences lately.", | ||
"I hope you can help us get to the bottom of this mystery." | ||
] | ||
} | ||
self.current_npc = None | ||
self.dialogue_index = 0 | ||
|
||
def start_dialogue(self, npc_name): | ||
self.current_npc = npc_name | ||
self.dialogue_index = 0 | ||
return self.dialogues[npc_name][self.dialogue_index] | ||
|
||
def next(self): | ||
self.dialogue_index += 1 | ||
if self.dialogue_index < len(self.dialogues[self.current_npc]): | ||
return self.dialogues[self.current_npc][self.dialogue_index] | ||
else: | ||
return None | ||
|
||
def draw(self, screen, current_dialogue): | ||
screen.blit(self.background, (50, 400)) | ||
font = pygame.font.Font(None, 28) | ||
text = font.render(current_dialogue, True, (255, 255, 255)) | ||
screen.blit(text, (60, 410)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# File: game.py | ||
|
||
import pygame | ||
from player import Player | ||
from npc import NPC | ||
from puzzle import Puzzle | ||
from dialogue import DialogueSystem | ||
import json | ||
|
||
class GameState: | ||
MENU = 0 | ||
PLAYING = 1 | ||
DIALOGUE = 2 | ||
PUZZLE = 3 | ||
INVENTORY = 4 | ||
|
||
class CosmicShadows: | ||
def __init__(self, screen, assets): | ||
self.screen = screen | ||
self.assets = assets | ||
self.state = GameState.MENU | ||
self.player = Player(400, 300, assets['player_image']) | ||
self.npcs = [NPC(200, 200, assets['npc_image'], "Detective")] | ||
self.puzzles = [Puzzle("Decrypt the ancient tome", "ELDRITCHHORROR", "Unscramble the letters")] | ||
self.dialogue_system = DialogueSystem(assets['dialogue_bg']) | ||
self.current_dialogue = None | ||
self.sanity_drain_rate = 1 # Sanity points lost per second | ||
self.load_game() | ||
|
||
def handle_event(self, event): | ||
if self.state == GameState.PLAYING: | ||
self.player.handle_event(event) | ||
elif self.state == GameState.DIALOGUE: | ||
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: | ||
self.current_dialogue = self.dialogue_system.next() | ||
if self.current_dialogue is None: | ||
self.state = GameState.PLAYING | ||
elif self.state == GameState.PUZZLE: | ||
if event.type == pygame.KEYDOWN: | ||
self.puzzles[0].input_letter(event.unicode) | ||
|
||
def update(self, dt): | ||
if self.state == GameState.PLAYING: | ||
self.player.update(dt) | ||
self.update_sanity(dt) | ||
self.check_npc_interactions() | ||
elif self.state == GameState.PUZZLE: | ||
if self.puzzles[0].is_solved(): | ||
self.state = GameState.PLAYING | ||
self.player.knowledge += 1 | ||
|
||
def draw(self): | ||
self.screen.fill((0, 0, 0)) # Black background | ||
|
||
if self.state == GameState.PLAYING: | ||
self.player.draw(self.screen) | ||
for npc in self.npcs: | ||
npc.draw(self.screen) | ||
elif self.state == GameState.DIALOGUE: | ||
self.dialogue_system.draw(self.screen, self.current_dialogue) | ||
elif self.state == GameState.PUZZLE: | ||
self.puzzles[0].draw(self.screen) | ||
|
||
# Draw sanity meter | ||
pygame.draw.rect(self.screen, (255, 0, 0), (10, 10, self.player.sanity * 2, 20)) | ||
|
||
pygame.display.flip() | ||
|
||
def update_sanity(self, dt): | ||
self.player.sanity -= self.sanity_drain_rate * dt | ||
if self.player.sanity <= 0: | ||
self.game_over() | ||
|
||
def check_npc_interactions(self): | ||
for npc in self.npcs: | ||
if self.player.rect.colliderect(npc.rect): | ||
self.start_dialogue(npc) | ||
|
||
def start_dialogue(self, npc): | ||
self.state = GameState.DIALOGUE | ||
self.current_dialogue = self.dialogue_system.start_dialogue(npc.name) | ||
|
||
def game_over(self): | ||
print("Game Over - Sanity depleted") | ||
self.save_game() | ||
pygame.quit() | ||
sys.exit() | ||
|
||
def save_game(self): | ||
game_state = { | ||
"player": { | ||
"x": self.player.rect.x, | ||
"y": self.player.rect.y, | ||
"sanity": self.player.sanity, | ||
"inventory": self.player.inventory, | ||
"knowledge": self.player.knowledge | ||
}, | ||
"npcs": [{"x": npc.rect.x, "y": npc.rect.y, "name": npc.name} for npc in self.npcs], | ||
"puzzles": [puzzle.to_dict() for puzzle in self.puzzles] | ||
} | ||
with open("savegame.json", "w") as f: | ||
json.dump(game_state, f) | ||
|
||
def load_game(self): | ||
try: | ||
with open("savegame.json", "r") as f: | ||
game_state = json.load(f) | ||
self.player.rect.x = game_state["player"]["x"] | ||
self.player.rect.y = game_state["player"]["y"] | ||
self.player.sanity = game_state["player"]["sanity"] | ||
self.player.inventory = game_state["player"]["inventory"] | ||
self.player.knowledge = game_state["player"]["knowledge"] | ||
self.npcs = [NPC(npc["x"], npc["y"], self.assets['npc_image'], npc["name"]) for npc in game_state["npcs"]] | ||
self.puzzles = [Puzzle.from_dict(puzzle_dict) for puzzle_dict in game_state["puzzles"]] | ||
except FileNotFoundError: | ||
print("No save game found. Starting new game.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# File: main.py | ||
|
||
import pygame | ||
import sys | ||
from game import CosmicShadows | ||
from assets import load_assets | ||
|
||
# Initialize Pygame | ||
pygame.init() | ||
|
||
# Set up the display | ||
WIDTH, HEIGHT = 800, 600 | ||
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | ||
pygame.display.set_caption("Cosmic Shadows") | ||
|
||
# Load assets | ||
assets = load_assets() | ||
|
||
def main(): | ||
clock = pygame.time.Clock() | ||
game = CosmicShadows(screen, assets) | ||
|
||
running = True | ||
while running: | ||
dt = clock.tick(60) / 1000 # Delta time in seconds | ||
|
||
for event in pygame.event.get(): | ||
if event.type == pygame.QUIT: | ||
running = False | ||
game.handle_event(event) | ||
|
||
game.update(dt) | ||
game.draw() | ||
|
||
pygame.display.flip() | ||
|
||
pygame.quit() | ||
sys.exit() | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# File: npc.py | ||
|
||
import pygame | ||
|
||
class NPC: | ||
def __init__(self, x, y, image, name): | ||
self.rect = pygame.Rect(x, y, 32, 32) | ||
self.image = image | ||
self.name = name | ||
|
||
def draw(self, screen): | ||
screen.blit(self.image, self.rect) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# File: player.py | ||
|
||
import pygame | ||
|
||
class Player: | ||
def __init__(self, x, y, image): | ||
self.rect = pygame.Rect(x, y, 32, 32) | ||
self.image = image | ||
self.speed = 5 | ||
self.sanity = 100 | ||
self.inventory = [] | ||
self.knowledge = 0 | ||
|
||
def handle_event(self, event): | ||
if event.type == pygame.KEYDOWN: | ||
if event.key == pygame.K_i: | ||
print(f"Inventory: {self.inventory}") | ||
|
||
def update(self, dt): | ||
keys = pygame.key.get_pressed() | ||
self.rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * self.speed | ||
self.rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * self.speed | ||
|
||
def draw(self, screen): | ||
screen.blit(self.image, self.rect) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# File: puzzle.py | ||
|
||
import pygame | ||
|
||
class Puzzle: | ||
def __init__(self, name, solution, clue): | ||
self.name = name | ||
self.solution = solution | ||
self.clue = clue | ||
self.current_input = "" | ||
|
||
def input_letter(self, letter): | ||
self.current_input += letter | ||
if len(self.current_input) > len(self.solution): | ||
self.current_input = self.current_input[1:] | ||
|
||
def is_solved(self): | ||
return self.current_input.lower() == self.solution.lower() | ||
|
||
def draw(self, screen): | ||
font = pygame.font.Font(None, 36) | ||
clue_text = font.render(self.clue, True, (255, 255, 255)) | ||
input_text = font.render(self.current_input, True, (255, 255, 255)) | ||
screen.blit(clue_text, (100, 100)) | ||
screen.blit(input_text, (100, 150)) | ||
|
||
def to_dict(self): | ||
return { | ||
"name": self.name, | ||
"solution": self.solution, | ||
"clue": self.clue, | ||
"current_input": self.current_input | ||
} | ||
|
||
@classmethod | ||
def from_dict(cls, data): | ||
puzzle = cls(data["name"], data["solution"], data["clue"]) | ||
puzzle.current_input = data["current_input"] | ||
return puzzle |