Updates to libstormgames submodule. Updated the game to use the menu improvements. Started work on survival mode.
This commit is contained in:
Submodule libstormgames updated: ca2d0d34bd...dcd204e476
@@ -1,10 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import pygame
|
||||
from os.path import isdir, join
|
||||
from libstormgames import speak
|
||||
from libstormgames import speak, instruction_menu
|
||||
|
||||
def get_available_games():
|
||||
"""Get list of available game directories in levels folder.
|
||||
@@ -13,12 +14,21 @@ def get_available_games():
|
||||
list: List of game directory names
|
||||
"""
|
||||
try:
|
||||
return [d for d in os.listdir("levels") if isdir(join("levels", d))]
|
||||
# Handle PyInstaller path issues
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
# Running as PyInstaller executable
|
||||
base_path = sys._MEIPASS
|
||||
else:
|
||||
# Running as script
|
||||
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
levels_path = os.path.join(base_path, "levels")
|
||||
return [d for d in os.listdir(levels_path) if isdir(join(levels_path, d))]
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
|
||||
def selection_menu(sounds, *options):
|
||||
"""Display level selection menu.
|
||||
"""Display level selection menu using instruction_menu.
|
||||
|
||||
Args:
|
||||
sounds (dict): Dictionary of loaded sound effects
|
||||
@@ -27,70 +37,7 @@ def selection_menu(sounds, *options):
|
||||
Returns:
|
||||
str: Selected option or None if cancelled
|
||||
"""
|
||||
loop = True
|
||||
pygame.mixer.stop()
|
||||
i = 0
|
||||
j = -1
|
||||
|
||||
# Clear any pending events
|
||||
pygame.event.clear()
|
||||
|
||||
speak("Select an adventure")
|
||||
time.sleep(1.0)
|
||||
|
||||
while loop:
|
||||
if i != j:
|
||||
speak(options[i])
|
||||
j = i
|
||||
|
||||
pygame.event.pump()
|
||||
event = pygame.event.wait()
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
return None
|
||||
|
||||
if event.key == pygame.K_DOWN and i < len(options) - 1:
|
||||
i = i + 1
|
||||
try:
|
||||
sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
|
||||
if event.key == pygame.K_UP and i > 0:
|
||||
i = i - 1
|
||||
try:
|
||||
sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
|
||||
if event.key == pygame.K_HOME and i != 0:
|
||||
i = 0
|
||||
try:
|
||||
sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
|
||||
if event.key == pygame.K_END and i != len(options) - 1:
|
||||
i = len(options) - 1
|
||||
try:
|
||||
sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
|
||||
if event.key == pygame.K_RETURN:
|
||||
try:
|
||||
sounds['menu-select'].play()
|
||||
time.sleep(sounds['menu-select'].get_length())
|
||||
except:
|
||||
pass
|
||||
return options[i]
|
||||
elif event.type == pygame.QUIT:
|
||||
return None
|
||||
|
||||
pygame.event.pump()
|
||||
event = pygame.event.clear()
|
||||
time.sleep(0.001)
|
||||
return instruction_menu(sounds, "Select an adventure", *options)
|
||||
|
||||
def select_game(sounds):
|
||||
"""Display game selection menu and return chosen game.
|
||||
@@ -134,4 +81,14 @@ def get_level_path(gameDir, levelNum):
|
||||
"""
|
||||
if gameDir is None:
|
||||
raise ValueError("gameDir cannot be None")
|
||||
return os.path.join("levels", gameDir, f"{levelNum}.json")
|
||||
|
||||
# Handle PyInstaller path issues
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
# Running as PyInstaller executable
|
||||
base_path = sys._MEIPASS
|
||||
else:
|
||||
# Running as script
|
||||
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
level_path = os.path.join(base_path, "levels", gameDir, f"{levelNum}.json")
|
||||
return level_path
|
||||
|
13
src/level.py
13
src/level.py
@@ -40,12 +40,13 @@ class Level:
|
||||
# Pass footstep sound to player
|
||||
self.player.set_footstep_sound(self.footstepSound)
|
||||
|
||||
# Level intro message
|
||||
levelIntro = f"Level {levelData['level_id']}, {levelData['name']}. "
|
||||
if self.isLocked:
|
||||
levelIntro += "This is a boss level. You must defeat all enemies before you can advance. "
|
||||
levelIntro += levelData['description']
|
||||
messagebox(levelIntro)
|
||||
# Level intro message (skip for survival mode)
|
||||
if levelData['level_id'] != 999: # 999 is survival mode
|
||||
levelIntro = f"Level {levelData['level_id']}, {levelData['name']}. "
|
||||
if self.isLocked:
|
||||
levelIntro += "This is a boss level. You must defeat all enemies before you can advance. "
|
||||
levelIntro += levelData['description']
|
||||
messagebox(levelIntro)
|
||||
|
||||
# Handle level music
|
||||
try:
|
||||
|
224
src/survival_generator.py
Normal file
224
src/survival_generator.py
Normal file
@@ -0,0 +1,224 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import copy
|
||||
from src.game_selection import get_level_path
|
||||
|
||||
|
||||
class SurvivalGenerator:
|
||||
def __init__(self, gamePack):
|
||||
"""Initialize the survival generator for a specific game pack.
|
||||
|
||||
Args:
|
||||
gamePack (str): Name of the game pack directory
|
||||
"""
|
||||
self.gamePack = gamePack
|
||||
self.levelData = {}
|
||||
self.objectTemplates = []
|
||||
self.enemyTemplates = []
|
||||
self.collectibleTemplates = []
|
||||
self.hazardTemplates = []
|
||||
self.ambientSounds = []
|
||||
self.footstepSounds = []
|
||||
self.loadLevelData()
|
||||
self.parseTemplates()
|
||||
|
||||
def loadLevelData(self):
|
||||
"""Load all level JSON files from the game pack."""
|
||||
levelFiles = []
|
||||
packPath = os.path.join("levels", self.gamePack)
|
||||
|
||||
if not os.path.exists(packPath):
|
||||
raise FileNotFoundError(f"Game pack '{self.gamePack}' not found")
|
||||
|
||||
# Get all JSON files in the pack directory
|
||||
for file in os.listdir(packPath):
|
||||
if file.endswith('.json') and file[0].isdigit():
|
||||
levelFiles.append(file)
|
||||
|
||||
# Load each level file
|
||||
for levelFile in levelFiles:
|
||||
levelPath = os.path.join(packPath, levelFile)
|
||||
with open(levelPath, 'r') as f:
|
||||
levelNum = int(levelFile.split('.')[0])
|
||||
self.levelData[levelNum] = json.load(f)
|
||||
|
||||
def parseTemplates(self):
|
||||
"""Parse all level data to extract object templates by type."""
|
||||
for levelNum, data in self.levelData.items():
|
||||
# Store ambience and footstep sounds
|
||||
if 'ambience' in data:
|
||||
self.ambientSounds.append(data['ambience'])
|
||||
if 'footstep_sound' in data:
|
||||
self.footstepSounds.append(data['footstep_sound'])
|
||||
|
||||
# Parse objects
|
||||
for obj in data.get('objects', []):
|
||||
objCopy = copy.deepcopy(obj)
|
||||
|
||||
# Categorize objects
|
||||
if 'enemy_type' in obj:
|
||||
self.enemyTemplates.append(objCopy)
|
||||
elif obj.get('collectible', False) or obj.get('sound') == 'bone_dust':
|
||||
self.collectibleTemplates.append(objCopy)
|
||||
elif obj.get('type') in ['skull_storm', 'catapult', 'grasping_hands']:
|
||||
self.hazardTemplates.append(objCopy)
|
||||
else:
|
||||
self.objectTemplates.append(objCopy)
|
||||
|
||||
def generate_survival_level(self, difficultyLevel=1, segmentLength=100):
|
||||
"""Generate an endless survival level segment.
|
||||
|
||||
Args:
|
||||
difficultyLevel (int): Current difficulty level (increases over time)
|
||||
segmentLength (int): Length of this level segment
|
||||
|
||||
Returns:
|
||||
dict: Generated level data
|
||||
"""
|
||||
# Base level structure
|
||||
levelData = {
|
||||
"level_id": 999, # Special ID for survival mode
|
||||
"name": f"Wave {difficultyLevel}",
|
||||
"description": "", # Empty description to avoid automatic messagebox
|
||||
"player_start": {"x": 0, "y": 0},
|
||||
"objects": [],
|
||||
"boundaries": {"left": 0, "right": segmentLength},
|
||||
"ambience": "Escaping the Grave.ogg", # Will be overridden below
|
||||
"footstep_sound": "footstep_stone" # Will be overridden below
|
||||
}
|
||||
|
||||
# Choose random music and footstep from any level
|
||||
randomLevel = random.choice(list(self.levelData.values()))
|
||||
if 'ambience' in randomLevel and randomLevel['ambience']:
|
||||
levelData["ambience"] = randomLevel['ambience']
|
||||
if 'footstep_sound' in randomLevel and randomLevel['footstep_sound']:
|
||||
levelData["footstep_sound"] = randomLevel['footstep_sound']
|
||||
|
||||
# Calculate spawn rates based on difficulty
|
||||
collectibleDensity = max(0.1, 0.3 - (difficultyLevel * 0.02)) # Fewer collectibles over time
|
||||
enemyDensity = min(0.8, 0.2 + (difficultyLevel * 0.05)) # More enemies over time
|
||||
hazardDensity = min(0.4, 0.1 + (difficultyLevel * 0.03)) # More hazards over time
|
||||
objectDensity = max(0.1, 0.2 - (difficultyLevel * 0.01)) # Fewer misc objects over time
|
||||
|
||||
# Generate objects across the segment
|
||||
currentX = 10 # Start placing objects at x=10
|
||||
|
||||
while currentX < segmentLength - 10:
|
||||
# Determine what to place based on probability
|
||||
rand = random.random()
|
||||
|
||||
if rand < collectibleDensity and self.collectibleTemplates:
|
||||
obj = self.place_collectible(currentX, difficultyLevel)
|
||||
currentX += random.randint(8, 15)
|
||||
elif rand < collectibleDensity + enemyDensity and self.enemyTemplates:
|
||||
obj = self.place_enemy(currentX, difficultyLevel)
|
||||
currentX += random.randint(15, 25)
|
||||
elif rand < collectibleDensity + enemyDensity + hazardDensity and self.hazardTemplates:
|
||||
obj = self.place_hazard(currentX, difficultyLevel)
|
||||
currentX += random.randint(20, 35)
|
||||
elif rand < collectibleDensity + enemyDensity + hazardDensity + objectDensity and self.objectTemplates:
|
||||
obj = self.place_object(currentX, difficultyLevel)
|
||||
currentX += random.randint(12, 20)
|
||||
else:
|
||||
currentX += random.randint(5, 15)
|
||||
continue
|
||||
|
||||
if obj:
|
||||
levelData["objects"].append(obj)
|
||||
|
||||
return levelData
|
||||
|
||||
def place_collectible(self, xPos, difficultyLevel):
|
||||
"""Place a collectible at the given position."""
|
||||
template = random.choice(self.collectibleTemplates)
|
||||
obj = copy.deepcopy(template)
|
||||
|
||||
# Handle x_range vs single x
|
||||
if 'x_range' in obj:
|
||||
rangeSize = obj['x_range'][1] - obj['x_range'][0]
|
||||
obj['x_range'] = [xPos, xPos + rangeSize]
|
||||
else:
|
||||
obj['x'] = xPos
|
||||
|
||||
return obj
|
||||
|
||||
def place_enemy(self, xPos, difficultyLevel):
|
||||
"""Place an enemy at the given position with scaled difficulty."""
|
||||
# Filter out boss enemies for early waves
|
||||
bossEnemies = ['witch', 'boogie_man', 'revenant', 'ghost', 'headless_horseman']
|
||||
|
||||
if difficultyLevel < 3: # Waves 1-2: no bosses
|
||||
availableEnemies = [e for e in self.enemyTemplates
|
||||
if e.get('enemy_type') not in bossEnemies]
|
||||
elif difficultyLevel < 5: # Waves 3-4: exclude the hardest bosses
|
||||
hardestBosses = ['revenant', 'ghost', 'headless_horseman']
|
||||
availableEnemies = [e for e in self.enemyTemplates
|
||||
if e.get('enemy_type') not in hardestBosses]
|
||||
else: # Wave 5+: all enemies allowed
|
||||
availableEnemies = self.enemyTemplates
|
||||
|
||||
# Fallback to all enemies if filtering removed everything
|
||||
if not availableEnemies:
|
||||
availableEnemies = self.enemyTemplates
|
||||
|
||||
template = random.choice(availableEnemies)
|
||||
obj = copy.deepcopy(template)
|
||||
|
||||
# Scale enemy stats based on difficulty
|
||||
healthMultiplier = 1 + (difficultyLevel * 0.15)
|
||||
damageMultiplier = 1 + (difficultyLevel * 0.1)
|
||||
|
||||
obj['health'] = int(obj.get('health', 1) * healthMultiplier)
|
||||
obj['damage'] = max(1, int(obj.get('damage', 1) * damageMultiplier))
|
||||
|
||||
# Handle x_range vs single x
|
||||
if 'x_range' in obj:
|
||||
rangeSize = obj['x_range'][1] - obj['x_range'][0]
|
||||
obj['x_range'] = [xPos, xPos + rangeSize]
|
||||
else:
|
||||
obj['x'] = xPos
|
||||
|
||||
return obj
|
||||
|
||||
def place_hazard(self, xPos, difficultyLevel):
|
||||
"""Place a hazard at the given position with scaled difficulty."""
|
||||
template = random.choice(self.hazardTemplates)
|
||||
obj = copy.deepcopy(template)
|
||||
|
||||
# Scale hazard difficulty
|
||||
if obj.get('type') == 'skull_storm':
|
||||
obj['damage'] = max(1, int(obj.get('damage', 1) * (1 + difficultyLevel * 0.1)))
|
||||
obj['maximum_skulls'] = min(6, obj.get('maximum_skulls', 2) + (difficultyLevel // 3))
|
||||
elif obj.get('type') == 'catapult':
|
||||
obj['fire_interval'] = max(1000, obj.get('fire_interval', 4000) - (difficultyLevel * 100))
|
||||
|
||||
# Handle x_range vs single x
|
||||
if 'x_range' in obj:
|
||||
rangeSize = obj['x_range'][1] - obj['x_range'][0]
|
||||
obj['x_range'] = [xPos, xPos + rangeSize]
|
||||
else:
|
||||
obj['x'] = xPos
|
||||
|
||||
return obj
|
||||
|
||||
def place_object(self, xPos, difficultyLevel):
|
||||
"""Place a misc object at the given position."""
|
||||
template = random.choice(self.objectTemplates)
|
||||
obj = copy.deepcopy(template)
|
||||
|
||||
# Handle graves - increase zombie spawn chance with difficulty
|
||||
if obj.get('type') == 'grave':
|
||||
baseChance = obj.get('zombie_spawn_chance', 0)
|
||||
obj['zombie_spawn_chance'] = min(50, baseChance + (difficultyLevel * 2))
|
||||
|
||||
# Handle x_range vs single x
|
||||
if 'x_range' in obj:
|
||||
rangeSize = obj['x_range'][1] - obj['x_range'][0]
|
||||
obj['x_range'] = [xPos, xPos + rangeSize]
|
||||
else:
|
||||
obj['x'] = xPos
|
||||
|
||||
return obj
|
180
wicked_quest.py
180
wicked_quest.py
@@ -10,6 +10,7 @@ from src.object import Object
|
||||
from src.player import Player
|
||||
from src.game_selection import select_game, get_level_path
|
||||
from src.save_manager import SaveManager
|
||||
from src.survival_generator import SurvivalGenerator
|
||||
|
||||
|
||||
class WickedQuest:
|
||||
@@ -24,6 +25,9 @@ class WickedQuest:
|
||||
self.currentGame = None
|
||||
self.runLock = False # Toggle behavior of the run keys
|
||||
self.saveManager = SaveManager()
|
||||
self.survivalGenerator = None
|
||||
self.survivalWave = 1
|
||||
self.survivalScore = 0
|
||||
|
||||
def load_level(self, levelNumber):
|
||||
"""Load a level from its JSON file."""
|
||||
@@ -87,7 +91,7 @@ class WickedQuest:
|
||||
return errors
|
||||
|
||||
def load_game_menu(self):
|
||||
"""Display load game menu with available saves"""
|
||||
"""Display load game menu with available saves using instruction_menu"""
|
||||
save_files = self.saveManager.get_save_files()
|
||||
|
||||
if not save_files:
|
||||
@@ -101,45 +105,17 @@ class WickedQuest:
|
||||
|
||||
options.append("Cancel")
|
||||
|
||||
# Show menu
|
||||
currentIndex = 0
|
||||
lastSpoken = -1
|
||||
# Use instruction_menu for consistent behavior
|
||||
choice = instruction_menu(self.sounds, "Select a save file to load:", *options)
|
||||
|
||||
messagebox("Select a save file to load:")
|
||||
|
||||
while True:
|
||||
if currentIndex != lastSpoken:
|
||||
speak(options[currentIndex])
|
||||
lastSpoken = currentIndex
|
||||
|
||||
event = pygame.event.wait()
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
return None
|
||||
elif event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(options) - 1:
|
||||
currentIndex += 1
|
||||
try:
|
||||
self.sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
elif event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0:
|
||||
currentIndex -= 1
|
||||
try:
|
||||
self.sounds['menu-move'].play()
|
||||
except:
|
||||
pass
|
||||
elif event.key == pygame.K_RETURN:
|
||||
try:
|
||||
self.sounds['menu-select'].play()
|
||||
except:
|
||||
pass
|
||||
|
||||
if currentIndex == len(options) - 1: # Cancel
|
||||
return None
|
||||
else:
|
||||
return save_files[currentIndex]
|
||||
|
||||
pygame.event.clear()
|
||||
if choice == "Cancel" or choice is None:
|
||||
return None
|
||||
else:
|
||||
# Find the corresponding save file
|
||||
for save_file in save_files:
|
||||
if save_file['display_name'] == choice:
|
||||
return save_file
|
||||
return None
|
||||
|
||||
def auto_save(self):
|
||||
"""Automatically save the game if player has enough bone dust"""
|
||||
@@ -448,10 +424,15 @@ class WickedQuest:
|
||||
display_text(errorLines)
|
||||
continue
|
||||
if self.currentGame:
|
||||
self.player = None # Reset player for new game
|
||||
self.gameStartTime = pygame.time.get_ticks()
|
||||
if self.load_level(1):
|
||||
self.game_loop()
|
||||
# Ask player to choose game mode
|
||||
mode_choice = game_mode_menu(self.sounds)
|
||||
if mode_choice == "campaign":
|
||||
self.player = None # Reset player for new game
|
||||
self.gameStartTime = pygame.time.get_ticks()
|
||||
if self.load_level(1):
|
||||
self.game_loop()
|
||||
elif mode_choice == "survival":
|
||||
self.start_survival_mode()
|
||||
elif choice == "high_scores":
|
||||
board = Scoreboard()
|
||||
scores = board.get_high_scores()
|
||||
@@ -465,6 +446,119 @@ class WickedQuest:
|
||||
elif choice == "learn_sounds":
|
||||
choice = learn_sounds(self.sounds)
|
||||
|
||||
def start_survival_mode(self):
|
||||
"""Initialize and start survival mode."""
|
||||
self.survivalGenerator = SurvivalGenerator(self.currentGame)
|
||||
self.survivalWave = 1
|
||||
self.survivalScore = 0
|
||||
self.player = Player(0, 0, self.sounds)
|
||||
self.gameStartTime = pygame.time.get_ticks()
|
||||
|
||||
# Generate first survival segment
|
||||
levelData = self.survivalGenerator.generate_survival_level(self.survivalWave, 300)
|
||||
self.currentLevel = Level(levelData, self.sounds, self.player)
|
||||
|
||||
messagebox(f"Survival Mode - Wave {self.survivalWave}! Survive as long as you can!")
|
||||
self.survival_loop()
|
||||
|
||||
def survival_loop(self):
|
||||
"""Main survival mode game loop with endless level generation."""
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while True:
|
||||
currentTime = pygame.time.get_ticks()
|
||||
pygame.event.pump()
|
||||
|
||||
# Handle events
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
messagebox(f"Survival ended! Final score: {self.survivalScore}")
|
||||
return
|
||||
elif event.key in [pygame.K_CAPSLOCK, pygame.K_TAB]:
|
||||
self.runLock = not self.runLock
|
||||
speak("Run lock " + ("enabled." if self.runLock else "disabled."))
|
||||
elif event.key == pygame.K_BACKSPACE:
|
||||
pause_game()
|
||||
elif event.type == pygame.QUIT:
|
||||
exit_game()
|
||||
|
||||
# Update game state (following main game_loop pattern)
|
||||
self.currentLevel.player.update(currentTime)
|
||||
self.handle_input()
|
||||
self.currentLevel.update_audio()
|
||||
|
||||
# Handle combat and projectiles
|
||||
self.currentLevel.handle_combat(currentTime)
|
||||
self.currentLevel.handle_projectiles(currentTime)
|
||||
|
||||
# Handle collisions (including collecting items)
|
||||
self.currentLevel.handle_collisions()
|
||||
|
||||
# Check if player reached end of segment - generate new one
|
||||
if self.player.xPos >= self.currentLevel.rightBoundary - 20:
|
||||
self.advance_survival_wave()
|
||||
|
||||
# Check for death first (following main game loop pattern)
|
||||
if self.currentLevel.player.get_health() <= 0:
|
||||
if self.currentLevel.player.get_lives() <= 0:
|
||||
# Game over - stop all sounds
|
||||
pygame.mixer.stop()
|
||||
messagebox(f"Game Over! Final wave: {self.survivalWave}, Final score: {self.survivalScore}")
|
||||
return
|
||||
else:
|
||||
# Player died but has lives left - respawn
|
||||
pygame.mixer.stop()
|
||||
self.currentLevel.player._health = self.currentLevel.player._maxHealth
|
||||
# Reset player position to beginning of current segment
|
||||
self.player.xPos = 10
|
||||
|
||||
# Update score based on survival time
|
||||
self.survivalScore += 1
|
||||
|
||||
clock.tick(60) # 60 FPS
|
||||
|
||||
def advance_survival_wave(self):
|
||||
"""Generate next wave/segment for survival mode."""
|
||||
self.survivalWave += 1
|
||||
|
||||
# Clear any lingering projectiles/sounds from previous wave
|
||||
if hasattr(self, 'currentLevel') and self.currentLevel:
|
||||
self.currentLevel.projectiles.clear()
|
||||
pygame.mixer.stop() # Stop any ongoing catapult/enemy sounds
|
||||
|
||||
# Generate new segment
|
||||
segmentLength = min(500, 300 + (self.survivalWave * 20)) # Longer segments over time
|
||||
levelData = self.survivalGenerator.generate_survival_level(self.survivalWave, segmentLength)
|
||||
|
||||
# Preserve player position but shift to start of new segment
|
||||
playerX = 10
|
||||
self.player.xPos = playerX
|
||||
|
||||
# Create new level
|
||||
self.currentLevel = Level(levelData, self.sounds, self.player)
|
||||
|
||||
speak(f"Wave {self.survivalWave}! Difficulty increased!")
|
||||
|
||||
|
||||
def game_mode_menu(sounds):
|
||||
"""Display game mode selection menu using instruction_menu.
|
||||
|
||||
Args:
|
||||
sounds (dict): Dictionary of loaded sound effects
|
||||
|
||||
Returns:
|
||||
str: Selected game mode or None if cancelled
|
||||
"""
|
||||
choice = instruction_menu(sounds, "Select game mode:", "Campaign", "Survival Mode")
|
||||
|
||||
if choice == "Campaign":
|
||||
return "campaign"
|
||||
elif choice == "Survival Mode":
|
||||
return "survival"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
game = WickedQuest()
|
||||
|
@@ -5,7 +5,11 @@ a = Analysis(
|
||||
['wicked_quest.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
datas=[
|
||||
('levels', 'levels'),
|
||||
('sounds', 'sounds'),
|
||||
('libstormgames', 'libstormgames'),
|
||||
],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
Reference in New Issue
Block a user