; Copyright (C) 2020 Christophe Meneboeuf ; ; 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 . .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+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 #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