Initial commit.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -0,0 +1,5 @@
|
||||
__pycache__/
|
||||
*\.pyc
|
||||
sounds/rec.sh
|
||||
*.flac
|
||||
*.wav
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "libstormgames"]
|
||||
path = libstormgames
|
||||
url = https://git.stormux.org/storm/libstormgames
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Submodule
+1
Submodule libstormgames added at 9f03de15b8
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user