rogue-like/src/builder/unite.asm

561 lines
13 KiB
NASM

; Copyright (C) 2020 Christophe Meneboeuf <christophe@xtof.info>
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
.include "rooms.inc"
.include "maze.inc"
.include "../io/textio.inc"
.include "../memory.inc"
.include "../math.inc"
.include "../common.inc"
.include "../world/world.inc"
.include "../actors/actors.inc"
.import World
.import Rooms
.import WIDTH_MAZE
.import HEIGHT_MAZE
.import Compute_Maze_Addr
.export Unite_Rooms
.CODE
.define PTR_TILE ZERO_2_1 ; 2 bytes
.define ZONE_NR ZERO_2_4
.define CPT_X ZERO_2_5
.define CPT_Y ZERO_2_6
.define QUEUE_ADDR $4000
.define REPLACED_NR ZERO_3
.define PTR_QUEUE ZERO_4_1 ; 2 bytes
.define PTR_TILE_LOCAL ZERO_4_3 ; 2 bytes
.define FILL_NR ZERO_4_5
; @param X fill color
; @param A replaced
; @return TRUE in A some tiles were filled, FALSE otherwise
_Flood_Fill :
stx FILL_NR
ldy #0
cmp (PTR_TILE), Y ; if (*ptr_tile != replaced) return;
beq fill_1
lda #FALSE
rts
fill_1:
sta REPLACED_NR
txa
sta (PTR_TILE), Y
lda #<QUEUE_ADDR
sta PTR_QUEUE
lda #>QUEUE_ADDR
sta PTR_QUEUE+1
; offset ptr_tile by -width_world to be quickly accessed with an Y offset
lda PTR_TILE
sec
sbc #WIDTH_WORLD
sta QUEUE_ADDR
lda PTR_TILE+1
sbc #0
sta QUEUE_ADDR+1
ldx #0
loop_fill:
; while ptr_queue >= QUEUE_ADDR
lda PTR_QUEUE
cmp #<(QUEUE_ADDR-2)
bne continue_fill
lda PTR_QUEUE+1
cmp #>(QUEUE_ADDR-2)
bne continue_fill
jmp end_fill
continue_fill:
; ptr_tile = *(ptr_queue--);
ldy #0
lda (PTR_QUEUE), Y
sta PTR_TILE_LOCAL
iny
lda (PTR_QUEUE), Y
sta PTR_TILE_LOCAL+1
DEC16 PTR_QUEUE, #2
tile_west:
; if (*tile_west == replaced)
ldy #(WIDTH_WORLD+1)
lda REPLACED_NR
cmp (PTR_TILE_LOCAL),Y
bne tile_east
; *tile_west = fill;
lda FILL_NR
sta (PTR_TILE_LOCAL),Y
; *(++ptr_queue) = tile_w
ADD16 PTR_QUEUE, #2, #0
clc
lda #1
adc PTR_TILE_LOCAL
ldy #0
sta (PTR_QUEUE),Y
lda #0
adc PTR_TILE_LOCAL+1
iny
sta (PTR_QUEUE),Y
tile_east:
; if (*tile_east == replaced)
ldy #(WIDTH_WORLD-1)
lda REPLACED_NR
cmp (PTR_TILE_LOCAL),Y
bne tile_north
; *tile_east = fill;
lda FILL_NR
sta (PTR_TILE_LOCAL),Y
; *(++ptr_queue) = tile_tile_east
ADD16 PTR_QUEUE, #2, #0
sec
lda PTR_TILE_LOCAL
sbc #1
ldy #0
sta (PTR_QUEUE),Y
lda PTR_TILE_LOCAL+1
sbc #0
iny
sta (PTR_QUEUE),Y
tile_north:
; if (*tile_north == replaced)
ldy #0
lda REPLACED_NR
cmp (PTR_TILE_LOCAL),Y
bne tile_south
; *tile_north = fill;
lda FILL_NR
sta (PTR_TILE_LOCAL),Y
; *(++ptr_queue) = tile_tile_north
ADD16 PTR_QUEUE, #2, #0
sec
lda PTR_TILE_LOCAL
sbc #WIDTH_WORLD
ldy #0
sta (PTR_QUEUE),Y
lda PTR_TILE_LOCAL+1
sbc #0
iny
sta (PTR_QUEUE),Y
tile_south:
; if (*tile_south == replaced)
ldy #(2*WIDTH_WORLD)
lda REPLACED_NR
cmp (PTR_TILE_LOCAL),Y
bne end_tiles
; *tile_south = fill;
lda FILL_NR
sta (PTR_TILE_LOCAL),Y
; *(++ptr_queue) = tile_tile_south
ADD16 PTR_QUEUE, #2, #0
clc
lda #WIDTH_WORLD
adc PTR_TILE_LOCAL
ldy #0
sta (PTR_QUEUE),Y
lda #0
adc PTR_TILE_LOCAL+1
iny
sta (PTR_QUEUE),Y
end_tiles:
jmp loop_fill
end_fill:
lda #TRUE
rts
.define ROOM_NR ZERO_3
.define SAVE_X ZERO_4_1
.define PTR_ROOM ZERO_4_2 ; 2 bytes
.define ZONE_0 eACTORTYPES::FLOOR_1 ; 1st useful zone: ZONE_1
Unite_Rooms:
; *** flood fill room to identify separated zones ***
; &Wordl[1][1] -> ptr_tile
clc
lda #<World
adc #WIDTH_WORLD+1
sta PTR_TILE
lda #>World
adc #0
sta PTR_TILE+1
lda #1
sta CPT_X
sta CPT_Y
lda #ZONE_0
sta ZONE_NR
inc ZONE_NR
loop_flood:
ldx ZONE_NR
lda #ZONE_0
jsr _Flood_Fill
cmp #TRUE
bne loop_flood_next
inc ZONE_NR
loop_flood_next:
;next tile
ADD16 PTR_TILE, #1, #0
; end line?
inc CPT_X
ldx CPT_X
cpx #WIDTH_WORLD-1
bne loop_flood
ldx #0
stx CPT_X
; next
ADD16 PTR_TILE, #1, #0
; the end?
inc CPT_Y
ldy CPT_Y
cpy #HEIGHT_WORLD-1
bne loop_flood
; ***
; reunite rooms that are not in the first zone
; find the location of a room in zone_1
; Its origin will be targeted by th other zones to join
; ***
.define SIZEOF_ROOM_T 4
.define ROOM_ZONE_1 ZERO_5_4
lda #0 ; FIXEME : useless?
sta ROOM_NR ; FIXEME : useless?
ldx #3
lda #WIDTH_WORLD
sta FAC2
loop_find_room:
lda Rooms, X
tay
stx SAVE_X
dex
lda Rooms, X
tax
jsr Compute_Maze_Addr
sta PTR_TILE+1
stx PTR_TILE
ldy #0
lda (PTR_TILE), Y
cmp #(ZONE_0 + 1)
beq room_found
; next room
lda SAVE_X
clc
adc #(SIZEOF_ROOM_T)
tax
jmp loop_find_room
room_found:
ldx SAVE_X
stx ROOM_ZONE_1
; ***
; Connect one room of each zone to the location found
.define END_LOOP_ZONE ZERO_5_1
lda ZONE_NR
sta END_LOOP_ZONE
lda #(ZONE_0 + 2) ; zone_2
cmp END_LOOP_ZONE
beq end_loop_zone ; only one zone
sta ZONE_NR
; loop over the zones
loop_zones:
ldx #3
loop_rooms: ; find a room of the zone
lda Rooms, X
tay
stx SAVE_X
dex
lda Rooms, X
tax
jsr Compute_Maze_Addr
stx ZERO_5_2
sta ZERO_5_3
ldy #0
lda (ZERO_5_2), Y
cmp ZONE_NR
beq zone_found
; next room
lda SAVE_X
clc
adc #(SIZEOF_ROOM_T)
tax
jmp loop_rooms
zone_found:
jsr _Connect_Room
; next zone
inc ZONE_NR
; end loop?
lda END_LOOP_ZONE
cmp ZONE_NR
beq end_loop_zone
; loop
jmp loop_zones
end_loop_zone:
rts
; to compute ptr_romm += ix / iy or ptr_romm += ix / iy, the code is patched.
.macro PATCH_POS address, address2
lda #$18 ; clc
sta address
sta address2
lda #$69 ; adc imm
sta address+3 ; adc #1 / #WIDTH_WORLD
sta address+9 ; adc #0
sta address2+3 ; adc #1 / #WIDTH_WORLD
sta address2+9 ; adc #0
.endmacro
.macro PATCH_NEG address, address2
lda #$38 ; sec
sta address
sta address2
lda #$E9 ; sbc imm
sta address+3 ; sbc #1 / #WIDTH_WORLD
sta address+9 ; sbc #0
sta address2+3 ; sbc #1 / #WIDTH_WORLD
sta address2+9 ; sbc #0
.endmacro
.define DELTA_X ZERO_9_1
.define DELTA_Y ZERO_9_2
.define DELTA_X_2 ZERO_9_3
.define DELTA_Y_2 ZERO_9_4
.define ROOM_Y ZERO_9_5
.define ROOM_X ZERO_9_6
.define D ZERO_9_7
.define ROOM_FOUND SAVE_X
_Connect_Room:
; d = 0
lda #0
sta D
; delta_y = zone1_y - room->y
ldx ROOM_FOUND
lda Rooms, X
sta ROOM_Y ; room->y
dex
lda Rooms, X
sta ROOM_X ; room->x
ldx ROOM_ZONE_1
lda Rooms, X ; zone1_y
sec
sbc ROOM_Y
sta DELTA_Y
; delta_x = zone1_x - room->x
dex
lda Rooms, X ; zone1_x
sec
sbc ROOM_X
sta DELTA_X
; delta_x = delta_x > 0 ? delta_x : -delta_x
lda #0
cmp DELTA_X
bmi end_abs_x
sec
sbc DELTA_X
sta DELTA_X
end_abs_x:
; int dx2 = 2 * dx
lda DELTA_X
asl
sta DELTA_X_2
; delta_y = delta_y > 0 ? delta_y : -delta_y
abs_delta_y:
lda #0
cmp DELTA_Y
bmi end_abs_y
sec
sbc DELTA_Y
sta DELTA_Y
end_abs_y:
; int dy2 = 2 * dy
lda DELTA_Y
asl
sta DELTA_Y_2
; uint8_t* ptr_room = &World[room->y][room->x]
ldx ROOM_X
ldy ROOM_Y
jsr Compute_Maze_Addr
stx PTR_ROOM
stx PTR_TILE
sta PTR_ROOM+1
sta PTR_TILE+1
ldx ROOM_ZONE_1
dex
; int ix = room->x < zone1_x ? 1 : -1
ix:
lda ROOM_X
cmp Rooms, X
bcc ix_positive
PATCH_NEG patch_ix1, patch_ix2
jmp iy
ix_positive:
PATCH_POS patch_ix1, patch_ix2
; int iy = room->y < zone1_y ? WIDTH_WORLD : -WIDTH_WORLD;
iy:
inx
lda ROOM_Y
cmp Rooms, X
bcc iy_positive
PATCH_NEG patch_iy1, patch_iy2
jmp iterate
iy_positive:
PATCH_POS patch_iy1, patch_iy2
iterate:
ldy #0
lda DELTA_X
cmp DELTA_Y
bcc dy_sup
; if (dx >= dy)
dx_sup:
while_1:
; ptr_room += ix
patch_ix1:
ADD16 PTR_ROOM, #1, #0
; d += dy2
clc
lda DELTA_Y_2
adc D
sta D
cmp DELTA_X
bcc d_infequal_dx
beq d_infequal_dx
; if (d > dx)
; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break;
lda (PTR_ROOM), Y ; Y = 0
cmp ZONE_NR
beq continue_1a
cmp #eACTORTYPES::LAST_FLOOR
beq end
bpl continue_1a
jmp end
continue_1a:
; *ptr_room = zone_nr
lda ZONE_NR
sta (PTR_ROOM), Y ; Y = 0
; ptr_room += iy
patch_iy1:
ADD16 PTR_ROOM, #WIDTH_WORLD, #0
; d -= dx2
sec
lda D
sbc DELTA_X_2
sta D
d_infequal_dx:
; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break;
lda (PTR_ROOM), Y ; Y = 0
cmp ZONE_NR
beq continue_1b
cmp #eACTORTYPES::LAST_FLOOR
beq end
bpl continue_1b
jmp end
continue_1b:
lda ZONE_NR
sta (PTR_ROOM), Y ; Y = 0
jmp while_1
; end label in the middle to be reachable by the branches
end:
; flood fills works on ptr_tile
ldx #(ZONE_0 + 1)
lda ZONE_NR
jsr _Flood_Fill
rts
dy_sup:
while_2:
; ptr_room += iy
patch_iy2:
ADD16 PTR_ROOM, #WIDTH_WORLD, #0
; d += dx2
clc
lda DELTA_X_2
adc D
sta D
cmp DELTA_Y
bcc d_infequal_dy
beq d_infequal_dy
; if (d > dy) {
; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break;
lda (PTR_ROOM), Y ; Y = 0
cmp ZONE_NR
beq continue_2a
cmp #eACTORTYPES::LAST_FLOOR
beq end
bpl continue_2a
jmp end
continue_2a:
; *ptr_room = zone_nr
lda ZONE_NR
sta (PTR_ROOM), Y ; Y = 0
; ptr_room += ix;
patch_ix2:
ADD16 PTR_ROOM, #1, #0
; d -= dy2
sec
lda D
sbc DELTA_Y_2
sta D
d_infequal_dy:
; (*ptr_room != zone_nr && *ptr_room <= WALKABLE)
lda (PTR_ROOM), Y ; Y = 0
cmp ZONE_NR
beq continue_2b
cmp #eACTORTYPES::LAST_FLOOR
beq end
bpl continue_2b
jmp end
continue_2b:
lda ZONE_NR
sta (PTR_ROOM), Y ; Y = 0
jmp while_2