Skip to navigation


Extra tracks: HookFlattenHills

Name: HookFlattenHills [Show more] Type: Subroutine Category: Extra tracks Summary: Flatten any hills in the verge buffer, calculate the hill height and track width, cut objects off at the hill height Deep dive: Secrets of the extra tracks Code hooks in the extra tracks
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * newContentHi calls HookFlattenHills * newContentLo calls HookFlattenHills

This routine is called from MapSegmentsToLines to flatten the height of the verge entries in the verge buffer that are hidden by the nearest hill to the player, so that the ground behind the nearest hill is effectively levelled off. It also sets horizonTrackWidth to 80% of the track width at the hill crest.
Arguments: Y Index of the last entry in the track verge buffer - 1: * segmentListRight - 1 for the right verge * segmentListPointer - 1 for the left verge
.HookFlattenHills TYA \ Set bit 5 of blockOffset to bit 5 of Y, so blockOffset AND #%00100000 \ is non-zero if Y >= 32 (i.e. Y is pointing to the STA blockOffset \ verge buffer for the outer verge edges) LDA #0 \ Set A = 0, so the track line starts at the bottom of \ the screen \ We now work our way backwards through the verge buffer \ from index Y - 1, starting with the closest segments, \ checking the pitch angles and maintaining a maximum \ value in topTrackLine .hill1 STA topTrackLine \ Set topTrackLine = A .hill2 DEY \ Decrement Y to point to the next entry in the verge \ buffer, so we are moving away from the player LDA yVergeRight,Y \ Set A to the pitch angle of the current entry in the \ verge buffer CMP horizonLine \ If A >= horizonLine, then the verge is on or higher BCS hill3 \ than the horizon line, so jump to hill3 to exit the \ hook routine and rejoin the original game code, as \ everything beyond this segment in the verge buffer \ will be hidden CMP topTrackLine \ If A >= topTrackLine, jump back to hill1 to set BCS hill1 \ topTrackLine to A and move on to the next segment, \ so topTrackLine maintains the maximum track line as \ we work through the verge buffer \ If we get here then A < horizonLine (so the verge is \ below the horizon) and A < topTrackLine (so the verge \ is lower than the highest segment already processed) \ \ In other words, this segment is lower than the ones \ before it, so it is hidden by a hill LDA topTrackLine \ Set the pitch angle of entry Y to topTrackLine (this ADC #0 \ ADC instruction has no effect, as we know the C flag STA yVergeRight,Y \ is clear, so I'm not sure what it's doing here - a \ bit of debug code, perhaps?) LDA blockOffset \ If blockOffset is non-zero, loop back to hill2 to move BNE hill2 \ on to the next segment \ If we get here then blockOffset = 0, which will only \ be the case if we are working through the inner verge \ edges (rather than the outer edges), and we haven't \ done the following already \ \ In other words, the following is only done once, for \ the closest segment whose pitch angle dips below the \ segment in front of it (i.e. the closest crest of a \ hill) LDA topTrackLine \ Modify the DrawObject routine at dobj3 instruction #6 STA &1FEA \ so that objects get cut off at track line number \ topTrackLine instead of horizonLine when they are \ hidden behind a hill INY \ Increment Y so the call to gtrm2+6 calculates the \ track width for the previous (i.e. closer) segment in \ the verge buffer JSR gtrm2+6 \ Call the following routine, which has already been \ modified by this point to calculate the following for \ track segment Y (i.e. the segment in front of the \ current one): \ \ horizonTrackWidth \ = 0.8 * |xVergeRightHi - xVergeLeftHi| \ \ So this sets horizonTrackWidth to 80% of the track \ width of the crest of the hill DEY \ Decrement Y back to the correct value for the current \ entry in the verge buffer SEC \ Rotate a 1 into bit 7 of blockOffset so it is now ROR blockOffset \ non-zero, so we only set horizonTrackWidth once as we \ work through the verge buffer BMI hill2 \ Jump back to hill2 (this BMI is effectively a JMP as \ we just set bit 7 of blockOffset) .hill3 LDY vergeBufferEnd \ Set the values of Y and U so they are the same as they DEY \ would be at this point in the original code, without STY U \ the above code being run JMP CheckVergeOnScreen \ Implement the call that we overwrote with the call to \ the hook routine, so we have effectively inserted the \ above code into the main game (the JMP ensures we \ return from the subroutine using a tail call) EQUB &00 \ This byte appears to be unused