EXCEL: Each tab (like CIRCLETINY) is the x,y offsets at each frame, using the parameters like radius and frames.  There are a few rows for each step of the scaling process to get 00-FF.  Then each level has the tables of each enemy, what movement type to use, when they start being active, etc.  Using formulas to rearrange the data into the .db format that just gets copy/pasted into the code.  

ASM: EnemyMoveGlobal is an offset for all enemies, like making them all move left/right like space invaders.  EnemyMoveLocal is each individual guy.  GetOffsetX/Y does the table lookup and it is just added to their current position.  Then the pointer is moved to the next index and either reverses or wraps when it hits the end.





  
  
GetOffsetX:
  sta <tempSpeedLo
  beq GetOffsetX00  ;;if 0, dont process
  lda <tempDir
  cmp #$FF
  beq GetOffsetXFF
GetOffsetX00
  lda <tempSpeedLo
  clc
  rts
GetOffsetXFF
  ;;all movements invert X when going other direction
GetOffsetXInvert
  lda #$00
  sec
  sbc <tempSpeedLo
  rts
  
  


GetOffsetY:
  sta <tempSpeedLo
  beq GetOffsetY00    ;;if 0, dont process
  lda <tempDir
  cmp #$FF
  beq GetOffsetYFF
GetOffsetY00
  lda <tempSpeedLo
  clc
  rts
GetOffsetYFF
  ;;all movements not dropping invert Y when going other direction
  lda <tempType 
  cmp #mDROP     ;;if type < drop, invert y
  bcc GetOffsetYInvert
  jmp GetOffsetY00
GetOffsetYInvert
  lda #$00
  sec
  sbc <tempSpeedLo
  rts





EnemyMove:
  lda frameCounter
  and #$07
  beq EnemyMoveStart
;  rts
EnemyMoveStart
  lda #$00
  sta <eIndex
EnemyMoveLoop
  ldx <eIndex
  lda <eType, x
 ;; cmp #tNONE
  bne EnemyMoveLoopGo      ;;if type = none, all done
  jmp EnemyMoveLoopNext
EnemyMoveLoopGo
  lda #BANK(MovementTablesX)/2     ;;movement tables
  jsr Bankswitch

  ldy #$00
  ldx <eIndex
  



EnemyMoveGlobal
 ; jmp EnemyMoveLocal    ;;no global movement
  lda <lMovementDir
  sta <tempDir
  lda <lMovementType            ;;global level movement
  sta <tempType
  clc
  adc #HIGH(MovementTablesX)
  sta <sourceHi
  lda <lMovementIndex
  sta <sourceLo
  lda [sourceLo], y
  jsr GetOffsetX     
  adc eHomeX, x
  sta eHomeX, x
  lda <sourceHi
  clc
  adc #HIGH(MovementTablesY)-HIGH(MovementTablesX)
  sta <sourceHi
  lda [sourceLo], y
  jsr GetOffsetY   
  adc eHomeY, x
  sta eHomeY, x






EnemyMoveLocal
  lda eMovementDir, x
  sta <tempDir
  
  lda eMovementType, x      ;;local enemy movement
  sta <tempType
  clc
  adc #HIGH(MovementTablesX)
  sta <sourceHi
  lda eMovementIndex, x
  sta <sourceLo
  lda [sourceLo], y
  jsr GetOffsetX       ;;invert X based on dir
  adc eHomeX, x
  sta eHomeX, x

  lda <sourceHi
  clc
  adc #HIGH(MovementTablesY)-HIGH(MovementTablesX)
  sta <sourceHi
  lda [sourceLo], y
  jsr GetOffsetY
  adc eHomeY, x
  sta eHomeY, x

  lda eMovementDir, x   ;;+01 or +FF
  clc
  adc eMovementIndex, x
  sta eMovementIndex, x
  
EnemyMoveWrap       ;;continue movement index, check for under/overflow, handle loop vs repeat
  lda eMovementType, x
  tay
  lda MovementTablesLength, y
  cmp eMovementIndex, x
  beq EnemyMoveWrapOverflow   ;;wrap when movement overflow
  lda eMovementIndex, x
  cmp #$FF
  beq EnemyMoveWrapUnderflow   ;;wrap when movement underflow
  jmp EnemyMoveWrapDone
  
EnemyMoveWrapOverflow
  lda eMovementRepeat, x
  cmp #MOVEMENTREPEATLOOP
  beq EnemyMoveWrapOverflowLoop
  cmp #MOVEMENTREPEATREVERSE
  beq EnemyMoveWrapOverflowReverse
  jmp EnemyMoveWrapDone
EnemyMoveWrapOverflowLoop
  lda #$00
  sta eMovementIndex, x
  jmp EnemyMoveWrapDone  ;;wrap to 0
EnemyMoveWrapOverflowReverse  
  lda #$00
  sec
  sbc eMovementDir, x
  sta eMovementDir, x  ;;01->FF   00->00  FF->01
  lda eMovementIndex, x
  sec
  sbc #$01
  sta eMovementIndex, x
  jmp EnemyMoveWrapDone

EnemyMoveWrapUnderflow
  lda eMovementRepeat, x
  cmp #MOVEMENTREPEATLOOP
  beq EnemyMoveWrapUnderflowLoop
  cmp #MOVEMENTREPEATREVERSE
  beq EnemyMoveWrapUnderflowReverse
  jmp EnemyMoveWrapDone
EnemyMoveWrapUnderflowLoop
  lda eMovementType, x
  tay
  lda MovementTablesLength, y  ;;wrap to max-1
  sta eMovementIndex, x
  lda eMovementIndex, x
  sec
  sbc #$01
  sta eMovementIndex, x
  jmp EnemyMoveWrapDone
EnemyMoveWrapUnderflowReverse  
  lda #$00
  sec
  sbc eMovementDir, x
  sta eMovementDir, x  ;;01->FF   00->00  FF->01
  lda #$00
  sta eMovementIndex, x
  jmp EnemyMoveWrapDone
EnemyMoveWrapDone

EnemyMoveCheckActive
  lda eActive, x
  beq EnemyMoveCheckActiveTimer
  jsr EnemyActive
  jmp EnemyMoveLoopNext          ;;already active, dont change timer
EnemyMoveCheckActiveTimer
  lda eHomeX, x
  sta <eActiveX, x
  lda eHomeY, x
  sta <eActiveY, x

  lda eActiveTimerLo, x
  bne EnemyMoveCheckActiveTimerNotZero
  lda eActiveTimerHi, x
  bne EnemyMoveCheckActiveTimerNotZero
EnemyMoveCheckActiveTimerZero
 ; lda eHomeX, x
 ; sta <eActiveX, x     ;;copy position   home->active
 ; lda eHomeY, x
 ; sta <eActiveY, x

  lda #BANK(EnemyData)/2           ;;active from enemy table
  jsr Bankswitch
  lda <eType, x
  tay
  lda EnemyActiveType, y
  sta eActiveType, x             ;;type from type
 ; cmp #mNONE
 ; beq EnemyMoveCheckActiveTimerNotZero   ;;no active for this enemy
 
  lda EnemyActiveDir, y
  sta eActiveDir, x             

  lda eActiveType, x             ;;pick random left/right
  cmp #mDROPDIAGONAL
  bne EnemyActivePickDirDone

  lda randomLo
  and #$01
  bne EnemyActivePickDirDone
  lda #$FF
  sta eActiveDir, x             ;;dir from type
EnemyActivePickDirDone
  lda #$01
  sta eActiveIndex, x              ;;index = 0
  lda EnemyActiveRepeat, y        ;;repeat from type
  sta eActiveRepeat, x
  lda #$01
  sta eActive, x     ;;now active
  lda #$01
  sta eActiveCounter, x
  jmp EnemyMoveCheckActiveTimerDone
EnemyMoveCheckActiveTimerNotZero
  lda eActiveTimerLo, x
  sec
  sbc #$01
  sta eActiveTimerLo, x
  lda eActiveTimerHi, x  ;;if active timer != 0, dec active timer
  sbc #$00
  sta eActiveTimerHi, x
EnemyMoveCheckActiveTimerDone

EnemyMoveLoopNext
  inc <eIndex
  lda <eIndex
  cmp <lEnemiesLoaded
  beq EnemyMoveDone
  jmp EnemyMoveLoop
EnemyMoveDone
  rts



