.GetRowVisibility LDA zTileRow \ Set the high byte of (Q P) to &60 + zTileRow CLC \ ADC #&60 \ This gives us the address where the GetTileAltitudes STA Q \ routine stored the altitude data for tile row zTileRow \ \ This works because we only ever call this routine with \ P = 0, and row zTile = 0 is at &6000, row zTile = 1 is \ at &6100 and so on LDA #31 \ Set xTileRow = 31 so we start iterating from the right STA xTileRow \ end of the current row (so xTileRow iterates from 31 \ to in the following loop) \ We now loop through each tile corner in the row, from \ right to left, in a loop that's split over part 1 and \ part 2 (and which has an anti-cracker interlude in the \ middle that has nothing to do with visibility checks) .rvis1 JSR ProcessSound \ Process any sounds or music that are being made in the \ background LDY #0 \ Set T = 0, so we can use it to capture the longest STY T \ axis in the vector calculation below DEY \ Set traceStepCounter = 255, so we start off by tracing STY traceStepCounter \ 255 steps (as the initial calculation calculates the \ vector to trace by dividing the full vector from the \ player to the tile corner by 256) \ \ We may reduce the number of steps below to make the \ process more efficient LDY xTileRow \ Set yTileRow to the altitude of the tile corner we are LDA (P),Y \ analysing LSR A \ STA yTileRow \ The value in the table at (Q P) contains the flatness \ of the tile in bit 0 and the altitude of the tile \ corner in bits 1-4, so we shift this value right by \ one place to extract the altitude LDX playerObject \ Fetch the cartesian coordinates of the player object JSR GetObjectCoords \ as three 24-bit numbers, as follows: \ \ xCoord(Hi Lo Bot) \ \ yCoord(Hi Lo Bot) \ \ zCoord(Hi Lo Bot) LDX #2 \ We now work through all three axes in turn, so set an \ axis counter in X to iterate through 2, 1 and 0 (for \ the z-axis, y-axis and x-axis respectively) \ \ The comments in the following loop will concentrate on \ the x-axis to keep things simple .rvis2 LDA #0 \ Zero the high byte of (xVector yVector zVector), which STA xVectorHi,X \ we will use to store the vector from the player to the \ tile corner SEC \ Set xVector(Lo Bot) = SBC xCoordLo,X \ ((xTileRow 0) - xCoord(Hi Lo)) / 256 STA xVectorBot,X \ LDA xTileRow,X \ So xVector(Lo Bot) contains the distance in the x-axis SBC xCoordHi,X \ from the player to the tile corner that we are STA xVectorLo,X \ analysing, divided by 256 \ \ The "divided by 256" part is subtle and important \ \ We want to divide the vector from the player to the \ tile corner into small steps, so we can move along the \ vector sequentially, checking on each step whether the \ vector is passing below ground level (in which case we \ will know that the line of sight from the player to \ the tile corner is obstructed by landscape) \ \ The above calculation subtracts two high byte/low byte \ 16-bit numbers in (xTileRow 0) and xCoord(Hi Lo) and \ puts the result into a low byte/bottom byte 16-bit \ number in xVector(Lo Bot) \ \ This is the same as dividing the result of the \ subtraction by 256, which we can see if we consider \ that this would be the normal high/low calculation: \ \ xVector(Hi Lo) = (xTileRow 0) - xCoord(Hi Lo) \ \ and we also have: \ \ xVector(Lo Bot) = xVector(Hi Lo) / 256 \ \ so we get: \ \ xVector(Lo Bot) \ = ((xTileRow 0) - xCoord(Hi Lo)) / 256 \ \ Also, it's worth noting that this is the vector from \ the player to the tile corner, because if you add this \ vector to the player's coordinates 256 times, then you \ get the tile corner's coordinates BPL rvis3 \ If the result is positive, jump to rvis3, with the low \ byte of the result (xVectorLo) in A DEC xVectorHi,X \ The result is negative, so set xVectorHi to %11111111 \ so it can be used as the high byte for the negative \ 24-bit number in xVector(Hi Lo Bot) LDA #0 \ Negate the result as follows (note that we ignore the SEC \ bottom byte, as we are only interested in the low byte SBC xVectorBot,X \ of the result): LDA #0 \ SBC xVectorLo,X \ (A *) = -xVector(Lo Bot) \ \ So this makes (A *) positive, to give this: \ \ (A *) = |xVectorLo xVectorBot| \ \ So the low byte of the result |xVectorLo| is in A .rvis3 \ By this point we have the following: \ \ * A = |xVectorLo| \ \ * xVectorHi is either 0 or %11111111, depending on \ the sign of xVector(Lo Bot) CMP T \ If A >= T then set T = A, so T keeps a record of the BCC rvis4 \ largest value of |xVectorLo| across all three axes STA T \ \ In other words, once we have finished looping through \ all three axes, T will contain the magnitude of the \ longest axis in the vector: \ \ T = max(|xVectorLo|, |yVectorLo|, |zVectorLo|) .rvis4 DEX \ Decrement the axis counter in X to move on to the next \ axis BPL rvis2 \ Loop back until we have processed all three axes \ At this point X is set to 255, which we use below when \ checking the secret entry code in CheckSecretStash LDA T \ If: ASL A \ ASL A \ T * 4 < 6 CMP #6 \ BCC rvis10 \ then: \ \ T < 1.5 \ \ and: \ \ max(|xVectorLo|, |yVectorLo|, |zVectorLo|) < 1.5 \ \ In other words, if this is true, then the longest axis \ of the vector from player to the tile corner is less \ than 1.5 (where each tile is of size 1), so the tile \ corner we are analysing is within 1.5 tile widths of \ the player \ \ We automatically mark all these close-by tiles as \ being potentially visible, so we jump to rvis10 with \ the C flag clear to store &FF as the visibility result \ (to record that this corner is potentially visible) \ The next step is an optimisation \ \ We could trace the vector from the player to the tile \ corner in steps of 256, but given that the landscape \ is only 32 tile corners across in each direction, this \ would mean that most of the time we would be stepping \ along the vector while staying above the same tile \ \ Instead we can scale up the size of our steps and do \ fewer of them, and as long as each individual step is \ smaller than a tile width, we can still check every \ landscape tile along the line of sight \ \ We therefore double the value in A, which contains the \ longest side of the vector, to be as high as possible \ while still fitting into one byte (which represents a \ fraction of a tile as it's the low byte of the vector) \ \ At the same time we double the size of the vector by \ doubling the following: \ \ xVector(Hi Lo Bot) \ \ yVector(Hi Lo Bot) \ \ zVector(Hi Lo Bot) \ \ though we don't actually need to shift the high bytes \ as we won't be shifting data out of the low bytes and \ they are either zero or %11111111 \ \ Because we have doubled the size of the vector, we \ also halve the number of steps in traceStepCounter \ \ This reduces the number of steps in the ray-tracing \ process as far as possible while keeping the size of \ each step to less than one tile (so that the tracing \ process still steps through each tile en route) .rvis5 ASL xVectorBot \ Double xVector(Lo Bot) to double the size of the ROL xVectorLo \ vector ASL yVectorBot ROL yVectorLo ASL zVectorBot ROL zVectorLo LSR traceStepCounter \ Halve traceStepCounter to halve the number of steps in \ the tracing process ASL A \ Shift A to the left to shift bit 7 into the C flag, to \ detect when A is as big as it can be within one byte BCC rvis5 \ Loop back to repeat the above scalings until we have \ scaled up the vector as much as we can \ So xVector(Hi Lo Bot) now contains the vector from the \ player to the tile corner, divided by 256 and scaled \ up as far as possible while still keeping the vector \ within the dimensions of a tile, and traceStepCounter \ contains the number of times this vector fits into the \ original vector from the player to the tile corner \ \ We can therefore trace the vector from the player to \ the tile corner by taking the player's coordinates and \ adding the xVector(Hi Lo Bot) vector traceStepCounter \ times, knowing that together, these steps will stop \ over every tile between the player and the tile corner \ in the process (we may stop over some tiles more than \ once, but that's OK) LDA zCoordHi \ Set zCoordHi = zCoordHi + &60 CLC \ ADC #&60 \ This sets zCoordHi to the high byte of the address for STA zCoordHi \ the extracted altitude data for the tile row on which \ the player is standing (as zCoordHi is the integer \ part of the player's z-coordinate, which is the number \ of the row containing the player) \ We now have a short interlude to check the secret code \ stash, as part of the game's anti-cracker code, and we \ pick up the tile visibility code in part 2Name: GetRowVisibility (Part 1 of 2) [Show more] Type: Subroutine Category: Drawing the landscape Summary: Set up the calculations to determine whether each tile corner in a tile row is obscured from the player by any intervening landscapeContext: See this subroutine in context in the source code References: This subroutine is called as follows: * GetTileVisibility calls GetRowVisibility
This routine populates a specified table with the visibility of each tile corner in a row, populating the table with 32 entries as follows: * %00000000 if the tile corner is not visible from the player's position * %11111111 if the tile corner is visible from the player's position
Arguments: zTileRow The tile z-coordinate of the tile row to analyse drawingTableOffset Defines where we store the results of the analysis: * oddVisibility when drawingTableOffset = 0 * evenVisibility when drawingTableOffset = 32 P P is always zero, so (Q P) is of the form &xx00 R R is always &20, so (S R) is of the form &xx20
Returns: (Q P) The address of the table that contains the altitude and shape data for tile row zTileRow, like this: * (Q P) = &6000 + (zTileRow 0)
[X]
Subroutine GetObjectCoords (category: 3D objects)
Get an object's coordinates
[X]
Subroutine ProcessSound (category: Sound)
Process any sound effects that have been configured so they play in the background (this is called regularly throughout gameplay)
[X]
Variable playerObject in workspace Zero page
The number of the player object
[X]
Label rvis10 in subroutine GetRowVisibility (Part 2 of 2)
[X]
Label rvis2 is local to this routine
[X]
Label rvis3 is local to this routine
[X]
Label rvis4 is local to this routine
[X]
Label rvis5 is local to this routine
[X]
Variable traceStepCounter in workspace Zero page
The number of steps in the ray-tracing process when calculating tile visibility in the GetRowVisibility routine
[X]
Variable xVectorBot in workspace Zero page
The x-coordinate of a vector (bottom byte)
[X]
Variable yVectorBot in workspace Zero page
The y-coordinate of a vector (bottom byte)
[X]
Variable zVectorBot in workspace Zero page
The z-coordinate of a vector (bottom byte)