Sunday, February 16, 2025

Ultima III - More strange things

Most of the time, an MSDOS's COM file had all its data (static and not) at the begin of it, and the code after. It begins with a JMP instruction though, to "skip" the data. But in the case of EXODUS.COM, data and code alternate several times. I used this observation to "cut" the executable into several modules -- understand several ASM files. This may or may not reflect the original development design, but at least it makes the sources a little more readable ... in my opinion at least.

Some of the data and code is local to the module in which it is declared, and some is not. Nothing strange here.

There are several text strings that are declared only once, and used by different modules; take this declaration for instance:

TXT_What    db "<-What?",0ah,0

This is the text displayed when the player issues an invalid command. This text is shared by several functions/procedures across several modules. And I think that it is a good design: It saves memory, it simplifies debugging, development, localization ...

On the other hand, you also have the same strings declared several times:

D_1707    db "Pass",0ah,0
...
D_77EA    db "Pass",0ah,0
...
D_8885    db "Pass",0ah,0

This  mixed approach on design unnecessary complicates the work of the developer. Of course, without the original source code in my possession all I can do is assumption: were those modules supposed to be separates executable files ? Was it because of the design of the apple II version that made it necessary to have redundancy ? Was there several programmers involved with different coding habits ? ... ?

Want another strange thing yet ?

All right, here are two pieces of code, the first one being called when the player casts the wizard spell Repond and the second one, the cleric spell Pontori:

;Wizard spell:"Repond"
C_5F9F:
	;-- combat only --
	CMP	party[PARTY_02],LOC_COMBAT
	JNZ	C_5FD1
	;-- --
	CMP	enemy_type,TIL_18	;Orc
	JNZ	C_5FD1
	;-- only once per combat --
	CMP	D_84E3,0
	JNZ	C_5FD1
	MOV	D_84E3,0FFh
	;-- 50% chance --
	MOV	AL,DL	;backup DL
	MOV	DH,0FFh
	CALL	u3_rand
	SHL	DL,1
	JB	C_5FD1
	MOV	DL,AL	;restore DL
	;-- --
	CALL	SpellSFX

	MOV	AL,0FFh
	CALL	C_5EAA	;magical attack(all)

	JMP	C_5E69	;cast spell:end
;----
C_5FD1:	JMP	w_Failed

...

;Cleric spell:"Pontori"
C_6153:
	;-- combat only --
	CMP	party[PARTY_02],LOC_COMBAT
	JNZ	C_6185
	;-- --
	CMP	enemy_type,TIL_19	;Skeleton
	JNZ	C_6185
	;-- only once per combat --
	CMP	D_84E3,0
	JNZ	C_6185
	MOV	D_84E3,0FFh
	;-- 50% chance --
	MOV	AL,DL	;backup DL
	MOV	DH,0FFh
	CALL	u3_rand
	SHL	DL,1
	JB	C_6185
	MOV	DL,AL	;restore DL
	;-- --
	CALL	SpellSFX

	MOV	AL,0FFh
	CALL	C_5EAA	;magical attack(all)

	JMP	C_5E69	;cast spell:end
;----
C_6185:	JMP	w_Failed

So what do we notice ? Well, the code is almost identical in both cases. Only the target of the spell differs: orc-related creatures for Repond and skeleton-related creatures for Pontori. And what do we notice also ? The "only once per combat" piece of code of course; it uses a flag that is reset at the start of each combat. The same flag for both spells, which insures that either spell can be cast only once per combat.

Well since magic is not in my main expertise field, it is hard for me to give any explanation to this, but I tried to check Ultima III's documentation and didn't find the description for this feature. Was the player supposed to guess it by himself ? And then, why make the two spells share the same flag ? Was it a copy/paste error ?

(To be honest, I just realized that one's party can only be attacked by one type of creature at the time, which means that having the flag shared by the two spells might not be a problem after all)

 Ok, that's all for this time. Stay tuned for more tech-related stuff on Ultima III.

No comments: