rogue-like/src/builder/builder.asm

340 lines
8.7 KiB
NASM

; Copyright (C) 2019 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/>.
; All the dungeon builder is based on this article: http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/
.include "rooms.inc"
.include "maze.inc"
.include "unite.inc"
.include "../common.inc"
.include "../actors/actors.inc"
.include "../io/textio.inc"
.include "../math.inc"
.include "../monitor.inc"
.include "../memory.inc"
.include "../world/world.inc"
.include "../world/level.inc"
; code
.import Random8
.import Grow_Maze ; to patch
.import Compute_Maze_Addr
.import Place_Actors
; data
.import World
.import Tile_player_standing_actor
.export Init_Dimensions_Maze
.export Build_Level
.export Rooms
.export WIDTH_MAZE
.export HEIGHT_MAZE
.BSS
; Describes a room to be built
; typedef struct {
; uint8_t height;
; uint8_t width;
; uint8_t x;
; uint8_t y;
; } room_t;
.define SIZEOF_ROOM_T 4
.align 256
Rooms: .res SIZEOF_ROOM_T*MAX_NB_ROOMS ; MAX 1 page of data!
WIDTH_MAZE: .res 1
HEIGHT_MAZE: .res 1
.DATA
STR_SIZE_MAZE_1: ASCIIZ "PLEASE ENTER THE SIZE OF THE LEVEL."
STR_SIZE_MAZE_2: ASCIIZ "A:TINY B:SMALL C:NORMAL D:BIG E:HUGE"
STR_SIZE_MAZE_3: ASCIIZ "PLEASE ENTER A VALID CHOICE."
STR_ROOMS: ASCIIZ "CARVING ROOMS..."
STR_MAZE: ASCIIZ "GROWING THE MAZE..."
STR_DOORS: ASCIIZ "OPENING DOORS..."
STR_DEADENDS: ASCIIZ "FILLING DEAD ENDS..."
STR_UNITE: ASCIIZ "UNITING THE ROOMS..."
STR_ACTORS: ASCIIZ "PLACING ACTORS..."
.CODE
; @brief Fills border walls
; @param type of the "wall" in A
; destroys ZERO_2_1, ZERO_2_2
.define ADDR_WORLD ZERO_2_1
.macro WORLD_NEXT_LINE
clc
lda ADDR_WORLD
adc #WIDTH_WORLD
sta ADDR_WORLD
lda ADDR_WORLD+1
adc #0
sta ADDR_WORLD+1
.endmacro
; DO NOT MESS WITH THIS FUNCTION: IT IS PATCHED!!
.define PATCH_WIDTH_MAZE_1 0
.define PATCH_HEIGHT_MAZE_2 0
_build_fences:
ldx #<World
stx ADDR_WORLD
ldx #>World
stx ADDR_WORLD+1
ldy #PATCH_WIDTH_MAZE_1
loop_wall_top:
sta (ADDR_WORLD), Y
dey
bne loop_wall_top
sta (ADDR_WORLD), Y
ldx #PATCH_HEIGHT_MAZE_2
loop_wall_left_right:
pha
WORLD_NEXT_LINE
pla
ldy #PATCH_WIDTH_MAZE_1
sta (ADDR_WORLD), Y
ldy #0
sta (ADDR_WORLD), Y
dex
bne loop_wall_left_right
pha
WORLD_NEXT_LINE
pla
ldy #PATCH_WIDTH_MAZE_1
loop_wall_bottom:
sta (ADDR_WORLD), Y
dey
bne loop_wall_bottom
sta (ADDR_WORLD), Y
rts
.undefine ADDR_WORLD
; @brief Sets the Maze's dimentions
; @param width in X
; @param height in Y
Init_Dimensions_Maze:
stx WIDTH_MAZE
; patch WIDTH_MAZE usage NO MORE PATCH: comment to be removed
dex
stx _build_fences + $9
stx _build_fences + $23
stx _build_fences + $3D
; patch HEIGHT_MAZE usage NO MORE PATCH: comment to be removed
sty HEIGHT_MAZE
dey
dey
sty _build_fences + $12
rts
; @brief Builds a whole level
; @param Uses NextLevel to get the conf
; @return player position in X and Y
.define DST_WORLD World
.define ADDR_TO_PATCH init_world_line + 3
.define NB_ROOMS ZERO_9_9 ; use same location as Place_Actors
Build_Level:
lda #UNDEF
sta Tile_player_standing_actor
; Filling World with eACTORTYPES::WALL_1
ldy #HEIGHT_WORLD
init_world:
ldx #0
init_world_line:
lda #eACTORTYPES::WALL_1
sta DST_WORLD, x
inx
cpx #WIDTH_WORLD
bne init_world_line
; patching DST_WORLD
lda ADDR_TO_PATCH
clc
adc #WIDTH_WORLD
sta ADDR_TO_PATCH
lda ADDR_TO_PATCH + 1
adc #0
sta ADDR_TO_PATCH + 1
dey
bne init_world
; patching back for a future execution
lda #<DST_WORLD
sta ADDR_TO_PATCH
lda #>DST_WORLD
sta ADDR_TO_PATCH+1
PRINT STR_ROOMS
lda #MAX_NB_ROOMS+1
jsr Carve_Rooms
sta NB_ROOMS
lda #eACTORTYPES::FLOOR_1
jsr _build_fences
PRINT STR_MAZE
jsr Grow_Maze
lda #eACTORTYPES::WALL_1
jsr _build_fences
PRINT STR_DOORS
.define PERCENT_7 #17
ldx PERCENT_7
lda NB_ROOMS
jsr Connect_Rooms
PRINT STR_DEADENDS
jsr Remove_Dead_Ends
PRINT STR_UNITE
jsr Unite_Rooms
; the two following defines must be the same as in Place_Actors
.define POS_X ZERO_3 ; ROOM_X in Place_Actors
.define POS_Y ZERO_2_4 ; ROOM_Y in Place_Actors
.define POS_STARDOWN_X ZERO_5_1
.define POS_STARDOWN_Y ZERO_5_2
.define ACTOR_NB ZERO_4_2
.define ACTOR_ID ZERO_9_1 ; use same location as Place_Actors
.define ACTOR_TYPE ZERO_9_2 ; use same location as Place_Actors
.define CURR_ACTOR_OFFSET ZERO_4_3
.define ADDR_LEVEL_CONF ZERO_5_3 ; 2 bytes
.define ADDR_ACTOR ZERO_4_3 ; 2 bytes
; place actors
PRINT STR_ACTORS
; offset to level conf
lda NextLevel
jsr level_get_config_offset
pha
clc
txa
adc #<LevelConfigs
sta ADDR_LEVEL_CONF
pla
adc #>LevelConfigs
sta ADDR_LEVEL_CONF+1
lda #eACTORTYPES::LAST_STATIC + 1 ; 1st dynamic actor id
sta ACTOR_ID
sta ACTOR_TYPE
tay
iny
iny ; offset to actors[ACTOR_ID] in level conf
loop_actors: ; loop over actors from conf
sty CURR_ACTOR_OFFSET
lda (ADDR_LEVEL_CONF), Y
sta ACTOR_NB
loop_actor_id:
beq end_loop_actor_id
jsr Place_Actors
; save stair down position
lda ACTOR_TYPE
cmp #eACTORTYPES::STAIR_DOWN
bne not_stair_down
lda POS_X
sta POS_STARDOWN_X
lda POS_Y
sta POS_STARDOWN_Y
not_stair_down:
inc ACTOR_ID
dec ACTOR_NB ; next
lda ACTOR_NB
jmp loop_actor_id
end_loop_actor_id:
ldy CURR_ACTOR_OFFSET
iny
inc ACTOR_TYPE
lda ACTOR_TYPE
cmp #(NB_ACTORS_MAX-1)
bne loop_actors
; Set the 1st position of the player in the level
; offset to level state
lda NextLevel
cmp #0
bne not_first_level
; Special case: first level
; TODO avoid non empty floor...
ldx Rooms+2 ; Rooms[0].x
ldy Rooms+3 ; Rooms[0].y
rts
not_first_level:
ldx POS_STARDOWN_X
ldy POS_STARDOWN_Y
jsr Compute_Maze_Addr
; addr offseted by - witdh_maze to access all tiles with offset
sta ADDR_ACTOR+1
txa
sec
sbc #WIDTH_WORLD
sta ADDR_ACTOR
lda ADDR_ACTOR+1
sbc #0
sta ADDR_ACTOR+1
ldx POS_STARDOWN_X
; NOTE: There is at least one solution, the tile is not surrounded!
; if (World[pos_stair_down.y][pos_stair_down.x - 1] == FLOOR_2)
ldy #(WIDTH_WORLD - 1)
lda (ADDR_ACTOR), Y
cmp #eACTORTYPES::FLOOR_2
bne not_x_minus
ldy POS_STARDOWN_Y
dex
rts
not_x_minus:
; if (World[pos_stair_down.y - 1][pos_stair_down.x] == FLOOR_2)
ldy #0
lda (ADDR_ACTOR), Y
cmp #eACTORTYPES::FLOOR_2
bne not_y_minus
ldy POS_STARDOWN_Y
dey
rts
not_y_minus:
; if (World[pos_stair_down.y + 1][pos_stair_down.x] == FLOOR_2)
ldy #(WIDTH_WORLD * 2)
lda (ADDR_ACTOR), Y
cmp #eACTORTYPES::FLOOR_2
bne not_y_plus
ldy POS_STARDOWN_Y
iny
rts
not_y_plus:
ldy POS_STARDOWN_Y
inx
rts