.AddEnemiesToTiles JSR GetHighestTiles \ Calculate both the highest tiles in each 4x4 block of \ tiles in the landscape and the altitude of the highest \ tile, putting the results in the following variables: \ \ * maxAltitude contains the altitude of the highest \ tile in each 4x4 block in the landscape \ \ * xTileMaxAltitude contains the tile x-coordinate of \ the highest tile in each 4x4 block in the \ landscape \ \ * zTileMaxAltitude contains the tile z-coordinate of \ the highest tile in each 4x4 block in the \ landscape \ \ * tileAltitude contains the altitude of the highest \ tile in the entire landscape LDX #0 \ We now loop through the number of enemies, adding one \ enemy for each loop and iterating numberOfEnemies \ times, so set an enemy counter in X to count the \ enemies as we add them \ \ If this is a level with only one enemy, then that \ enemy must be the Sentinel, so when X = 0, we add the \ Sentinel to the landscape, otherwise we add a sentry \ \ Enemies are allocated object numbers from 0 to 7, with \ the Sentinel in object #0, and sentries from object #1 \ to object #7 (if there are any) .aden1 STX enemyCounter \ Set enemyCounter to the enemy counter in X, so we can \ retrieve it later in the loop LDA #1 \ Set the object type for object #X to type 1, which STA objectTypes,X \ denotes a sentry, so we spawn sentries in objects #1 \ to #numberOfEnemies (this also sets the type for \ object #0 to type 1, but this gets overridden when \ the Sentinel is spawned as object #0 below) \ We now work down the landscape, from the highest peaks \ down to lower altitudes, looking for suitable tile \ blocks to place an enemy \ \ To do this we start with tile blocks that are at an \ altitude of tileAltitude (which we set above to the \ altitude of the highest tile in the landscape), and we \ work down in steps of 16 .aden2 JSR GetTilesAtAltitude \ Set tilesAtAltitude to a list of tile block numbers \ whose highest tiles in the 4x4 block are at an \ altitude of tileAltitude, returning the length of the \ list in T and a bit mask in bitMask that has a \ matching number of leading zeroes as T BCC aden3 \ If the call to GetTilesAtAltitude returns at least one \ tile block at this altitude then the C flag will be \ clear, so jump to aden3 to add an enemy to one of the \ matched tile blocks LDA tileAltitude \ Otherwise we didn't find any tile blocks at this SEC \ altitude, so subtract 1 from the high nibble of SBC #%00010000 \ tileAltitude to move down one level (we subtract from STA tileAltitude \ the high nibble because tileAltitude contains tile \ data, which has the tile altitude in the high nibble \ and the tile shape in the low nibble) BNE aden2 \ Loop back to check for tile blocks at the lower \ altitude until we have reached an altitude of zero STX numberOfEnemies \ When the GetTilesAtAltitude routine returns with no \ matching tile blocks, it also returns a value of &FF \ in X, so this sets numberOfEnemies to -1 JMP aden6 \ Jump to aden6 to set the value of minEnemyAltitude \ and return from the subroutine .aden3 \ If we get here then we have found at least one tile \ block at the current altitude, so we now pick one of \ them, using the next seed number to choose which one, \ and we then add an enemy to the highest tile in the \ block \ \ We only pick one tile at this altitude so that the \ enemies are spread out over various altitudes JSR GetNextSeedNumber \ Set A to the next number from the landscape's sequence \ of seed numbers AND bitMask \ The call to GetTilesAtAltitude above sets bitMask to a \ bit mask that has a matching number of leading zeroes \ as the number of tile blocks at this altitude, so this \ instruction converts A into a number with the same \ range of non-zero bits as T CMP T \ If A >= T then jump back to fetch another seed number BCS aden3 \ When we get here, A is a seed number and A < T, so \ A can be used as an offset into the list of tile \ blocks in tilesAtAltitude (which contains T entries) TAY \ Set Y to the seed number in A so we can use it as an \ index in the following instruction LDX tilesAtAltitude,Y \ Set X to the Y-th tile block number in the list of \ tile blocks at an altitude of tilesAtAltitude \ We now zero the maximum tile altitudes for tile block \ X and the eight surrounding tile blocks, so that \ further calls to GetTilesAtAltitude won't match these \ tiles, so we therefore won't put any enemies on those \ blocks (this ensures we don't create enemies too close \ to each other) LDA #0 \ Set A = 0 so we can zero the maximum tile altitudes \ for tile block X and the eight surrounding blocks STA maxAltitude-9,X \ Zero the altitudes of the three tile blocks in the row STA maxAltitude-8,X \ in front of tile block X STA maxAltitude-7,X STA maxAltitude-1,X \ Zero the altitudes of tile block X and the two blocks STA maxAltitude,X \ to the left and right STA maxAltitude+1,X STA maxAltitude+7,X \ Zero the altitudes of the three tile blocks in the row STA maxAltitude+8,X \ behind tile block X STA maxAltitude+9,X LDA xTileMaxAltitude,X \ Set (xTile, zTile) to the tile coordinates of the STA xTile \ highest tile in the tile block, which is where we can LDA zTileMaxAltitude,X \ place an enemy STA zTile LDX enemyCounter \ Set X to the loop counter that we stored above BNE aden4 \ If the loop number is non-zero then we are adding a \ sentry, so jump to aden4 \ If we get here then the enemy counter is zero, so we \ are adding the Sentinel and the Sentinel's tower STA zTileSentinel \ Set (xTileSentinel, zTileSentinel) to the tile LDA xTile \ coordinates of the highest tile in the tile block, STA xTileSentinel \ which we put in (xTile, zTile) above, so this is the \ tile coordinate where we now spawn the Sentinel LDA #5 \ Set the object type for object #0 to type 5, which STA objectTypes \ denotes the Sentinel (so the Sentinel is always \ object #0, while other objects that are spawned are \ allocated to object #63 and work down the numbers) LDA #6 \ Spawn the Sentinel's tower (an object of type 6), JSR SpawnObject \ returning the object number of the new object in X \ and currentObject JSR PlaceObjectOnTile \ Place object #X on the tile anchored at (xTile, zTile) \ to place the tower on the landscape LDA #0 \ Set the tower object's objectYawAngle to 0, so it's STA objectYawAngle,X \ facing forwards and into the screen LDX enemyCounter \ Set X to the enemy counter, so X now contains the \ object number of the Sentinel (which is always zero \ as we only add the Sentinel on the first iteration of \ the loop) \ \ We now place the Sentinel object on the tile, which \ therefore places the Sentinel on top of the tower .aden4 JSR PlaceObjectOnTile \ Place object #X on the tile anchored at (xTile, zTile) \ to place the Sentinel or sentry in the correct place \ on the landscape JSR ResetMeanieScan \ Reset the data stored for any meanie scans that the \ enemy has tried in the past, so the enemy can start \ looking for potential meanies with a clean slate JSR GetNextSeedNumber \ Set A to the next number from the landscape's sequence \ of seed numbers LSR A \ Set the C flag to bit 7 of A (this also clears bit 7 \ of A but that doesn't matter as we are about to clear \ it in the next instruction anyway) AND #63 \ Set A to a number in the range 5 to 63 ORA #5 STA enemyTacticTimer,X \ Set the enemy's tactics timer to the number in A, so \ we wait for A * 0.06 seconds before applying tactics \ to this enemy in the ApplyTactics routine (so that's \ somewhere between 5 * 0.06 = 0.3 seconds and \ 63 * 0.06 = 3.8 seconds) LDA #20 \ Set A to either 20 or 236, depending on the value that BCC aden5 \ we gave to the C flag above LDA #236 .aden5 STA enemyYawStep,X \ Set enemyYawStep for the enemy to the value of A, \ so this sets the yaw step (i.e. the rate of rotation) \ to either 20 or 236 \ \ The degree system in the Sentinel looks like this: \ \ 0 \ -32 | +32 Overhead view of object \ \ | / \ \ | / 0 = looking straight ahead \ \|/ +64 = looking sharp right \ -64 -----+----- +64 -64 = looking sharp left \ /|\ \ / | \ \ / | \ \ -96 | +96 \ 128 \ \ The enemyYawStep speed is the angle through which the \ enemy rotates on each rotation, so this means we are \ setting the rotation speed to +20 degrees (a clockwise \ turn) or -20 degrees (an anticlockwise turn) INX \ Increment the enemy loop counter in X CPX numberOfEnemies \ If we have added a total of numberOfEnemies enemies, BCS aden6 \ jump to aden6 to finish off JMP aden1 \ Otherwise loop back to add another enemy .aden6 LDA tileAltitude \ Extract the altitude from tileAltitude, which is in LSR A \ the high nibble (as tileAltitude contains tile data, LSR A \ which has the tile altitude in the high nibble and LSR A \ the tile shape in the low nibble) LSR A STA minEnemyAltitude \ Store the result in minEnemyAltitude, so it contains \ the lowest altitude of the enemies we just added to \ the landscape CLC \ Clear the C flag (though this has no effect as the C \ flag is set as soon as we return to the SpawnEnemies \ routine) RTS \ Return from the subroutineName: AddEnemiesToTiles [Show more] Type: Subroutine Category: Landscape Summary: Add the required number of enemies to the landscape, starting from the highest altitude and working down, with one enemy per contourContext: See this subroutine in context in the source code References: This subroutine is called as follows: * SpawnEnemies calls AddEnemiesToTiles
[X]
Subroutine GetHighestTiles (category: Landscape)
Calculate both the highest tiles in each 4x4 block of tiles in the landscape and the altitude of the highest tile in the landscape
[X]
Subroutine GetNextSeedNumber (category: Maths (Arithmetic))
Set A to a seed number
[X]
Subroutine GetTilesAtAltitude (category: Landscape)
Return a list of tile blocks at a specified altitude
[X]
Subroutine PlaceObjectOnTile (category: 3D objects)
Place an object on a tile, putting it on top of any existing boulders or towers
[X]
Subroutine ResetMeanieScan (category: Gameplay)
Reset the data stored for any meanie scans that the enemy has tried in the past, so we can start looking with a clean slate
[X]
Subroutine SpawnObject (category: 3D objects)
Add a new object of the specified type to the objectTypes table
[X]
Label aden1 is local to this routine
[X]
Label aden2 is local to this routine
[X]
Label aden3 is local to this routine
[X]
Label aden4 is local to this routine
[X]
Label aden5 is local to this routine
[X]
Label aden6 is local to this routine
[X]
Variable enemyCounter in workspace Zero page
Used as a loop counter when adding enemies to the landscape
[X]
Variable enemyTacticTimer in workspace Main variable workspace
A timer for each enemy that counts down every 0.06
[X]
Variable enemyYawStep (category: 3D objects)
The yaw angle through which each enemy rotates on each scheduled rotation
[X]
Variable maxAltitude (category: Landscape)
The maximum tile altitude for each 4x4 block of tiles
[X]
Variable minEnemyAltitude in workspace Main variable workspace
The altitude of the lowest enemy on the landscape
[X]
Variable numberOfEnemies in workspace Main variable workspace
The number of enemies in the current landscape, including the Sentinel (in the range 1 to 8)
[X]
Variable objectTypes (category: 3D objects)
The object types table for up to 64 objects
[X]
Variable objectYawAngle (category: 3D objects)
The yaw angle for each object (i.e. the horizontal direction in which they are facing)
[X]
Variable tileAltitude in workspace Zero page
The altitude of the tile that we are currently processing
[X]
Variable tilesAtAltitude (category: Landscape)
Storage for tile blocks at specific altitudes for placing enemies on the landscape
[X]
Variable xTileMaxAltitude (category: Landscape)
The tile x-coordinate of the highest tile within each 4x4 block of tiles
[X]
Variable xTileSentinel in workspace Main variable workspace
The tile x-coordinate of the Sentinel
[X]
Variable zTileMaxAltitude (category: Landscape)
The tile z-coordinate of the highest tile within each 4x4 block of tiles
[X]
Variable zTileSentinel in workspace Main variable workspace
The tile z-coordinate of the Sentinel