Some modifications to the video games Ultima VII: The Black Gate (1992), Ultima VII Part Two: Serpent Isle (1993), Ultima Underworld: The Stygian Abyss (1992), and Ultima Underworld II: Labyrinth of Worlds (1993), written as bits of (mostly 16-bit) x86 assembly, together with a program, written in Java, that patches them into the game's executable.
There is also a website (mentioned below) which can quickly apply pre-assembled versions of the patches, for people not interested in building the patches from source code.
The patches take the form of assembly files written in NASM syntax, and rely on some supporting macros that include metadata in the assembled output files regarding overall placement in the patched executable file as well as the locations of segment references (in particular, the segment portions of far procedure calls) which necessitate edits to the patched executable's relocation tables.
Each game with patches applied is fully playable in DOSBox and most likely on original (386/486) hardware as well (though I haven't had a chance to test the patches on original hardware).
Changes focus on improving usability - allowing the player to do things more easily or more directly - and on adding keyboard control to what was a largely mouse-driven game.
Keep moving in the same direction by pressing q
or Q
.
Place a bag or backpack more easily within another, as each is now considered a flexible container, with volume corresponding to the sum of its contents, rather than being of a fixed size.
Cast spells by key in real-time:
/
: start casting<letter>
: add runeBackspace
: remove last runeEnter
: cast selected runesShift
when opening or clicking the spellbook to see the rune-keys
of each spell.)Select party members by number in many contexts:
N
: open or target Nth party memberShift+N
: open or target Nth party member's backpackAlt+N
: open Nth party member's statsN
: give dragged item to Nth party membert
, 2
: talk to 2nd party memberf
, 3
: feed 3rd party member4
: target 4th party member with a spellt
, g
, g
, 2
: have 2nd party member get a nearby itemt
, t
, t
, 2
: have 2nd party member attack a nearby NPC (in combat mode)(U7BG) Target with crosshairs, as in U7SI, with t
.
Use keys to talk, use, get, or attack (while targeting with crosshairs)
t
: cycle NPCsr
: cycle usable itemsg
: cycle gettable itemsa
: cycle all itemsEnter
: select, use, talk, get, or attack with entire partyN
: attack or get with Nth party memberEsc
: cancel targetingSelect and use items. (Originally available only in U7SI, without open dialogs; now available in U7BG and U7SI and even with open dialogs.)
k
: find and use the key for a door or chest (emulating U7SI's keyring)f
: use foodm
: use world mapb
: use spellbook(s)p
: use lockpicksw
: use pocketwatchx
: use sextantg
: use abacus (to count party gold)o
: use Orb of the Moons (U7BG)j
: use Serpent Jawbone (U7SI)Query items' weight, volume, and contents:
Shift+click
: show item weightCtrl+click
: show item volumeAlt+click
: show a container's first contained itemAlt+click
: show a magic scroll's spell (U7SI)Control conversations with keys
←→↑↓
, Tab
, Shift+Tab
: move cursor among optionsEnter
, Ctrl
, Space
: advance text; select option at cursorControl dialogs more easily:
Tab
↑↓
←→
, Shift+←→
, ↑↓
, and Enter
.Use additional keys in key-mouse mode (initiated with Space
):
Space
: stop key-mouseEsc
: stop key-mouse, close inventoriesToggle cheats in-game by pressing Alt+Backslash
.
Press Alt+k
in game, or click the image below, to see a comprensive,
multi-page listing of mouse and keyboard controls.
(U7SI) The Dark Path map is now labeled with the names of destinations corresponding to held serpent teeth.
The background is now darkened during conversation, to increase contrast and enhance the readability of text.
(U7SI) The game no longer crashes if the player tries to open Combat Status with more than six party members.
Some overwhelmingly loud or frequent background sounds (such as the flickering of fire swords) have been silenced or attenuated.
The cheat-menu ability to set and inspect Game Flags has been restored.
(U7BG) The cheat ability to press Alt+6 and choose a specific music track to play, originally available in U7SI, has been added.
(U7BG) Calculating the elapsed number of hours on a timer for Usecode now uses absolute day number (as in U7SI) rather than day-of-the-week; this fixes the bug where Jaana would not be able to heal again even after waiting the required 5 hours.
The fault address is now printed on a fatal divide-by-zero error.
The biggest change is the addition of mouse-look (looking around by moving the mouse), which can be toggled on and off with a keypress. In support of this, the allowed range of vertical view angle has been greatly expanded, and the 3D rendering engine has been hacked to have it draw the bits of the world that become visible when the player looks sharply upward or downward.
Also, spell runes can be typed directly (with Ctrl+Alt+<letter>
), without having to
navigate through the inventory and the rune bag.
Things made more convenient:
A number of keys have been added or changed. The most important:
` (Backquote): toggle mouse-look
Alt+`: invert mouse-look axes (cycles through 4 states)
wasd: movement, typical of modern shooters
Space: attack (using last attack type, or slash)
Shift: jump
q: look at object in 3D view
e: use object in 3D view
z: display map
r: flip to rune-bag panel
f: flip to character panel
Keys for spell-casting:
Ctrl+Alt+<letter>: select a rune
Ctrl+Alt+Backspace: clear selected runes
Ctrl+Alt+Space: cast the selected runes
"Easy Move" actions:
Ctrl+Up: Easy Move Walk Forward
Ctrl+Left: Easy Move Left
Ctrl+Right: Easy Move Right
Ctrl+Down: Easy Move Backwards
Some miscellaneous keys:
g: activate compass
h: activate health and mana flasks
Ctrl+Shift: standing long jump
Shift: fly up
Ctrl: fly down
c: close container in inventory view
v: scroll inventory down
b: scroll inventory up
Keys for navigating the map:
s: up one level
w: down next level
d: previous realm (Ultima Underworld II only)
a: next realm (Ultima Underworld II only)
c: go to Avatar's level
These patches are intended to be applied to particular versions of the games, all as distributed by GOG.com:
Pre-assembled patches can be applied to a game's executable in seconds with the Hack Applier website:
https://johnglassmyer.github.io/UltimaHacks/patcher-site
To use the website, select the game executable file (U7.EXE, SI.EXE, UW.EXE, or UW2.EXE) from your installation of the game, select the corresponding "hack" for that game from the drop-down, and then click the "Apply" button to save a patched copy of the executable, with which you can replace the original file in your installation of the game.
Game executables, as well as saved games, should be backed up before applying patches. Always keep a copy of the original, un-patched executable on-hand.
Note that if you intend to patch a GOG.com installation of Ultima Underworld
or Ultima Underworld II that uses an ISO CD image (game.gog
), then the
executable file you need to patch is contained within that CD image. In
that case, you can either
extract the contents of the game.gog
image to a directory using a program
such as 7-Zip, and then edit DOSBox's configuration to have it mount that
directory as the CD-ROM drive in place of the game.gog
image, or
extract the contents of game.gog
, patch the executable, and then
build a new game.gog
ISO with the patched executable included
If all you want to do is play the games with the patches applied, please use the Hack Applier site mentioned in the preceding section.
Building the patches from source requires that NASM (for assembling the individual patches), as well as a Java 1.9 or higher JDK and Apache Maven (for building the UltimaPatcher program), be installed and on the system path.
On Windows systems, Git for Windows provides a Bash shell capable of processing the example commands given.
The Java program UltimaPatcher must be built by invoking Maven in the
UltimaPatcher
directory:
UltimaHacks/UltimaPatcher$ mvn compile package
This should ultimately build a file UltimaPatcher/target/UltimaPatcher.jar
.
The script scripts/patchFreshExe.sh
performs the remaining steps necessary to
build and apply patches to the game executable:
*.o
)*.asm
) to binaries (*.o
)First, configure this script by editing patchingVariables.sh
in the game's
patches directory to set the correct paths to the original, unmodified executable
(ORIGINAL_EXE
) and the destination for the patched executable (TARGET_EXE
).
E.g. in u7bg/patchingVariables.sh
:
...
ORIGINAL_EXE=path/to/U7.EXE-original
TARGET_EXE=path/to/U7.EXE
...
Then, from the game's patches directory, run the script to build and apply the patches, e.g.:
UltimaHacks/u7bg$ ../scripts/patchFreshExe.sh *.asm
More generally, UltimaPatcher has the capability to
FBOV
overlaysThe assembly file UltimaPatcher.asm
provides NASM assembly macros used to
produce assembled object files containing metadata that UltimaPatcher uses
to apply patch blocks to an executable.