This is for you if:
- The Ultima VII font is hard to read at modern resolutions, and you would rather listen to the conversations.
- You want Ultima VII to work as an audio game for the dialogue: every NPC line spoken, with the speaker's name first (
Iolo: ...). - You want the long books and scrolls read to you instead of paging through them.
- You want a text log of everything said in a conversation, to read back later.
What the patch does
Conversations
Each page of an NPC's speech is spoken as it appears on screen. Clicking on to the next page cuts the voice off and starts the next page, so you never wait for a long line to finish. The first page is spoken with the NPC's name prefix (Iolo: ...); nameless NPCs get no prefix, and the Avatar's lines carry the name you chose.
Exult's conversation code only knows the face it is showing, not who owns it. The name is resolved one level up, where usecode shows an NPC's face, and passed down with the face.
Books, scrolls and signs
The visible pages are read when a book or scroll opens and each time you turn the page. Exult repaints these windows often, so a page that was already spoken is not repeated. Rune signs, plaques and gravestones are also expanded and read aloud when examined. Closing the window stops the voice.
Clean text
The game's text carries markup meant for its fonts: * for a page break, ~, ^ for capitals, @ for quote marks, and font-mapped bytes. These are turned into plain sentences before they reach the speech engine.
A history in the terminal
Every line is printed to standard output as TTS: <line>, voice on or off. Start Exult from a terminal and you get a readable transcript; exult | tee talk.log keeps it.
An on/off switch
The Audio options screen gets a three-way Read aloud toggle (No / Yes / Yes + signs), saved in exult.cfg. It says "Read aloud (no command)" if the speech command cannot be found.
posix_spawn, not a shell, so quotes and symbols in the text cannot break it. It runs in its own process group and is stopped by signalling that group — no other speech programs on your system are touched.Build Exult with the patch
The patch is against upstream Exult commit 97984e9e0cbb4ccacaab0249da9c51b533818f74 (2026-10-29). It uses POSIX process calls; on other platforms the voice is simply off.
git clone https://github.com/exult/exult.git
cd exult
git checkout 97984e9e0cbb4ccacaab0249da9c51b533818f74
git apply /path/to/exult-tts/exult_tts.patch
autoreconf -i && ./configure && make
sudo make install
Set up the voice
Exult runs the command in config/audio/tts/command with the line as its only argument. By default that is tts_wrapper in Exult's data directory (/usr/local/share/exult after the install above). Any program that speaks its first argument and exits will do.
The included tts_wrapper uses Piper:
- Unpack a Piper binary from the Piper releases into
~/.local/share/piper/. - Download a voice — the
.onnxand its.onnx.json— from piper-voices into~/.local/share/piper/models/. The default isen_GB-jenny_dioco-medium. - Install the wrapper:
sudo install -m 755 tts_wrapper /usr/local/share/exult/tts_wrapper.
| variable | default |
|---|---|
EXULT_TTS_PIPER_DIR | ~/.local/share/piper |
EXULT_TTS_PIPER | $EXULT_TTS_PIPER_DIR/piper |
EXULT_TTS_MODEL | en_GB-jenny_dioco-medium |
EXULT_TTS_VOICE | $EXULT_TTS_PIPER_DIR/models/$EXULT_TTS_MODEL.onnx |
EXULT_TTS_HISTORY | unset; a file to append every spoken line to |
At startup Exult logs TTS: using <path>, or TTS: <path> not executable, voice off.
Tested on
Ubuntu 26.04.1 LTS with Exult 97984e9 and Ultima VII game data: conversations read aloud with the speaker's name, and the Read aloud switch in Audio options. The patch applies cleanly to a fresh checkout of that commit and builds, and the included standalone test (make check) covers the name prefix, markup cleanup, cut-off and book repaint handling.
Books and scrolls were not tried in game; they are covered only by make check. The sudo make install layout above was not itself run; the game was played from a local build with the wrapper in its data directory.
Not affiliated with the Exult team and not submitted upstream. The patch carries Exult's licence (GPLv2).