Editing the HUD
Editing the HUD is somewhat difficult. You can edit the mappings in SonMapEd, but there's no art file you can load because the art is dynamically refreshed on each frame. You can, however, take a savestate, extract the VRAM starting at $D940 (where the HUD patterns start in VRAM) from the savestate, and load that as the art file.
Forcing leading zeroes on the HUD is done in the code. The HudUpdate subroutine updates the patterns in VRAM that are used the HUD sprite mappings. Just remove these lines to get leading zeroes for the score and rings (you probably don't care about the score since you want to remove it):
Here, d2 is the value of the current digit. When it encounters a digit that is not zero, it sets d4 to 1. Zeroes that come before d4 is set to 1 are left blank, and zeroes that come after d4 is set are drawn as "0". By removing these lines, we always draw all zeroes as "0". Since the rings counter is already programmed for 3 digits, you don't have anything else to do for the rings counter!
For the lives counter, it's the same pattern, but elsewhere. Remove these lines:
To get an animated ring in the HUD, I'd create a new object type (or add a routine to an existing object, like Obj25) that uses the same art_tile, mappings and animation code as the debug ring object type (Obj25) or scattering ring object type (Obj37), but without collision and with screen-space coordinates.
Default player configuration
There are 2 variables controlling the player configuration: Player_option and Player_mode. The value of Player_option is the value selected on the options screen. Player_mode is the value actually used during a level. When a level is started, if the game is playing a demo or in 2P versus mode, Player_mode is forced to 0 (Sonic and Tails); otherwise, the value of Player_option is used.
The value of Player_option is never explicitly set; it is set to 0 when the game clears most of the RAM on startup. I think the best place to set it is just before the MainGameLoop label:
move.b #GameModeID_SegaScreen,(Game_Mode).w ; => SegaScreen
move.w #1,(Player_option).w ; +++
This way, it's set to Sonic alone
on reset, but it won't override the choice the player made on the options screen.
The ring animation is controlled globally; all rings display the same frame at the same time. The Rings_anim_frame variable specifies what's the current frame. The ChangeRingFrame subroutine controls that variable, and uses Rings_anim_counter to control the animation speed. This is the original code:
addq.b #1,(Rings_anim_frame).w ; animate rings in the level (obj25)
When Rings_anim_counter becomes negative, its value is reset to 7 and the current frame is incremented by 1. The ANDI instruction then restricts the value to the 0-3 range. If you want to double the number of frames while keeping the same duration for an animation cycle, you'll need to reset Rings_anim_counter to 3 and use andi.b #7,(Rings_anim_frame).w. Then, you'll have to add frames between the existing frames in the mappings (obj37_a.bin). Note that this will move the sparkle frames, so you'll have to fix the animation script for it (Ani_Ring). Change dc.b 5,4,5,6,7,$FC to dc.b 5,8,9,$A,$B,$FC in Ani_Ring to reference the new frame numbers of the sparkle frames.
Spilled rings use a separate counter: Ring_spill_anim_counter. The algorithm to control it is a bit more complicated:
beq.s + ; rts
move.b d0,(Ring_spill_anim_frame).w ; animate scattered rings (obj37)
Change rol.w #7,d0 to rol.w #6,d0 and change andi.w #3,d0 to andi.w #7,d0. The first change should preserve the animation speed and the second change will use all 8 frames.