Initial commit.
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
*\.pyc
|
||||||
|
sounds/rec.sh
|
||||||
|
*.flac
|
||||||
|
*.wav
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "libstormgames"]
|
||||||
|
path = libstormgames
|
||||||
|
url = https://git.stormux.org/storm/libstormgames
|
36
levels/1.json
Normal file
36
levels/1.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
1
libstormgames
Submodule
1
libstormgames
Submodule
Submodule libstormgames added at 9f03de15b8
BIN
sounds/coin.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/coin.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/footstep.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/footstep.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/game-intro.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/game-intro.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/get_coin.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/get_coin.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/goblin.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/goblin.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/grave.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/grave.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/jump.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/jump.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
76
src/level.py
Normal file
76
src/level.py
Normal file
@@ -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
|
10
src/object.py
Normal file
10
src/object.py
Normal file
@@ -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
|
13
src/player.py
Normal file
13
src/player.py
Normal file
@@ -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
|
94
wicked_quest.py
Normal file
94
wicked_quest.py
Normal file
@@ -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