diff --git a/Toby Doom Launcher.py b/Toby Doom Launcher.py index bcd1afe..01f71c0 100755 --- a/Toby Doom Launcher.py +++ b/Toby Doom Launcher.py @@ -1459,8 +1459,10 @@ class DoomLauncher(QMainWindow): mapFiles = ["None"] # Start with None option mapsDir = self.gamePath / "Addons/MAPS" if mapsDir.exists(): - mapFiles.extend([p.name for p in mapsDir.glob("*.wad") - if p.name not in ["TobyDeathArena_V2-0.wad"]]) + # Include both .wad and .pk3 files for maps + 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 opMDK = self.gamePath / "OpMDK.wad" @@ -1524,11 +1526,12 @@ class DoomLauncher(QMainWindow): if fullPath.exists(): gameFiles.append(str(fullPath)) - # Add optional files last + # Add optional files last (only first one found, for priority) for optFile in config.get('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 gameFlags = config.get('flags', []) @@ -1762,6 +1765,19 @@ class DoomLauncher(QMainWindow): mainLayout.addWidget(QLabel("Narration Style:")) 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 # Single Player singlePlayerLayout = QHBoxLayout() @@ -1907,6 +1923,121 @@ class DoomLauncher(QMainWindow): else: # Update speech handler state directly 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 + # 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 + + # Check speechd + try: + import speechd + available_providers.append("speechd") + 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): """Populate the game selection combo box""" @@ -2325,8 +2456,10 @@ class DoomLauncher(QMainWindow): mapFiles = ["None"] # Start with None option mapsDir = self.gamePath / "Addons/MAPS" if mapsDir.exists(): - mapFiles.extend([p.name for p in mapsDir.glob("*.wad") - if p.name not in ["TobyDeathArena_V2-0.wad"]]) + # Include both .wad and .pk3 files for maps + 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 opMDK = self.gamePath / "OpMDK.wad" @@ -2406,11 +2539,12 @@ class DoomLauncher(QMainWindow): if fullPath.exists(): gameFiles.append(str(fullPath)) - # Add optional files last + # Add optional files last (only first one found, for priority) for optFile in config.get('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 gameFlags = config.get('flags', []) diff --git a/TobyCustom/' b/TobyCustom/' deleted file mode 100644 index d056ea6..0000000 --- a/TobyCustom/' +++ /dev/null @@ -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 diff --git a/TobyCustom/ArmyOfDarknessDoom.json b/TobyCustom/ArmyOfDarknessDoom.json index ec03732..8fcfd37 100644 --- a/TobyCustom/ArmyOfDarknessDoom.json +++ b/TobyCustom/ArmyOfDarknessDoom.json @@ -14,6 +14,7 @@ "aoddoom1.wad" ], "optional_files": [ + "DOOM Metal X IDKFA Soundtrack.pk3", "DoomMetalVol7.wad", "DoomMetalVol6.wad" ], diff --git a/TobyCustom/DoomGuyVersesTheDaleks.json b/TobyCustom/DoomGuyVersesTheDaleks.json index 919306f..e981f88 100644 --- a/TobyCustom/DoomGuyVersesTheDaleks.json +++ b/TobyCustom/DoomGuyVersesTheDaleks.json @@ -17,6 +17,7 @@ "DoomguyVsTheDaleks_V1.1.wad" ], "optional_files": [ + "DOOM Metal X IDKFA Soundtrack.pk3", "DoomMetalVol7.wad", "DoomMetalVol6.wad" ], diff --git a/TobyCustom/Project_Brutality.json b/TobyCustom/Project_Brutality.json index 0fd9ba7..a8f4e56 100644 --- a/TobyCustom/Project_Brutality.json +++ b/TobyCustom/Project_Brutality.json @@ -17,6 +17,7 @@ "Project_Brutality.pk3" ], "optional_files": [ + "DOOM Metal X IDKFA Soundtrack.pk3", "DoomMetalVol7.wad", "DoomMetalVol6.wad" ], diff --git a/TobyCustom/Project_BrutalityLatest.json b/TobyCustom/Project_BrutalityLatest.json index 0acb2e9..8a4c3e4 100644 --- a/TobyCustom/Project_BrutalityLatest.json +++ b/TobyCustom/Project_BrutalityLatest.json @@ -15,6 +15,7 @@ "Project_Brutality-Latest.pk3" ], "optional_files": [ + "DOOM Metal X IDKFA Soundtrack.pk3", "DoomMetalVol7.wad", "DoomMetalVol6.wad" ],