Sonic and Sega Retro Message Board: Converting Sonic 2 Title Cards to an act-sensitive edition - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help
Page 1 of 1
    Locked
    Locked Forum

Converting Sonic 2 Title Cards to an act-sensitive edition I'll start with this, but I expect to extend it to the entire game

#1 User is offline silent.creature 

Posted 14 October 2016 - 08:05 PM

  • Silent Creature
  • Posts: 16
  • Joined: 28-January 09
  • Gender:Male
  • Location:Humaitá - Amazonas - Brazil
  • Project:None, actually.
  • Wiki edits:45
Well, after more than 5 years away, my real life finally allowed me to get back to my project (Sonic 2: Innovative). Since I lost the original code, I started again with the Git disassembly and I think I should share what I achieved now, that I wanted to achieve long time ago...

First, I need to apologize, because at that time (2010/2011), I started a mess on the SCHG-How To article "Extended level index past $10 in Sonic 2". I wanted to update some procedures kram wrote since I thought I could make them work at the time, but parts of them I accidentally deleted. Hopefully I got help to restore partially its content and kram came up with a revision, even updating it to the Git disassembly.

And, for now, I wanted to post a guide for making the game detect the title cards in an act-sensitive way instead of a zone-sensitive way. I found it useful, at least, for me, but it will not avoid me from extending the game properly, since I want, in my project, to keep intact the original zones (especially because of the built-in 2-player mode).

Ok, now to action. First of all, I recommend you to keep all the routines and tables associated with the title card object (Obj34) near its definition, and I'll tell how it should work while you read.

We must know that the main routines controlling title card data are LoadTitleCard, Obj34_ZoneName and Obj34_ActNumber; the main tables containing proper data are Off_TitleCardLetters, Obj34_TitleCardData and Obj34_MapUnc_147BA. We'll create some additional data to make this attempt to work properly.


Step 1: Prepare the card reading:

EDIT: I've mistaken the art. Now the procedure will affect the correct art.
Before the table Obj34_TitleCardData, and even before the macro there defined (titlecardobjdata), input two procedures, intercalated by two tables. The first of them will load the letters for each act, and the second will properly load the art needed. They are based on the original LoadTitleCard and LoadTitleCard0 routines:

Obj34_PrepareTitleCardData:
	bsr.w	Obj34_PrepareDecompressionRoutine
	
	moveq	#0,d0
	move.b	(Current_Zone).w,d0
	add.b	d0,d0
	add.b	(Current_Act).w,d0
	
	move.b	Off_TitleCardLetters(pc,d0.w),d0
	lea	TitleCardLetters(pc),a0
	lea	(a0,d0.w),a0
	move.l	#vdpComm(tiles_to_bytes(ArtTile_LevelName),VRAM,WRITE),d0
	rts

;==================
Off_TitleCardLetters:
	(1)
	
	(2)

;==================
Obj34_PrepareDecompressionRoutine:
		move.l	#vdpComm(tiles_to_bytes(ArtTile_ArtNem_TitleCard),VRAM,WRITE),(VDP_control_port).l
	lea	(ArtNem_TitleCard).l,a0
	jsrto	(NemDec).l, JmpTo2_NemDec
	lea	(Level_Layout).w,a4
	
	moveq	#0,d6
	move.b	(Current_Zone).w,d6
	add.b	d6,d6
	add.b	(Current_Act).w,d6
	
	add.b	d6,d6
	add.b	d6,d6
	
	movea.l	TitleCard_Art_Handler(pc,d6.w),a0
	jmpto	(NemDecToRAM).l, JmpTo_NemDecToRAM

;==================
TitleCard_Art_Handler:
	(3)

; ===========================================================================
; This macro declares data for an object. The data includes:
	[...]

Now, in the space reserved with (1), you will build a table referring to the existing title card letters, so, move them to the space reserved in (2). Now it will become

Off_TitleCardLetters:
	(1)

	 ; temporarily remap characters to title card letter format
 ; Characters are encoded as Aa, Bb, Cc, etc. through a macro
 charset 'A',0	; can't have an embedded 0 in a string
 charset 'B',"\4\8\xC\4\x10\x14\x18\x1C\x1E\x22\x26\x2A\4\4\x30\x34\x38\x3C\x40\x44\x48\x4C\x52\x56\4"

	[...]

TitleCardLetters_DEZ:
	titleLetters	"DEATH EGG"

 charset ; revert character set

The table that must be built in space (1) is a modified version of the table Off_TitleCardLetters. So, delete the original data and build another one in space (1) as follows:

Off_TitleCardLetters:
	dc.w	TitleCardLetters_EHZ-Off_TitleCardLetters, TitleCardLetters_EHZ-Off_TitleCardLetters	; 00 EHZ1/EHZ2
	dc.w	TitleCardLetters_EHZ-Off_TitleCardLetters, TitleCardLetters_EHZ-Off_TitleCardLetters	; 01 ----/----

	[...]

	dc.w	TitleCardLetters_SCZ-Off_TitleCardLetters, TitleCardLetters_SCZ-Off_TitleCardLetters	; 10 SCZ /SCZ


In the space (3), in addition to the routine I provided, I turned the game able to receive art for each act. This way, for the original behaviour, the table must be built as:

TitleCard_Art_Handler:
	dc.l	ArtNem_TitleCard, ArtNem_TitleCard	; 00 EHZ1/EHZ2
	dc.l	ArtNem_TitleCard, ArtNem_TitleCard	; 01 ----/----

	[...]

	dc.l	ArtNem_TitleCard, ArtNem_TitleCard	; 10 SCZ /SCZ

I want to note this is not a relational index, but an offset index.


Step 2: fixing title card main configuration

Now, we need to fix the table Obj34_TitleCardData, so the game can properly process what texts are from zones, and what texts are reserved. To do this, we should define some indexes before it:

; Pointer declaration
const_nzon = $10 ; number of zones (this may exist)
const_acts = const_nzon+const_nzon+1 ; (doubling it and adding 1, so we have all the game acts)
const_text = const_acts+1 ; (the text immediately after the zone labels is "ZONE")
const_actn = const_text+1 ; (immediately come after that number "1")
const_botm = const_actn+3 ; (after the numbers, the yellow bar)
const_redp = const_botm+1 ; (and, finally, the red stripe)

Obj34_TitleCardData:

Maybe the names are strange, but you can rename them as you feel comfortable provided you change the body of what follows.

Now, we fix properly the table, and it becomes

Obj34_TitleCardData:
	titlecardobjdata  8,          0, $80, $1B, $240, $120, $B8	; zone name
	titlecardobjdata $A, const_text, $40, $1C,  $28, $148, $D0	; "ZONE"
	titlecardobjdata $C, const_actn, $18, $1C,  $68, $188, $D0	; act number
	titlecardobjdata  2,          0,   0,   0,    0,    0,   0	; blue background
	titlecardobjdata  4, const_botm, $48,   8, $2A8, $168,$120	; bottom yellow part
	titlecardobjdata  6, const_redp,   8, $15,  $80,  $F0, $F0	; left red part
Obj34_TitleCardData_End:

Let's continue. If you build and try to run, you'll get or a crash or a strange repeating mapping.


Step 3: fix inner routines to load names and system reserved data

Next to fix is the routine Obj34_ZoneName. It needs to process the name as we already have configured it, instead of its original zone-sensitive way. There is its start:

Obj34_ZoneName:		; the name of the zone, coming in
	jsr	Obj34_Wait(pc)
	move.b	(Current_Zone).w,mapping_frame(a0)
	[...]

Change the move.b line to:

	moveq	#0,d6
	move.b	(Current_Zone).w,d6
	add.b	d6,d6
	add.b	(Current_Act).w,d6
	move.b	d6,mapping_frame(a0)

What we've done is teaching the game how to process our tables. But, we still need to fix the remaining pointers (ZONE, ...). We will, also, make another table, which allows us to make these fixes more easily. Get to the routine Obj34_ActNumber. It will be like this:

Obj34_ActNumber:	; the act number, coming in
	jsr	Obj34_Wait(pc)
	move.b	(Current_Zone).w,d0	; get the current zone
	cmpi.b	#sky_chase_zone,d0	; is it Sky Chase?
	beq.s	BranchTo9_DeleteObject	; if yes, branch
	cmpi.b	#wing_fortress_zone,d0	; is it Wing Fortress?
	beq.s	BranchTo9_DeleteObject	; if yes, branch
	cmpi.b	#death_egg_zone,d0	; is it Death Egg Zone?
	beq.s	BranchTo9_DeleteObject	; if yes, branch
	move.b	(Current_Act).w,d1	; get the current act
	addi.b	#$12,d1			; add $12 to it (this is the index of the "1" frame in the mappings)
	cmpi.b	#metropolis_zone_2,d0	; are we in Metropolis Zone Act 3?
	bne.s	+			; if not, branch
	moveq	#$14,d1			; use the "3" frame instead
+
	move.b	d1,mapping_frame(a0)	; set the mapping frame

Change this whole thing to

Obj34_ActNumber:	; the act number, coming in
	jsr	Obj34_Wait(pc)
	
	jsr	Obj34_CardCheck

And we will create the subroutine Obj34_CardCheck after all the definitions of Obj34, namely, after the table Animal_PLCTable. So, there, our routine will fit properly, as:

Animal_PLCTable: zoneOrderedTable 1,1

	[...]

    zoneTableEnd

	dc.b PLCID_SczAnimals	; level slot $11 (non-existent), not part of main table
	even

Obj34_CardCheck:	

	moveq	#0,d0
	move.b	(Current_Zone).w,d0
	add.b	d0,d0
	add.b	(Current_Act).w,d0

	; Set a word-size offset
	add.w	d0,d0
	move.w	Card_Display_Index(pc,d0.w),d0
	jmp	Card_Display_Index(pc,d0.w)
	
Card_Display_Index:
	(4)

Ok. This table, Card_Display_Index is a response table. There the subroutine will know if you are placing "Act 1", "Act 2", "Act 3", or nothing. After this reserved space, let's put our responses as routines:

Card_Have_Null:
	bra.w	DeleteObject	

Card_Have_Act1:
	moveq	#0,d1
	addi.b	#const_actn+0,d1
	bra.s	Card_Display_Frame
	
Card_Have_Act2:
	moveq	#0,d1
	addi.b	#const_actn+1,d1
	bra.s	Card_Display_Frame
	
Card_Have_Act3:
	moveq	#0,d1
	addi.b	#const_actn+2,d1
	;bra.s	Card_Display_Frame
	
Card_Display_Frame:
	move.b	d1,mapping_frame(a0)
	
	rts

Now we can build the response table accordingly. So, this table will be like:

Card_Display_Index:
	dc.w	Card_Have_Act1-Card_Display_Index, Card_Have_Act2-Card_Display_Index	; 00 EHZ1/EHZ2
	
	[...]
	
	dc.w	Card_Have_Null-Card_Display_Index, Card_Have_Null-Card_Display_Index	; 10 SCZ /SCZ 



Step 4: converting the mappings table

Then, we need to relocate and properly convert the table Obj34_MapUnc_147BA and its associated data. You can move all the data

Obj34_MapUnc_147BA:
	
	[...]
	
word_14C32:	
	dc.w 7
	
	dc.w $9003, $85D4, $82EA, 0
	dc.w $B003, $85D4, $82EA, 0
	dc.w $D003, $85D4, $82EA, 0
	dc.w $F003, $85D4, $82EA, 0
	dc.w $1003, $85D4, $82EA, 0
	dc.w $3003, $85D4, $82EA, 0
	dc.w $5003, $85D4, $82EA, 0

to immediately after the routine Card_Display_Frame. To build the table, I used new label names according to the zone names. This is the equivalence:

	word_147E8 > EHZ
	word_14842 > MZ/MTZ (I prefer MZ, but that doesn't matter)
	word_14894 > HTZ
	word_148CE > HPZ
	word_14930 > OOZ
	word_14972 > MCZ
	word_149C4 > CNZ
	word_14A1E > CPZ
	word_14A88 > ARZ
	word_14AE2 > SCZ
	word_14B24 > WFZ
	word_14B86 > DEZ
	word_14BC8 > "ZONE"
	word_14BEA > "1"
	word_14BF4 > "2"
	word_14BFE > "3"
	word_14C08 > yellow bar written "SONIC THE HEDGEHOG"
	word_14C32 > left red stripe

And the source table we need for Obj34_MapUnc_147BA is built like:

Obj34_MapUnc_147BA:
	dc.w	TC_EHZ-Obj34_MapUnc_147BA,     TC_EHZ-Obj34_MapUnc_147BA	; 00 EHZ1/EHZ2
	dc.w	TC_EHZ-Obj34_MapUnc_147BA,     TC_EHZ-Obj34_MapUnc_147BA	; 01 ----/----

	[...]

	dc.w	TC_SCZ-Obj34_MapUnc_147BA,     TC_SCZ-Obj34_MapUnc_147BA	; 10 SCZ /SCZ 


Step 5: allow the game to properly load the data

And, finally, we should tell the game to load the parameters we defined. Go to LoadTitleCard. There will be its original routine yet, as:

LoadTitleCard:
	bsr.s	LoadTitleCard0
	moveq	#0,d0
	move.b	(Current_Zone).w,d0
	move.b	Off_TitleCardLetters(pc,d0.w),d0
	lea	TitleCardLetters(pc),a0
	lea	(a0,d0.w),a0
	move.l	#vdpComm(tiles_to_bytes(ArtTile_LevelName),VRAM,WRITE),d0

Just change it to:

LoadTitleCard:
	jsr	Obj34_Load_Title_Card


Note

If you run into errors while building, try to fix the branches to BranchTo9_DeleteObject in the middle of Obj34 (instead of .s, fix to .w).
This post has been edited by silent.creature: 15 October 2016 - 01:17 PM

Page 1 of 1
    Locked
    Locked Forum

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users