From 0d115b2bef7262a69c30ffb8b386f86f159539c7 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 30 Jan 2025 01:17:52 -0500 Subject: [PATCH] Initial commit. --- .gitattributes | 1 + .gitignore | 5 +++ .gitmodules | 3 ++ levels/1.json | 36 +++++++++++++++++ libstormgames | 1 + sounds/coin.ogg | 3 ++ sounds/footstep.ogg | 3 ++ sounds/game-intro.ogg | 3 ++ sounds/get_coin.ogg | 3 ++ sounds/goblin.ogg | 3 ++ sounds/grave.ogg | 3 ++ sounds/jump.ogg | 3 ++ src/__init__.py | 0 src/level.py | 76 ++++++++++++++++++++++++++++++++++ src/object.py | 10 +++++ src/player.py | 13 ++++++ wicked_quest.py | 94 +++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 260 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 levels/1.json create mode 160000 libstormgames create mode 100644 sounds/coin.ogg create mode 100644 sounds/footstep.ogg create mode 100644 sounds/game-intro.ogg create mode 100644 sounds/get_coin.ogg create mode 100644 sounds/goblin.ogg create mode 100644 sounds/grave.ogg create mode 100644 sounds/jump.ogg create mode 100644 src/__init__.py create mode 100644 src/level.py create mode 100644 src/object.py create mode 100644 src/player.py create mode 100644 wicked_quest.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b6dd0bb --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ogg filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c24d07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ +*\.pyc +sounds/rec.sh +*.flac +*.wav diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4696c1b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libstormgames"] + path = libstormgames + url = https://git.stormux.org/storm/libstormgames diff --git a/levels/1.json b/levels/1.json new file mode 100644 index 0000000..a1ca11c --- /dev/null +++ b/levels/1.json @@ -0,0 +1,36 @@ +{ + "level_id": 1, + "name": "The Mausoleum", + "description": "After years of existing as a pile of bones, someone was crazy enough to assemble your skeleton. Time to reak some havoc!", + "player_start": { + "x": 0, + "y": 0 + }, + "objects": [ + { + "x": 5, + "y": 3, + "sound": "coin", + "collectible": true, + "static": true + }, + { + "x": 25, + "y": 0, + "sound": "goblin", + "hazard": true, + "static": false + }, + { + "x": 50, + "y": 0, + "hazard": true, + "sound": "grave", + "static": true + } + ], + "boundaries": { + "left": 0, + "right": 500 + } +} diff --git a/libstormgames b/libstormgames new file mode 160000 index 0000000..9f03de1 --- /dev/null +++ b/libstormgames @@ -0,0 +1 @@ +Subproject commit 9f03de15b88315b2ed9108dc37bdfa1b2d25170f diff --git a/sounds/coin.ogg b/sounds/coin.ogg new file mode 100644 index 0000000..7da0534 --- /dev/null +++ b/sounds/coin.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01e8d5549f912d90a379310b3d9bd4fd2b8c1efd8f8f96ec87fed3687806d638 +size 4596 diff --git a/sounds/footstep.ogg b/sounds/footstep.ogg new file mode 100644 index 0000000..e54f661 --- /dev/null +++ b/sounds/footstep.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71611c99f845fb0f558050112f1c2f908384dd408e02ce2a8792ef72f7a9b07f +size 5358 diff --git a/sounds/game-intro.ogg b/sounds/game-intro.ogg new file mode 100644 index 0000000..91c1b30 --- /dev/null +++ b/sounds/game-intro.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dede7bb3a52be41eb6700f87ca0aec6fee099de07548d9b63b4f562b1d1aaf1 +size 66611 diff --git a/sounds/get_coin.ogg b/sounds/get_coin.ogg new file mode 100644 index 0000000..9e0b1ff --- /dev/null +++ b/sounds/get_coin.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b6c7e8baae9ffe4813433ed4a26269291c6310312cb0d094870fbe4c5fc27ea +size 8961 diff --git a/sounds/goblin.ogg b/sounds/goblin.ogg new file mode 100644 index 0000000..7f22878 --- /dev/null +++ b/sounds/goblin.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:525ed7e10a0bee26aae3ef3e4c75dfd20ca2e7eee40c24c26e022dd0ab55cb48 +size 15232 diff --git a/sounds/grave.ogg b/sounds/grave.ogg new file mode 100644 index 0000000..7e8f128 --- /dev/null +++ b/sounds/grave.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff0a3ffba13be71a6db1a7b913a808f6974229fcd814a689c82714b286c1f5fe +size 117118 diff --git a/sounds/jump.ogg b/sounds/jump.ogg new file mode 100644 index 0000000..2468b6e --- /dev/null +++ b/sounds/jump.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b81014ad4fedd89d7846d42689cdd026630ec52ae9a228458ab0733fe9aa6e4 +size 5612 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/level.py b/src/level.py new file mode 100644 index 0000000..dcad680 --- /dev/null +++ b/src/level.py @@ -0,0 +1,76 @@ +from libstormgames import * +from src.player import Player +from src.object import Object + +class Level: + def __init__(self, levelData, sounds): + self.sounds = sounds + self.objects = [] + self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"]) + + # Load objects from level data + for obj in levelData["objects"]: + gameObject = Object( + obj["x"], + obj["y"], + obj["sound"], + isStatic=obj.get("static", True), + isCollectible=obj.get("collectible", False), + isHazard=obj.get("hazard", False) + ) + self.objects.append(gameObject) + + def update_audio(self): + # Update all object sounds based on player position + for obj in self.objects: + if not obj.isActive: + continue + + # For non-static objects like goblins, ensure continuous sound + if not obj.isStatic: + # If channel doesn't exist or sound stopped playing, restart it + if obj.channel is None or not obj.channel.get_busy(): + obj.channel = obj_play(self.sounds, obj.soundName, self.player.xPos, obj.xPos) + else: + # Original logic for static objects + if obj.channel is None: + obj.channel = obj_play(self.sounds, obj.soundName, self.player.xPos, obj.xPos) + + # Update position-based audio for all objects + if obj.channel is not None: + obj.channel = obj_update(obj.channel, self.player.xPos, obj.xPos) + + def handle_collisions(self): + for obj in self.objects: + if not obj.isActive: + continue + + # Single-tile collision detection (more precise) + collision_threshold = 0.5 # Half a tile width from center + distance = abs(self.player.xPos - obj.xPos) + + if distance <= collision_threshold: + if obj.isCollectible: + # Coins must be collected while jumping + if self.player.isJumping: + self.sounds[f'get_{obj.soundName}'].play() + speak(f"Collected {obj.soundName}") + obj.isActive = False + obj_stop(obj.channel) + self.player.collectedItems.append(obj.soundName) + elif obj.isHazard: + # Only affect player if they're not jumping (for ground-based hazards) + if not self.player.isJumping: + if obj.soundName == "grave": + self.sounds['grave'].play() + speak("You fell into a pit!") + elif obj.soundName == "goblin": + self.sounds['goblin'].play() + speak("A goblin got you!") + else: # Other hazards + self.sounds[obj.soundName].play() + speak(f"A {obj.soundName} got you!") + + # Apply knockback for any hazard hit + knockback = 2 + self.player.xPos = self.player.xPos - knockback if self.player.facingRight else self.player.xPos + knockback diff --git a/src/object.py b/src/object.py new file mode 100644 index 0000000..264be71 --- /dev/null +++ b/src/object.py @@ -0,0 +1,10 @@ +class Object: + def __init__(self, xPos, yPos, soundName, isStatic=True, isCollectible=False, isHazard=False): + self.xPos = xPos + self.yPos = yPos + self.soundName = soundName + self.isStatic = isStatic + self.isCollectible = isCollectible + self.isHazard = isHazard + self.channel = None # For tracking the sound channel + self.isActive = True diff --git a/src/player.py b/src/player.py new file mode 100644 index 0000000..115d391 --- /dev/null +++ b/src/player.py @@ -0,0 +1,13 @@ +class Player: + def __init__(self, xPos, yPos): + self.xPos = xPos + self.yPos = yPos + self.moveSpeed = 0.05 + self.jumpDuration = 1000 # Jump duration in milliseconds + self.jumpStartTime = 0 + self.isJumping = False + self.facingRight = True + self.collectedItems = [] + # Track distance for footsteps + self.distanceSinceLastStep = 0 + self.stepDistance = 0.5 # Play a step sound every 0.5 units moved diff --git a/wicked_quest.py b/wicked_quest.py new file mode 100644 index 0000000..ce59446 --- /dev/null +++ b/wicked_quest.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +import json +import os +from libstormgames import * +from src.level import Level +from src.object import Object +from src.player import Player + + +class WickedQuest: + def __init__(self): + self.sounds = initialize_gui("Wicked Quest") + self.currentLevel = None + + def load_level(self, levelNumber): + levelFile = f"levels/{levelNumber}.json" + try: + with open(levelFile, 'r') as f: + levelData = json.load(f) + self.currentLevel = Level(levelData, self.sounds) + speak(f"Level {levelNumber} loaded") + except FileNotFoundError: + speak("Level not found") + return False + return True + + def handle_input(self): + keys = pygame.key.get_pressed() + player = self.currentLevel.player + + # Calculate current speed based on jumping state + currentSpeed = player.moveSpeed * 1.5 if player.isJumping else player.moveSpeed + + # Track movement distance for this frame + movement = 0 + + # Horizontal movement + if keys[pygame.K_a]: # Left + movement = currentSpeed + player.xPos -= currentSpeed + player.facingRight = False + elif keys[pygame.K_d]: # Right + movement = currentSpeed + player.xPos += currentSpeed + player.facingRight = True + + # Play footstep sounds if moving and not jumping + if movement > 0 and not player.isJumping: + player.distanceSinceLastStep += movement + if player.distanceSinceLastStep >= player.stepDistance: + self.sounds['footstep'].play() + player.distanceSinceLastStep = 0 + + # Handle jumping + currentTime = pygame.time.get_ticks() + + # Start jump + if keys[pygame.K_w] and not player.isJumping: + player.isJumping = True + player.jumpStartTime = currentTime + self.sounds['jump'].play() + + # Check if jump should end + if player.isJumping and currentTime - player.jumpStartTime >= player.jumpDuration: + player.isJumping = False + # Reset step distance tracking after landing + player.distanceSinceLastStep = 0 + + def game_loop(self): + clock = pygame.time.Clock() + + while True: + if check_for_exit(): + return + + self.handle_input() + + # Update audio positioning and handle collisions + self.currentLevel.update_audio() + self.currentLevel.handle_collisions() + + clock.tick(60) # 60 FPS + + def run(self): + while True: + choice = game_menu(self.sounds, "play", "instructions", "learn_sounds", "credits", "donate") + + if choice == "play": + if self.load_level(1): + self.game_loop() + +if __name__ == "__main__": + game = WickedQuest() + game.run()