
About this mod
To make the color of the needle for the tachometer and speedometer, editable changes to the source code had to be made.
The rebuild executable loads faster and supports different color needles if they are set in the car file.
This Package includes the original 11 and 27 additional cars (max 32 are accepted at once) and support file
- Requirements
- Permissions and credits
- Mirrors
- Donations
For the configurable needle colour, the initial implementation was very simple. Near the middle of seg005.asm, there's the label loc_23456. At this point, the game engine calculates how the needle is to be drawn by passing the needle axis coordinates as one point and the corresponding spoke for the current speed as the second point and then calling a function that draws a line between them. This functions also requests a line colour as a parameter, so Stunts was originally passing a variable that, reaching this point, was always valued at 15 (white). The same thing is later done for the tachometer:
https://bitbucket.org/dreadnaut/restunts/src/master/src/restunts/asmorig/seg005.asm#lines-2710
loc_23456:
cmp [bp+var_6], 0
jnz short loc_23485
mov ax, si
shl ax, 1
mov [bp+var_20], ax
pushmeter_needle_color ; This variable in Stunts original code always ended up with a value of 15
mov bx, ax
mov al, (simd_player.spdpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.spdpoints[bx]
push ax
push simd_player.spdcenter.y2
push simd_player.spdcenter.x2
call preRender_line
add sp, 0Ah
loc_23485:
mov ax, di
shl ax, 1
mov [bp+var_20], ax
pushmeter_needle_color ; Again, same colour is passed for the tachometer
mov bx, ax
mov al, (simd_player.revpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.revpoints[bx]
push ax
push simd_player.revcenter.y2
push simd_player.revcenter.x2
call preRender_line
add sp, 0Ah
mov al, [bp+var_2]
cbw
or ax, ax
jz short loc_234BE
cmp ax, 2 ; st. whl. position flag
jz short loc_234EC
jmp short loc_234DE
; align 2
db 144
I noticed that there exists a structure called simd_player, which contains the car information from the "simd" chunk in the corresponding CAR*.RES file for the current player car. Individual fields from this structure could be used as variables, so I found the reference name for an unused car configuration that's known in CarWorks as "Red #5". I picked this one because all original cars have this value set to 16, which is the closest to a perfect white (15) in Stunts palette. It's actually just slightly darker. This way, after the patch, old cars would continue to display white needles. So all we had to do was replace meter_needle_color with simd_player.field_A6+8, which is a pointer to Red #5. The resulting code goes as follows:
loc_23456:
cmp [bp+var_6], 0
jnz short loc_23485
mov ax, si
shl ax, 1
mov [bp+var_20], ax
; Patch #1 ----*
; Replaced needle colour for speed-o-meter
; Original code:
;pushmeter_needle_color
push simd_player.field_A6+8
mov bx, ax
mov al, (simd_player.spdpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.spdpoints[bx]
push ax
push simd_player.spdcenter.y2
push simd_player.spdcenter.x2
call preRender_line
add sp, 0Ah
loc_23485:
mov ax, di
shl ax, 1
mov [bp+var_20], ax
; Replaced needle colour for RPM meter
; Original code:
;pushmeter_needle_color
push simd_player.field_A6+8
mov bx, ax
mov al, (simd_player.revpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.revpoints[bx]
push ax
push simd_player.revcenter.y2
push simd_player.revcenter.x2
call preRender_line
add sp, 0Ah
mov al, [bp+var_2]
cbw
or ax, ax
jz short loc_234BE
cmp ax, 2 ; st. whl. position flag
jz short loc_234EC
jmp short loc_234DE
; align 2
db 144
Notice how this change only replaces a pointer with another, thus resulting in the same code length, guaranteed to be perfectly stable. Only drawback is that both needles have to be the same colour.
To achieve a separately configurable second needle, more complex changes had to be made. The first needle (speedometer) works exactly the same way as before, but now, for the tachometer needle, we needed a procedure that guaranteed the default would also result in white-white, but that could also allow for two different colours being represented. Because the colour parameter accepted by the line function in Stunts is a word, yet only the lower byte is read (since Stunts uses 8 bit colour), the most efficient way of achieving this was by using the higher byte of Red #5 to define the tachometer colour. But the default for this high byte is zero. So I had to make it so that when this byte is zero, both needles will be the colour defined by the lower byte, while, when non-zero, this value would give the tachometer needle colour and the lower byte would give the one for the speedometer. Implementation resulted in the following code:
loc_23456:
cmp [bp+var_6], 0
jnz short loc_23485
mov ax, si
shl ax, 1
mov [bp+var_20], ax
; Patch #1 ----*
; Replaced needle colour for speed-o-meter
; Original code:
;pushmeter_needle_color
push simd_player.field_A6+8
mov bx, ax
mov al, (simd_player.spdpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.spdpoints[bx]
push ax
push simd_player.spdcenter.y2
push simd_player.spdcenter.x2
call preRender_line
add sp, 0Ah
loc_23485:
mov ax, di
shl ax, 1
mov [bp+var_20], ax
; Replaced needle colour for RPM meter
; Original code:
;pushmeter_needle_color
; This line is for the first version of the patch
;push simd_player.field_A6+8
; This block is the new version of the patch
cmp byte ptr [simd_player.field_A6+9], 0 ; Check to see if the high byte is zero
jz use_the_same_colour
push simd_player.field_A6+9 ; It's not, so push this high byte as the colour
jmp needle_value_successfully_set ; Continue with the old code
use_the_same_colour:
push simd_player.field_A6+8 ; It is, so push the low byte as the colour
needle_value_successfully_set:
nop ; Alignment bytes
nop
nop
mov bx, ax
mov al, (simd_player.revpoints+1)[bx]
sub ah, ah
push ax
mov al, simd_player.revpoints[bx]
push ax
push simd_player.revcenter.y2
push simd_player.revcenter.x2
call preRender_line
add sp, 0Ah
mov al, [bp+var_2]
cbw
or ax, ax
jz short loc_234BE
cmp ax, 2 ; st. whl. position flag
jz short loc_234EC
jmp short loc_234DE
; align 2
db 144
Because this change required additional bytes in the code, Stunts quickly complained about the alignment. We solved this by testing how many bytes we needed to add. We correctly guessed that an alignment to 16 bytes would be enough because it's the real mode paragraph length. It turned out that four bytes did the trick to align to 16 bytes. Note that these four nops actually take processor time, which is negligible, but it'd be better to move these bytes to non-executable space, like after the db 144, for example. That'd also make the code cleaner.
Even with this alignment, it's impossible to be sure that Stunts doesn't make any assumption that could make it unstable. After quite some testing, it appears that it is indeed stable, but the more we test, the better. While having different colour needles isn't a very useful feature, implementing this is important as it shows how Stunts is accepting inserted code and complex mods can be made.