diff --git a/EldritchEchoes/artifact/Archaic-Petra-Artifact-Set.webp b/EldritchEchoes/artifact/Archaic-Petra-Artifact-Set.webp new file mode 100644 index 0000000..8f6f4a7 Binary files /dev/null and b/EldritchEchoes/artifact/Archaic-Petra-Artifact-Set.webp differ diff --git a/EldritchEchoes/bg/bg1.png b/EldritchEchoes/bg/bg1.png new file mode 100644 index 0000000..83420d5 Binary files /dev/null and b/EldritchEchoes/bg/bg1.png differ diff --git a/EldritchEchoes/bg/bg3.avif b/EldritchEchoes/bg/bg3.avif new file mode 100644 index 0000000..b47c153 Binary files /dev/null and b/EldritchEchoes/bg/bg3.avif differ diff --git a/EldritchEchoes/cosmic-shadows-single-file.py b/EldritchEchoes/cosmic-shadows-single-file.py new file mode 100644 index 0000000..8a41f46 --- /dev/null +++ b/EldritchEchoes/cosmic-shadows-single-file.py @@ -0,0 +1,429 @@ +import pygame +import sys +import random +import math + +# 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: Eldritch Encounters") + +# Colors and Fonts +BLACK, WHITE, RED, GREEN, BLUE = (0, 0, 0), (255, 255, 255), (255, 0, 0), (0, 255, 0), (0, 0, 255) +PURPLE, YELLOW, BROWN = (128, 0, 128), (255, 255, 0), (165, 42, 42) +FONT = pygame.font.Font(None, 36) +SMALL_FONT = pygame.font.Font(None, 24) + +# Load images (using colored rectangles for simplicity) +player_img = pygame.Surface((32, 32)) +player_img.fill(BLUE) +shoggoth_img = pygame.Surface((64, 64)) +shoggoth_img.fill(PURPLE) +migo_img = pygame.Surface((48, 48)) +migo_img.fill(GREEN) +deep_one_img = pygame.Surface((56, 56)) +deep_one_img.fill((0, 100, 100)) +chest_img = pygame.Surface((40, 40)) +chest_img.fill(YELLOW) +weapon_img = pygame.Surface((24, 24)) +weapon_img.fill(RED) +food_img = pygame.Surface((24, 24)) +food_img.fill(BROWN) + +class Player: + def __init__(self, x, y): + self.rect = pygame.Rect(x, y, 32, 32) + self.image = player_img + self.speed = 5 + self.sanity = 100 + self.max_sanity = 100 + self.experience = 0 + self.skills = {"Investigation": 1, "Occult Knowledge": 1, "Mental Fortitude": 1} + self.inventory = [] + self.equipped_weapon = None + + def move(self, dx, dy): + self.rect.x += dx * self.speed + self.rect.y += dy * self.speed + self.rect.clamp_ip(screen.get_rect()) + + def draw(self): + screen.blit(self.image, self.rect) + name = SMALL_FONT.render("Investigator", True, WHITE) + screen.blit(name, (self.rect.x, self.rect.y - 20)) + + def gain_experience(self, amount): + self.experience += amount + if self.experience >= 100: + self.level_up() + + def level_up(self): + self.experience -= 100 + skill = random.choice(list(self.skills.keys())) + self.skills[skill] += 1 + print(f"Leveled up {skill}! The forbidden knowledge grows...") + + def add_to_inventory(self, item): + self.inventory.append(item) + + def use_item(self, item): + if isinstance(item, Weapon): + self.equipped_weapon = item + print(f"Equipped {item.name}") + elif isinstance(item, Food): + self.sanity = min(self.sanity + item.sanity_restore, self.max_sanity) + self.inventory.remove(item) + print(f"Used {item.name}. Sanity restored to {self.sanity}") + +class Enemy: + def __init__(self, name, image, health): + self.name = name + self.image = image + self.rect = self.image.get_rect(center=(WIDTH // 2, HEIGHT // 2)) + self.health = health + self.max_health = health + + def draw(self): + screen.blit(self.image, self.rect) + name = SMALL_FONT.render(self.name, True, WHITE) + screen.blit(name, (self.rect.x, self.rect.y - 20)) + + # Health bar + bar_width = self.image.get_width() + health_width = (self.health / self.max_health) * bar_width + pygame.draw.rect(screen, RED, (self.rect.x, self.rect.y - 10, bar_width, 5)) + pygame.draw.rect(screen, GREEN, (self.rect.x, self.rect.y - 10, health_width, 5)) + +class CombatAnimation: + def __init__(self, start_pos, end_pos, color): + self.start_pos = start_pos + self.end_pos = end_pos + self.color = color + self.progress = 0 + self.speed = 0.05 + + def update(self): + self.progress += self.speed + if self.progress > 1: + return True + return False + + def draw(self): + current_pos = ( + self.start_pos[0] + (self.end_pos[0] - self.start_pos[0]) * self.progress, + self.start_pos[1] + (self.end_pos[1] - self.start_pos[1]) * self.progress + ) + pygame.draw.line(screen, self.color, self.start_pos, current_pos, 2) + +class Combat: + def __init__(self, player): + self.player = player + self.enemy = None + self.is_active = False + self.eldritch_horrors = [ + ("Shoggoth", shoggoth_img, 100), + ("Mi-go", migo_img, 80), + ("Deep One", deep_one_img, 90) + ] + self.animations = [] + + def start_combat(self): + enemy_data = random.choice(self.eldritch_horrors) + self.enemy = Enemy(*enemy_data) + self.is_active = True + + def resolve(self, action): + start_pos = (self.player.rect.centerx, self.player.rect.centery) + end_pos = (self.enemy.rect.centerx, self.enemy.rect.centery) + + weapon_bonus = self.player.equipped_weapon.damage if self.player.equipped_weapon else 0 + + if action == "evade": + success = random.random() < 0.9 + (self.player.skills["Mental Fortitude"] * 0.05) + self.animations.append(CombatAnimation(start_pos, end_pos, BLUE)) + if success: + self.enemy.health -= 10 + weapon_bonus + result = f"You successfully evaded the {self.enemy.name}, but the memory lingers..." + else: + self.player.sanity -= 5 + result = f"Evasion failed. The {self.enemy.name}'s presence shatters your perception of reality." + elif action == "confront": + success = random.random() < 0.7 + (self.player.skills["Occult Knowledge"] * 0.05) + self.animations.append(CombatAnimation(start_pos, end_pos, RED)) + if success: + self.enemy.health -= 20 + weapon_bonus + self.player.gain_experience(20) + result = f"You successfully confronted the {self.enemy.name}! The victory feels hollow as you grasp the true nature of existence." + else: + self.player.sanity -= 10 + result = f"Confrontation failed. The {self.enemy.name}'s true form is beyond human comprehension." + + if self.enemy.health <= 0: + self.is_active = False + result += " The entity dissipates into the aether." + + return result + + def update(self): + completed_animations = [] + for anim in self.animations: + if anim.update(): + completed_animations.append(anim) + for anim in completed_animations: + self.animations.remove(anim) + + def draw(self): + if self.is_active: + self.enemy.draw() + for anim in self.animations: + anim.draw() + +class NPC: + def __init__(self, name, x, y, dialogue): + self.name = name + self.rect = pygame.Rect(x, y, 32, 32) + self.dialogue = dialogue + self.image = pygame.Surface((32, 32)) + self.image.fill(PURPLE) + + def draw(self, player): + screen.blit(self.image, self.rect) + name = SMALL_FONT.render(self.name, True, WHITE) + screen.blit(name, (self.rect.x, self.rect.y - 20)) + + if self.rect.colliderect(player.rect.inflate(50, 50)): + prompt = SMALL_FONT.render("Click to interact", True, WHITE) + screen.blit(prompt, (self.rect.x, self.rect.y + 40)) + + def get_dialogue(self, player_sanity): + return self.dialogue[min(key for key in self.dialogue.keys() if key >= player_sanity)] + +class DialogueSystem: + def __init__(self): + self.active = False + self.current_dialogue = [] + self.current_index = 0 + + def start_dialogue(self, dialogue): + self.active = True + self.current_dialogue = dialogue + self.current_index = 0 + + def next_dialogue(self): + self.current_index += 1 + if self.current_index >= len(self.current_dialogue): + self.active = False + + def draw(self): + if self.active: + pygame.draw.rect(screen, BLACK, (50, HEIGHT - 150, WIDTH - 100, 100)) + pygame.draw.rect(screen, WHITE, (50, HEIGHT - 150, WIDTH - 100, 100), 2) + text = FONT.render(self.current_dialogue[self.current_index], True, WHITE) + screen.blit(text, (60, HEIGHT - 140)) + +class Chest: + def __init__(self, x, y): + self.rect = pygame.Rect(x, y, 40, 40) + self.image = chest_img + self.opened = False + + def draw(self, player): + screen.blit(self.image, self.rect) + if not self.opened and self.rect.colliderect(player.rect.inflate(50, 50)): + prompt = SMALL_FONT.render("Click to open", True, WHITE) + screen.blit(prompt, (self.rect.x, self.rect.y + 50)) + + def open(self): + if not self.opened: + self.opened = True + return random.choice([Weapon("Eldritch Blade", 15), Food("Sanity Potion", 30)]) + return None + +class Weapon: + def __init__(self, name, damage): + self.name = name + self.damage = damage + self.image = weapon_img + + def draw(self, x, y): + screen.blit(self.image, (x, y)) + +class Food: + def __init__(self, name, sanity_restore): + self.name = name + self.sanity_restore = sanity_restore + self.image = food_img + + def draw(self, x, y): + screen.blit(self.image, (x, y)) + +class CosmicShadows: + def __init__(self): + self.player = Player(WIDTH // 2, HEIGHT // 2) + self.combat = Combat(self.player) + self.sanity_drain_rate = 0.001 + self.message = "" + self.message_timer = 0 + self.npcs = [ + NPC("Old Man", 100, 100, { + 100: ["The stars are aligning...", "Beware the ancient ones!"], + 50: ["The shadows... they're moving!", "Can you hear the whispers?"], + 0: ["The truth! It burns! Make it stop!"] + }), + NPC("Scholar", 700, 500, { + 100: ["I've seen things...", "The knowledge comes at a price."], + 50: ["The books... they speak to me now.", "Reality is not what it seems."], + 0: ["The cosmos! The vast, uncaring cosmos!"] + }) + ] + self.dialogue_system = DialogueSystem() + self.chests = [Chest(200, 200), Chest(600, 400)] + self.combat_chance = 0.001 # Reduced from 0.005 to 0.001 for less frequent combat + + def handle_events(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return False + if event.type == pygame.MOUSEBUTTONDOWN: + if self.combat.is_active: + if event.button == 1: # Left click + self.message = self.combat.resolve("evade") + self.message_timer = 3 + elif event.button == 3: # Right click + self.message = self.combat.resolve("confront") + self.message_timer = 3 + else: + if event.button == 1: # Left click + for npc in self.npcs: + if npc.rect.collidepoint(event.pos): + self.dialogue_system.start_dialogue(npc.get_dialogue(self.player.sanity)) + for chest in self.chests: + if chest.rect.collidepoint(event.pos) and chest.rect.colliderect(self.player.rect.inflate(50, 50)): + item = chest.open() + if item: + self.player.add_to_inventory(item) + self.message = f"You found a {item.name}!" + self.message_timer = 3 + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_i: + self.show_inventory() + elif pygame.K_1 <= event.key <= pygame.K_9: + index = event.key - pygame.K_1 + if index < len(self.player.inventory): + self.player.use_item(self.player.inventory[index]) + elif event.key == pygame.K_SPACE: + if self.dialogue_system.active: + self.dialogue_system.next_dialogue() + return True + + def update(self, dt): + keys = pygame.key.get_pressed() + if not self.combat.is_active and not self.dialogue_system.active: + dx = keys[pygame.K_d] - keys[pygame.K_a] + dy = keys[pygame.K_s] - keys[pygame.K_w] + self.player.move(dx, dy) + + self.player.sanity -= self.sanity_drain_rate * (1 - (self.player.skills["Mental Fortitude"] * 0.05)) + self.player.sanity = max(0, min(self.player.sanity, self.player.max_sanity)) + + if self.player.sanity <= 0: + print("Your mind shatters as the true nature of reality is revealed.") + return False + + self.combat.update() + + if self.message_timer > 0: + self.message_timer -= dt + + + if random.random() < self.combat_chance and not self.combat.is_active: + self.combat.start_combat() + + return True + + def draw(self): + screen.fill(BLACK) + + for chest in self.chests: + chest.draw(self.player) + + self.player.draw() + self.combat.draw() + + for npc in self.npcs: + npc.draw(self.player) + + # Draw sanity bar + sanity_color = (int(255 * (1 - self.player.sanity / 100)), int(255 * (self.player.sanity / 100)), 0) + pygame.draw.rect(screen, sanity_color, (10, 10, self.player.sanity * 2, 20)) + + # Draw stats + stats_text = SMALL_FONT.render(f"Sanity: {int(self.player.sanity)} XP: {self.player.experience}", True, WHITE) + screen.blit(stats_text, (WIDTH - stats_text.get_width() - 10, 10)) + + # Draw skills + for i, (skill, level) in enumerate(self.player.skills.items()): + skill_text = SMALL_FONT.render(f"{skill}: {level}", True, WHITE) + screen.blit(skill_text, (WIDTH - skill_text.get_width() - 10, 40 + i * 20)) + + if self.combat.is_active: + combat_text = FONT.render(f"A {self.combat.enemy.name} appears! Left click to Evade, Right click to Confront", True, RED) + screen.blit(combat_text, (WIDTH // 2 - combat_text.get_width() // 2, 50)) + + if self.message_timer > 0: + message_text = SMALL_FONT.render(self.message, True, WHITE) + screen.blit(message_text, (WIDTH // 2 - message_text.get_width() // 2, HEIGHT - 50)) + + self.dialogue_system.draw() + + pygame.display.flip() + + def show_inventory(self): + inventory_surface = pygame.Surface((WIDTH, HEIGHT)) + inventory_surface.set_alpha(200) + inventory_surface.fill(BLACK) + screen.blit(inventory_surface, (0, 0)) + + title = FONT.render("Inventory", True, WHITE) + screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 50)) + + for i, item in enumerate(self.player.inventory): + item_text = SMALL_FONT.render(f"{i+1}. {item.name}", True, WHITE) + screen.blit(item_text, (WIDTH // 2 - 100, 100 + i * 30)) + item.draw(WIDTH // 2 + 100, 100 + i * 30) + + if self.player.equipped_weapon: + equipped_text = SMALL_FONT.render(f"Equipped: {self.player.equipped_weapon.name}", True, WHITE) + screen.blit(equipped_text, (WIDTH // 2 - equipped_text.get_width() // 2, HEIGHT - 50)) + + pygame.display.flip() + waiting = True + while waiting: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_i or event.key == pygame.K_ESCAPE: + waiting = False + +def main(): + clock = pygame.time.Clock() + game = CosmicShadows() + + running = True + while running: + dt = clock.tick(60) / 1000.0 # Delta time in seconds + running = game.handle_events() + if not game.update(dt): + running = False + game.draw() + + pygame.quit() + sys.exit() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/EldritchEchoes/eldritch-echoes-game-description.md b/EldritchEchoes/eldritch-echoes-game-description.md new file mode 100644 index 0000000..0841c86 --- /dev/null +++ b/EldritchEchoes/eldritch-echoes-game-description.md @@ -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. + diff --git a/EldritchEchoes/enemy/Genshin_Dendro_Abyss_Mage.webp b/EldritchEchoes/enemy/Genshin_Dendro_Abyss_Mage.webp new file mode 100644 index 0000000..49fc393 Binary files /dev/null and b/EldritchEchoes/enemy/Genshin_Dendro_Abyss_Mage.webp differ diff --git a/EldritchEchoes/librarian/lib.png b/EldritchEchoes/librarian/lib.png new file mode 100644 index 0000000..ca43791 Binary files /dev/null and b/EldritchEchoes/librarian/lib.png differ diff --git a/EldritchEchoes/player/p1.png b/EldritchEchoes/player/p1.png new file mode 100644 index 0000000..00a5960 Binary files /dev/null and b/EldritchEchoes/player/p1.png differ diff --git a/EldritchEchoes/portal/portal.png b/EldritchEchoes/portal/portal.png new file mode 100644 index 0000000..88f7fe8 Binary files /dev/null and b/EldritchEchoes/portal/portal.png differ diff --git a/EldritchEchoes/shadowssss/__pycache__/assets.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/assets.cpython-311.pyc new file mode 100644 index 0000000..6fe4d11 Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/assets.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/__pycache__/dialogue.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/dialogue.cpython-311.pyc new file mode 100644 index 0000000..8961eae Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/dialogue.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/__pycache__/game.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/game.cpython-311.pyc new file mode 100644 index 0000000..99a2517 Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/game.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/__pycache__/npc.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/npc.cpython-311.pyc new file mode 100644 index 0000000..835fc6b Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/npc.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/__pycache__/player.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/player.cpython-311.pyc new file mode 100644 index 0000000..b19e82d Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/player.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/__pycache__/puzzle.cpython-311.pyc b/EldritchEchoes/shadowssss/__pycache__/puzzle.cpython-311.pyc new file mode 100644 index 0000000..529dc7d Binary files /dev/null and b/EldritchEchoes/shadowssss/__pycache__/puzzle.cpython-311.pyc differ diff --git a/EldritchEchoes/shadowssss/assets.py b/EldritchEchoes/shadowssss/assets.py new file mode 100644 index 0000000..38aab9d --- /dev/null +++ b/EldritchEchoes/shadowssss/assets.py @@ -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 + } + diff --git a/EldritchEchoes/shadowssss/dialogue.py b/EldritchEchoes/shadowssss/dialogue.py new file mode 100644 index 0000000..06fd27c --- /dev/null +++ b/EldritchEchoes/shadowssss/dialogue.py @@ -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)) \ No newline at end of file diff --git a/EldritchEchoes/shadowssss/game.py b/EldritchEchoes/shadowssss/game.py new file mode 100644 index 0000000..61bfa6a --- /dev/null +++ b/EldritchEchoes/shadowssss/game.py @@ -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.") diff --git a/EldritchEchoes/shadowssss/main.py b/EldritchEchoes/shadowssss/main.py new file mode 100644 index 0000000..3ef85f3 --- /dev/null +++ b/EldritchEchoes/shadowssss/main.py @@ -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() \ No newline at end of file diff --git a/EldritchEchoes/shadowssss/npc.py b/EldritchEchoes/shadowssss/npc.py new file mode 100644 index 0000000..844a856 --- /dev/null +++ b/EldritchEchoes/shadowssss/npc.py @@ -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) diff --git a/EldritchEchoes/shadowssss/player.py b/EldritchEchoes/shadowssss/player.py new file mode 100644 index 0000000..806d43c --- /dev/null +++ b/EldritchEchoes/shadowssss/player.py @@ -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) diff --git a/EldritchEchoes/shadowssss/puzzle.py b/EldritchEchoes/shadowssss/puzzle.py new file mode 100644 index 0000000..58a344f --- /dev/null +++ b/EldritchEchoes/shadowssss/puzzle.py @@ -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 \ No newline at end of file