;; ======================================================================== ;; ;; Bad guy thinker routines ;; ;; ;; ;; These are called when their timers expire. Timers are counted down ;; ;; at a 30Hz rate. ;; ;; ;; ;; Thinkers have 2 bytes of state at their disposal, and can call BGRAND, ;; ;; which returns a predictable hash of level-offset, elapsed time, R1, R3. ;; ;; The first byte of state is initialized when the bad-guy is spawned. ;; ;; The intended use of this bytes is to specify "aggressiveness" and ;; ;; "evasiveness." Aggressiveness is how often the bad-guy fires. ;; ;; Evasiveness is how much the bad guy tries to stay away from the tank. ;; ;; ;; ;; Thinkers: ;; ;; ;; ;; BGT_SAUCxx -- saucer flight programs. ;; ;; BGT_BOULDER -- rolling boulder program (set hscroll, roll) ;; ;; BGT_TURRET -- turret firing program (set hscroll, fire) ;; ;; BGT_MINE -- Land mine: (set hscroll, blink randomly) ;; ;; BGT_PLANT -- space-plant program: duck up/down randomly ;; ;; ;; ;; Support (implemented): ;; ;; ;; ;; BGRAND -- predictable RAND ;; ;; BGBRAND -- predictable RAND, bounded by R2 ;; ;; BGFIRE -- make a bad guy fire ;; ;; BGKILL -- remove bad guy from list of bad guys ;; ;; ;; ;; Support (todo -- maybe won't bother): ;; ;; ;; ;; BGSETHS -- Set horiz-scroll bit for this bad-guy ;; ;; BGSETVEL -- Set velocity for this bad-guy ;; ;; BGSETATTR -- Set attribute number for this bad-guy ;; ;; ;; ;; The thinkers are called with R1 == slot #, R3 == state pointer. ;; ;; R0, R2, R4 are available as scratch. ;; ;; ;; ;; Thinkers return with delay in R0. ;; ;; ======================================================================== ;; ;; ======================================================================== ;; ;; BGRAND -- A purposefully not-very-random random. The LSBs are the ;; ;; only useful bits. ;; ;; ======================================================================== ;; BGRAND PROC MVI RCIDX, R0 ; mix in rock/crater/spawn index ADDR R3, R0 ; add BGMPTBL pointer XORR R1, R0 ; mix in slot number ADD TIMSEC, R0 ; add time in seconds. JR R5 ENDP ;; ======================================================================== ;; ;; BGBRAND -- Like BGRAND, only bounded to the range specified in R2. ;; ;; ======================================================================== ;; BGBRAND PROC CLRR R0 CMPR R2, R0 BEQ @@leave @@l1 SETC RLC R0, 1 CMPR R2, R0 BNC @@l1 ; add a couple extra bits to mask. ; this hopefully reduces the bias we give to certain numbers ; with our crappy modulo technique. SLLC R0, 2 ADDI #3, R0 PSHR R0 MVI RCIDX, R0 ; mix in rock/crater/spawn index ADDR R3, R0 ; add BGMPTBL pointer XORR R1, R0 ; mix in slot number ADD TIMSEC, R0 ; add time in seconds. PSHR R0 ;\ SWAP R0 ; |-- fold upper, lower halves XOR@ R6, R0 ;/ AND@ R6, R0 ; mask approximately into range. @@l2 SUBR R2, R0 BPL @@l2 ADDR R2, R0 @@leave: JR R5 ENDP ;; ======================================================================== ;; ;; BGT_TURRET0: Set horiz scroll, then set thinker to fire program. ;; ;; ======================================================================== ;; BGT_TURRET0 PROC ;; ------------------------------------------------------------ ;; ;; Set up thinker for turret to just fire occasionally. ;; ;; ------------------------------------------------------------ ;; MVII #BGI.turret1, R0 B BG_SETTHINK ENDP ;; ======================================================================== ;; ;; BGT_BOULDER0: Set horiz scroll + left vel, then set thinker to roller. ;; ;; ======================================================================== ;; BGT_BOULDER0 PROC ;; ------------------------------------------------------------ ;; ;; Set up boulder to have some leftward velocity in addition ;; ;; to what hscroll will give us. ;; ;; ------------------------------------------------------------ ;; MOVR R1, R2 ADDR R1, R2 ADDI #SPXYV, R2 MVII #$A8FF, R0 MVO@ R0, R2 ; Set up thinker for boulder to just animate. MVII #BGI.boulder1, R0 B BG_SETTHINK ENDP ;; ======================================================================== ;; ;; BGT_PLANT0: Set horizontal-scroll, and then set ducker as thinker. ;; ;; ======================================================================== ;; BGT_PLANT0 PROC MVII #BGI.plant1, R0 B BG_SETTHINK ENDP ;; ======================================================================== ;; ;; BGT_MINE0: Set horizontal-scroll, and then set blinker as thinker. ;; ;; ======================================================================== ;; BGT_MINE0 PROC ;; ------------------------------------------------------------ ;; ;; The mines just blink after they've been set h-scroll. ;; ;; ------------------------------------------------------------ ;; MVII #BGI.mine1, R0 ; Get address of main thinker ; B BG_SETTHINK ; XXX: fall through to BG_SETTHINK ENDP ;; ======================================================================== ;; ;; BG_SETTHINK ;; ;; This changes the thinker for the bad-guy in R1. ;; ;; R3 is expected to point to offset 3 in the BGMPTBL record (the first ;; ;; "status" byte). ;; ;; ======================================================================== ;; BG_SETTHINK PROC ;; ------------------------------------------------------------ ;; ;; Set the thinker-pointer for this bad-guy. ;; ;; ------------------------------------------------------------ ;; SUBI #2, R3 ; Rewind to program pointer @@1: MVO@ R0, R3 ; Set thinker to R0 ADDI #2, R3 ; Restore R3 ;; ------------------------------------------------------------ ;; ;; Return a delay of 1 + (slot >> 1) for this MOB to stagger ;; ;; the thinking out over multiple frames. This should help ;; ;; stagger boulders/mines that cue as a group. ;; ;; ------------------------------------------------------------ ;; MOVR R1, R0 SLR R0, 1 INCR R0 B BGT_RET ENDP ;; ======================================================================== ;; ;; BGT_MINE1: Just blink pseudo-randomly. ;; ;; ======================================================================== ;; BGT_MINE1 PROC CALL BGRAND ; random number to R0 ADD@ R3, R0 ; Get status byte 1 MVO@ R0, R3 SARC R0 ; Set Red/White based on lsb of status byte. PSHR R0 ; MVII #SPATBL.lmna, R0 BC @@not1 MVII #SPATBL.lmnb, R0 @@not1: MVII #SPAT, R2 ; ADDR R1, R2 ; MVO@ R0, R2 PULR R0 ; delay randomly from 4 to 11 ANDI #7, R0 ADDI #4, R0 B BGT_RET ENDP ;; ======================================================================== ;; ;; BGT_PLANT1: Just duck pseudo-randomly. ;; ;; ======================================================================== ;; BGT_PLANT1 PROC MVII #SPAT, R2 ; ADDR R1, R2 ; MVI@ R2, R0 CMPI #SPATBL.plta, R0 BEQ @@pltb CMPI #SPATBL.pltb, R0 BEQ @@plta CMPI #SPATBL.pltc, R0 BEQ @@pltd @@pltc MVII #SPATBL.pltc, R0 B @@done @@pltd MVII #SPATBL.pltd, R0 B @@done @@plta MVII #SPATBL.plta, R0 B @@done @@pltb MVII #SPATBL.pltb, R0 @@done: MVO@ R0, R2 CALL BGRAND ; random number to R0 ADD@ R3, R0 ; Get status byte 1 MVO@ R0, R3 ; delay randomly from 5 to 12 ANDI #7, R0 ADDI #5, R0 B BGT_RET ENDP ;; ======================================================================== ;; ;; BGT_TURRET1 -- turret firing program ;; ;; ======================================================================== ;; BGT_TURRET1 PROC MVI@ R3, R0 TSTR R0 BEQ @@not_1st @@1st MVII #15, R0 ; First time 'round, wait 16 tics MVO@ R0, R3 ; Initialize flag saying "not 1st" B BGT_RET @@not_1st: ; 2nd and subsequent times, MVI HBCOL1, R0 SLL R0, 1 BEQ @@ok_fire ADDI #8, R0 MOVR R1, R2 SLL R2, 1 ADDI #SPXYP, R2 MVI@ R2, R4 ANDI #$FF, R4 CMPR R0, R4 BLT @@no_fire ; don't fire -- bullet too close @@ok_fire CALL BGFIRE.c ; fire every 45 tics. @@no_fire MVII #44, R0 B BGT_RET ENDP ;; ======================================================================== ;; ;; BGT_STAL0 -- Stalactite: Shudder when nearing fall point. ;; ;; ======================================================================== ;; BGT_STAL0 PROC MOVR R1, R2 SLL R2, 1 ADDI #SPXYP, R2 MVI@ R2, R4 SUBI #16, R4 ANDI #$FF, R4 CLRR R0 CMP@ R3, R4 BGT BGT_RET MVII #SPATBL.stala, R0 MVII #BGI.stal1, R4 @@1: MVII #SPAT, R2 ADDR R1, R2 MVO@ R0, R2 MOVR R3, R2 SUBI #2, R2 MVO@ R4, R2 CLRR R0 B BGT_RET ENDP ;; ======================================================================== ;; ;; BGT_STAL1 -- Stalactite: Fall once X gets below state byte 1. ;; ;; Then go to STAL2, which is for accel and landing. ;; ;; ======================================================================== ;; BGT_STAL1 PROC MOVR R1, R2 SLL R2, 1 ADDI #SPXYP, R2 MVI@ R2, R4 ANDI #$FF, R4 CLRR R0 CMP@ R3, R4 BGT BGT_RET MVII #SPATBL.stal, R0 MVII #BGI.stal2, R4 B BGT_STAL0.1 ENDP ;; ======================================================================== ;; ;; BGT_STAL2 -- Fall, and then stick in the ground once we get there. ;; ;; ======================================================================== ;; BGT_STAL2 PROC MOVR R1, R2 ; SLL R2, 1 ; ADDI #SPXYP+1, R2 ; \ MVI@ R2, R0 ; |_ Are we on the ground yet? SWAP R0 ; | CMPI #$4500, R0 ; / BGE @@hit ADDI #SPXYV-SPXYP,R2 ; MVI@ R2, R4 ; \ ADDI #$4000, R4 ; |_ accelerate by $00.40 ADCR R4 ; | MVO@ R4, R2 ; / CLRR R0 B BGT_RET @@hit MVII #$4A, R0 ; \_ stick in the ground MVO@ R0, R2 ; / ADDI #SPXYV-SPXYP,R2 ; CLRR R0 ; \_ zero out our velocity MVO@ R0, R2 ; / MVII #SPAT, R2 ; ADDR R1, R2 ; MVII #SPATBL.stalf, R0 ; \_ Set prio so we're behind MVO@ R0, R2 ; / the ground ; PSHR R1 CALL PLAYSFX DECLE FXSMEXP ; PULR R1 MVII #BGI.null, R0 B BG_SETTHINK ENDP ;; ======================================================================== ;; ;; See 'bgsaucer.asm' for the Pink/Blue/White saucer motion programs. ;; ;; ======================================================================== ;;