Morrowind: Analysing the Dark Brotherhood attacks

The Tribunal expansion of Morrowind has an interesting way of starting. Lots of game expansions offer a new place you can explore. You can usually visit these places by talking to a random NPC with an updated conversation tree or by talking to specific NPCs who are able to take you to the new place.

By contrast, Tribunal throws you into the expansion content by throwing assassins at you! How does this mechanic work? In this article I will explain the finer details of this mission and I will also address misconceptions that still float around.

Let’s consider the “journal” for this mission.

IndexFinishes QuestJournal Entry(TR_DBAttack)
10An attempt was made on my life as I tried to rest. I do not know who wishes me dead, but the attack should probably be reported to a guard.
20One of the assassins had an odd dart on his body, the look of which I’ve never seen before.
30A guard has told me that my attackers were likely members of the Dark Brotherhood, and that I have been targeted for assassination. He suggests I speak with Apelles Matius in Ebonheart for more information.
40Apparently, the Dark Brotherhood does not have a large base of operations here on Vvardenfell, but has a very large contingent in Mournhold. Due to the Blight, no residents are allowed to travel to the capital city, and all visits are made only by the special order of Duke Dren.
50I’ve been told to speak to Asciene Rane in the Grand Council Chambers about transport to Mournhold.
60Asciene Rane has agreed to transport me to Mournhold. If I wish to return to the mainland, I should speak to Effe-Tei, an Argonian Mage in the Royal Palace. When I arrive, I should speak with one of the Royal Guard for more information about the Dark Brotherhood.
100☑I’ve been told that the Dark Brotherhood is rumored to have a base in the ruins of Old Mournhold, accessible through the sewer system in the Great Bazaar. I’ve been warned that I enter there at my own peril.

As you can see, the mission starts when you are attacked by an assassin. To complete the mission you need to do the following:

  • Talk to a guard
  • Talk to Apelles Matius
  • Talk to Asciene Rane
  • Talk to a Royal Guard

Talking to a (generic) Royal Guardsman and talking about the topic of the Dark Brotherhood attack ends the mission. This will also end the Dark Brotherhood attacks. But how does the attack mechanic work? Let’s look at the (big) relevant script in question “dbAttackScript”.

Begin dbattackScript

float dbchance
short journalOnce
short attackOnce
short playerLevel
short attackmod
short othermod
short dbnumber
short temp
short sleepOnce

if ( GetJournalIndex TR_dbAttack >= 50 )
	return
endif

if ( player->GetLevel >= 30 )
	set playerLevel to 5
else
	if ( player->GetLevel >=20 )
		set playerLevel to 4
	else
		if ( player->GetLevel >= 10 )
			set playerLevel to 3
		else
			if ( player->GetLevel >=4 )
				set playerLevel to 2
			else
				set playerLevel to 1
			endif
		endif
	endif
endif

if ( GetPCCell "Seyda Neen, Census and Excise Office" == 1 )
	return
endif

if ( journalOnce == 1 )
	;Journal TR_DBAttack 10
	set journalOnce to -1
endif

if ( GetPCSleep == 1 )
	if ( sleepOnce == 1 )
		return
	endif
	set sleepOnce to 1
	set dbchance to Random 100
	set attackmod to ( attackonce * 10 )
	if ( playerlevel == 5 )
		set othermod to ( 90- attackmod )
		if ( dbchance <= othermod )
			WakeUpPC
			MessageBox "You are awakened by a loud noise."
			set dbnumber to ( dbnumber + 1 )
			if ( dbnumber > 2 )
				set dbnumber to 2
			endif
			set temp to dbnumber
			while ( temp != 0 )
				PlaceAtPC "db_assassin4" 1 128 1
				set temp to ( temp - 1 )
			endwhile
			set attackonce to ( attackonce + 1 )
				if ( journalOnce == -1 )
					return
				endif
				set journalOnce to 1
				set DBAttack to 1
		endif
	else
		if ( playerLevel == 4 )
			set othermod to ( 70 - attackmod )
			if ( dbchance <= othermod )
				WakeUpPC
				MessageBox "You are awakened by a loud noise."
				set dbnumber to ( dbnumber + 1 )
				if ( dbnumber > 2 )
					set dbnumber to 2
				endif
				set temp to dbnumber
				while ( temp != 0 )
					PlaceAtPC "db_assassin3" 1 128 1
					set temp to ( temp - 1 )
				endwhile
				set attackonce to ( attackonce + 1 )
					if ( journalOnce == -1 )
						return
					endif
					set journalOnce to 1
					set DBAttack to 1
			endif
		else
			if ( playerLevel == 3 )
				set othermod to ( 50 - attackmod )
				if ( dbchance <= othermod )
					WakeUpPC
					MessageBox "You are awakened by a loud noise."
					PlaceAtPC "db_assassin2" 1 128 1
					set attackonce to ( attackonce + 1 )
						if ( journalOnce == -1 )
							return
						endif
						set journalOnce to 1
						set DBAttack to 1
				endif
			else
				if ( playerLevel == 2 )
					set othermod to ( 40 - attackmod )
					if ( dbchance <= othermod )
						WakeUpPC
						MessageBox "You are awakened by a loud noise."
						PlaceAtPC "db_assassin1" 1 128 1
						set attackonce to ( attackonce + 1 )
							if ( journalOnce == -1 )
								return
							endif
							set journalOnce to 1
							set DBAttack to 1
					endif
				else
					if ( playerLevel == 1 )
						set othermod to ( 20 - attackmod )
						if ( dbchance <= othermod )
							WakeUpPC
							MessageBox "You are awakened by a loud noise."
							PlaceAtPC "db_assassin1b" 1 128 1
							set attackonce to ( attackonce + 1 )
								if ( journalOnce == -1 )
									return
								endif
								set journalOnce to 1
								set DBAttack to 1
						endif
					endif
				endif	
			endif
		endif
	endif
else
	set sleepOnce to 0
endif



End

That’s a lot to take in all at once! Let’s analyze the code blocks one by one.

Begin dbattackScript

float dbchance
short journalOnce
short attackOnce
short playerLevel
short attackmod
short othermod
short dbnumber
short temp
short sleepOnce

if ( GetJournalIndex TR_dbAttack >= 50 )
	return
endif

We see a large amount of defined variables, some ending with “Once”. This is the way of the Bethesda-team to define booleans or “counters” as the scripting language used is quite limited.
playerLevel seems evident but isn’t, other variables aren’t clear yet from this context. The first “active” code checks if the journal has state 50 or greater. This corresponds to the conversation with Apelles Matius. In that case, the script stops.

if ( player->GetLevel >= 30 )
	set playerLevel to 5
else
	if ( player->GetLevel >=20 )
		set playerLevel to 4
	else
		if ( player->GetLevel >= 10 )
			set playerLevel to 3
		else
			if ( player->GetLevel >=4 )
				set playerLevel to 2
			else
				set playerLevel to 1
			endif
		endif
	endif
endif

So the playerLevel is an estimation of the power of player, capping at lvl 30+ players. The minimum playerLevel is designated to be ‘1’.

if ( GetPCCell "Seyda Neen, Census and Excise Office" == 1 )
	return
endif

if ( journalOnce == 1 )
	;Journal TR_DBAttack 10
	set journalOnce to -1
endif

The script stops if we are in the Seyda Neen Census and Excise Office, the “tutorial building” of Morrowind.
If the journalOnce is equal to 1, we set it to -1.

if ( GetPCSleep == 1 )
	if ( sleepOnce == 1 )
		return
	endif
	set sleepOnce to 1
	set dbchance to Random 100
	set attackmod to ( attackonce * 10 )
[cut code]
else
	set sleepOnce to 0
endif

This code looks a bit weird but it is one of the effects of the weird Morrowind scripting engine. The DB attack script is a “global script” which means it is run many times per second. The check for GetPCSleep checks if the player is asleep. If he is, we change the sleepOnce in such a way that the inner block is validated only once per player sleep. Now, the code gets interesting. The dbchange is a random number from 0-100. attackmod is set to be 10 times attackonce. Since we haven’t updated attackonce, we can assume it to be 0.
We get 5 somewhat equal blocks of code depending on the “strength” of the player. I will discuss the highest player level as it seems the most complex.

if ( playerlevel == 5 )
		set othermod to ( 90- attackmod )
		if ( dbchance <= othermod )
			WakeUpPC
			MessageBox "You are awakened by a loud noise."
			set dbnumber to ( dbnumber + 1 )
			if ( dbnumber > 2 )
				set dbnumber to 2
			endif
			set temp to dbnumber
			while ( temp != 0 )
				PlaceAtPC "db_assassin4" 1 128 1
				set temp to ( temp - 1 )
			endwhile
			set attackonce to ( attackonce + 1 )
				if ( journalOnce == -1 )
					return
				endif
				set journalOnce to 1
				set DBAttack to 1
		endif

The variable othermode is defined as 90 - attackmod. We know that attackmod is 10 times attackOnce. If the random dbchance is smaller than or equal to othermode, the assassin attack happens. dbnumber is increased by 1 with a maximum value of 2. Then, dbnumber of assassins are spawned of “quality” db_assassin4. This means that you will be attacked by one assassin the first time and two every time after that when level 30+. Then attackOnce is increased by one. This means that every attack decreases the othermod by 10. After 9 attacks on this playerLevel, the attack mechanism will not trigger! Afterwards, journalOnce is set to 1/true and DBAttack as well. Other playerLevel values have a lower othermod, weaker assassins and limit the amount of attackers to 1.

Which conclusions can we draw from this analysis?

  • Talking to Appeles Matius about the attacks stops the attacks
  • You will not be attacked in the Census Office
  • Being a higher level means you will be attacked by higher level assassins
  • Being a high enough level will spawn more assassins after the first attempt
  • The assassin attacks dry up after while on their own

This addresses some misconceptions that are held about the attacks. You can be attacked while being any level (though this is changed on the Xbox version). Talking to Appeles Matius is enough although some believe you need to travel to Mournhold. The attacks will end when “enough” assassins have been sent your way.