# Aiming 16 enemies aim towards the player to target them. Some of those enemies, for example a sniper (enemy type = #$06), will aim towards the closest player and fire bullets. Others will track the player actively. For example, the white blobs (enemy type = #$13) will follow the player for a specified amount of time. Note that some enemies will fire, but they aren't aiming like the scuba soldier, or even the regular soldier. Some enemies can aim in more directions than others. For example, snipers (enemy type = #$06) can aim in #$17 directions, whereas rotating guns (enemy type = #$04) can only aim in #$0b directions. The number of directions that an enemy can aim are determined by which table is used when aiming. * `quadrant_aim_dir_00` is used for outdoor enemies and has #$03 aiming directions per quadrant for a total of #$0b aiming directions. * `quadrant_aim_dir_01` is used for all indoor/base enemies, tank, sniper, white blob and spinning bubbles. The table has values #$06 aiming directions per quadrant for a total of #$17 aiming directions. * `quadrant_aim_dir_02` is only used by dragon arm when seeking (stage 3 boss) and has #$0f aiming directions per quadrant for a total of #$3b aiming directions. Aiming direction values start at #$00, which represents 3 o'clock. Incrementing the aiming directions moves the direction clockwise. The main method to determine what value within a quadrant to aim is `get_quadrant_aim_dir`. I refer to this value as the "quadrant aim direction" as specifies the index within a quadrant to aim. The quadrant aim direction along with the quadrant is combined to determine the full aim direction. The `get_quadrant_aim_dir` method targets the location ($0b, $0a) from position ($09, $08). This location is almost always the location of the closest player to the enemy on the x-axis, which is determined by the method `player_enemy_x_dist`. However, white blobs (enemy type = #$13) will target players randomly based on the frame counter. `get_quadrant_aim_dir` determines where the player is in relation to the enemy, i.e. above or below, to the left or to the right. It will also calculate an index into a single quadrant that most closely targets the player position based on `quadrant_aim_dir_xx`. The quadrant aim direction is then converted to a full aim direction according to the table below. Essentially, the quadrant code is either added or subtracted from the x-axis aim direction depending on the relative quadrant of the player in relation to the enemy. | Quadrant | Math | Description | |----------|-------------------------------------------------------|------------------------------------------------| | I | new_aim_dir = max_aim_dir - new_aim_dir | result subtracted from 3 o'cock aim direction | | II | new_aim_dir = max_aim_dir - mid_aim_dir + new_aim_dir | result added to 9 o'clock aim direction | | III | new_aim_dir = mid_aim_dir - new_aim_dir | result subtracted from 9 o'clock aim direction | | IV | new_aim_dir | no math | Many enemies will use the full aim direction value to set their graphic to the appropriate value. For example, the rotating gun will take the calculated full aim direction from `aim_var_1_for_quadrant_aim_dir_00`, adjust it by adding or subtracting, then setting the super-tile to the value as an offset into `level_xx_nametable_update_supertile_data`. ## Bullets When creating bullets, the value returned by `get_quadrant_aim_dir` is converted into the full aim direction. Then the full aim direction is used by `calc_bullet_velocities` as an offset into `bullet_fract_vel_dir_lookup_tbl`. This value is then used as an offset into `bullet_fract_vel_tbl`. These values, along with the bullet speed code and quadrant, are used to determine the bullet X and Y velocities. For most enemy generated bullets, the method `aim_and_create_enemy_bullet` is used. This method calls `get_quadrant_aim_dir` and `calc_bullet_velocities`. However, some enemies instead call `get_quadrant_aim_dir_for_player` directly and then call `calc_bullet_velocities` separately to set the bullet velocities. | Enemy | Type | Calling Method | |-------------------------------|------|-----------------------------------| | Wall Turret | #$13 | `aim_and_create_enemy_bullet` | | Core | #$14 | `aim_and_create_enemy_bullet` | | Dragon Tentacle Orb | #$15 | `aim_and_create_enemy_bullet` | | Jumping Soldier | #$16 | `aim_and_create_enemy_bullet` | | Boss Eye Fire Ring Projectile | #$1b | `get_quadrant_aim_dir_for_player` | | Gardegura | #$1d | `get_quadrant_aim_dir_for_player` | | Rangel | #$1f | `aim_and_create_enemy_bullet` | All of the enemies using this pattern to create bullets use `quadrant_aim_dir_01`, which gives quadrant offsets between 0-6 inclusively. Since each quadrant has 6 values, there are 24 (#$18) possible aim directions. Below is the table that is used to to determine the velocity from the calculated full aim direction. Once the velocity is obtained from this table, the quadrant is incorporated to adjust the signs (+/-) on the direction. Finally, the velocity is adjusted again based on bullet speed code (`adjust_bullet_velocity`). | `bullet_fract_vel_dir_lookup_tbl` | `bullet_fract_vel_tbl` | X vel | Y vel | degree off axis | |-----------------------------------|------------------------|--------------|--------------|-----------------| | #$00 | #$00 | #$ff (.9961) | #$00 | 0° | | #$01 | #$02 | #$f7 (.9648) | #$42 (.2578) | 15° | | #$02 | #$04 | #$dd (.8633) | #$80 (.5) | 30° | | #$03 | #$06 | #$b5 (.7070) | #$b5 (.7070) | 45° | | #$04 | #$08 | #$80 (.5) | #$dd (.8633) | 60° | | #$05 | #$0a | #$42 (.2578) | #$f7 (.9648) | 75° | | #$06 | #$0c | #$00 | #$ff (.9961) | 90° | | #$07 | #$0a | #$42 (.2578) | #$f7 (.9648) | 75° | | #$08 | #$08 | #$80 (.5) | #$dd (.8633) | 60° | | #$09 | #$06 | #$b5 (.7070) | #$b5 (.7070) | 45° | | #$0a | #$04 | #$dd (.8633) | #$80 (.5) | 30° | | #$0b | #$02 | #$f7 (.9648) | #$42 (.2578) | 15° | | #$0c | #$00 | #$00 | #$ff (.9961) | 90° | | #$0d | #$02 | #$f7 (.9648) | #$42 (.2578) | 15° | | #$0e | #$04 | #$dd (.8633) | #$80 (.5) | 30° | | #$0f | #$06 | #$b5 (.7070) | #$b5 (.7070) | 45° | | #$10 | #$08 | #$80 (.5) | #$dd (.8633) | 60° | | #$11 | #$0a | #$42 (.2578) | #$f7 (.9648) | 75° | | #$12 | #$0c | #$00 | #$ff (.9961) | 90° | | #$13 | #$0a | #$42 (.2578) | #$f7 (.9648) | 75° | | #$14 | #$08 | #$80 (.5) | #$dd (.8633) | 60° | | #$15 | #$06 | #$b5 (.7070) | #$b5 (.7070) | 45° | | #$16 | #$04 | #$dd (.8633) | #$80 (.5) | 30° | | #$17 | #$02 | #$f7 (.9648) | #$42 (.2578) | 15° | ## Dragon seeking Dragon Tentacle Orb (enemy type = #$15) is the only enemy type that uses `quadrant_aim_dir_02`. It uses this during the 'arm seeking player' attack pattern (attack pattern #$04). `quadrant_aim_dir_02` has the most precision with #$0f aim directions per quadrant. ## Aim Call Flow ```mermaid flowchart TD get_rotate_dir_for_index --> get_quadrant_aim_dir get_rotate_00 --> get_rotate_dir_for_index get_rotate_01 --> get_rotate_dir_for_index rotating_gun_routine_03:::enemy --> aim_var_1_for_quadrant_aim_dir_00 alien_fetus_routine_01:::enemy --> aim_var_1_for_quadrant_aim_dir_00 red_turret_routine_03:::enemy --> get_rotate_00 white_blob_routine_00:::enemy --> get_rotate_01 sniper_routine_02:::enemy --> get_rotate_01 aim_var_1_for_quadrant_aim_dir_00 --> get_rotate_00 aim_var_1_for_quadrant_aim_dir_01 --> get_rotate_01 spinning_bubbles_routine_01:::enemy --> aim_var_1_for_quadrant_aim_dir_01 tank_routine_02:::enemy --> aim_var_1_for_quadrant_aim_dir_01 white_blob_aim_to_player:::enemy --> aim_var_1_for_quadrant_aim_dir_01 spinning_bubbles_routine_00:::enemy --> get_quadrant_aim_dir_for_player eye_projectile_routine_00:::enemy --> get_quadrant_aim_dir_for_player dragon_arm_orb_seek_should_move:::enemy --> get_quadrant_aim_dir_for_player dragon_arm_orb_fire_projectile:::enemy --> aim_and_create_enemy_bullet aim_and_create_enemy_bullet --> get_quadrant_aim_dir_for_player aim_and_create_enemy_bullet --> get_quadrant_aim_dir get_quadrant_aim_dir_for_player --> get_quadrant_aim_dir wall_turret_routine_03:::enemy --> aim_and_create_enemy_bullet wall_core_routine_03:::enemy --> aim_and_create_enemy_bullet jumping_soldier_routine_01:::enemy --> aim_and_create_enemy_bullet red_soldier_routine_02:::enemy --> aim_and_create_enemy_bullet get_rotate_dir_for_index --> get_quadrant_aim_dir_for_player classDef enemy fill:#f96 ``` ## Pseudocode Below is a c-like pseudo-code showing how the quadrant aim direction from `get_quadrant_aim_dir` value is converted to a full aim direction in `get_rotate_dir`. ``` int quadrant_aim_dir = get_quadrant_aim_dir(); // mid_aim_dir is 9 o'clock // max_aim_dir is 3 o'clock int mid_aim_dir, max_aim_dir, new_aim_dir; if (quadrant_aim_dir_00 || quadrant_aim_dir_02) { mid_aim_dir = 0x06; max_aim_dir = 0x0c; } else { mid_aim_dir = 0x0c; max_aim_dir = 0x18; } if(player_left_of_enemy) { new_aim_dir = mid_aim_dir - quadrant_aim_dir; } if(player_above_enemy) { if(quadrant_aim_dir != 0x00) { new_aim_dir = max_aim_dir - new_aim_dir; } else { new_aim_dir = 0x00; } } // new full aim direction calculated new_aim_dir = quadrant_aim_dir; ```