top of page
Search
cedarcantab

Space Invaders in Phaser 3 (Part 12): The other 2 types of bombs

In this post I document the challenges in my attempt to replicate the alien shots.


The behaviour of the plunger and the squiggle shots are quite similar. I will analyse the code of the Plunger shot.


Game Object Descriptor

Let's start by looking at the game object descriptor



The task handler

The task handler, as indicated above starts from $04B6.


This particular shot type has a skipPlunger flag which is checked at the beginning.


As with the Rolling shot, I do not fully understand the code in RED, but I assume to be code that runs the task every 3 frames for each object.


GameObj3:
; Game object 3: Alien plunger-shot
; This is skipped if there is only one alien left on the screen.
;
04B6: E1              POP     HL                  ; Game object data
04B7: 3A 6E 20        LD      A,(skipPlunger)     ; One alien left? Skip plunger shot?
04BA: A7              AND     A                   ; Check
04BB: C0              RET     NZ                  ; Yes. Only one alien. Skip this shot.
04BC: 3A 80 20        LD      A,(shotSync)        ; Sync flag (copied from GO-2's timer value)
04BF: FE 01           CP      $01                 ; GO-2 and GO-4 are idle?
04C1: C0              RET     NZ                  ; No ... only one shot at a time

04C2: 11 45 20        LD      DE,$2045            ; Plunger alien shot data structure
04C5: 3E ED           LD      A,$ED               ; Last picture of "plunger" alien shot
04C7: CD 50 05        CALL    ToShotStruct        ; Copy the plunger alien to the active structure
04CA: 3A 36 20        LD      A,(rolShotStepCnt)  ; Step count from rolling-shot
04CD: 32 70 20        LD      (otherShot1),A      ; Hold it
04D0: 3A 56 20        LD      A,(squShotStepCnt)  ; Step count from squiggly shot
04D3: 32 71 20        LD      (otherShot2),A      ; Hold it
04D6: CD 63 05        CALL    HandleAlienShot     ; Handle active shot structure
04D9: 3A 76 20        LD      A,(aShotCFirLSB)    ; LSB of column-firing table
04DC: FE 10           CP      $10                 ; Been through all entries in the table?
04DE: DA E7 04        JP      C,$04E7             ; Not yet ... table is OK
04E1: 3A 48 1B        LD      A,($1B48)           ; Been through all ..
04E4: 32 76 20        LD      (aShotCFirLSB),A    ; ... so reset pointer into firing-column table
04E7: 3A 78 20        LD      A,(aShotBlowCnt)    ; Get the blow up timer
04EA: A7              AND     A                   ; Zero means shot is done
04EB: 21 45 20        LD      HL,$2045            ; Plunger shot data
04EE: C2 5B 05        JP      NZ,FromShotStruct   ; If shot is still running, go copy the updated data and out
;
04F1: 11 40 1B        LD      DE,$1B40            ; Reload ...
04F4: 21 40 20        LD      HL,$2040            ; ... object ...
04F7: 06 10           LD      B,$10               ; ... structure ...
04F9: CD 32 1A        CALL    BlockCopy           ; ... from mirror
;
04FC: 3A 82 20        LD      A,(numAliens)       ; Number of aliens on screen
04FF: 3D              DEC     A                   ; Is there only one left?
0500: C2 08 05        JP      NZ,$0508            ; No ... move on
0503: 3E 01           LD      A,$01               ; Disable plunger shot ...
0505: 32 6E 20        LD      (skipPlunger),A     ; ... when only one alien remains
0508: 2A 76 20        LD      HL,(aShotCFirLSB)   ; Set the plunger shot's ...
050B: C3 7E 06        JP      $067E               ; ... column-firing pointer data

The main different between the plunger and squiggle shots vs the rolling shot is the fact that the plunger shot is fired by the column of aliens in accordance with a fixed table.


ColFireTable:
; This table decides which column a shot will fall from. The column number is read from the
; table (1-11) and the pointer increases for the shot type. For instance, the "squiggly" shot will fall from columns in this order: 0B, 01, 06, 03. If you play the game you'll see that
; order.
;
; The "plunger" shot uses index 00-0F (inclusive)
; The "squiggly" shot uses index 06-14 (inclusive)
; The "rolling" shot targets the player
1D00: 01 07 01 01 01 04 0B 01 06 03 01 01 0B 09 02 08                                    
1D10: 02 0B 04 07 0A    

You can see from the table that the "squiggly" shot will fall first in column 0x0B (i.e. 11) and then 1, 6, and 3.


I will not go into the task handler routine for the squiggly shot except to say that the only difference vs the plunger shot is the lack of the check for the number of remaining invaders.


Speed of the alien bomb

From the main loop is called the following code which adjusts the speed of alien shot depending on the number of remaining aliens. If less than 8 aliens, the alien shot speeds up from 4 x (60/3) = 80 pixels per second to 5 x (60/3) = 100 pixels per second.


SpeedShots:
; With less than 9 aliens on the screen the alien shots get a tad bit faster. Probably
; because the advancing rack can catch them.
;
08D8: 3A 82 20        LD      A,(numAliens)       ; Number of aliens on screen
08DB: FE 09           CP      $09                 ; More than 8?
08DD: D0              RET     NC                  ; Yes ... leave shot speed alone
08DE: 3E FB           LD      A,$FB               ; Normally FF (-4) ... now FB (-5)
08E0: 32 7E 20        LD      (alienShotDelta),A  ; Speed up alien shots
08E3: C9              RET                         ; Done

All I have done is to amend the dropBomb method of the base Bomb class to include this check (I have not reproduced the code in this blog - see CODEPEN).


Javascript / Phaser 3 version

Armed with the above knowledge, I have amended the handleBombTask as follows.

  handleBombTask() {
    switch (this.bombTaskTimer) {
      case 0:
        // run the rolling shot task - below
        if (this.rolling.skip) {
          this.rolling.skip = false;
        } else  {
          this.otherShot1 = this.plunger.shotStepCount;
          this.otherShot2 = this.squiggle.shotStepCount;
          this.bombColumn = this.rack.findColumn();
          this.handleAlienShot(this.rolling);
        }
        break;
      case 1:
        // run the plunger shot task - below
        if (this.rack.liveInvaders > 1) {   
          this.otherShot1 = this.rolling.shotStepCount;
          this.otherShot2 = this.squiggle.shotStepCount;
          this.bombColumn = this.plunger.getFireColumn();       
          this.handleAlienShot(this.plunger);
        }
        break;
      case 2:
        // run the squiggle shot/saucer task - below
        this.otherShot1 = this.rolling.shotStepCount;
        this.otherShot2 = this.plunger.shotStepCount;
        this.bombColumn = this.squiggle.getFireColumn();       
        this.handleAlienShot(this.squiggle);
        break;
    }
    this.bombTaskTimer = this.bombTaskTimer <= 1 ? this.bombTaskTimer + 1 : 0;
    this.bombColumn = undefined;
  }

In comparison to the rolling shot, instead of calling the findColumn method of the rack class, the other 2 bomb types call getFireColumn method of the bomb class, in order to "read" the which column in the rack of aliens the bomb should fall from, from the fixed table defined in the relevant bomb classes.


  getFireColumn() {
    const colIndex = this.colIndex;
    this.colIndex = (this.colIndex <= this.COLFIRETABLE.length-1) ? this.colIndex+1 : 0; 
    return this.COLFIRETABLE[colIndex]
  }

The one very very important change I made to the code from the previous post is an addition of one line to the dropBomb method of the base bomb class, as follows.


dropBomb(x,y) {
    this.shotStepCount = 1; // when bomb is triggerd, immediate set stepcount to 1
    this.enableBody(true, x, y, true, true);
    this.setStatus(Bomb.Status.DROPPED);
  }

The shotStepCount is incremented by the handleAlienShot method, as shown below. So it should start incrementing immediately after it's "dropped". However, without the above one line in RED above, the handleAlienShot seems to catch the "other 2" bombs when their stepCount is still at zero, and all 3 bombs are always launched at the same time. At least, I think that is what's happening.


handleAlienShot(bomb) {
    if (bomb.isActive()) {
      // if the bomb is already active, update the stepCount but do no need to do any more here
      bomb.shotStepCount ++;  
      return;
    } 
    if (this.otherShot1 > 0 && (this.reloadRate > this.otherShot1)) return
    // if not enough time since last bomb dropped, do no more
    if (this.otherShot2 > 0 && this.reloadRate > this.otherShot2) return; // if not enough time since last bomb dropped, do no more
    // get to here means we can fire (so long as there is an alien that can shoot)
    let shooter = this.rack.getAlienCoords(this.bombColumn);
        if (shooter) bomb.dropBomb(shooter.x, shooter.y)
  }

Anyway, with the above, I now have all 3 bombs being fired by the rack at regular intervals.


The CODEPEN for the code up to and including the above is below.



2 views0 comments

Recent Posts

See All

p2 naive broadphase

var Broadphase = require('../collision/Broadphase'); module.exports = NaiveBroadphase; /** * Naive broadphase implementation. Does N^2...

sopiro motor constranit

import { Matrix2, Vector2 } from "./math.js"; import { RigidBody } from "./rigidbody.js"; import { Settings } from "./settings.js";...

Comments


記事: Blog2_Post
bottom of page