Compared to Wizardry, whose Pascal source code I spent most of last August analyzing, Ultima has generally simpler gameplay mechanics to decipher, and BASIC is more of a beginner’s language. So this ought to be a cake walk in comparison, right?
Well, no. First of all, most of the work in understanding Wizardry had already been done for me, as it had been originally distributed in compiled P-code format. Thomas William Ewers had reverse-engineered it into human-readable source, and picked his own names for the variables and functions. They weren’t always the most sensible names, such as LUCKSKIL[0] for “Save Vs. Death,” but were much preferable to Garriott’s arbitrary two-letter names, such as AM for “Index of the monster currently being fought in the overworld” (or “alertness status of the guards/Mondain”).
Secondly, BASIC, in my opinion, was never intended to be used for a game as big in scope as Ultima, but it was the best tool available to most developers of the time, and like so many other developers, Garriott had to horrifically abuse the BASIC syntax to get things done with it, not least because having comments and meaningful variable names eats up precious memory. Observe this line from the Dungeon routine:
2075 PRINT "HIT! DAMAGE= ";D:MN(MB + ZX,2) = MN(MB + ZX,2) - D: IF MN(MB + ZX,2) < 0 THEN MN(MB + ZX,0) = 1: PRINT MN$(MB + ZX);" KILLED! ":X = ZX:DN%(PX + DX * ZZ,PY + DY * ZZ) = DN%(PX + DX * ZZ,PY + DY * ZZ) - 100 * ZX: GOTO 2293
This example isn’t especially an especially bad one – I could have picked plenty that are far less comprehensible. Here one can probably figure out the gist of what the line is trying to do, but I think in its partial comprehensibility, it illustrates the difficulty of my task better than gibberish like this does:
95 TN = T1:DR = 0: POKE - 16301,0: POKE - 16297,0: POKE - 16300,0: POKE - 16304,0: SCALE= 1: ROT= 0: HCOLOR= 3: POKE 232,116: POKE 233,14:AM = 0: DEF FN PN(T2) = PEEK (2816 + (PX + DX) * 22 + PY + DY): DEF FN MX(T4) = PEEK (2816 + (MX(X) + DX) * 22 + MY(X)): DEF FN MY(T4) = PEEK (2816 + MX(X) * 22 + MY(X) + DY)
Pascal was designed to encourage structured coding, largely as a reaction to BASIC’s shortcomings, but Apple Pascal was bleeding edge technology when Ultima was under development. Take this sample routine from Wizardry, which is fairly understandable thanks to the structure and meaningful variable names.
BEGIN
TOTAL := MINADD;
WHILE TRIES > 0 DO
BEGIN
TOTAL := TOTAL + (RANDOM MOD AVEAMT) + 1;
TRIES := TRIES - 1
END;
CALCULAT := TOTAL
END;
So, comprehending Ultima’s BASIC code is in some ways more difficult than Wizardry’s Pascal, but I’ve tried my best. It's a smaller task, since there's a lot less of it. There's also not all that much data - most of the tables I've made are based on formulas and hard-coded BASIC, rather than actual on-disk tables as seen so extensively in Wizardry.
Ultima is neatly divided into a number of separate BASIC programs, each one small enough to fit into the computer’s memory one at a time, leaving room for a few variables that get shared between programs. The programs are:
- Castle
- Dungeon
- Initialize Display
- Outside
- Player Disk
- Space
- Time Machine
- Town
- Ultima
Not all of these are interesting to me. Ultima is the title screen. Initialize Display is the self-explanatory character creator, some variable initializations, and various low-level routines that I don’t care to decipher. Space is a pretty basic action game that doesn’t lend itself well to data obsession. Player Disk is the looping demo and disk copying utility. In addition, not all of the game code appears to be present in the readable files. For instance, I couldn’t find any data representing monster’s internal stats, including their names (but I’ve assembled most of it through observation), but it has to be there somewhere.
Throughout this post, I will refer to some BASIC functions. One of the most important is:
RND(X)
This returns a random floating point number smaller than X, with an even distribution. For instance, RND(20) may return 19.99999, and may return 0, but it will not return 20 or any negative. Values that need to be used as integers will always be rounded down, so if I say, for instance, that weapon damage is
RND(20) + 1
, this means it can be anywhere from 1 to 20, since weapon damage is always an integer.Occasionally, the RND function is used in this manner:
RND(1)^2
This will return a random number smaller than 1, but distribution will be more frequent on low values. So for example, this formula:
RND(1)^2 * 5 + 1
will return anything from 1 to 5, but 1’s will be returned almost half of the time.
Common tables
Weapons:
Index | Weapon | Town/Castle | Dungeon/Mondain | Outdoors |
0 | Hands | |||
1 | Dagger | |||
2 | Mace | |||
3 | Axe | |||
4 | Rope and spikes | No attack, saves vs. traps | ||
5 | Sword | |||
6 | Great sword | |||
7 | Bow and arrows | Ranged | Ranged | Ranged |
8 | Amulet | Ranged | No attack | No attack, +16 magic missile damage |
9 | Wand | Ranged | No attack | No attack, +18 magic missile damage |
10 | Staff | Ranged | No attack | No attack, +20 magic missile damage |
11 | Triangle | Ranged | +22 magic missile damage | |
12 | Pistol | Ranged | Ranged | Ranged |
13 | Light sword | |||
14 | Phazor | Ranged | Ranged | Ranged |
15 | Blaster | Ranged | Ranged | Ranged |
The wild differences in ranged capability for some weapons are likely due to Garriott forgetting to code the rules consistently across the various BASIC modules. The amulet, wand, and staff are all over the place, being non-weapon magic enhancers outdoors, completely useless in dungeons, and ranged weapons in towns and castles.
The index serves as the base stat from which all other weapon stats – price, accuracy, and damage – are calculated.
In the towns and castles, weapon damage is:
RND(Strength + Index/2) + 1
In the dungeons and against Mondain, weapon damage is:
RND(Strength/5 + Index*3) + Strength/5
In the overworld, weapon damage is:
RND(Strength + Index) + 1
Armor
Index | Armor |
0 | Nothing |
1 | Leather |
2 | Chain |
3 | Plate |
4 | Vacuum |
5 | Reflect |
Armor reduces likelihood of getting hit, but does not reduce damage when you do.
In the towns, your odds of getting hit are:
22.5/(Agility + Index*4)
In the castles, your odds of getting hit are:
40/(Agility + Index*4)
Outdoors and dungeons use their own convoluted rules. I’ll get to them later.
Spells
Index | Spell | Use |
0 | Prayer | Outdoors |
1 | Open | Dungeon |
2 | Unlock | Dungeon |
3 | Magic Missile | Outdoors + dungeon + Mondain |
4 | Steal | Nowhere |
5 | Ladder Down | Dungeon |
6 | Ladder Up | Dungeon |
7 | Blink | Dungeon + Mondain |
8 | Create | Dungeon + Mondain |
9 | Destroy | Dungeon + Mondain |
10 | Kill | Outdoors + dungeon + Mondain |
The Steal spell seems to be completely useless. You can’t cast spells in town or the castle, and there’s nothing to steal anywhere else.
Blink, create, destroy, and kill are for wizards only.
Vehicles
Index | Vehicle | Food rate | Time rate | Notes |
0 | Nothing | 0.5 | 1.6 | |
1 | Horse | 0.43 | 1.46 | |
2 | Cart | 0.36 | 1.31 | Improves carrying capacity |
3 | Raft | 0.29 | 1.17 | Water only |
4 | Frigate | 0.21 | 1.03 | Water only, has guns |
5 | Air car | 0.14 | 0.89 | Can't enter woods, has guns |
6 | Shuttle | Goes to space |
On experience and leveling
Leveling in Ultima does not work as you might expect. Experience points have nothing to do with it; they are just a currency used to purchase spells. Nor does your level have anything to do with your strength in combat.Levels are determined entirely by how much “time” has passed in-game. And there are only three things that levels do. First, the shops in town will stock better items as you gain levels, with the best items only available at level 7. Second, monsters in the overworld gain more HP as you gain levels. Third, you must be level 8 or higher to use the time machine and win the game.
Each level represents 1000 passed time units. Time is advanced in the following ways:
- Passing anywhere by idling consumes 0.05 food and advances time 0.5 units.
- Passing in the town, castle, or dungeon by pressing space consumes 0.01 food and advances time 0.1 units.
- Moving in the town or castle consumes 0.01 food and advances time 0.1 units.
- Moving in the dungeon consumes 0.1 food and advances time 1 unit.
- Moving outdoors consumes up to 0.5 food and advances time up to 1.6 units.
But most efficiently of all is:
- Each parsed command while outdoors advances time 0.6 units.
So if you want to level quickly, just go outdoors and rapidly press a key that doesn’t do anything, like ‘W’. You won’t even consume food! Though you will have to contend with monsters. Or you can do what I did and put the emulator into warp speed and keep your finger on the ‘A’ key for a few minutes.
Town + Castle common rules
In both the towns and castles, it’s possible to steal armor, weapons, or food. Thieves have an easier time overall, but castles are more difficult than towns for thieves, and towns are more difficult than castles for non-thieves.Successfully stealing armor performs this calculation:
RND(1)^2 * 5 + 1
And then you receive the armor with the corresponding index value.
Successfully stealing a weapon performs this calculation:
RND(1)^3 * 15 + 1
And then you receive the weapon with the corresponding index value.
Successfully stealing food gets you
RND(30) + 1
units.Ranged weapons can hit at up to 5 tiles away.
Alerted guards will approach if their range is within 9 tiles. Distance is calculated by the Pythagorean Theorem.
I’ve had trouble figuring out the odds that Iolo/Gwino will steal something, but when they do, it will be your worst unequipped weapon, and the odds that you notice are
Wisdom/50
.Town
Towns with odd indices stock these weapons, based on your level.Weapon | Power | Min level | Base price |
Dagger | 1 | 1 | 6 |
Axe | 3 | 1 | 14 |
Sword | 5 | 1 | 30 |
Bow and Arrows | 7 | 4 | 54 |
Wand | 9 | 4 | 86 |
Triangle | 11 | 7 | 126 |
Light sword | 13 | 7 | 174 |
Blaster | 15 | 7 | 230 |
Towns with even indices stock these weapons, based on your level.
Weapon | Power | Min level | Base price |
Mace | 2 | 1 | 9 |
Rope and spikes | 4 | 1 | 21 |
Great sword | 6 | 4 | 41 |
Amulet | 8 | 4 | 69 |
Staff | 10 | 4 | 105 |
Pistol | 12 | 7 | 149 |
Phazor | 14 | 7 | 201 |
The base price is subject to a percentage discount of
Intelligence/200
. The final purchase price is determined by this formula:(1 – Intelligence/200) * (WeaponPower ^ 2) + 5
Selling weapons gets you a percentage rate on the buying price of
Charisma/50
.Armor is also stocked according to your level, and doesn’t vary between towns.
Armor | Power | Min level | Base price |
Leather | 1 | 1 | 50 |
Chain | 2 | 1 | 100 |
Plate | 3 | 1 | 150 |
Vacuum | 4 | 3 | 200 |
Reflect | 5 | 3 | 250 |
The base price is subject to a percentage discount of
Intelligence/200
. The final purchase price is determined by this formula:(1 – Intelligence/200) * (ArmorPower * 50)
Selling armor gets you a percentage rate on the buying price of
Charisma/50
.Your carrying capacity is equal to
(Strength * 4) + (Carts * 20)
. Your carrying load is (Gold/100) + Armor + Weapons
. Each armor or weapon counts for 1 unit, regardless of type. Capacity vs. load is checked only when buying weapons or armor.Vehicles are stocked according to your level.
Vehicle | Power | Min level | Base price |
Horse | 1 | 1 | 40 |
Cart | 2 | 1 | 160 |
Raft | 3 | 1 | 360 |
Frigate | 4 | 1 | 640 |
Air car | 5 | 4 | 1000 |
Shuttle | 6 | 4 | 1440 |
The base price is subject to a percentage discount of
Intelligence/200
. The final purchase price is equivalent to this formula:(40 - Intelligence/5)* (VehicleIndex^2)
Spells are always fully stocked.
Base price | Index | Base price |
Open | 1 | 5 |
Unlock | 2 | 10 |
Magic Missile | 3 | 15 |
Steal | 4 | 20 |
Ladder Down | 5 | 25 |
Ladder Up | 6 | 30 |
Blink | 7 | 35 |
Create | 8 | 40 |
Destroy | 9 | 45 |
Kill | 10 | 50 |
Spells cost both gold and experience, in equal amounts. The base price of each spell is
[Spell index] * 5
. It is then subject to a percentage discount of Wisdom/200. Algebraically, the price can be expressed as:[Spell index] * (5 – Wisdom/40)
Food packs cost
5 – Intelligence/20
.If you consume a total number of drinks greater than
Stamina/5
or Wisdom/5
, and are standing near the wench, you will be seduced. You’ll lose all of your gold and one point of Wisdom if you have 6 or more.When you buy a drink and are not seduced, there is a 70% chance of hearing a randomly chosen tip. All tips have a 10% chance unless otherwise noted.
BUB, YOU BEST KNOW
ABOUT SPACE TRAVEL! AND THAT YOU MUST DESTROY AT LEAST 20 ENEMY VESSELS TO BECOME AN ACE!
TO WATCH THE WENCH?
THE PRINCESS WILL GIVE GREAT REWARD TO THE ONE WHO SAVES HER, AND AN EXTRA GIFT IF THE PLAYER IS 8TH LEVEL OR GREATER!
YOU SHOULD GO BACK IN TIME!
(20% odds)YOU SHOULD DESTROY THE EVIL GEM!
THAT MANY, IF NOT MOST LAKES AND PONDS HAVE STRONG MAGICAL POWERS!
THIS IS A GREAT GAME!
(20% odds)THAT OVER 1000 YEARS AGO MONDAIN THE WIZARD CREATED AN EVIL GEM. WITH THIS GEM, HE IS IMMORTAL AND CANNOT BE DEFEATED. THE QUEST OF --ULTIMA-- IS TO TRAVERSE THE LANDS IN SEARCH OF A TIME MACHINE. UPON FINDING SUCH A DEVICE, YOU SHOULD GO BACK IN TIME TO THE DAYS BEFORE MONDAIN CREATED THE EVIL GEM AND DESTROY HIM BEFORE IT'S CREATION. IF YOU DO THIS, YOU WILL SAVE THE UNIVERSE, AND WIN THE GAME!!!
Targets have the following values:
Target | AC | HP | EXP |
Guard 1 (left of the entrance) | -8 | 20 | 50 |
Guard 2 (right of the entrance) | -7 | 20 | 50 |
Guard 3 (near the food shop) | -6 | 20 | 50 |
Guard 4 (near the armour shop) | -5 | 20 | 50 |
Guard 5 (near the pub) | -4 | 20 | 50 |
Guard 6 (near the magic shop) | -3 | 20 | 50 |
Bard | -2 | 0 | 20 |
Thief | -1 | 0 | 40 |
Merchant | 0 | 0 | 25 |
Wench | 1 | 0 | 10 |
Your odds of missing an attack are:
50/(75 + Agility + WeaponPower + [TargetAC *3])
A guard’s attack has these odds of hitting you:
22.5/(Agility + ArmorPower*4)
Guard attacks inflict damage of 1% of your HP, plus
RND(10)
.Dropping gold into the lake grants you
(1.5 * gold)
HP. You will also receive 4 daggers the first time per visit.Stealing has a 15% chance of failure if you are a thief, and a 40.5% chance of failure otherwise. It always fails if the guards are alerted.
Castle
Dropping gold into the west fountain grants(Gold/10)
points to a randomly chosen stat, up to a maximum of 99 points. Possible stats are:- HP
- Strength
- Agility
- Stamina
- Charisma
- Wisdom
- Intelligence
If “HP” is selected, odds are you’ll wind up reducing it to 99 points.
Dropping 10 or more gold into the center-north fountain grants a random weapon.
Dropping gold into the center-south fountain grants
(Gold*5)
food.When you receive the white gem from Shamino for killing a Balron, you are also permitted nine rewards from the armour store, weapon store, or food store.
Armour store rewards grant you a randomly chosen armor, with even distribution.
Weapon store rewards grant you a randomly chosen weapon, by this formula:
RND(1)^2 * 15 + 1
Food rewards gives you
RND(30) + 1
food units.You may also steal from the stores, which has a 20% chance of failure if you are a thief, and a 36% chance of failure otherwise. It always fails if the guards are alerted. Looting odds are not as good as the reward odds, and match the odds of stealing from merchants.
Your reward for completing landmark quests is more strength, in the amount of:
9.9 – OldStrength/10
Gold may be donated in increments of 10, to a maximum of 90. The reward is higher if you donate a larger portion of your gold.
The reward is HP by this formula:
3 * Donation^2 / Gold
So, if you have 10 gold and donate all of it, you receive
3 * 100/10 = 30 HP
A special case is made for donating 90 gold. It guarantees at least 135 HP, even if you are so rich that the formula says it should buy you less than that.
Targets have the following values:
Target | AC | HP | EXP |
Guard 1 (by the entrance) | -8 | 500 | 50 |
Guard 2 (by the gate, north) | -7 | 500 | 50 |
Guard 3 (by the gate, south) | -6 | 500 | 50 |
Guard 4 (center-north fountain) | -5 | 500 | 50 |
Guard 5 (center-south fountain) | -4 | 500 | 50 |
Guard 6 (throne room) | -3 | 500 | 50 |
Jester | -2 | 0 | 30 |
King | -1 | Infinite | 1000 |
Merchant | 0 | 0 | 25 |
Princess | 1 | 0 | 10 |
Your approximate odds of missing an attack are:
50/(50 + Agility + WeaponPower + [TargetAC *3])
Killing the jester gives you a key to a random cell. Your inventory will show you whether it is cell 1 or cell 2.
Attacking the king performs this calculation:
Agility * RND(1)^3
If the result is greater than 50, then the king dies.
A guard’s attack has these odds of hitting you:
40/(Agility + ArmorPower*4)
Guard attacks inflict damage of 1/75th of your HP, plus
RND(20)
.Dungeon
Every mention of “level” here refers to the dungeon level, unless otherwise noted.Ultima’s dungeons, unsurprisingly, have quite a bit of code from Akalabeth. The dungeon routine does involve a bit more data than Akalabeth, but not much. Monster stats are all derived from two hard-coded stats.
One big difference from Akalabeth is that dungeons always have ten levels, and the monsters are divided into five groups of ascending difficulty. Every two dungeon levels corresponds to one monster group, and may spawn any monster in that group.
First, here is a table of all monsters and their root stats. Four of the monsters, thieves, gelatinous cubes, gremlins, and mind whippers, will do special things when they hit you in lieu of doing damage if they can, and will damage you normally if they cannot.
Name | Set | MB | MN | Notes |
Ranger | 1 | 20 | 1 | |
Skeleton | 1 | 20 | 2 | |
Thief | 1 | 20 | 3 | Steals your worst unequipped weapon |
Giant Rat | 1 | 20 | 4 | |
Bat | 1 | 20 | 5 | |
Spider | 2 | 25 | 1 | |
Viper | 2 | 25 | 2 | |
Orc | 2 | 25 | 3 | |
Cyclops | 2 | 25 | 4 | |
Gelatinous Cube | 2 | 25 | 5 | Destroys your equipped armor |
Ettin | 3 | 30 | 1 | |
Mimic | 3 | 30 | 2 | Looks like a chest |
Lizard Man | 3 | 30 | 3 | |
Minotaur | 3 | 30 | 4 | |
Carrion Creeper | 3 | 30 | 5 | |
Tangler | 4 | 35 | 1 | |
Gremlin | 4 | 35 | 2 | Steals half of your food |
Wandering Eyes | 4 | 35 | 3 | |
Wraith | 4 | 35 | 4 | |
Liche | 4 | 35 | 5 | |
Invisible Seeker | 5 | 40 | 1 | Invisible, but battle console works normally |
Mind Whipper | 5 | 40 | 2 | 50% chance of setting your intelligence to (Intelligence * 0.6 + 5)
|
Zorn | 5 | 40 | 3 | |
Daemon | 5 | 40 | 4 | |
Balron | 5 | 40 | 5 |
Monsters of set X may spawn on level
(X*2 – 1)
or (X*2)
.MB is a calculated value representing the difficulty of the monster group. It is equal to:
Set * 5 + 15
MN represents the monster’s toughness relative to other monsters in its group. Quest monsters are always
MN=5
.MB and MN are used for a variety of calculations.
For the rest of the stats, I’ve split the tables up by level, since monsters have different stats on different levels. All stats here are presented as ranges.
Level 1
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Ranger | 10-10 | 15-16 | 3-12 | 2-2 | 1-2 | 1-5 | 9-17 |
Skeleton | 10-11 | 15-18 | 3-12 | 2-3 | 1-3 | 1-10 | 9-17 |
Thief | 10-12 | 15-20 | 3-12 | 2-4 | 1-4 | 1-15 | 9-17 |
Giant Rat | 10-13 | 15-22 | 3-12 | 2-5 | 1-5 | 1-20 | 9-17 |
Bat | 10-14 | 15-24 | 3-12 | 2-6 | 1-6 | 1-25 | 9-17 |
Level 2
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Ranger | 10-13 | 15-22 | 6-15 | 4-7 | 2-3 | 2-11 | 9-44 |
Skeleton | 10-17 | 15-30 | 6-15 | 4-11 | 2-4 | 2-21 | 9-44 |
Thief | 10-21 | 15-38 | 6-15 | 4-15 | 2-5 | 2-31 | 9-44 |
Giant Rat | 10-25 | 15-46 | 6-15 | 4-19 | 2-6 | 2-41 | 9-44 |
Bat | 10-29 | 15-54 | 6-15 | 4-23 | 2-7 | 2-51 | 9-44 |
Level 3
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Spider | 10-18 | 15-32 | 9-18 | 6-14 | 3-5.67 | 3-17 | 9-89 |
Viper | 10-27 | 15-50 | 9-18 | 6-23 | 3-6.67 | 3-32 | 9-89 |
Orc | 10-36 | 15-68 | 9-18 | 6-32 | 3-7.67 | 3-47 | 9-89 |
Cyclops | 10-45 | 15-86 | 9-18 | 6-41 | 3-8.67 | 3-62 | 9-89 |
Gelatinous Cube | 10-54 | 15-104 | 9-18 | 6-50 | 3-9.67 | 3-77 | 9-89 |
Level 4
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Spider | 10-25 | 15-46 | 12-20 | 8-23 | 4-6.67 | 4-23 | 9-152 |
Viper | 10-41 | 15-78 | 12-20 | 8-39 | 4-7.67 | 4-43 | 9-152 |
Orc | 10-57 | 15-110 | 12-20 | 8-55 | 4-8.67 | 4-63 | 9-152 |
Cyclops | 10-73 | 15-142 | 12-20 | 8-71 | 4-9.67 | 4-83 | 9-152 |
Gelatinous Cube | 10-89 | 15-174 | 12-20 | 8-87 | 4-10.67 | 4-103 | 9-152 |
Level 5
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Ettin | 10-34 | 15-64 | 15-20 | 10-34 | 5-9.33 | 5-29 | 9-233 |
Mimic | 10-59 | 15-114 | 15-20 | 10-59 | 5-10.33 | 5-54 | 9-233 |
Lizard Man | 10-84 | 15-164 | 15-20 | 10-84 | 5-11.33 | 5-79 | 9-233 |
Minotaur | 10-109 | 15-214 | 15-20 | 10-109 | 5-12.33 | 5-104 | 9-233 |
Carrion Creeper | 10-134 | 15-264 | 15-20 | 10-134 | 5-13.33 | 5-129 | 9-233 |
Level 6
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Ettin | 10-45 | 15-86 | 18-20 | 12-47 | 6-10.33 | 6-35 | 9-332 |
Mimic | 10-81 | 15-158 | 18-20 | 12-83 | 6-11.33 | 6-65 | 9-332 |
Lizard Man | 10-117 | 15-230 | 18-20 | 12-119 | 6-12.33 | 6-95 | 9-332 |
Minotaur | 10-153 | 15-302 | 18-20 | 12-155 | 6-13.33 | 6-125 | 9-332 |
Carrion Creeper | 10-189 | 15-374 | 18-20 | 12-191 | 6-14.33 | 6-155 | 9-332 |
Level 7
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Tangler | 10-58 | 15-112 | 20-20 | 14-62 | 7-13 | 7-41 | 9-449 |
Gremlin | 10-107 | 15-210 | 20-20 | 14-111 | 7-14 | 7-76 | 9-449 |
Wandering Eyes | 10-156 | 15-308 | 20-20 | 14-160 | 7-15 | 7-111 | 9-449 |
Wraith | 10-205 | 15-406 | 20-20 | 14-209 | 7-16 | 7-146 | 9-449 |
Liche | 10-254 | 15-504 | 20-20 | 14-258 | 7-17 | 7-181 | 9-449 |
Level 8
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Tangler | 10-73 | 15-142 | 20-20 | 16-79 | 8-14 | 8-47 | 9-584 |
Gremlin | 10-137 | 15-270 | 20-20 | 16-143 | 8-15 | 8-87 | 9-584 |
Wandering Eyes | 10-201 | 15-398 | 20-20 | 16-207 | 8-16 | 8-127 | 9-584 |
Wraith | 10-265 | 15-526 | 20-20 | 16-271 | 8-17 | 8-167 | 9-584 |
Liche | 10-329 | 15-654 | 20-20 | 16-335 | 8-18 | 8-207 | 9-584 |
Level 9
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Invisible Seeker | 10-90 | 15-176 | 20-20 | 18-98 | 9-16.67 | 9-53 | 9-737 |
Mind Whipper | 10-171 | 15-338 | 20-20 | 18-179 | 9-17.67 | 9-98 | 9-737 |
Zorn | 10-252 | 15-500 | 20-20 | 18-260 | 9-18.67 | 9-143 | 9-737 |
Daemon | 10-333 | 15-662 | 20-20 | 18-341 | 9-19.67 | 9-188 | 9-737 |
Balron | 10-414 | 15-824 | 20-20 | 18-422 | 9-20 | 9-233 | 9-737 |
Level 10
Name | HP1 | HP2 | Att | Dam | Def | EXP | G |
Invisible Seeker | 10-109 | 15-214 | 20-20 | 20-119 | 10-17.67 | 10-59 | 9-908 |
Mind Whipper | 10-209 | 15-414 | 20-20 | 20-219 | 10-18.67 | 10-109 | 9-908 |
Zorn | 10-309 | 15-614 | 20-20 | 20-319 | 10-19.67 | 10-159 | 9-908 |
Daemon | 10-409 | 15-814 | 20-20 | 20-419 | 10-20 | 10-209 | 9-908 |
Balron | 10-509 | 15-1014 | 20-20 | 20-519 | 10-20 | 10-259 | 9-908 |
HP1 is the monster’s maximum HP if it is one of the initially spawning monsters when you enter. Minimum HP1 is always 10. The formula is:
RND(MN*Level^2) + 10
HP2 is the monster’s maximum HP if it spawned after you killed another monster. Minimum HP2 is always 15. The formula is:
RND(2*MN*Level^2) + 15
Att is the range for attack rolls. Minimum is always
3 * Level
. The formula for attack rolls is:RND(10) + 3*Level
But it will never exceed 20.
Your defense roll will be:
RND(Stamina/3) + 3*ArmorPower
There is no upper limit, but a 20 or higher is a guaranteed miss.
If the monster’s attack roll beats your defense roll, you take damage, or the monster uses its special attack move if it has one.
Dam is the range of damage the monster may do to you. The formula is:
RND(MN*Level^2) + (2*Level)
Def is the range for defense rolls. The formula for defense rolls is:
RND(MN + (MB – 20)/3) + Level
But it will never exceed 20.
When attacking, your attack accuracy is:
RND(Agility/4 + WeaponPower)
The monster’s defense is then rolled. If your accuracy beats it, then you hit.
Damage from your hits will be:
RND(Strength/5 + WeaponPower*3) + Strength/5
Ranged weapons have unlimited range, but don’t pass through traps, doors, or false walls.
If you kill a monster, another one, not necessarily of the same type, but always one of a type not currently in the dungeon, will spawn in a random unoccupied space, and will have HP in the range of the HP2 column.
XP is the immediate experience reward for defeating the monster. Minimum is always equal to Level. When you leave the dungeon, you will be granted HP equal to double all of the experience accumulated in the trip. The formula for XP is:
RND(Level * MN * 5) + Level
G is the gold reward. The formula is:
RND(Level^2 * 9) + 9
Each dungeon follows this template:
On level 1, the Up ladder will be in the top-left corner instead. You enter facing south.
On all even levels, the Up and Down ladders switch places.
On level 10, there will be no Down ladder.
When a level is generated, each yellow and orange spot will be a randomly chosen feature, according to these odds:
- 10% nothing
- 30% false wall
- 50% door
- 5% pit
- 5% trap
Level 10 will not have pits or traps, but I don’t really follow the logic that replaces them with something else.
Then, chests, coffins, and fields will be placed. The total number of them will be equal to the level number, and they will be placed in random orange spots, or one spot below an orange spot. They will override any features already present, except for ladders.
Then, three monsters spawn, always of different types. They will be placed in random unoccupied squares.
Falling into a trap without possessing any rope & spikes descends you a level and inflicts
RND(Level*10) + Level
damage.Pressing
I
when facing a trap will turn it into a pit.Pressing
I
when facing a false wall will turn it into a door.Opening a coffin will always be successful if the space in front of you is occupied.
Otherwise, there is a 60% chance of success. If it fails, then this logic follows:
- Pick a random monster to spawn
- If the monster type is not already present, then spawn it.
- If it is already present, then there’s an 80% chance of repeating from the top, and a 20% chance of just opening the coffin.
Monsters spawned from coffins will die in one hit, but this will cause another monster to spawn somewhere in the dungeon with its HP2 hitpoints.
Non-thieves have a
(50% + Agility * 1%)
chance to unlock chests. Thieves are always successful.A failed unlock does [Level] damage.
Gold from coffins and chests pays the same as any monster on the level.
Casting spells has a
(50% + Intelligence * 1%)
chance of success for non-clerics. Clerics never fail.Prayer always fails in dungeons. It looks as though prayer was once meant to invoke a random spell effect, but Garriott may have intentionally disabled this. Or it could just be a bug.
Magic Missile has a range of 5 spaces, and always hits for
(Wisdom/2 + 11)
damage, rounded down. It won’t pass through traps, doors, or false walls.Steal does nothing.
Ladder Down doesn’t work on level 10.
Blink takes you to a random unoccupied spot in the dungeon.
Create and destroy have a range of 1 square, and create only works if the square is unoccupied.
Kill has a range of 1 square, and just kills. Doesn’t matter what kind of monster it is.
Outside
Time passes 0.6 units with each keyboard command.Moving consumes
(0.5 – VehiclePower/14)
food and advances time (1 – VehiclePower/7)
units, plus the 0.6 time units for the keyboard command.Each step on land has a 5% chance of spawning a monster group.
Each step in the woods or at sea has a 10% chance of spawning a monster group.
I haven’t been able determine the monster group size ranges, but each monster type has a fixed maximum group size, and the formula for group size is:
RND(1)^2 * MaxGroup
If walking away from an encounter, odds of escaping are:
1/7 + Strength*3/1400 + Agility*3/1400 + VehiclePower*(1/14 – Strength/5600 – Agility/5600)
If you fail, then you are stuck in place for a turn and the enemy gets a free round of attacks on you.
Monsters
Name | Index | Type | Base HP | EXP | Def | G |
Ness creature | 6 | Sea | 30-69 | 20 | 20 | 10-89 |
Giant squid | 7 | Sea | 30-89 | 30 | 20 | 10-129 |
Dragon turtle | 8 | Sea | 30-109 | 40 | 20 | 10-169 |
Giant octopus | 9 | Sea | 30-129 | 50 | 20 | 10-209 |
Hood | 10 | Woods | 30-49 | 5 | 15 | 10-29 |
Bear | 11 | Woods | 30-69 | 10 | 20 | 10-49 |
Hidden Archer | 12 | Woods | 30-89 | 20 | 20 | 10-89 |
Dark Knight | 13 | Woods | 30-109 | 30 | 20 | 10-129 |
Evil Trent | 14 | Woods | 30-129 | 40 | 20 | 10-169 |
Thief | 15 | Land | 10-19 | 10 | 20 | 10-49 |
Orc | 16 | Land | 10-29 | 20 | 20 | 10-89 |
Knight | 17 | Land | 10-39 | 30 | 20 | 10-129 |
Necromancer | 18 | Land | 10-49 | 40 | 20 | 10-169 |
Evil Ranger | 19 | Land | 10-59 | 45 | 20 | 10-189 |
Wandering Warlock | 20 | Land | 10-69 | 50 | 20 | 10-209 |
Sea creatures and archers may only be hit with ranged weapons.
Index is used for HP calculations.
Base HP represents the range of HP that the first monster in the group might have. This only applies to the first in the group; the rest will have much less.
The formula for sea monsters is:
RND(20*Index – 80)) + 30
The formula for woods monsters is:
RND(20*Index – 180) + 30
The formula for land monsters is:
RND(10*Index – 140) + 10
In addition, the first monster in the group will receive additional HP based on the amount of time passed.
+ RND(1)^2 * Time/100
That is Time/100, and not Time/1000, so the maximum bonus is roughly ten times your level.
Exp is a fixed amount, and I couldn't find where it is defined in the code, but it wasn't hard to determine from observation.
Defense is strangely coded. It’s programmed to be equal to
EXP + 10
, capped at 20. But Hoods are the only monsters weak enough not to hit that cap.When attacking, your attack accuracy will be:
RND(20) + Strength/5 + Agility/5 + WeaponPower
If this beats the monster’s defense, you hit and do this much damage:
RND(WeaponPower + Strength) + 1
Firing vehicle guns always has an 80% chance to hit. Damage done is:
RND(10 * VehiclePower) + 30
If you kill a monster, and there are more in the group, the next one’s HP will be:
RND(1)^2 * PlayerLevel + RND(20)
When monsters attack, their attack accuracy, per monster, will be:
RND(15)^2
For your defense, variable ‘D’ will be set to
ArmorPower/5
, but not greater than 0.5. Your defense value per attack will be:(D + 1.5) * Agility
But it will not exceed 80.
If the attack exceeds your defense, then you take a random amount of damage not greater than the monster’s EXP value. If you take multiple hits during a turn, you won’t see the damage per-hit, only the total number of times you were hit, and your overall decrease in HP.
Visiting landmarks that grant stat boosts will grant the following amount:
INT(9.9 – OldStat/10)
Spells do not have any random chance of failure outdoors.
Prayer has a 33% chance of each of the following effects:
- If there are monsters present, kill them all (no reward). If not, lose 20 food if you have more than 20, and the next effect happens.
- If you have fewer than 10 HP, set HP to 10. If not, the next effect happens.
- If you have fewer than 10 food, set food to 10. If not, no effect.
To be clear, there is both random chance and conditional logic involved. There are three steps, but there's a 33% chance of starting on each step. Let's say there are no monsters, you have less than 10 HP, and you have 21 food. There's a 33% chance that we start on step 1, you lose 20 food, and then step 2 happens and your HP is set to 10. There's a 33% chance that we start on step 2 and you just gain HP. And there's a 33% chance that we start on step 3 and nothing at all happens.
Magic missile does
Wisdom/2
damage. If you have an amulet, wand, staff, or triangle equipped, it does an additional WeaponPower*2
damage. This weapon bonus only applies outdoors.The Kill spell kills one monster in a group.
Time Machine
Getting burned does 10% damage to your HP.Ranged weapons have a range of 4 tiles. Distance is calculated with the Pythagorean Theorem.
Mondain spawns with 1000 HP. He will be alerted if you attack him or stand near him.
Mondain periodically gains 10 HP. I think this happens every round, but am not sure.
If Mondain is unconscious and the gem is still intact, he will at some point gain 25 HP and regain consciousness. Again, I think this happens every round after he is unconscious, but am not sure.
Your attack accuracy roll is:
RND(Strength/2 + Agility/2) + 3*WeaponPower
Mondain’s defense roll is:
RND(100) + 50
But it will not exceed 70.
If your attack roll beats his defense roll, then you hit for this much damage:
RND(Strength/5) + WeaponPower*3 + Strength/5
When Mondain’s HP drops to 500, he turns into a bat.
Mondain’s melee attack has a roll of:
RND(300)
Your defense will be:
Strength/3 + Agility/3 + Stamina/3 + ArmorPower*2
If his attack beats your defense, then he inflicts damage of 1/25th of your HP, plus
RND(20)
.If Mondain is outside of melee range, but less than 7 tiles away from you, then there is a 50% chance of a magical attack. He has three possible spells:
- Magic Missile
- Mind Blaster
- Psyonic Shock
Magic Missile’s odds of hitting you are:
(1 – Agility/100)*(1 – Intelligence/100)
If it hits, it does damage to 1/500th of your HP, plus RND(100).
Mind Blaster has a 30% chance of reducing all of your stats by 10%. Otherwise it misses.
Psyonic Shock has a 30% chance of doing up to 5% damage. Otherwise it misses. Kind of a pathetic spell.
Your own spells have a 30% chance of failure if you are not a cleric. Clerics never fail. In this light, I think Mondain's spells were meant to have a 30% chance of failure too, but instead have a 30% success rate thanks to Garriott using the wrong comparison operator.
Your spells fail if you are more than 6 tiles away from Mondain.
Your magic Missile does this much damage:
RND(Wisdom + Intelligence)
The Kill spell doubles Mondain’s HP!
Taking the gem does 75% damage to your HP.
Spaghetti code at its finest!
ReplyDeleteInteresting to see how it all fits together.
"Non-thieves have a (50% - Agility * 1%) chance to unlock chests. Thieves are always successful."
ReplyDeleteIs that correct? That would imply that better agility makes you worse at opening chests!
Whoops! Good catch. That's the chance to FAIL to unlock chests. I rephrased it for clarity, but I forgot to inverse the formula. Corrected!
DeleteThis is great stuff! If you could do some reverse engineering and do a similar article for Ultima III or IV, that would be really something!
ReplyDeleteProbably not going to happen. Ultima is BASIC, but the sequels are assembly. Ultima IV does have a ScummVM implementation in C++, but it is based on xu4 which claims to be a "recreation" and not a source port, so it is unlikely to be accurate.
Delete