Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c359c037e1 | |||
| 08be529a33 | |||
| 380bb2ba81 | |||
| 96fc322d6e | |||
| f1e197729a | |||
| 5ac894a6ec | |||
| d906292d5e | |||
| e9ca4f02e0 | |||
| 34c42fa55c | |||
| 1c9d1127d1 |
+407
-103
@@ -23,7 +23,7 @@
|
|||||||
# Update version number here for new releases of Toby Doom Accessibility Mod
|
# Update version number here for new releases of Toby Doom Accessibility Mod
|
||||||
# Example: 8.0 for version 8.0, 8.5 for version 8.5, etc
|
# Example: 8.0 for version 8.0, 8.5 for version 8.5, etc
|
||||||
|
|
||||||
TOBY_VERSION_NUMBER = 8.0
|
TOBY_VERSION_NUMBER = 9.0
|
||||||
|
|
||||||
# DO NOT EDIT ANYTHING BELOW THIS LINE!
|
# DO NOT EDIT ANYTHING BELOW THIS LINE!
|
||||||
# ===================================
|
# ===================================
|
||||||
@@ -126,26 +126,28 @@ if platform.system() == "Windows":
|
|||||||
print(f"Failed to initialize accessible_output2: {e}")
|
print(f"Failed to initialize accessible_output2: {e}")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
else:
|
else:
|
||||||
# Linux/Mac path - prioritize Orca remote controller
|
# Linux/Mac path - prioritize speech-dispatcher first
|
||||||
orca_remote = OrcaRemoteController()
|
try:
|
||||||
if orca_remote.available:
|
import speechd
|
||||||
speechProvider = "orca_remote"
|
spd = speechd.Client()
|
||||||
orca = orca_remote
|
speechProvider = "speechd"
|
||||||
else:
|
except ImportError:
|
||||||
try:
|
# Fall back to Orca remote controller
|
||||||
output = subprocess.check_output(["pgrep", "cthulhu"])
|
orca_remote = OrcaRemoteController()
|
||||||
speechProvider = "cthulhu"
|
if orca_remote.available:
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
speechProvider = "orca_remote"
|
||||||
|
orca = orca_remote
|
||||||
|
else:
|
||||||
|
# Fall back to Cthulhu
|
||||||
try:
|
try:
|
||||||
import accessible_output2.outputs.auto
|
output = subprocess.check_output(["pgrep", "cthulhu"])
|
||||||
s = accessible_output2.outputs.auto.Auto()
|
speechProvider = "cthulhu"
|
||||||
speechProvider = "accessible_output2"
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
except ImportError as e:
|
|
||||||
try:
|
try:
|
||||||
import speechd
|
import accessible_output2.outputs.auto
|
||||||
spd = speechd.Client()
|
s = accessible_output2.outputs.auto.Auto()
|
||||||
speechProvider = "speechd"
|
speechProvider = "accessible_output2"
|
||||||
except ImportError:
|
except ImportError as e:
|
||||||
print("No speech providers found.")
|
print("No speech providers found.")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
@@ -903,6 +905,7 @@ class DoomLauncher(QMainWindow):
|
|||||||
"""Main launcher window for Toby Doom"""
|
"""Main launcher window for Toby Doom"""
|
||||||
|
|
||||||
deathmatchMaps = [
|
deathmatchMaps = [
|
||||||
|
"lobby",
|
||||||
"Com Station (2-4 players)",
|
"Com Station (2-4 players)",
|
||||||
"Warehouse (2-4 players)",
|
"Warehouse (2-4 players)",
|
||||||
"Sector 3 (2-4 players)",
|
"Sector 3 (2-4 players)",
|
||||||
@@ -1347,28 +1350,27 @@ class DoomLauncher(QMainWindow):
|
|||||||
if dialog.exec():
|
if dialog.exec():
|
||||||
values = dialog.get_dialog_values()
|
values = dialog.get_dialog_values()
|
||||||
|
|
||||||
# Set up game files
|
# Get base game files (includes TobyAccMod and all addons)
|
||||||
gameFiles = [
|
gameFiles = self.get_selected_game_files()
|
||||||
str(self.gamePath / f"TobyAccMod_V{self.tobyVersion}.pk3")
|
|
||||||
]
|
# Note: selectedMod is already included in base game files via get_selected_game_files()
|
||||||
|
|
||||||
# Add menu addons
|
|
||||||
menuPath = self.gamePath / "Addons/MENU"
|
|
||||||
if menuPath.exists():
|
|
||||||
gameFiles.extend(str(p) for p in menuPath.glob("Toby*.pk3"))
|
|
||||||
|
|
||||||
# Add selected mod
|
|
||||||
gameFiles.append(selectedMod)
|
|
||||||
|
|
||||||
# Add deathmatch map only if checkbox is checked
|
# Add deathmatch map only if checkbox is checked
|
||||||
if values.get('use_toby_maps', True):
|
if values.get('use_toby_maps', True):
|
||||||
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V1-5.wad")
|
# Add bot spawner menu
|
||||||
|
botSpawnerMenu = str(self.gamePath / "Addons/MENU/TobyBotSpawnerMenu.pk3")
|
||||||
|
if Path(botSpawnerMenu).exists():
|
||||||
|
gameFiles.append(botSpawnerMenu)
|
||||||
|
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V2-0.wad")
|
||||||
if Path(deathMatchMap).exists():
|
if Path(deathMatchMap).exists():
|
||||||
gameFiles.append(deathMatchMap)
|
gameFiles.append(deathMatchMap)
|
||||||
|
|
||||||
# Get deathmatch flags and add map selection
|
# Get deathmatch flags and add map selection
|
||||||
gameFlags = self.get_deathmatch_flags(values)
|
gameFlags = self.get_deathmatch_flags(values)
|
||||||
gameFlags.extend(["-warp", str(mapIndex)])
|
if selectedMap.lower() == "lobby":
|
||||||
|
gameFlags.extend(["+map", "lobby"])
|
||||||
|
else:
|
||||||
|
gameFlags.extend(["-warp", str(mapIndex)])
|
||||||
|
|
||||||
# Check/set freedm.wad as IWAD
|
# Check/set freedm.wad as IWAD
|
||||||
freedmPath = self.find_freedm()
|
freedmPath = self.find_freedm()
|
||||||
@@ -1459,8 +1461,10 @@ class DoomLauncher(QMainWindow):
|
|||||||
mapFiles = ["None"] # Start with None option
|
mapFiles = ["None"] # Start with None option
|
||||||
mapsDir = self.gamePath / "Addons/MAPS"
|
mapsDir = self.gamePath / "Addons/MAPS"
|
||||||
if mapsDir.exists():
|
if mapsDir.exists():
|
||||||
mapFiles.extend([p.name for p in mapsDir.glob("*.wad")
|
# Include both .wad and .pk3 files for maps
|
||||||
if p.name != "TobyDeathArena_V1-5.wad"])
|
for extension in ["*.wad", "*.pk3"]:
|
||||||
|
mapFiles.extend([p.name for p in mapsDir.glob(extension)
|
||||||
|
if p.name not in ["TobyDeathArena_V2-0.wad"]])
|
||||||
|
|
||||||
# Add Operation MDK as special case
|
# Add Operation MDK as special case
|
||||||
opMDK = self.gamePath / "OpMDK.wad"
|
opMDK = self.gamePath / "OpMDK.wad"
|
||||||
@@ -1496,7 +1500,7 @@ class DoomLauncher(QMainWindow):
|
|||||||
if Path(mapPath).exists():
|
if Path(mapPath).exists():
|
||||||
gameFiles.append(mapPath)
|
gameFiles.append(mapPath)
|
||||||
|
|
||||||
if selectedMap == "TobyDoomLevels.wad":
|
if selectedMap == "TobyDoomLevels.pk3":
|
||||||
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
||||||
if musicRenamer.exists():
|
if musicRenamer.exists():
|
||||||
gameFiles.append(str(musicRenamer))
|
gameFiles.append(str(musicRenamer))
|
||||||
@@ -1524,11 +1528,35 @@ class DoomLauncher(QMainWindow):
|
|||||||
if fullPath.exists():
|
if fullPath.exists():
|
||||||
gameFiles.append(str(fullPath))
|
gameFiles.append(str(fullPath))
|
||||||
|
|
||||||
# Add optional files last
|
# Add music files with custom music mod priority
|
||||||
for optFile in config.get('optional_files', []):
|
optional_files = config.get('optional_files', [])
|
||||||
optPath = self.gamePath / optFile
|
music_added = False
|
||||||
if optPath.exists():
|
|
||||||
gameFiles.append(str(optPath))
|
# Check if any optional files are music-related (contain metal/music keywords)
|
||||||
|
has_music_optionals = any('metal' in opt.lower() or 'music' in opt.lower() for opt in optional_files)
|
||||||
|
|
||||||
|
if has_music_optionals:
|
||||||
|
# First try to find custom music mod (highest priority)
|
||||||
|
customMusicMod = self.find_custom_music_mod()
|
||||||
|
if customMusicMod:
|
||||||
|
gameFiles.append(str(customMusicMod))
|
||||||
|
music_added = True
|
||||||
|
else:
|
||||||
|
# Fall back to standard optional files priority
|
||||||
|
for optFile in optional_files:
|
||||||
|
optPath = self.gamePath / optFile
|
||||||
|
if optPath.exists():
|
||||||
|
gameFiles.append(str(optPath))
|
||||||
|
music_added = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Add any remaining non-music optional files
|
||||||
|
if not music_added:
|
||||||
|
for optFile in optional_files:
|
||||||
|
optPath = self.gamePath / optFile
|
||||||
|
if optPath.exists():
|
||||||
|
gameFiles.append(str(optPath))
|
||||||
|
break # Only add the first optional file found
|
||||||
|
|
||||||
# Get any custom flags
|
# Get any custom flags
|
||||||
gameFlags = config.get('flags', [])
|
gameFlags = config.get('flags', [])
|
||||||
@@ -1762,6 +1790,19 @@ class DoomLauncher(QMainWindow):
|
|||||||
mainLayout.addWidget(QLabel("Narration Style:"))
|
mainLayout.addWidget(QLabel("Narration Style:"))
|
||||||
mainLayout.addWidget(self.narrationCombo)
|
mainLayout.addWidget(self.narrationCombo)
|
||||||
|
|
||||||
|
# Speech method selection (only show when TTS is enabled)
|
||||||
|
self.speechMethodCombo = AccessibleComboBox(self)
|
||||||
|
self.speechMethodCombo.setAccessibleName("Speech Method")
|
||||||
|
self.populate_speech_methods()
|
||||||
|
self.speechMethodCombo.currentTextChanged.connect(self.speech_method_changed)
|
||||||
|
|
||||||
|
self.speechMethodLabel = QLabel("Speech Method:")
|
||||||
|
mainLayout.addWidget(self.speechMethodLabel)
|
||||||
|
mainLayout.addWidget(self.speechMethodCombo)
|
||||||
|
|
||||||
|
# Show/hide speech method based on current narration type
|
||||||
|
self.update_speech_method_visibility()
|
||||||
|
|
||||||
# Create button layouts with pairs of launch and generate buttons
|
# Create button layouts with pairs of launch and generate buttons
|
||||||
# Single Player
|
# Single Player
|
||||||
singlePlayerLayout = QHBoxLayout()
|
singlePlayerLayout = QHBoxLayout()
|
||||||
@@ -1907,12 +1948,125 @@ class DoomLauncher(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
# Update speech handler state directly
|
# Update speech handler state directly
|
||||||
self.speechHandler.set_tts_state(value == 2)
|
self.speechHandler.set_tts_state(value == 2)
|
||||||
|
# Update speech method visibility
|
||||||
|
self.update_speech_method_visibility()
|
||||||
|
|
||||||
|
def detect_available_speech_providers(self):
|
||||||
|
"""Detect which speech providers are available on the current platform"""
|
||||||
|
available_providers = []
|
||||||
|
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
# On Windows, only accessible_output2 is used
|
||||||
|
try:
|
||||||
|
import accessible_output2.outputs.auto
|
||||||
|
available_providers.append("accessible_output2")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# On Linux, check each provider in priority order
|
||||||
|
# Check speechd first (highest priority)
|
||||||
|
try:
|
||||||
|
import speechd
|
||||||
|
available_providers.append("speechd")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check Orca remote controller
|
||||||
|
orca_remote = OrcaRemoteController()
|
||||||
|
if orca_remote.available:
|
||||||
|
available_providers.append("orca_remote")
|
||||||
|
|
||||||
|
# Check Cthulhu
|
||||||
|
try:
|
||||||
|
subprocess.check_output(["pgrep", "cthulhu"])
|
||||||
|
available_providers.append("cthulhu")
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check accessible_output2
|
||||||
|
try:
|
||||||
|
import accessible_output2.outputs.auto
|
||||||
|
available_providers.append("accessible_output2")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return available_providers
|
||||||
|
|
||||||
|
def populate_speech_methods(self):
|
||||||
|
"""Populate the speech method combobox with available providers"""
|
||||||
|
available_providers = self.detect_available_speech_providers()
|
||||||
|
|
||||||
|
# Map internal names to user-friendly names
|
||||||
|
provider_names = {
|
||||||
|
"accessible_output2": "Accessible Output2",
|
||||||
|
"orca_remote": "Orca Remote",
|
||||||
|
"cthulhu": "Cthulhu",
|
||||||
|
"speechd": "Speech Dispatcher"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.speechMethodCombo.clear()
|
||||||
|
for provider in available_providers:
|
||||||
|
friendly_name = provider_names.get(provider, provider)
|
||||||
|
self.speechMethodCombo.addItem(friendly_name, provider)
|
||||||
|
|
||||||
|
# Set current selection based on global speech provider
|
||||||
|
current_provider = self.get_current_speech_provider()
|
||||||
|
for i in range(self.speechMethodCombo.count()):
|
||||||
|
if self.speechMethodCombo.itemData(i) == current_provider:
|
||||||
|
self.speechMethodCombo.setCurrentIndex(i)
|
||||||
|
break
|
||||||
|
|
||||||
|
def get_current_speech_provider(self):
|
||||||
|
"""Get the current speech provider being used globally"""
|
||||||
|
global speechProvider
|
||||||
|
return speechProvider
|
||||||
|
|
||||||
|
def update_speech_method_visibility(self):
|
||||||
|
"""Show/hide speech method combobox based on narration type"""
|
||||||
|
is_tts = self.get_narration_type() == 2
|
||||||
|
self.speechMethodLabel.setVisible(is_tts)
|
||||||
|
self.speechMethodCombo.setVisible(is_tts)
|
||||||
|
|
||||||
|
def speech_method_changed(self, text: str):
|
||||||
|
"""Handle speech method combobox changes"""
|
||||||
|
selected_provider = self.speechMethodCombo.currentData()
|
||||||
|
if selected_provider:
|
||||||
|
self.set_speech_provider(selected_provider)
|
||||||
|
|
||||||
|
def set_speech_provider(self, provider: str):
|
||||||
|
"""Update the global speech provider"""
|
||||||
|
global speechProvider, s, spd, orca
|
||||||
|
|
||||||
|
try:
|
||||||
|
if provider == "accessible_output2":
|
||||||
|
import accessible_output2.outputs.auto
|
||||||
|
s = accessible_output2.outputs.auto.Auto()
|
||||||
|
speechProvider = "accessible_output2"
|
||||||
|
elif provider == "orca_remote":
|
||||||
|
orca_remote = OrcaRemoteController()
|
||||||
|
if orca_remote.available:
|
||||||
|
orca = orca_remote
|
||||||
|
speechProvider = "orca_remote"
|
||||||
|
else:
|
||||||
|
QMessageBox.warning(self, "Error", "Orca Remote is not available")
|
||||||
|
return
|
||||||
|
elif provider == "cthulhu":
|
||||||
|
try:
|
||||||
|
subprocess.check_output(["pgrep", "cthulhu"])
|
||||||
|
speechProvider = "cthulhu"
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
QMessageBox.warning(self, "Error", "Cthulhu is not running")
|
||||||
|
return
|
||||||
|
elif provider == "speechd":
|
||||||
|
import speechd
|
||||||
|
spd = speechd.Client()
|
||||||
|
speechProvider = "speechd"
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.warning(self, "Error", f"Failed to initialize {provider}: {e}")
|
||||||
|
|
||||||
def populate_game_list(self):
|
def populate_game_list(self):
|
||||||
"""Populate the game selection combo box"""
|
"""Populate the game selection combo box"""
|
||||||
gameList = [
|
gameList = [
|
||||||
"Toby Demo Map",
|
|
||||||
"Funny Pack - Demo Map",
|
|
||||||
"Classic Doom",
|
"Classic Doom",
|
||||||
"Funny Pack - Classic Doom",
|
"Funny Pack - Classic Doom",
|
||||||
"Toby Doom",
|
"Toby Doom",
|
||||||
@@ -1952,6 +2106,64 @@ class DoomLauncher(QMainWindow):
|
|||||||
|
|
||||||
return shutil.which("gzdoom")
|
return shutil.which("gzdoom")
|
||||||
|
|
||||||
|
def find_custom_music_mod(self):
|
||||||
|
"""Find custom music mod files with flexible pattern matching.
|
||||||
|
|
||||||
|
Looks for files containing "Custom Music Mod" in sequence anywhere in the filename.
|
||||||
|
Supports variations like:
|
||||||
|
- Custom Music Mod
|
||||||
|
- Custom_Music_Mod
|
||||||
|
- CustomMusicMod
|
||||||
|
|
||||||
|
Returns the first matching file found (.pk3 files prioritized), or None if no match exists.
|
||||||
|
"""
|
||||||
|
if not self.gamePath.exists():
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Define the sequence patterns to look for (case-insensitive)
|
||||||
|
sequence_patterns = [
|
||||||
|
["custom", "music", "mod"], # spaces or any separators
|
||||||
|
["custom_music_mod"], # underscores as single token
|
||||||
|
["custommusicmod"] # no separators
|
||||||
|
]
|
||||||
|
|
||||||
|
# Check for .pk3 files first (higher priority), then .wad
|
||||||
|
for extension in [".pk3", ".wad"]:
|
||||||
|
for file_path in self.gamePath.iterdir():
|
||||||
|
if file_path.is_file() and file_path.suffix.lower() == extension:
|
||||||
|
filename_lower = file_path.name.lower()
|
||||||
|
|
||||||
|
# Check each sequence pattern
|
||||||
|
for pattern_sequence in sequence_patterns:
|
||||||
|
if self._contains_sequence(filename_lower, pattern_sequence):
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _contains_sequence(self, text, sequence):
|
||||||
|
"""Check if text contains all words in sequence in order.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to search in (already lowercase)
|
||||||
|
sequence: List of words that must appear in order
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if all words in sequence are found in order, False otherwise
|
||||||
|
"""
|
||||||
|
if len(sequence) == 1:
|
||||||
|
# Single token match (like "custommusicmod")
|
||||||
|
return sequence[0] in text
|
||||||
|
|
||||||
|
# Multi-word sequence match
|
||||||
|
current_pos = 0
|
||||||
|
for word in sequence:
|
||||||
|
pos = text.find(word, current_pos)
|
||||||
|
if pos == -1:
|
||||||
|
return False
|
||||||
|
current_pos = pos + len(word)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def get_addon_files(self, game_type: str = "DOOM", use_funny_pack: bool = False) -> List[str]:
|
def get_addon_files(self, game_type: str = "DOOM", use_funny_pack: bool = False) -> List[str]:
|
||||||
"""Get all addon PK3 files for specified game type
|
"""Get all addon PK3 files for specified game type
|
||||||
|
|
||||||
@@ -1960,28 +2172,54 @@ class DoomLauncher(QMainWindow):
|
|||||||
use_funny_pack: Whether to use the Funny Pack instead of normal monsters
|
use_funny_pack: Whether to use the Funny Pack instead of normal monsters
|
||||||
"""
|
"""
|
||||||
addonFiles = []
|
addonFiles = []
|
||||||
# MENU addons are common to all games
|
|
||||||
menuPath = self.gamePath / "Addons" / "MENU"
|
# Game specific addons first (following bat file order)
|
||||||
if menuPath.exists():
|
|
||||||
addonFiles.extend(str(p) for p in menuPath.glob("Toby*.pk3"))
|
|
||||||
|
|
||||||
# Game specific addons
|
|
||||||
gamePath = self.gamePath / "Addons" / game_type
|
gamePath = self.gamePath / "Addons" / game_type
|
||||||
if gamePath.exists():
|
if gamePath.exists():
|
||||||
if game_type == "HERETIC":
|
if game_type == "HERETIC":
|
||||||
pattern = "TobyHeretic*.pk3"
|
heretic_addons = [
|
||||||
addonFiles.extend(str(p) for p in gamePath.glob(pattern))
|
"TobyHereticWeaponsV8.pk3",
|
||||||
|
"TobyHereticMonsters.pk3",
|
||||||
|
"TobyHereticItemsV8.pk3",
|
||||||
|
"TobyHereticDecorations.pk3",
|
||||||
|
"TobyHereticMenu.wad",
|
||||||
|
"TobyHereticBeacons.pk3"
|
||||||
|
]
|
||||||
|
|
||||||
|
for addon in heretic_addons:
|
||||||
|
addon_path = gamePath / addon
|
||||||
|
if addon_path.exists():
|
||||||
|
addonFiles.append(str(addon_path))
|
||||||
|
|
||||||
elif game_type == "HEXEN":
|
elif game_type == "HEXEN":
|
||||||
pattern = "TobyHexen*.pk3"
|
hexen_addons = [
|
||||||
addonFiles.extend(str(p) for p in gamePath.glob(pattern))
|
"TobyHexenWeapons.pk3",
|
||||||
|
"TobyHexenMonsters.pk3",
|
||||||
|
"TobyHexenItems.pk3",
|
||||||
|
"TobyHexenDecorations.pk3",
|
||||||
|
"TobyHexenMenu.wad"
|
||||||
|
]
|
||||||
|
|
||||||
|
for addon in hexen_addons:
|
||||||
|
addon_path = gamePath / addon
|
||||||
|
if addon_path.exists():
|
||||||
|
addonFiles.append(str(addon_path))
|
||||||
else: # DOOM
|
else: # DOOM
|
||||||
# Add normal Doom addons (but skip monster packs if using Funny Pack)
|
# Add Doom addons in specific order matching bat file
|
||||||
|
doom_addons = [
|
||||||
|
"TobyV8_Guns.pk3",
|
||||||
|
"TobyV7_Monsters.pk3" if not use_funny_pack else None,
|
||||||
|
"TobyV8_Pickups.pk3",
|
||||||
|
"TobyV8_Decorations.pk3"
|
||||||
|
]
|
||||||
|
|
||||||
|
for addon in doom_addons:
|
||||||
|
if addon:
|
||||||
|
addon_path = gamePath / addon
|
||||||
|
if addon_path.exists():
|
||||||
|
addonFiles.append(str(addon_path))
|
||||||
|
|
||||||
if use_funny_pack:
|
if use_funny_pack:
|
||||||
# Only add non-monster Doom addons
|
|
||||||
for pk3Path in gamePath.glob("Toby*.pk3"):
|
|
||||||
if "Monsters" not in pk3Path.name:
|
|
||||||
addonFiles.append(str(pk3Path))
|
|
||||||
|
|
||||||
# Look for Funny Pack in the APRIL folder
|
# Look for Funny Pack in the APRIL folder
|
||||||
aprilPath = self.gamePath / "Addons" / "APRIL"
|
aprilPath = self.gamePath / "Addons" / "APRIL"
|
||||||
funnyPackPath = aprilPath / "Toby_Funny_Pack.pk3"
|
funnyPackPath = aprilPath / "Toby_Funny_Pack.pk3"
|
||||||
@@ -1995,10 +2233,20 @@ class DoomLauncher(QMainWindow):
|
|||||||
"Funny Pack Missing",
|
"Funny Pack Missing",
|
||||||
f"Toby_Funny_Pack.pk3 not found in {aprilPath}. Please make sure it's installed correctly in the Addons/APRIL folder."
|
f"Toby_Funny_Pack.pk3 not found in {aprilPath}. Please make sure it's installed correctly in the Addons/APRIL folder."
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
# Normal behavior for non-Funny Pack games
|
# MENU addons after game addons (following bat file order)
|
||||||
pattern = "Toby*.pk3"
|
menuPath = self.gamePath / "Addons" / "MENU"
|
||||||
addonFiles.extend(str(p) for p in gamePath.glob(pattern))
|
if menuPath.exists():
|
||||||
|
# Add menu files in order
|
||||||
|
menu_addons = [
|
||||||
|
"TobyV9_SimpleMenu.pk3",
|
||||||
|
f"TobySoundDirectory_{game_type}.pk3"
|
||||||
|
]
|
||||||
|
|
||||||
|
for addon in menu_addons:
|
||||||
|
addon_path = menuPath / addon
|
||||||
|
if addon_path.exists():
|
||||||
|
addonFiles.append(str(addon_path))
|
||||||
|
|
||||||
return addonFiles
|
return addonFiles
|
||||||
|
|
||||||
@@ -2015,34 +2263,51 @@ class DoomLauncher(QMainWindow):
|
|||||||
# Determine game type and get corresponding addons
|
# Determine game type and get corresponding addons
|
||||||
if "Heretic" in selectedGame:
|
if "Heretic" in selectedGame:
|
||||||
gameType = "HERETIC"
|
gameType = "HERETIC"
|
||||||
|
elif "Hexen" in selectedGame:
|
||||||
|
gameType = "HEXEN"
|
||||||
|
else: # Doom games
|
||||||
|
gameType = "DOOM"
|
||||||
|
|
||||||
|
# Get game-specific addons first (following bat file order)
|
||||||
|
baseFiles.extend(self.get_addon_files(gameType, isFunnyPack))
|
||||||
|
|
||||||
|
# Then add maps and other game-specific files
|
||||||
|
if "Heretic" in selectedGame:
|
||||||
if "Toby Heretic" in selectedGame:
|
if "Toby Heretic" in selectedGame:
|
||||||
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyHereticLevels.wad"))
|
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyHereticLevels.wad"))
|
||||||
elif "Hexen" in selectedGame:
|
elif "Hexen" in selectedGame:
|
||||||
gameType = "HEXEN"
|
|
||||||
if "Toby Hexen" in selectedGame:
|
if "Toby Hexen" in selectedGame:
|
||||||
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyHexen.pk3"))
|
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyHexen.pk3"))
|
||||||
else: # Doom games
|
else: # Doom games
|
||||||
gameType = "DOOM"
|
if "OperationMDK" in selectedGame:
|
||||||
if "Demo Map" in selectedGame:
|
|
||||||
baseFiles.append(str(self.gamePath / "Addons/MAPS/Toby-Demo-Level.wad"))
|
|
||||||
elif "Toby Doom" in selectedGame:
|
|
||||||
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyDoomLevels.wad"))
|
|
||||||
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
|
||||||
if musicRenamer.exists() and not isFunnyPack:
|
|
||||||
baseFiles.append(str(musicRenamer))
|
|
||||||
elif "OperationMDK" in selectedGame:
|
|
||||||
baseFiles.append(str(self.gamePath / "OpMDK.wad"))
|
baseFiles.append(str(self.gamePath / "OpMDK.wad"))
|
||||||
|
|
||||||
if not isFunnyPack:
|
if "Toby Doom" in selectedGame:
|
||||||
|
baseFiles.append(str(self.gamePath / "Addons/MAPS/TobyDoomLevels.pk3"))
|
||||||
|
|
||||||
|
# Add music files at the end for Doom games
|
||||||
|
if gameType == "DOOM" and not isFunnyPack:
|
||||||
|
# First check for custom music mod patterns (highest priority)
|
||||||
|
customMusicMod = self.find_custom_music_mod()
|
||||||
|
if customMusicMod:
|
||||||
|
baseFiles.append(str(customMusicMod))
|
||||||
|
else:
|
||||||
|
# Fall back to standard metal mod detection
|
||||||
|
metalV10 = self.gamePath / "DOOM Metal X IDKFA Soundtrack.pk3"
|
||||||
metalV7 = self.gamePath / "DoomMetalVol7.wad"
|
metalV7 = self.gamePath / "DoomMetalVol7.wad"
|
||||||
metalV6 = self.gamePath / "DoomMetalVol6.wad"
|
metalV6 = self.gamePath / "DoomMetalVol6.wad"
|
||||||
if metalV7.exists():
|
if metalV10.exists():
|
||||||
|
baseFiles.append(str(metalV10))
|
||||||
|
elif metalV7.exists():
|
||||||
baseFiles.append(str(metalV7))
|
baseFiles.append(str(metalV7))
|
||||||
elif metalV6.exists():
|
elif metalV6.exists():
|
||||||
baseFiles.append(str(metalV6))
|
baseFiles.append(str(metalV6))
|
||||||
|
|
||||||
|
if "Toby Doom" in selectedGame:
|
||||||
|
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
||||||
|
if musicRenamer.exists():
|
||||||
|
baseFiles.append(str(musicRenamer))
|
||||||
|
|
||||||
# Get game-specific addons (but we'll modify if it's Funny Pack)
|
|
||||||
baseFiles.extend(self.get_addon_files(gameType, isFunnyPack))
|
|
||||||
return baseFiles
|
return baseFiles
|
||||||
|
|
||||||
def show_custom_deathmatch_dialog(self):
|
def show_custom_deathmatch_dialog(self):
|
||||||
@@ -2149,28 +2414,27 @@ class DoomLauncher(QMainWindow):
|
|||||||
if dialog.exec():
|
if dialog.exec():
|
||||||
values = dialog.get_dialog_values()
|
values = dialog.get_dialog_values()
|
||||||
|
|
||||||
# Set up game files
|
# Get base game files (includes TobyAccMod and all addons)
|
||||||
gameFiles = [
|
gameFiles = self.get_selected_game_files()
|
||||||
str(self.gamePath / f"TobyAccMod_V{self.tobyVersion}.pk3")
|
|
||||||
]
|
# Note: selectedMod is already included in base game files via get_selected_game_files()
|
||||||
|
|
||||||
# Add menu addons
|
|
||||||
menuPath = self.gamePath / "Addons/MENU"
|
|
||||||
if menuPath.exists():
|
|
||||||
gameFiles.extend(str(p) for p in menuPath.glob("Toby*.pk3"))
|
|
||||||
|
|
||||||
# Add selected mod
|
|
||||||
gameFiles.append(selectedMod)
|
|
||||||
|
|
||||||
# Add deathmatch map only if checkbox is checked
|
# Add deathmatch map only if checkbox is checked
|
||||||
if values.get('use_toby_maps', True):
|
if values.get('use_toby_maps', True):
|
||||||
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V1-5.wad")
|
# Add bot spawner menu
|
||||||
|
botSpawnerMenu = str(self.gamePath / "Addons/MENU/TobyBotSpawnerMenu.pk3")
|
||||||
|
if Path(botSpawnerMenu).exists():
|
||||||
|
gameFiles.append(botSpawnerMenu)
|
||||||
|
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V2-0.wad")
|
||||||
if Path(deathMatchMap).exists():
|
if Path(deathMatchMap).exists():
|
||||||
gameFiles.append(deathMatchMap)
|
gameFiles.append(deathMatchMap)
|
||||||
|
|
||||||
# Get deathmatch flags and add map selection
|
# Get deathmatch flags and add map selection
|
||||||
gameFlags = self.get_deathmatch_flags(values)
|
gameFlags = self.get_deathmatch_flags(values)
|
||||||
gameFlags.extend(["-warp", str(mapIndex)])
|
if selectedMap.lower() == "lobby":
|
||||||
|
gameFlags.extend(["+map", "lobby"])
|
||||||
|
else:
|
||||||
|
gameFlags.extend(["-warp", str(mapIndex)])
|
||||||
|
|
||||||
# Check/set freedm.wad as IWAD
|
# Check/set freedm.wad as IWAD
|
||||||
freedmPath = self.find_freedm()
|
freedmPath = self.find_freedm()
|
||||||
@@ -2288,8 +2552,10 @@ class DoomLauncher(QMainWindow):
|
|||||||
mapFiles = ["None"] # Start with None option
|
mapFiles = ["None"] # Start with None option
|
||||||
mapsDir = self.gamePath / "Addons/MAPS"
|
mapsDir = self.gamePath / "Addons/MAPS"
|
||||||
if mapsDir.exists():
|
if mapsDir.exists():
|
||||||
mapFiles.extend([p.name for p in mapsDir.glob("*.wad")
|
# Include both .wad and .pk3 files for maps
|
||||||
if p.name != "TobyDeathArena_V1-5.wad"])
|
for extension in ["*.wad", "*.pk3"]:
|
||||||
|
mapFiles.extend([p.name for p in mapsDir.glob(extension)
|
||||||
|
if p.name not in ["TobyDeathArena_V2-0.wad"]])
|
||||||
|
|
||||||
# Add Operation MDK as special case
|
# Add Operation MDK as special case
|
||||||
opMDK = self.gamePath / "OpMDK.wad"
|
opMDK = self.gamePath / "OpMDK.wad"
|
||||||
@@ -2339,7 +2605,7 @@ class DoomLauncher(QMainWindow):
|
|||||||
if Path(mapPath).exists():
|
if Path(mapPath).exists():
|
||||||
gameFiles.append(mapPath)
|
gameFiles.append(mapPath)
|
||||||
|
|
||||||
if selectedMap == "TobyDoomLevels.wad":
|
if selectedMap == "TobyDoomLevels.pk3":
|
||||||
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
musicRenamer = self.gamePath / "Toby-Doom-Level-Music-Renamer.pk3"
|
||||||
if musicRenamer.exists():
|
if musicRenamer.exists():
|
||||||
gameFiles.append(str(musicRenamer))
|
gameFiles.append(str(musicRenamer))
|
||||||
@@ -2369,11 +2635,35 @@ class DoomLauncher(QMainWindow):
|
|||||||
if fullPath.exists():
|
if fullPath.exists():
|
||||||
gameFiles.append(str(fullPath))
|
gameFiles.append(str(fullPath))
|
||||||
|
|
||||||
# Add optional files last
|
# Add music files with custom music mod priority
|
||||||
for optFile in config.get('optional_files', []):
|
optional_files = config.get('optional_files', [])
|
||||||
optPath = self.gamePath / optFile
|
music_added = False
|
||||||
if optPath.exists():
|
|
||||||
gameFiles.append(str(optPath))
|
# Check if any optional files are music-related (contain metal/music keywords)
|
||||||
|
has_music_optionals = any('metal' in opt.lower() or 'music' in opt.lower() for opt in optional_files)
|
||||||
|
|
||||||
|
if has_music_optionals:
|
||||||
|
# First try to find custom music mod (highest priority)
|
||||||
|
customMusicMod = self.find_custom_music_mod()
|
||||||
|
if customMusicMod:
|
||||||
|
gameFiles.append(str(customMusicMod))
|
||||||
|
music_added = True
|
||||||
|
else:
|
||||||
|
# Fall back to standard optional files priority
|
||||||
|
for optFile in optional_files:
|
||||||
|
optPath = self.gamePath / optFile
|
||||||
|
if optPath.exists():
|
||||||
|
gameFiles.append(str(optPath))
|
||||||
|
music_added = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Add any remaining non-music optional files
|
||||||
|
if not music_added:
|
||||||
|
for optFile in optional_files:
|
||||||
|
optPath = self.gamePath / optFile
|
||||||
|
if optPath.exists():
|
||||||
|
gameFiles.append(str(optPath))
|
||||||
|
break # Only add the first optional file found
|
||||||
|
|
||||||
# Get any custom flags
|
# Get any custom flags
|
||||||
gameFlags = config.get('flags', [])
|
gameFlags = config.get('flags', [])
|
||||||
@@ -2536,13 +2826,20 @@ class DoomLauncher(QMainWindow):
|
|||||||
|
|
||||||
# Add deathmatch map only if checkbox is checked
|
# Add deathmatch map only if checkbox is checked
|
||||||
if values.get('use_toby_maps', True):
|
if values.get('use_toby_maps', True):
|
||||||
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V1-5.wad")
|
# Add bot spawner menu
|
||||||
|
botSpawnerMenu = str(self.gamePath / "Addons/MENU/TobyBotSpawnerMenu.pk3")
|
||||||
|
if Path(botSpawnerMenu).exists():
|
||||||
|
gameFiles.append(botSpawnerMenu)
|
||||||
|
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V2-0.wad")
|
||||||
if Path(deathMatchMap).exists():
|
if Path(deathMatchMap).exists():
|
||||||
gameFiles.append(deathMatchMap)
|
gameFiles.append(deathMatchMap)
|
||||||
|
|
||||||
gameFlags = self.get_deathmatch_flags(values)
|
gameFlags = self.get_deathmatch_flags(values)
|
||||||
# Add map selection flag
|
# Add map selection flag
|
||||||
gameFlags.extend(["-warp", str(mapIndex)])
|
if selectedMap.lower() == "lobby":
|
||||||
|
gameFlags.extend(["+map", "lobby"])
|
||||||
|
else:
|
||||||
|
gameFlags.extend(["-warp", str(mapIndex)])
|
||||||
|
|
||||||
# Check/set freedm.wad as IWAD
|
# Check/set freedm.wad as IWAD
|
||||||
freedmPath = self.find_freedm()
|
freedmPath = self.find_freedm()
|
||||||
@@ -2630,14 +2927,21 @@ class DoomLauncher(QMainWindow):
|
|||||||
|
|
||||||
# Add deathmatch map only if checkbox is checked
|
# Add deathmatch map only if checkbox is checked
|
||||||
if values.get('use_toby_maps', True):
|
if values.get('use_toby_maps', True):
|
||||||
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V1-5.wad")
|
# Add bot spawner menu
|
||||||
|
botSpawnerMenu = str(self.gamePath / "Addons/MENU/TobyBotSpawnerMenu.pk3")
|
||||||
|
if Path(botSpawnerMenu).exists():
|
||||||
|
gameFiles.append(botSpawnerMenu)
|
||||||
|
deathMatchMap = str(self.gamePath / "Addons/MAPS/TobyDeathArena_V2-0.wad")
|
||||||
if Path(deathMatchMap).exists():
|
if Path(deathMatchMap).exists():
|
||||||
gameFiles.append(deathMatchMap)
|
gameFiles.append(deathMatchMap)
|
||||||
|
|
||||||
gameFlags = self.get_deathmatch_flags(values)
|
gameFlags = self.get_deathmatch_flags(values)
|
||||||
|
|
||||||
# Add map selection flag
|
# Add map selection flag
|
||||||
gameFlags.extend(["-warp", str(mapIndex)])
|
if selectedMap.lower() == "lobby":
|
||||||
|
gameFlags.extend(["+map", "lobby"])
|
||||||
|
else:
|
||||||
|
gameFlags.extend(["-warp", str(mapIndex)])
|
||||||
|
|
||||||
# Check/set freedm.wad as IWAD
|
# Check/set freedm.wad as IWAD
|
||||||
freedmPath = self.find_freedm()
|
freedmPath = self.find_freedm()
|
||||||
|
|||||||
-124
@@ -1,124 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
pushd "$doomPath"
|
|
||||||
|
|
||||||
# Set up the pk3 and wad files
|
|
||||||
gameOption=(
|
|
||||||
"$(find /usr/share/games/ -name 'Project_Brutality-master.pk3')"
|
|
||||||
"${doomPath}/TobyAccMod_V${tobyVersion}.pk3"
|
|
||||||
"${doomPath}/PB-Toby-Compatibility-Addon.pk3"
|
|
||||||
"${doomPath}/Toby-Universal-Pickup-Beacon-Prototype.pk3"
|
|
||||||
"${doomPath}/TobyDeathArena_V1-0.wad"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Death match setup
|
|
||||||
ipAddress="$(dialog --backtitle "Deathmatch Options" \
|
|
||||||
--clear \
|
|
||||||
--no-tags \
|
|
||||||
--ok-label "Join" \
|
|
||||||
--cancel-label "Exit" \
|
|
||||||
--extra-button \
|
|
||||||
--extra-label "Host" \
|
|
||||||
--inputbox "Enter ip or URL, required for join." -1 -1 --stdout)"
|
|
||||||
buttonCode=$?
|
|
||||||
[[ $buttonCode -eq 1 ]] && exit 0
|
|
||||||
if [[ $buttonCode -eq 0 ]]; then
|
|
||||||
if [[ "${#ipAddress}" -lt 3 ]]; then
|
|
||||||
dialog --backtitle "Deathmatch" --clear --msgbox "No ip address or URL given." -1 -1 --stdout
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
flags=('-join' "${ipAddress}")
|
|
||||||
else
|
|
||||||
# List of maps included:
|
|
||||||
maps=(
|
|
||||||
"1" "Com Station (2-4 players)"
|
|
||||||
"2" "Warehouse (2-4 players)"
|
|
||||||
"3" "Sector 3 (2-4 players)"
|
|
||||||
"4" "Dungeon of Doom (2-4 players)"
|
|
||||||
"5" "Ocean Fortress (2-4 players)"
|
|
||||||
"6" "Water Treatment Facility (2-4 players)"
|
|
||||||
"7" "Phobos Base Site 4 (2-4 players)"
|
|
||||||
"8" "Hangar Bay 18 (2-4 players)")
|
|
||||||
# Array of how many players a given map supports in dialog rangebox syntax
|
|
||||||
declare -a mapPlayers=(
|
|
||||||
[1]="2 4"
|
|
||||||
[2]="2 4"
|
|
||||||
[3]="2 4"
|
|
||||||
[4]="2 4"
|
|
||||||
[5]="2 4"
|
|
||||||
[6]="2 4"
|
|
||||||
[7]="2 4"
|
|
||||||
[8]="2 4")
|
|
||||||
map="$(dialog --backtitle "Select Map" \
|
|
||||||
--clear \
|
|
||||||
--no-tags \
|
|
||||||
--cancel-label "Exit" \
|
|
||||||
--ok-label "Next" \
|
|
||||||
--menu "Please select one" 0 0 0 "${maps[@]}" --stdout)"
|
|
||||||
fraglimit="$(dialog --backtitle "Fraglimit" \
|
|
||||||
--clear \
|
|
||||||
--ok-label "Next" \
|
|
||||||
--cancel-label "Exit" \
|
|
||||||
--rangebox "Select Fraglimit" -1 -1 1 500 20 --stdout)"
|
|
||||||
[[ $? -eq 1 ]] && exit 0
|
|
||||||
# Get ip address
|
|
||||||
yourIpAddress="$(curl -4s https://icanhazip.com)"
|
|
||||||
players="$(dialog --backtitle "Host Deathmatch Game" \
|
|
||||||
--clear \
|
|
||||||
--ok-label "Next" \
|
|
||||||
--cancel-label "Exit" \
|
|
||||||
--rangebox "Select number of players. Remember to give them your IP address: ${yourIpAddress}" -1 -1 ${mapPlayers[$map]} --stdout)"
|
|
||||||
[[ $? -eq 1 ]] && exit 0
|
|
||||||
skillLevel="$(dialog --backtitle "Host Deathmatch Game" \
|
|
||||||
--clear \
|
|
||||||
--ok-label "Start" \
|
|
||||||
--cancel-label "Exit" \
|
|
||||||
--extra-button \
|
|
||||||
--extra-label "Bots Only" \
|
|
||||||
--rangebox "Select difficulty. 1 easiest, 5 hardest." -1 -1 1 5 3 --stdout)"
|
|
||||||
code=$?
|
|
||||||
[[ $code -eq 1 ]] && exit 0
|
|
||||||
if [[ $code -eq 3 ]]; then
|
|
||||||
players=1
|
|
||||||
dialog --backtitle "Preparing to Launch" \
|
|
||||||
--msgbox "When the game starts, press \` to open the console. Type addbot, press enter. Repeat addbot for as many bots as you would like. Press \` again to close the console." -1 -1 --stdout
|
|
||||||
fi
|
|
||||||
flags=(
|
|
||||||
'-host' "${players}"
|
|
||||||
'-skill' "${skillLevel}"
|
|
||||||
'-deathmatch'
|
|
||||||
'+set' 'sv_cheats' '1'
|
|
||||||
'+fraglimit' "$fraglimit"
|
|
||||||
'+dmflags' '16384' '+dmflags' '4' '+dmflags' '128' '+dmflags' '4096'
|
|
||||||
'+dmflags2' '512' '+dmflags2' '1024'
|
|
||||||
'-extratic' '-dup' '3'
|
|
||||||
'-warp' "$map"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for and include if present a wad. Some people may not have it.
|
|
||||||
if [[ -e "${doomPath}/DoomMetalVol7.wad" ]]; then
|
|
||||||
gameOption+=" DoomMetalVol7.wad"
|
|
||||||
elif [[ -e "${doomPath}/DoomMetalVol6.wad" ]]; then
|
|
||||||
gameOption+=" DoomMetalVol6.wad"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extend the search for new messages to be read.
|
|
||||||
grepStrings+=('-e' ' died.'
|
|
||||||
'-e' 'Ectoplasmic Surge!'
|
|
||||||
'-e' ' has been '
|
|
||||||
'-e' '^(Armor|Health) boosted!'
|
|
||||||
'-e' 'Lesser demon energy'
|
|
||||||
'-e' '^Found '
|
|
||||||
'-e' 'Got the '
|
|
||||||
'-e' 'Picked up '
|
|
||||||
'-e' '^(Mega|Soul)sphere$'
|
|
||||||
'-e' '^Took '
|
|
||||||
'-e' ' was .*(\.|!)'
|
|
||||||
'-e' '^Vanguard of the gods!$'
|
|
||||||
'-e' "You've found "
|
|
||||||
'-e' 'You (collected|got|found|picked up) ')
|
|
||||||
|
|
||||||
# Launch the game and pipe things to be spoken through speech-dispatcher.
|
|
||||||
# This also leaves the console output intact for people who may want to read it.
|
|
||||||
exec stdbuf -oL ${gzdoom} ${gameOption[@]} "${flags[@]}" | while IFS= read -r l ; do echo "$l" | { grep "${grepStrings[@]}" | grep "${antiGrepStrings[@]}" | sed "${sedStrings[@]}" | spd-say -e ${spd_module} ${spd_pitch} ${spd_rate} ${spd_voice} ${spd_volume} -- > /dev/null 2>&1; }; echo "$l";done
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"aoddoom1.wad"
|
"aoddoom1.wad"
|
||||||
],
|
],
|
||||||
"optional_files": [
|
"optional_files": [
|
||||||
|
"DOOM Metal X IDKFA Soundtrack.pk3",
|
||||||
"DoomMetalVol7.wad",
|
"DoomMetalVol7.wad",
|
||||||
"DoomMetalVol6.wad"
|
"DoomMetalVol6.wad"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"DoomguyVsTheDaleks_V1.1.wad"
|
"DoomguyVsTheDaleks_V1.1.wad"
|
||||||
],
|
],
|
||||||
"optional_files": [
|
"optional_files": [
|
||||||
|
"DOOM Metal X IDKFA Soundtrack.pk3",
|
||||||
"DoomMetalVol7.wad",
|
"DoomMetalVol7.wad",
|
||||||
"DoomMetalVol6.wad"
|
"DoomMetalVol6.wad"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"Project_Brutality.pk3"
|
"Project_Brutality.pk3"
|
||||||
],
|
],
|
||||||
"optional_files": [
|
"optional_files": [
|
||||||
|
"DOOM Metal X IDKFA Soundtrack.pk3",
|
||||||
"DoomMetalVol7.wad",
|
"DoomMetalVol7.wad",
|
||||||
"DoomMetalVol6.wad"
|
"DoomMetalVol6.wad"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"Project_Brutality-Latest.pk3"
|
"Project_Brutality-Latest.pk3"
|
||||||
],
|
],
|
||||||
"optional_files": [
|
"optional_files": [
|
||||||
|
"DOOM Metal X IDKFA Soundtrack.pk3",
|
||||||
"DoomMetalVol7.wad",
|
"DoomMetalVol7.wad",
|
||||||
"DoomMetalVol6.wad"
|
"DoomMetalVol6.wad"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user