asar 1.90 lorom dpbase 0 optimize dp always optimize address ram ;;; RAM Variables samus_collected_items = $7E09A4 samus_pos_x = $7E0AF6 samus_pos_x_sub = $7E0AF8 samus_pos_y = $7E0AFA samus_pos_y_sub = $7E0AFC enemy_index = $7E0E54 enemies_pos_x = $7E0F7A enemies_pos_x_sub = $7E0F7C enemies_pos_y = $7E0F7E enemies_pos_y_sub = $7E0F80 enemies_flags1 = $7E0F86 enemies_flags2 = $7E0F88 enemies_spritemap = $7E0F8E enemies_timer = $7E0F90 enemies_instruction_list = $7E0F92 enemies_instruction_timer = $7E0F94 enemies_ai_var0 = $7E0FA8 enemies_ai_var1 = $7E0FAA enemies_ai_var2 = $7E0FAC enemies_ai_var3 = $7E0FAE enemies_ai_var4 = $7E0FB0 enemies_ai_var5 = $7E0FB2 enemies_parameter1 = $7E0FB4 enemies_parameter2 = $7E0FB6 ram_hdma_buffer = $7E9100 ;;; Common enemy bank routines/constants !enemy_empty_spritemap = $804D ;;; External routines snd_playLib2Sfx = $8090CB ; $90CB: Queue sound, sound library 2, max queued sounds allowed = 6 hdma_spawnEyeHdma = $88E8D9 ; $E8D9: Spawn morph ball eye beam HDMA object ai_util_isSamusWithinYRadius = $A0AEED ; $AEED: Is Samus within [A] pixel rows of enemy ai_util_isSamusWithinXRadius = $A0AF0B ; $AF0B: Is Samus within [A] pixel columns of enemy ai_util_atan2 = $A0C0AE ; $C0AE: Calculate angle of ([$12], [$14]) offset ;;; Variable aliases ; Read by beam HDMA object, TODO purpose unknown eye_vars_hdma_flag = enemies_ai_var2 ; Read by beam HDMA object eye_vars_angle = enemies_ai_var3 ; Used to add a delay during the opening/closing AI states eye_vars_anim_delay = enemies_ai_var4 ; Pointer to current AI state handler, called from pre-instruction eye_vars_state = enemies_ai_var5 org $A88F8C ;;; $8F8C: Palette - enemy $E6BF (morph ball eye) ;;; skip $20 ; Let SMART manage this ;dw 3800, 72B2, 71C7, 2461, 1840, 7A8E, 660B, 4D03, 4900, 7FE0, 7E80, 44E0, 2C20, 0000, 0000, 0000 ;;; $8FAC: Instruction list - ;;; { instrs_eye_directions: dw $000A,$9210 dw $000A,$9210 dw $000A,$9217 dw $000A,$921E dw $000A,$9225 dw $000A,$922C dw $000A,$9233 dw $000A,$923A dw $000A,$9209 dw $000A,$9202 dw $000A,$91FB dw $000A,$91F4 dw $000A,$91ED dw $000A,$91E6 dw $000A,$91DF dw $000A,$91DF dw $80ED,instrs_eye_directions ; Go to } ;;; $8FF0: Instruction list - ;;; { instrs_eye_left_closing: dw $0008,$9257 dw $0030,$91F4 dw $0005,$9257 .closed: dw $0030,$9241 dw $812F ; Sleep } ;;; $9002: Instruction list - ;;; { instrs_eye_right_closing: dw $0008,$9283 dw $0030,$9225 dw $0005,$9283 .closed: dw $0030,$926D dw $812F ; Sleep } ;;; $9014: Instruction list - ;;; { instrs_eye_left_opening: dw $0020,$9241 dw $0005,$9257 dw $0030,$91F4 dw $0008,$9257 dw $812F ; Sleep } ;;; $9026: Instruction list - ;;; { instrs_eye_right_opening: dw $0020,$926D dw $0005,$9283 dw $0030,$9225 dw $0008,$9283 dw $812F ; Sleep } ;;; $9038: Instruction list - ;;; { instrs_mount_right: dw $0001,$9299 dw $812F ; Sleep } ;;; $903E: Instruction list - ;;; { instrs_mount_down: dw $0001,$92A5 dw $812F ; Sleep } ;;; $9044: Instruction list - ;;; { instrs_mount_left: dw $0001,$92B1 dw $812F ; Sleep } ;;; $904A: Instruction list - ;;; { instrs_mount_up: dw $0001,$92BD dw $812F ; Sleep } ;;; $9050: Morph ball eye constants ;;; { ; How close Samus needs to be for the eye to wake up and start tracking her eye_acquire_xradius: dw $80 ; How close Samus needs to be for the eye to continue tracking once it's already awake eye_track_xradius: dw $B0 eye_acquire_yradius: dw $80 eye_track_yradius: dw $80 } ;;; $9058: Initialisation AI - enemy $E6BF (morph ball eye) ;;; { eye_ai_init: LDX enemy_index ; Set "Process Instructions" flag LDA enemies_flags1,x ORA #$2000 STA enemies_flags1,x LDA #!enemy_empty_spritemap STA enemies_spritemap,x LDA.w #1 STA enemies_instruction_timer,x STZ enemies_timer,x ; If (param2 & 8000h != 0) this is the mount part, initialize mount graphics LDA enemies_parameter2,x BMI .init_mount ; Otherwise, this is the eye part, continue with eye initialization LDA #eye_state_waiting STA eye_vars_state,x ; param1 & 0x0001 determines eye facing: 0 = Left, 1 = Right LDA enemies_parameter1,x BIT #$0001 BEQ .facing_left ; Right-facing LDA #instrs_eye_right_closing_closed STA enemies_instruction_list,x BRA .return .facing_left: ; Left-facing LDA #instrs_eye_left_closing_closed STA enemies_instruction_list,x BRA .return .init_mount: ; Depending on the mount orientation, offset origin and set sprite accordingly ; Y = table offset for configured orientation ((param2 & Fh) * 2) AND #$000F ASL TAY ; Offset X position LDA enemies_pos_x,x CLC ADC .x_offsets_tbl,y STA enemies_pos_x,x ; Offset Y position LDA enemies_pos_y,x CLC ADC .y_offsets_tbl,y STA enemies_pos_y,x ; Mount uses a null AI state LDA #rtl_91DC STA eye_vars_state,x ; Set instruction list with correct sprites for this orientation LDA .instruction_lists_tbl,y STA enemies_instruction_list,x ; Fill $7E:9100..92FF ("some HDMA RAM"?) with 00FFh LDX #$01FE - LDA #$00FF STA ram_hdma_buffer,x DEX #2 BPL - .return: RTL ; Left, Right, Up, Down .x_offsets_tbl: dw -8, 8, 0, 0 .y_offsets_tbl: dw 0, 0, -8, 8 .instruction_lists_tbl: dw instrs_mount_left, instrs_mount_right, instrs_mount_up, instrs_mount_down } ;;; $90E2: Main AI - enemy $E6BF (morph ball eye) ;;; { eye_ai_main: LDX enemy_index LDA samus_collected_items BIT #$0004 ; Morph ball BEQ .return ; If morph ball has been collected, run current AI state handler JMP (eye_vars_state,x) .return: RTL } ;;; $90F1: AI State: Eye closed, waiting for Samus to come nearby { eye_state_waiting: ; Check if Samus is near the vicinity of the enemy and return otherwise LDA eye_acquire_yradius JSL ai_util_isSamusWithinYRadius TAY BEQ .return LDA eye_acquire_xradius JSL ai_util_isSamusWithinXRadius TAY BEQ .return LDA.w #32 STA eye_vars_anim_delay,x LDA.w #1 STA enemies_instruction_timer,x ; Check facing direction (param1 & 0x0001), 0 = Left, 1 = Right LDA enemies_parameter1,x BIT #$0001 BEQ .facing_left ; Otherwise facing right LDA #instrs_eye_right_opening STA enemies_instruction_list,x BRA .next_state .facing_left: LDA #instrs_eye_left_opening STA enemies_instruction_list,x .next_state: LDA #eye_state_opening STA eye_vars_state,x .return: RTL } ;;; $912E: AI State: Eye spotted Samus and is playing opening animation { eye_state_opening: DEC eye_vars_anim_delay,x BEQ .eye_opened BPL .return .eye_opened: ; Queue sound 17h, sound library 2, max queued sounds allowed = 6 (morph ball eye's ray) LDA.w #$17 JSL snd_playLib2Sfx JSL hdma_spawnEyeHdma LDA #eye_state_tracking STA eye_vars_state,x ; Calculate angle from enemy to Samus LDA samus_pos_x SEC : SBC enemies_pos_x,x STA $12 LDA samus_pos_y SEC : SBC enemies_pos_y,x STA $14 JSL ai_util_atan2 ; Takes $12,$14 as params STA eye_vars_angle,x .return: RTL } ;;; $9160: AI State: Eye is open and tracking Samus' direction { eye_state_tracking: ; Check if Samus is still near the eye object LDA eye_track_yradius JSL ai_util_isSamusWithinYRadius TAY BEQ .samus_out_of_range LDA eye_track_xradius JSL ai_util_isSamusWithinXRadius TAY BNE .track_samus .samus_out_of_range: ; Stop x-ray sound ; Queue sound 71h, sound library 2, max queued sounds allowed = 6 (silence) LDA.w #$71 JSL snd_playLib2Sfx STZ eye_vars_hdma_flag,x LDA.w #32 STA eye_vars_anim_delay,x ; Play eye closing animation. The correct instruction list is set ; according to the eye direction. ((param1 & 1): 0=Left, 1=Right) LDA enemies_parameter1,x BIT #$0001 BEQ .facing_left LDA #instrs_eye_right_closing STA enemies_instruction_list,x BRA .next_state .facing_left: LDA #instrs_eye_left_closing STA enemies_instruction_list,x .next_state: LDA #eye_state_closing STA eye_vars_state,x BRA .return .track_samus: ; Update beam angle by calculating new angle towards samus LDA samus_pos_x SEC : SBC enemies_pos_x,x STA $12 LDA samus_pos_y SEC : SBC enemies_pos_y,x STA $14 JSL ai_util_atan2 ; Takes $12,$14 as params STA eye_vars_angle,x ; Update eye sprite to look in the correct direction. Uses the angle to ; index into the instruction list containing each of the poses. ; angle / 16 (num. of poses) * 4 (bytes per instruction in the list) AND #$00F0 : LSR #2 CLC : ADC #instrs_eye_directions STA enemies_instruction_list,x .return: LDA #$0001 STA enemies_instruction_timer,x RTL } ;;; $91CE: AI State: Eye lost track of Samus and is playing its closing animation before going back to waiting state { eye_state_closing: DEC eye_vars_anim_delay,x BEQ .counter_expired BPL .return .counter_expired: LDA #eye_state_waiting STA eye_vars_state,x .return: RTL } ;;; $91DC: RTL { rtl_91DC: RTL } ;;; $91DD: RTL (unused?) { rtl_91DD: RTL } ;;; $91DE: RTL (unused?) { rtl_91DE: RTL } warnpc $A891DF ;;; $91DF: Morph ball eye spritemaps ;;; { macro sprmap_entry(xoff, yoff, tile) dw : db : dw endmacro ; Open eye looking towards various angles eye_spritemaps: .f0: dw 1 : %sprmap_entry($81F8, $F8, $2100) .f1: dw 1 : %sprmap_entry($81F8, $F8, $2102) .f2: dw 1 : %sprmap_entry($81F8, $F8, $2104) .f3: dw 1 : %sprmap_entry($81F8, $F8, $2106) .f4: dw 1 : %sprmap_entry($81F8, $F8, $2108) .f5: dw 1 : %sprmap_entry($81F8, $F8, $210A) .f6: dw 1 : %sprmap_entry($81F8, $F8, $210C) .f7: dw 1 : %sprmap_entry($81F8, $F8, $6100) .f8: dw 1 : %sprmap_entry($81F8, $F8, $6102) .f9: dw 1 : %sprmap_entry($81F8, $F8, $6104) .f10: dw 1 : %sprmap_entry($81F8, $F8, $6106) .f11: dw 1 : %sprmap_entry($81F8, $F8, $6108) .f12: dw 1 : %sprmap_entry($81F8, $F8, $610A) .f13: dw 1 : %sprmap_entry($81F8, $F8, $610C) ; Eye opening left .f14: dw 4 %sprmap_entry($01F8, $00, $A11F) %sprmap_entry($01F8, $F8, $211F) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) .f15: dw 4 %sprmap_entry($01F8, $00, $A11E) %sprmap_entry($01F8, $F8, $211E) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) ; Eye opening right .f16: dw 4 %sprmap_entry($0000, $00, $E11F) %sprmap_entry($0000, $F8, $611F) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) .f17: dw 4 %sprmap_entry($0000, $00, $E11E) %sprmap_entry($0000, $F8, $611E) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) ; Mount in 4 directions .f18: dw 2 %sprmap_entry($01FC, $00, $A10E) %sprmap_entry($01FC, $F8, $210E) .f19: dw 2 %sprmap_entry($0000, $FC, $610F) %sprmap_entry($01F8, $FC, $210F) .f20: dw 2 %sprmap_entry($01FC, $00, $E10E) %sprmap_entry($01FC, $F8, $610E) .f21: dw 2 %sprmap_entry($0000, $FC, $E10F) %sprmap_entry($01F8, $FC, $A10F) ; Unused. Seem to be duplicates of 9241-9298 .f22: dw 4 %sprmap_entry($01F8, $00, $A11F) %sprmap_entry($01F8, $F8, $211F) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) .f23: dw 4 %sprmap_entry($01F8, $00, $A11E) %sprmap_entry($01F8, $F8, $211E) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) .f24: dw 4 %sprmap_entry($0000, $00, $E11F) %sprmap_entry($0000, $F8, $611F) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) .f25: dw 4 %sprmap_entry($0000, $00, $E11E) %sprmap_entry($0000, $F8, $611E) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) .f26: dw 4 %sprmap_entry($0000, $00, $E11F) %sprmap_entry($0000, $F8, $611F) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) .f27: dw 4 %sprmap_entry($0000, $00, $E11E) %sprmap_entry($0000, $F8, $611E) %sprmap_entry($01F8, $00, $6117) %sprmap_entry($01F8, $F8, $6107) .f28: dw 4 %sprmap_entry($01F8, $00, $A11F) %sprmap_entry($01F8, $F8, $211F) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) .f29: dw 4 %sprmap_entry($01F8, $00, $A11E) %sprmap_entry($01F8, $F8, $211E) %sprmap_entry($0000, $00, $2117) %sprmap_entry($0000, $F8, $2107) } } ; End of eye code warnpc $A89379