And onwards we go. Picking up items showed a new problem. When an item is picked up we want the background behind restored (plus the item characters should not cut holes into the play field, allowing the player to fall through floors).
In the end I decided to have a second back buffer screen that contains the original play screen. Now every time an item is removed the character blocks are copied from the back buffer. Also, the back buffer is now used for collision detection. I could not avoid having to redraw the still existing item images in case the removed item was overlapping.
Effectively we double the work during level building. We start out with the new "buffers":
;address of the screen backbuffer
SCREEN_BACK_CHAR = $C800
;address of the screen backbuffer
SCREEN_BACK_COLOR = $C400
After calling the BuildScreen subroutine we copy the screen and color RAM to the backup buffers. Note the check for 230 bytes. We only have a play field of 40x23 characters, so only 4 * 230 = 920 bytes are needed.
;copy level data to back buffer
ldx #$00
.ClearLoop
lda SCREEN_CHAR,x
sta SCREEN_BACK_CHAR,x
lda SCREEN_CHAR + 230,x
sta SCREEN_BACK_CHAR + 230,x
lda SCREEN_CHAR + 460,x
sta SCREEN_BACK_CHAR + 460,x
lda SCREEN_CHAR + 690,x
sta SCREEN_BACK_CHAR + 690,x
inx
cpx #230
bne .ClearLoop
ldx #$00
.ColorLoop
lda SCREEN_COLOR,x
sta SCREEN_BACK_COLOR,x
lda SCREEN_COLOR + 230,x
sta SCREEN_BACK_COLOR + 230,x
lda SCREEN_COLOR + 460,x
sta SCREEN_BACK_COLOR + 460,x
lda SCREEN_COLOR + 690,x
sta SCREEN_BACK_COLOR + 690,x
inx
cpx #230
bne .ColorLoop
The repaint item function is thusly modified to simply copy the character and color values from the backup buffer:
;------------------------------------------------------------
;remove item image from screen
;Y = item index
;------------------------------------------------------------
!zone RemoveItemImage
RemoveItemImage
sty PARAM2
;set up pointers
lda ITEM_POS_Y,y
tay
lda SCREEN_LINE_OFFSET_TABLE_LO,y
sta ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_2
sta ZEROPAGE_POINTER_3
sta ZEROPAGE_POINTER_4
lda SCREEN_LINE_OFFSET_TABLE_HI,y
sta ZEROPAGE_POINTER_1 + 1
clc
adc #( ( SCREEN_COLOR - SCREEN_CHAR ) & 0xff00 ) >> 8
sta ZEROPAGE_POINTER_2 + 1
sec
sbc #( ( SCREEN_COLOR - SCREEN_BACK_CHAR ) & 0xff00 ) >> 8
sta ZEROPAGE_POINTER_3 + 1
sec
sbc #( ( SCREEN_BACK_CHAR - SCREEN_BACK_COLOR ) & 0xff00 ) >> 8
sta ZEROPAGE_POINTER_4 + 1
ldx PARAM2
ldy ITEM_POS_X,x
;... and copying
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_2),y
lda (ZEROPAGE_POINTER_3),y
sta (ZEROPAGE_POINTER_1),y
iny
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_2),y
lda (ZEROPAGE_POINTER_3),y
sta (ZEROPAGE_POINTER_1),y
tya
clc
adc #39
tay
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_2),y
lda (ZEROPAGE_POINTER_3),y
sta (ZEROPAGE_POINTER_1),y
iny
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_2),y
lda (ZEROPAGE_POINTER_3),y
sta (ZEROPAGE_POINTER_1),y
;repaint other items to avoid broken overlapped items
ldx #0
.RepaintLoop
lda ITEM_ACTIVE,x
cmp #ITEM_NONE
beq .RepaintNextItem
txa
pha
jsr PutItemImage
pla
tax
.RepaintNextItem
inx
cpx #ITEM_COUNT
bne .RepaintLoop
ldy PARAM2
rts