Pobtastic / Booty / Disappearing Floors

Created Sat, 13 Apr 2024 12:11:04 +0100 Modified Thu, 12 Dec 2024 14:35:47 +0000

Introduction

This handler is very simple, it’s a handler for “counting” between states for disappearing floors (trapdoors maybe?) The game flips between showing and hiding the floor graphic, this feature only appears in a few rooms, but they’re a lovely simple addition to the game and the code is relatively easy to follow.

Table: Room “Disappearing Floor” Data

Here’s an example of some “Disappearing Floors” room data (see Room #17 for the full list).

; Data: Room #17

; Disappearing floors:

; Instance #01.
 $C04A DEFB $1A,$12                 ; Coordinates: 1A/ 12.
 $C04C DEFB $82                     ; Width: 02 (DISAPPEARED).
 $C04D DEFB $8C                     ; Limit for "VISIBLE" timer.
 $C04E DEFB $46                     ; Limit for "DISAPPEARED" timer.
 $C04F DEFB $00                     ; Floor change timer.

; Instance #02.
 $C050 DEFB $1A,$0D                 ; Coordinates: 1A/ 0D.
 $C052 DEFB $02                     ; Width: 02 (VISIBLE).
 $C053 DEFB $8C                     ; Limit for "VISIBLE" timer.
 $C054 DEFB $46                     ; Limit for "DISAPPEARED" timer.
 $C055 DEFB $46                     ; Floor change timer.

; Instance #03.
 $C056 DEFB $1A,$08                 ; Coordinates: 1A/ 08.
 $C058 DEFB $02                     ; Width: 02 (VISIBLE).
 $C059 DEFB $8C                     ; Limit for "VISIBLE" timer.
 $C05A DEFB $46                     ; Limit for "DISAPPEARED" timer.
 $C05B DEFB $00                     ; Floor change timer.

; All room data is terminated using $FF.
 $C05C DEFB $FF                     ; Terminator.

In the routine, the data is accessed using the IX register which means all the values will be accessed using offsets:

Offset Containing
+$00 Horizontal Position
+$01 Vertical Position
+$02 Width/ State*
+$03 Visible Max. Count
+$04 Disappeared Max. Count
+$05 Change Timer

NOTE; *IX+$02 holds both the floor width and current “state”. It does this by utilising bit 7 for holding the current “state”:

  • When bit 7 is set then the floor doesn’t show.
  • When it isn’t set, the floor is present.

And bits 0-6, as they’re never higher than $80, hold the floor section length.

Routine: Disappearing Floors

The room data is of varying lengths and on room load, each section of data has its starting address stored in a number of reference pointer addresses.

; Handler: Disappearing Floors
@label=Handler_DisappearingFloors

; Fetch the address contained at $5BE6 (the data isn't stored at $5BE6 itself).
c$E581 LD IX,($5BE6)   ; IX=*ReferenceDisappearingFloors.

; The body of the loop; check each instance of disappearing floor data.
@label=Handler_DisappearingFloors_Loop
; If a termination character is returned then this indicates we're finished
; checking all available disappearing floor data so then return.
*$E585 LD A,(IX+$00)   ; {Return if the terminator character has been received
 $E588 CP $FF          ; instead of a co-ordinate ($FF).
 $E58A RET Z           ; }

; Bit 7 of *IX+$02 holds the floor "state".
; Is the floor currently visible?
 $E58B LD A,(IX+$02)   ; A=Sprite width+state (*IX+$02).
 $E58E AND %10000000   ; Keep only the current "state".
 $E590 JP Z,$E5C5      ; Jump to DisplayFloor if the floor should be visible.

; We didn't jump, so the floor is currently NOT visible.
; The game uses a bunch of $00 bytes at Graphics_MaskSprite to "print empty
; space" - this is how we remove sprites.
 $E593 LD HL,$9F6C     ; Write $9F6C (Graphics_MaskSprite) to *CHARS.
 $E599 INC (IX+$05)    ; Increment the floor change timer by one.

; Check the current floor change timer against the max. count for how long the
; floor should stay disappeared for.
 $E59C LD A,(IX+$04)   ; {Jump to PrintDisappearedFloor if the current floor
 $E59F CP (IX+$05)     ; change timer has not yet reached the maximum
 $E5A2 JR NZ,$E5B0     ; value set in the disappeared timer count.}

; The timer for how long the floor should be "disappeared" has elapsed.
 $E5A4 LD A,(IX+$02)   ; A=Sprite width+state (*IX+$02).
 $E5A7 AND %01111111   ; Strip off bit 7 (which is the current "state") as we
                       ; need to unset it so the floor will display next cycle.
 $E5A9 LD (IX+$02),A   ; Write this value back to sprite width+state (*IX+$02).
 $E5AC LD (IX+$05),$00 ; Reset the floor change timer back to $00.

; Whilst this routine could also print the floor as well, the game needs to
; update other buffers when it changes, so this version is a lot simpler.
@label=PrintDisappearedFloor
*$E5B0 LD C,(IX+$00)   ; C=Horizontal position.
 $E5B3 LD B,(IX+$01)   ; B=Vertical position.
; Bits 0-6 of *IX+$02 hold the floor width.
 $E5B6 LD A,(IX+$02)   ; A=Sprite width+state (*IX+$02).
 $E5B9 AND %01111111   ; Strip off bit 7 (which is the current "state") as we need the
                       ; actual width to render the empty space where the floor should be.
 $E5BB LD E,A          ; E=Sprite width (A).
 $E5BC LD D,$01        ; D=Sprite height (always $01).
 $E5BE LD A,$20        ; A=base sprite ID ($20).
 $E5C0 CALL $EA93      ; Call PrintSprite.
 $E5C3 JR $E5ED        ; Jump to DisappearingFloors_Next.

; The floor is currently visible so handle checking the current floor change
; timer against the max. count for how long the floor should stay visible for.
@label=DisplayFloor
*$E5C5 LD A,(IX+$03)   ; Fetch the maximum visible count value.
 $E5C8 INC (IX+$05)    ; Increment the floor change timer by one.
 $E5CB CP (IX+$05)     ; {Jump to PrintVisibleFloor if the current floor change
 $E5CE JR NZ,$E5DC     ; timer has not yet reached the maximum value set in the
                       ; visible timer count.}

; The timer for how long the floor should be "visible" has elapsed.
 $E5D0 LD A,(IX+$02)   ; A=Sprite width+state (*IX+$02).
 $E5D3 OR %10000000    ; Set bit 7 (which is the current "state") so the floor
                       ; will disappear next cycle.
 $E5D5 LD (IX+$02),A   ; Write this value back to sprite width+state (*IX+$02).
 $E5D8 LD (IX+$05),$00 ; Reset the floor change timer back to $00.

; Display the visible floor. Note the similarity to PrintDisappearedFloor.
@label=PrintVisibleFloor
*$E5DC LD C,(IX+$00)   ; C=Horizontal position.
 $E5DF LD B,(IX+$01)   ; B=Vertical position.
; Bits 0-6 of *IX+$02 hold the floor width.
 $E5E2 LD A,(IX+$02)   ; A=Sprite width+state byte.
 $E5E5 AND %01111111   ; Strip off bit 7 (which is the current "state") as we
                       ; need the actual width.
 $E5E7 LD E,A          ; E=Set the sprite width from above.
 $E5E8 LD D,$01        ; D=Sprite height (always $01).
 $E5EA CALL $E787      ; Call PrintSpriteUpdateBuffer.

; Move onto the next disappearing floor data.
@label=DisappearingFloors_Next
*$E5ED LD DE,$0006     ; {IX+=$0006.
 $E5F0 ADD IX,DE       ; }
 $E5F2 JR $E585        ; Jump to #R$E585.

Booty Disassembly