Stats tracking updated. Work on skull storms. Enemy death sound added for goblins. Various updates and fixes.
This commit is contained in:
118
levels/2.json
Normal file
118
levels/2.json
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"level_id": 2,
|
||||||
|
"name": "The Graveyard",
|
||||||
|
"description": "The mausoleum led to an ancient graveyard. Watch out for falling skulls!",
|
||||||
|
"player_start": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"x": 5,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [15, 17],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 25,
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 4,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"movement_range": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 35,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [45, 60],
|
||||||
|
"y": 12,
|
||||||
|
"type": "skull_storm",
|
||||||
|
"damage": 3,
|
||||||
|
"maximum_skulls": 2,
|
||||||
|
"frequency": {
|
||||||
|
"min": 3,
|
||||||
|
"max": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 55,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [65, 67],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 75,
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 5,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"movement_range": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 85,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 95,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [105, 107],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 120,
|
||||||
|
"y": 0,
|
||||||
|
"type": "catapult",
|
||||||
|
"direction": -1,
|
||||||
|
"fire_interval": 5000,
|
||||||
|
"range": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [130, 160],
|
||||||
|
"y": 15,
|
||||||
|
"type": "skull_storm",
|
||||||
|
"damage": 4,
|
||||||
|
"maximum_skulls": 3,
|
||||||
|
"frequency": {
|
||||||
|
"min": 2,
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"boundaries": {
|
||||||
|
"left": 0,
|
||||||
|
"right": 170
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule libstormgames updated: 658709ebce...80fe2caff3
BIN
sounds/falling_skull2.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/falling_skull2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/goblin_dies.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/goblin_dies.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/player_takes_damage.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/player_takes_damage.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -3,8 +3,9 @@ from src.object import Object
|
|||||||
from src.powerup import PowerUp
|
from src.powerup import PowerUp
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
class CoffinObject(Object):
|
class CoffinObject(Object):
|
||||||
def __init__(self, x, y, sounds):
|
def __init__(self, x, y, sounds, level):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
x, y, "coffin",
|
x, y, "coffin",
|
||||||
isStatic=True,
|
isStatic=True,
|
||||||
@@ -12,6 +13,7 @@ class CoffinObject(Object):
|
|||||||
isHazard=False
|
isHazard=False
|
||||||
)
|
)
|
||||||
self.sounds = sounds
|
self.sounds = sounds
|
||||||
|
self.level = level # Store level reference
|
||||||
self.is_broken = False
|
self.is_broken = False
|
||||||
self.dropped_item = None
|
self.dropped_item = None
|
||||||
|
|
||||||
@@ -20,6 +22,8 @@ class CoffinObject(Object):
|
|||||||
if not self.is_broken:
|
if not self.is_broken:
|
||||||
self.is_broken = True
|
self.is_broken = True
|
||||||
self.sounds['coffin_shatter'].play()
|
self.sounds['coffin_shatter'].play()
|
||||||
|
self.level.player.stats.update_stat('Coffins broken', 1)
|
||||||
|
self.level.player.stats.update_stat('Coffins remaining', -1)
|
||||||
|
|
||||||
# Stop the ongoing coffin sound
|
# Stop the ongoing coffin sound
|
||||||
if self.channel:
|
if self.channel:
|
||||||
|
|||||||
17
src/enemy.py
17
src/enemy.py
@@ -4,18 +4,19 @@ import pygame
|
|||||||
|
|
||||||
|
|
||||||
class Enemy(Object):
|
class Enemy(Object):
|
||||||
def __init__(self, xRange, y, enemyType, sounds, **kwargs):
|
def __init__(self, xRange, y, enemyType, sounds, level, **kwargs):
|
||||||
# Initialize base object properties
|
# Initialize base object properties
|
||||||
super().__init__(
|
super().__init__(
|
||||||
xRange,
|
xRange,
|
||||||
y,
|
y,
|
||||||
f"{enemyType}", # Base sound for ambient noise
|
f"{enemyType}", # Base sound
|
||||||
isStatic=False,
|
isStatic=False,
|
||||||
isHazard=True
|
isHazard=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enemy specific properties
|
# Enemy specific properties
|
||||||
self.enemyType = enemyType
|
self.enemyType = enemyType
|
||||||
|
self.level = level
|
||||||
self.health = kwargs.get('health', 5) # Default 5 HP
|
self.health = kwargs.get('health', 5) # Default 5 HP
|
||||||
self.damage = kwargs.get('damage', 1) # Default 1 damage
|
self.damage = kwargs.get('damage', 1) # Default 1 damage
|
||||||
self.attackRange = kwargs.get('attack_range', 1) # Default 1 tile range
|
self.attackRange = kwargs.get('attack_range', 1) # Default 1 tile range
|
||||||
@@ -106,7 +107,7 @@ class Enemy(Object):
|
|||||||
self.sounds[attackSound].play()
|
self.sounds[attackSound].play()
|
||||||
# Deal damage to player
|
# Deal damage to player
|
||||||
player.set_health(player.get_health() - self.damage)
|
player.set_health(player.get_health() - self.damage)
|
||||||
speak(f"The {self.enemyType} hits you!")
|
self.sounds['player_takes_damage'].play()
|
||||||
|
|
||||||
def take_damage(self, amount):
|
def take_damage(self, amount):
|
||||||
"""Handle enemy taking damage"""
|
"""Handle enemy taking damage"""
|
||||||
@@ -120,7 +121,11 @@ class Enemy(Object):
|
|||||||
if self.channel:
|
if self.channel:
|
||||||
obj_stop(self.channel)
|
obj_stop(self.channel)
|
||||||
self.channel = None
|
self.channel = None
|
||||||
# Play death sound if available
|
# Play death sound if available using positional audio
|
||||||
deathSound = f"{self.enemyType}_death"
|
deathSound = f"{self.enemyType}_dies"
|
||||||
if deathSound in self.sounds:
|
if deathSound in self.sounds:
|
||||||
self.sounds[deathSound].play()
|
self.channel = obj_play(self.sounds, deathSound, self.level.player.xPos, self.xPos, loop=False)
|
||||||
|
|
||||||
|
# Update stats
|
||||||
|
self.level.player.stats.update_stat('Enemies killed', 1)
|
||||||
|
self.level.player.stats.update_stat('Enemies remaining', -1)
|
||||||
|
|||||||
23
src/level.py
23
src/level.py
@@ -72,7 +72,8 @@ class Level:
|
|||||||
coffin = CoffinObject(
|
coffin = CoffinObject(
|
||||||
xPos[0],
|
xPos[0],
|
||||||
obj["y"],
|
obj["y"],
|
||||||
self.sounds
|
self.sounds,
|
||||||
|
self # Pass level reference
|
||||||
)
|
)
|
||||||
self.objects.append(coffin)
|
self.objects.append(coffin)
|
||||||
# Check if this is an enemy
|
# Check if this is an enemy
|
||||||
@@ -82,6 +83,7 @@ class Level:
|
|||||||
obj["y"],
|
obj["y"],
|
||||||
obj["enemy_type"],
|
obj["enemy_type"],
|
||||||
self.sounds,
|
self.sounds,
|
||||||
|
self, # Pass level reference
|
||||||
health=obj.get("health", 5),
|
health=obj.get("health", 5),
|
||||||
damage=obj.get("damage", 1),
|
damage=obj.get("damage", 1),
|
||||||
attack_range=obj.get("attack_range", 1),
|
attack_range=obj.get("attack_range", 1),
|
||||||
@@ -99,6 +101,10 @@ class Level:
|
|||||||
zombie_spawn_chance=obj.get("zombie_spawn_chance", 0)
|
zombie_spawn_chance=obj.get("zombie_spawn_chance", 0)
|
||||||
)
|
)
|
||||||
self.objects.append(gameObject)
|
self.objects.append(gameObject)
|
||||||
|
enemyCount = len(self.enemies)
|
||||||
|
coffinCount = sum(1 for obj in self.objects if hasattr(obj, 'is_broken'))
|
||||||
|
player.stats.update_stat('Enemies remaining', enemyCount)
|
||||||
|
player.stats.update_stat('Coffins remaining', coffinCount)
|
||||||
|
|
||||||
def update_audio(self):
|
def update_audio(self):
|
||||||
"""Update all audio and entity state."""
|
"""Update all audio and entity state."""
|
||||||
@@ -126,6 +132,7 @@ class Level:
|
|||||||
obj.yPos,
|
obj.yPos,
|
||||||
"zombie",
|
"zombie",
|
||||||
self.sounds,
|
self.sounds,
|
||||||
|
self, # Pass the level reference
|
||||||
health=3,
|
health=3,
|
||||||
damage=10,
|
damage=10,
|
||||||
attack_range=1
|
attack_range=1
|
||||||
@@ -235,13 +242,19 @@ class Level:
|
|||||||
self.sounds[f'get_{obj.soundName}'].play()
|
self.sounds[f'get_{obj.soundName}'].play()
|
||||||
obj.collect_at_position(currentPos)
|
obj.collect_at_position(currentPos)
|
||||||
self.player.collectedItems.append(obj.soundName)
|
self.player.collectedItems.append(obj.soundName)
|
||||||
|
self.player.stats.update_stat('Items collected', 1)
|
||||||
if obj.soundName == "coin":
|
if obj.soundName == "coin":
|
||||||
self.player._coins += 1
|
self.player._coins += 1
|
||||||
|
self.player.stats.update_stat('Bone dust', 1)
|
||||||
elif obj.isHazard and not self.player.isJumping:
|
elif obj.isHazard and not self.player.isJumping:
|
||||||
self.sounds[obj.soundName].play()
|
if not self.player.isInvincible:
|
||||||
speak("You fell in an open grave!")
|
self.sounds[obj.soundName].play()
|
||||||
self.player.set_health(0)
|
speak("You fell in an open grave!")
|
||||||
return False
|
self.player.set_health(0)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# When invincible, treat it like a successful jump over the grave
|
||||||
|
pass
|
||||||
|
|
||||||
# Handle boundaries
|
# Handle boundaries
|
||||||
if self.player.xPos < self.leftBoundary:
|
if self.player.xPos < self.leftBoundary:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import pygame
|
import pygame
|
||||||
from libstormgames import *
|
from libstormgames import *
|
||||||
|
from src.stat_tracker import StatTracker
|
||||||
from src.weapon import Weapon
|
from src.weapon import Weapon
|
||||||
|
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ class Player:
|
|||||||
self._lives = 1
|
self._lives = 1
|
||||||
self.distanceSinceLastStep = 0
|
self.distanceSinceLastStep = 0
|
||||||
self.stepDistance = 0.5
|
self.stepDistance = 0.5
|
||||||
|
self.stats = StatTracker()
|
||||||
|
|
||||||
# Inventory system
|
# Inventory system
|
||||||
self.inventory = []
|
self.inventory = []
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ class SkullStorm(Object):
|
|||||||
fallProgress = timeElapsed / skull['fall_duration']
|
fallProgress = timeElapsed / skull['fall_duration']
|
||||||
currentY = self.yPos * (1 - fallProgress)
|
currentY = self.yPos * (1 - fallProgress)
|
||||||
|
|
||||||
if skull['channel'] is None or not skull['channel'].get_busy():
|
skull['channel'] = play_random_falling(
|
||||||
skull['channel'] = play_random_falling(
|
self.sounds,
|
||||||
self.sounds,
|
'falling_skull',
|
||||||
'falling_skull',
|
player.xPos,
|
||||||
player.xPos,
|
skull['x'],
|
||||||
skull['x'],
|
self.yPos,
|
||||||
self.yPos,
|
currentY,
|
||||||
currentY
|
existingChannel=skull['channel']
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if we should spawn a new skull
|
# Check if we should spawn a new skull
|
||||||
if (len(self.activeSkulls) < self.maxSkulls and
|
if (len(self.activeSkulls) < self.maxSkulls and
|
||||||
|
|||||||
44
src/stat_tracker.py
Normal file
44
src/stat_tracker.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
class StatTracker:
|
||||||
|
def __init__(self):
|
||||||
|
# Base dictionary for tracking stats
|
||||||
|
self.total = {
|
||||||
|
'Bone dust': 0,
|
||||||
|
'Enemies killed': 0,
|
||||||
|
'Enemies remaining': 0,
|
||||||
|
'Coffins broken': 0,
|
||||||
|
'Coffins remaining': 0,
|
||||||
|
'Items collected': 0,
|
||||||
|
'Total time': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create level stats from total (shallow copy is fine here)
|
||||||
|
self.level = self.total.copy()
|
||||||
|
|
||||||
|
self.total['levelsCompleted'] = 0
|
||||||
|
|
||||||
|
def reset_level(self):
|
||||||
|
"""Reset level stats based on variable type"""
|
||||||
|
for key in self.level:
|
||||||
|
if isinstance(self.level[key], (int, float)):
|
||||||
|
self.level[key] = 0
|
||||||
|
elif isinstance(self.level[key], str):
|
||||||
|
self.level[key] = ""
|
||||||
|
elif isinstance(self.level[key], list):
|
||||||
|
self.level[key] = []
|
||||||
|
elif self.level[key] is None:
|
||||||
|
self.level[key] = None
|
||||||
|
|
||||||
|
def update_stat(self, statName, value=1, levelOnly=False):
|
||||||
|
"""Update a stat in both level and total (unless levelOnly is True)"""
|
||||||
|
if statName in self.level:
|
||||||
|
self.level[statName] += value
|
||||||
|
if not levelOnly and statName in self.total:
|
||||||
|
self.total[statName] += value
|
||||||
|
|
||||||
|
def get_level_stat(self, statName):
|
||||||
|
"""Get a level stat"""
|
||||||
|
return self.level.get(statName, 0)
|
||||||
|
|
||||||
|
def get_total_stat(self, statName):
|
||||||
|
"""Get a total stat"""
|
||||||
|
return self.total.get(statName, 0)
|
||||||
131
wicked_quest.py
131
wicked_quest.py
@@ -9,22 +9,42 @@ from src.player import Player
|
|||||||
|
|
||||||
class WickedQuest:
|
class WickedQuest:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize game and load sounds."""
|
||||||
self.sounds = initialize_gui("Wicked Quest")
|
self.sounds = initialize_gui("Wicked Quest")
|
||||||
self.currentLevel = None
|
self.currentLevel = None
|
||||||
|
self.lastThrowTime = 0
|
||||||
|
self.throwDelay = 250
|
||||||
|
self.player = None # Will be initialized when first level loads
|
||||||
|
|
||||||
def load_level(self, levelNumber):
|
def load_level(self, levelNumber):
|
||||||
|
"""Load a level from its JSON file."""
|
||||||
levelFile = f"levels/{levelNumber}.json"
|
levelFile = f"levels/{levelNumber}.json"
|
||||||
try:
|
try:
|
||||||
with open(levelFile, 'r') as f:
|
with open(levelFile, 'r') as f:
|
||||||
levelData = json.load(f)
|
levelData = json.load(f)
|
||||||
self.currentLevel = Level(levelData, self.sounds)
|
|
||||||
speak(f"Level {levelNumber} loaded")
|
# Create player if this is the first level
|
||||||
|
if self.player is None:
|
||||||
|
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"], self.sounds)
|
||||||
|
else:
|
||||||
|
# Just update player position for new level
|
||||||
|
self.player.xPos = levelData["player_start"]["x"]
|
||||||
|
self.player.yPos = levelData["player_start"]["y"]
|
||||||
|
|
||||||
|
# Pass existing player to new level
|
||||||
|
self.currentLevel = Level(levelData, self.sounds, self.player)
|
||||||
|
|
||||||
|
# Announce level details
|
||||||
|
levelIntro = f"Level {levelData['level_id']}, {levelData['name']}. {levelData['description']}"
|
||||||
|
messagebox(levelIntro)
|
||||||
|
|
||||||
|
return True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
speak("Level not found")
|
speak("Level not found")
|
||||||
return False
|
return False
|
||||||
return True
|
|
||||||
|
|
||||||
def handle_input(self):
|
def handle_input(self):
|
||||||
|
"""Process keyboard input for player actions."""
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
player = self.currentLevel.player
|
player = self.currentLevel.player
|
||||||
currentTime = pygame.time.get_ticks()
|
currentTime = pygame.time.get_ticks()
|
||||||
@@ -45,17 +65,20 @@ class WickedQuest:
|
|||||||
player.xPos += currentSpeed
|
player.xPos += currentSpeed
|
||||||
player.facingRight = True
|
player.facingRight = True
|
||||||
|
|
||||||
|
# Status queries
|
||||||
if keys[pygame.K_c]:
|
if keys[pygame.K_c]:
|
||||||
speak(f"{player.get_coins()} coins")
|
speak(f"{player.get_coins()} coins")
|
||||||
|
|
||||||
if keys[pygame.K_h]:
|
if keys[pygame.K_h]:
|
||||||
speak(f"{player.get_health()} HP")
|
speak(f"{player.get_health()} HP")
|
||||||
|
|
||||||
if keys[pygame.K_l]:
|
if keys[pygame.K_l]:
|
||||||
speak(f"{player.get_lives()} lives")
|
speak(f"{player.get_lives()} lives")
|
||||||
|
if keys[pygame.K_j]: # Check jack o'lanterns
|
||||||
if keys[pygame.K_f]: # Throw projectile
|
speak(f"{player.get_jack_o_lanterns()} jack o'lanterns")
|
||||||
self.currentLevel.throw_projectile()
|
if keys[pygame.K_f]:
|
||||||
|
currentTime = pygame.time.get_ticks()
|
||||||
|
if currentTime - self.lastThrowTime >= self.throwDelay:
|
||||||
|
self.currentLevel.throw_projectile()
|
||||||
|
self.lastThrowTime = currentTime
|
||||||
|
|
||||||
# Handle attack with either CTRL key
|
# Handle attack with either CTRL key
|
||||||
if (keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]) and player.start_attack(currentTime):
|
if (keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]) and player.start_attack(currentTime):
|
||||||
@@ -81,45 +104,103 @@ class WickedQuest:
|
|||||||
# Reset step distance tracking after landing
|
# Reset step distance tracking after landing
|
||||||
player.distanceSinceLastStep = 0
|
player.distanceSinceLastStep = 0
|
||||||
|
|
||||||
|
def display_level_stats(self, timeTaken):
|
||||||
|
"""Display level completion statistics."""
|
||||||
|
# Convert time from milliseconds to minutes:seconds
|
||||||
|
minutes = timeTaken // 60000
|
||||||
|
seconds = (timeTaken % 60000) // 1000
|
||||||
|
|
||||||
|
# Update time in stats
|
||||||
|
self.currentLevel.player.stats.update_stat('Total time', timeTaken, levelOnly=True)
|
||||||
|
|
||||||
|
report = [f"Level {self.currentLevel.levelId} Complete!"]
|
||||||
|
report.append(f"Time taken: {minutes} minutes and {seconds} seconds")
|
||||||
|
|
||||||
|
# Add all level stats
|
||||||
|
for key in self.currentLevel.player.stats.level:
|
||||||
|
if key != 'Total time': # Skip time since we already displayed it
|
||||||
|
report.append(f"{key}: {self.currentLevel.player.stats.get_level_stat(key)}")
|
||||||
|
|
||||||
|
pygame.mixer.stop()
|
||||||
|
display_text(report)
|
||||||
|
self.currentLevel.player.stats.reset_level()
|
||||||
|
|
||||||
|
def display_game_over(self, timeTaken):
|
||||||
|
"""Display game over screen with statistics."""
|
||||||
|
minutes = timeTaken // 60000
|
||||||
|
seconds = (timeTaken % 60000) // 1000
|
||||||
|
|
||||||
|
report = ["Game Over!"]
|
||||||
|
report.append(f"Time taken: {minutes} minutes and {seconds} seconds")
|
||||||
|
|
||||||
|
# Add all total stats
|
||||||
|
for key in self.currentLevel.player.stats.total:
|
||||||
|
if key not in ['Total time', 'levelsCompleted']: # Skip these
|
||||||
|
report.append(f"Total {key}: {self.currentLevel.player.stats.get_total_stat(key)}")
|
||||||
|
|
||||||
|
display_text(report)
|
||||||
|
|
||||||
def game_loop(self):
|
def game_loop(self):
|
||||||
|
"""Main game loop handling updates and state changes."""
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
|
startTime = pygame.time.get_ticks()
|
||||||
while self.currentLevel.player.get_health() > 0 and self.currentLevel.player.get_lives() > 0:
|
currentLevelNum = 1
|
||||||
|
|
||||||
|
while True:
|
||||||
currentTime = pygame.time.get_ticks()
|
currentTime = pygame.time.get_ticks()
|
||||||
|
|
||||||
if check_for_exit():
|
if check_for_exit():
|
||||||
return
|
return
|
||||||
|
|
||||||
# Update player state (including power-ups)
|
# Update game state
|
||||||
self.currentLevel.player.update(currentTime)
|
self.currentLevel.player.update(currentTime)
|
||||||
|
|
||||||
self.handle_input()
|
self.handle_input()
|
||||||
|
|
||||||
# Update audio positioning and handle collisions
|
|
||||||
self.currentLevel.update_audio()
|
self.currentLevel.update_audio()
|
||||||
self.currentLevel.handle_collisions()
|
|
||||||
|
# Handle combat and projectiles
|
||||||
# Handle combat interactions
|
|
||||||
self.currentLevel.handle_combat(currentTime)
|
self.currentLevel.handle_combat(currentTime)
|
||||||
|
|
||||||
# Update projectiles
|
|
||||||
self.currentLevel.handle_projectiles(currentTime)
|
self.currentLevel.handle_projectiles(currentTime)
|
||||||
|
|
||||||
|
# Check for death first
|
||||||
|
if self.currentLevel.player.get_health() <= 0:
|
||||||
|
if self.currentLevel.player.get_lives() <= 0:
|
||||||
|
# Game over
|
||||||
|
pygame.mixer.stop()
|
||||||
|
self.display_game_over(pygame.time.get_ticks() - startTime)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Handle collisions and check level completion
|
||||||
|
if self.currentLevel.handle_collisions(): # Changed from elif to if
|
||||||
|
# Level completed
|
||||||
|
self.display_level_stats(pygame.time.get_ticks() - startTime)
|
||||||
|
|
||||||
|
# Try to load next level
|
||||||
|
currentLevelNum += 1
|
||||||
|
if self.load_level(currentLevelNum):
|
||||||
|
# Reset timer for new level
|
||||||
|
startTime = pygame.time.get_ticks()
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# No more levels - game complete!
|
||||||
|
messagebox("Congratulations! You've completed all available levels!")
|
||||||
|
self.display_game_over(pygame.time.get_ticks() - startTime)
|
||||||
|
return
|
||||||
|
|
||||||
clock.tick(60) # 60 FPS
|
clock.tick(60) # 60 FPS
|
||||||
|
|
||||||
# Player died or ran out of lives
|
|
||||||
speak("Game Over")
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""Main game loop with menu system."""
|
||||||
while True:
|
while True:
|
||||||
choice = game_menu(self.sounds, "play", "instructions", "learn_sounds", "credits", "donate", "exit")
|
choice = game_menu(self.sounds, "play", "instructions", "learn_sounds", "credits", "donate", "exit")
|
||||||
|
|
||||||
if choice == "exit":
|
if choice == "exit":
|
||||||
exit_game()
|
exit_game()
|
||||||
elif choice == "play":
|
elif choice == "play":
|
||||||
|
self.player = None # Reset player for new game
|
||||||
if self.load_level(1):
|
if self.load_level(1):
|
||||||
self.game_loop()
|
self.game_loop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
game = WickedQuest()
|
game = WickedQuest()
|
||||||
game.run()
|
game.run()
|
||||||
|
|||||||
Reference in New Issue
Block a user