-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerate.s
351 lines (330 loc) · 6.79 KB
/
generate.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
.include "global.inc"
; todo do this in batches - first generate maze, with *no* overlapping corridors
; todo then, generate rooms with max dimensions of 4x4
; todo this way, lighting & scrolling code don't update as much tiles at once
;
; todo can also do some clever tricks by making rooms have doors, so that we
; todo only show corridor *or* room at once (door would block sight)
;
; todo if we limit corridors to max length of 4, this would also eliminate
; todo need for sprite flicker
.export generate
.segment "ZEROPAGE"
tunnels: .res 1
tunnel_len: .res 1
direction: .res 1 ; represents corridor direction
prevdir: .res 1 ; previous direction
prevdlevel: .res 1 ; previous dlevel
max_tunnels = 90 ; maximum tunnels
max_length = 6 ; maximum length for tunnel
.segment "CODE"
; generate level
;
; clobbers: x, y, a1, and a2
.proc generate
initialize:
lda dlevel
cmp #0
bne clear_tiles
; initialize dlevel and prevdlevel
sta prevdlevel
inc dlevel
clear_tiles:
ldx #$00 ; counter for background sprite position
ldy #$00 ; counter for background bit index
lda #$00
clear_loop:
sta tiles, x
sta seen, x
inx
cpx #maxtiles
bne clear_loop
; random maze generator (with length limits & no repeats) as way to make more interesting
; see https://medium.freecodecamp.org/how-to-make-your-own-procedural-dungeon-map-generator-using-the-random-walk-algorithm-e0085c8aa9a?gi=74f51f176996
generate_corridors:
sta prevdir
sta direction
sta tunnels
sta tunnel_len
sta up_x
sta up_y
sta down_x
sta down_y
jsr randxy
; push xpos & ypos to stack
lda xpos
pha
lda ypos
pha
random_dir:
; restore xpos and ypos from stack (for check function)
pla
sta ypos
pla
sta xpos
random_dir_loop:
; pick random direction
jsr d4
sta a2
; prevent picking same direction as previous loop
cmp prevdir
beq random_dir_loop
; prevent picking opposite direction
jsr is_opposite_dir
beq random_dir_loop
; push xpos and ypos to stack to restore after check
lda xpos
pha
lda ypos
pha
; update direction
lda a2
sta direction
ldx #$00
txa
pha
; pick random length
random_length:
jsr d6 ; todo don't hardcode random value
; prevent picking previous length
cmp tunnel_len
beq random_length
; check max & min length
cmp #max_length
beq length_done
bcs random_length ; greater than max_length
length_done:
sta tunnel_len
pla
tax
check_dir:
lda direction
jsr update_pos
jsr within_bounds
bne random_dir
inx
cpx tunnel_len
bne check_dir
; update prevdir for next loop
lda direction
sta prevdir
; restore xpos and ypos from stack (for check function)
pla
sta ypos
pla
sta xpos
; update xpos and ypos
ldx #$00
update_tile:
inc tunnels
lda tunnels
cmp #max_tunnels
beq tiles_done
update_tile_loop:
lda direction
jsr update_pos
; update tile
jsr get_byte_offset
tay
txa
pha
jsr get_byte_mask
ora tiles, y
sta tiles, y
pla
tax
; keep updating until tunnel length
inx
cpx tunnel_len
bne update_tile_loop
; done, pick a new direction
jmp random_dir_loop
tiles_done:
; generate up or down stair
jsr rand_passable
; update player x & y to up or down stair, depending on prevdlevel
update_player:
lda dlevel
cmp prevdlevel
bcc update_player_downstair
update_player_upstair:
; update upstair
ldx xpos
ldy ypos
stx up_x
sty up_y
; update player coords
stx mobs+Mob::coords+Coord::xcoord
sty mobs+Mob::coords+Coord::ycoord
jmp done_update_player
update_player_downstair:
; update down
ldx xpos
ldy ypos
stx down_x
sty down_y
; update player coords
stx mobs+Mob::coords+Coord::xcoord
sty mobs+Mob::coords+Coord::ycoord
done_update_player:
; update prevdlevel for next generation
lda dlevel
sta prevdlevel
; generate max mobs
jsr d4
sta a2
ldy #mob_size
ldx #0
; generate mobs
; rand_mob generates a mob each time it is called
generate_mobs:
txa
pha
jsr rand_mob
pla
tax
; increment y by mob_size
tya
clc
adc #mob_size
tay
inx
cpx a2
beq clear_mobs_loop
jmp generate_mobs
; clear the rest of the mobs
clear_mobs_loop:
jsr kill_mob
; increment y by mob_size
tya
clc
adc #mob_size
tay
cmp #mobs_size
bne clear_mobs_loop
; for features loop
ldy #00
; generate random dungeon features
; rand_feature has a *chance* to generate feature each time called
generate_features:
tya
pha
jsr rand_floor
pla
tay
jsr rand_feature
; increment y by Feature size
tya
clc
adc #.sizeof(Feature)
tay
cpy #maxfeatures * .sizeof(Feature) ; leave room for drops, since they count as "features" for now
bne generate_features
ldx #0
; todo generate random items in dungeon
;generate_items:
; txa
; pha
; jsr rand_floor
; ldx xpos
; ldy ypos
; jsr rand_feature
; pla
; clc
; adc #.sizeof(Feature)
; tax
; cpx #maxfeatures * .sizeof(Feature) ; leave room for drops, since they count as "features" for now
; bne generate_features
; finally, generate next level stairs
lda up_x
bne generate_down
lda up_y
bne generate_down
generate_up:
; todo make this better with min width away from up
jsr rand_passable
ldx xpos
ldy ypos
cpx down_x
beq generate_up
cpy down_y
beq generate_up
stx up_x
stx up_y
jmp generate_done
generate_down:
; todo make this better with min width away from up
jsr rand_passable
ldx xpos
ldy ypos
cpx up_x
beq generate_down
cpy up_y
beq generate_down
stx down_x
sty down_y
generate_done:
rts
.endproc
; check if direction is opposite of previous "direction" var
; in: current dir
; out: 0 if true (invalid current dir)
is_opposite_dir:
cmp #1
beq cmp_dir_3
cmp #2
beq cmp_dir_4
cmp #3
beq cmp_dir_1
cmp #4
beq cmp_dir_2
cmp_dir_1:
lda direction
cmp #1
beq is_opposite
jmp isnt_opposite
cmp_dir_2:
lda direction
cmp #2
beq is_opposite
jmp isnt_opposite
cmp_dir_3:
lda direction
cmp #3
beq is_opposite
jmp isnt_opposite
cmp_dir_4:
lda direction
cmp #4
beq is_opposite
jmp isnt_opposite
is_opposite:
lda #0
rts
isnt_opposite:
lda #1
rts
; update xpos and ypos
; in: direction (1-4)
; affects: xpos and ypos
update_pos:
cmp #1
beq dec_ypos
cmp #2
beq inc_xpos
cmp #3
beq inc_ypos
cmp #4
beq dec_xpos
dec_ypos:
dec ypos
rts
inc_xpos:
inc xpos
rts
inc_ypos:
inc ypos
rts
dec_xpos:
dec xpos
rts