-
Notifications
You must be signed in to change notification settings - Fork 39
/
Entities.fs
320 lines (279 loc) · 6.25 KB
/
Entities.fs
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
######################################################
##
## Entities:
##
## A system for dynamically allocating game entities
## with an associated hardware sprite. Every entity
## can be assigned a word which carries out game logic
## and also serves as an identifier. The 'whoever'
## word makes it easy to perform some action upon
## valid entities which pass a predicate.
##
## Note that the 'Sprites.fs' standard library must
## be loaded before this lexicon.
##
## To use the entity system you must also first
## define 'ent-max', the maximum sprite index that
## will be used and a few prototypes as noted below.
##
## Finally, a number of vectored words and
## configurable defaults are available for
## customizing the behavior of this system:
##
## - spawn-size (default size of a spawned entity)
## - -sprites (control sorting order)
## - seek
## - distance
##
## John Earnest
##
######################################################
:proto ent-clear ( id -- )
:proto ent-swap ( a b -- )
:proto ent-blocked ( id -- flag )
######################################################
##
## Allocation system
##
######################################################
:const bogus-ent -1
0 :array kinds ent-max 0 : kind kinds + ;
0 :array readies ent-max 0 : ready readies + ;
0 :array timers ent-max 0 : timer timers + ;
0 :array dirs ent-max 0 : dir dirs + ;
0 :array prevs ent-max 0 : prev prevs + ;
0 :array solids ent-max 0 : solid solids + ;
: valid kind @ 0 != ; ( id -- flag )
: reset ( id -- )
dup ent-clear
0 over ready !
0 over timer !
0 over dir !
0 over prev !
0 over solid !
0 swap sprite@ !
;
: free ( id -- )
0 over kind !
0 swap sprite@ !
;
: alloc ( -- n )
0 bogus-ent kind !
ent-max 1 - loop
dup valid -if dup reset break then
1 -
again
;
:data spawn-size 16x16
: spawn ( tile-x tile-y tile 'func -- id )
alloc >r
i kind ! i tile!
spawn-size @ i sprite@ !
8 * i py!
8 * i px!
r>
;
: spawn-rel ( delta-x delta-y id tile 'func -- id )
alloc >r
i kind ! i tile!
spawn-size @ i sprite@ !
>r
i py + j py!
r> px + i px!
r>
;
######################################################
##
## High-level entity management
##
######################################################
: whoever ( 'filter 'func -- )
>r >r
0 loop
dup i exec over valid and
if dup j exec then
1 + dup ent-max <
while
drop r> r> 2drop
;
: apply-kind ( id -- )
dup ready @ -if true swap ready ! exit then
dup kind @ exec
;
: count+ drop swap 1 + swap ;
: count 0 swap ' count+ whoever ; ( 'filter -- n )
: always drop true ;
: think ' always ' apply-kind whoever ; ( -- )
: clear-entities ' always ' free whoever ; ( -- )
: find-entity ( 'pred -- id? flag )
ent-max 1 - for
i over exec if drop r> true exit then
next
drop false
;
: any? ( 'kind -- flag )
ent-max 1 - for
i kind @ over xor -if
drop rdrop true exit
then
next
drop false
;
: instance ( 'kind -- id )
ent-max 1 - for
i kind @ over xor -if
drop r> exit
then
next
drop bogus-ent
;
# positive if a>b, negative if a<b.
:vector -sprites ( a b -- )
py swap py swap -
;
: swap@ 2dup @ >r @ swap ! r> swap ! ;
# sort sprites from smallest to largest,
# and thus from back to front.
: sort-sprites ( -- )
ent-max 1 - for
i
i for
dup i -sprites 0 <
if drop i then
next
# don't swap with ourselves:
dup i xor if
# swap entity data
dup kind i kind swap@
dup ready i ready swap@
dup timer i timer swap@
dup dir i dir swap@
dup prev i prev swap@
dup solid i solid swap@
dup i ent-swap
# swap sprite registers:
sprite@ i sprite@
3 for
over i +
over i +
swap@
next
drop
then
drop
next
;
######################################################
##
## Movement and pathfinding logic
##
######################################################
: opp 4 + 8 mod ; ( dir -- dir )
: lf 2 - 8 mod ; ( dir -- dir )
: rt 2 + 8 mod ; ( dir -- dir )
:const n 0 :const ne 1
:const e 2 :const se 3
:const s 4 :const sw 5
:const w 6 :const nw 7
:data dir-x 0 1 1 1 0 -1 -1 -1 : delta-x dir-x + @ ;
:data dir-y -1 -1 0 1 1 1 0 -1 : delta-y dir-y + @ ;
: change ( src target -- dx dy )
2dup py swap py - >r
px swap px - r>
;
# find the closest direction
# for moving from src to target
:vector seek ( src target -- dir )
change
over 2 * over >= 2 and 1 - >r
over over 2 * < 1 and >r
over over -2 * > 1 and r> + >r
over -2 * over < 1 and r> + r> * >r
swap 2 * > 7 and r> +
;
# seek only in orthogonal directions
: ortho ( src target -- dir )
change
# n^2 works as well as abs(n):
over dup * over dup * > if
drop 0 > -4 and 6 + else
swap drop 0 > 4 and then
;
# seek only in diagonal directions
: diago ( src target -- dir )
change 0 > if
0 > -2 and 5 + else
0 > -6 and 7 + then
;
:vector distance ( a b -- dist )
# yields distance^2 by default
over px over px - dup * >r
py swap py - dup * r> +
;
: nearest ( src ' filter -- )
>r >r bogus-ent +infinity
0 loop
dup valid over i != and if dup j exec if
dup i distance >r over r> swap over >
if >r >r 2drop r> r> over else drop then
then then
1 + dup ent-max <
while
2drop rdrop rdrop
;
: offscreen? ( id -- flag )
>r
i px i sprite@ .sprite-w -1 * <
i py i sprite@ .sprite-h -1 * < or
i px 320 > or
r> py 240 > or
;
: move ( magnitude id -- )
over over dir @ delta-x * over +px
swap over dir @ delta-y * swap +py
;
: blocked ( id -- flag )
dup ent-blocked if drop true then
0 loop
2dup != over valid and over solid @ and if
2dup c-sprites? if 2drop true exit then
then
1 + dup ent-max <
while
2drop false
;
: c-move ( magnitude id -- )
2dup move
dup blocked if over -1 * over move then
2drop
;
######################################################
##
## Reusable behaviors
##
######################################################
: [waiting] ( id -- )
>r
i timer @ 1 - i timer !
i timer @ -if dup prev @ 0 i prev ! i kind ! then
rdrop
;
: make-wait ( ticks id -- )
>r
i timer !
i kind @ i prev !
' [waiting] r> kind !
;
: [walking] ( id -- )
>r
i [waiting]
1 i move
rdrop
;
: make-walk ( dir id -- )
>r
i dir !
8 i timer !
i kind @ i prev !
' [walking] r> kind !
;