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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
|
#! /usr/bin/env gforth
\ adventure game (very WIP)
\ 24 Dec 2023
\ no content yet
\ i plan to try and keep development of this on
\ tilde.town, so i have a reason to come back other
\ than website stuff.
\ to do:
\ - add exits and movement
\ - implement basic actions for movement, examination, object manipulation
\ - start fleshing out game world with stuff to do
\ - assemble a... plot? a plotline? a story? gotta have some end goal to
\ work towards, even if it's just something like "find all thirteen
\ ancient artifacts and put them in the museum case! :3"
\ - polish, polish, polish. ANSI colors, custom display words to allow
\ for screen effects or whatver
\ - tell people about it?
( State )
variable playing?
: init playing? on ;
: fini ;
( Utils )
: ], ] postpone literal ;
( ANSI escape sequences )
char 1 constant red
char 2 constant green
char 3 constant yellow
char 4 constant blue
char 5 constant magenta
char 6 constant cyan
char 7 constant white
char 9 constant default
: fg! .\" \x1b[3" emit ." m" ;
: bg! .\" \x1b[4" emit ." m" ;
: norm .\" \x1b[0m" ;
: bold .\" \x1b[1m" ;
: ;bold .\" \x1b[22m" ;
( Display output )
\ carriage returns will cause the next character to
\ be uppercased automatically! very convenient, i
\ think, given how new lines generally comprise new
\ sentences
variable cap variable capslock variable col
: emwait ;
: crwait ;
: cr cr crwait col off ;
: lower? dup [char] a >= swap [char] z <= and ;
: ?>upper dup lower? if -33 and then ;
variable (pbl)
: emit dup bl = >r 1 col +! dup bl <> (pbl) @ and
col @ 64 >= and r> (pbl) ! if cr then
cap @ if ?>upper cap @ capslock @ and cap ! then emit emwait ;
: type bounds ?do i c@ emit loop ;
: ." postpone s" postpone type ; immediate
: cr cr cap on ;
: space bl emit ;
\ FIXME: scan for parenthesized bits of code, such that
\ .{ You poke [dob .the].} gets ultimately compiled as
\ S" You poke " TYPE DOB .THE S" ." TYPE
char [ constant lesc
char ] constant resc
: /scan >r over swap r> scan drop over - ;
\ feels inefficient -- probably a better way
: {...} begin 2dup lesc scan nip while
2dup lesc /scan tuck postpone sliteral postpone type
1+ /string 2dup resc /scan tuck evaluate
1+ /string repeat dup 0>
if postpone sliteral postpone type
else 2drop then ;
: .{ postpone cr
begin [char] } parse {...}
tib >in @ 1- + c@ [char] } = ?dup 0=
if postpone space refill 0= then until ; immediate
( String hashing )
: digit? dup [char] 0 >= swap [char] 9 <= and ;
\ collides deliberately, for entertainment purposes.
\ to calculate max number of letters stored in a single
\ 64-bit cell, we need to solve for x in 26^x = 2^64.
\ which is log26(2^64), which is log(2^64)/log(26), which
\ is a little over thirteen and a half characters.
\ i could be wrong about all of that btw <3
\ considering this is a stupid litle text adventure game,
\ and something like "construction" is only twelve letters
\ long, and really it doesn't matter if things get truncated
\ anyway, i think we're safe.
\ another way fit for 16-bit systems would be to have a
\ limited vocabulary of words that appear in object names
\ etc., like the old Infocom games did. writing a vocabulary
\ by hand would be boring. i guess i could make a global
\ flag called FLESHVOCAB? or sth, and have it so when set
\ the vocabulary-searching word would add a new vocabulary
\ entry on failure instead of just returning zero.
\ i'm not gonna do that today! it would be more portable,
\ and mister moore says portability's the devil's business.
\ maybe next time i do something for DOS? that'd be cool.
\ forth feels a good fit for text adventure making on small
\ systems -- 'specially since string parsing words can be
\ user-defined so it'd be really easy to introduce a custom
\ text encoding/compression scheme...
\ that sounds so cool, now i think of it...
\ a project for another time!
\ XOR'ing a letter with 32 will toggle upper and lowercase
: >5bit dup digit? if [char] 0 - else -33 and [char] A - then 1+ ;
: hash ( a u -- n ) 0 -rot bounds do 26 * i c@ >5bit + loop ;
: hash" [char] " parse hash state @ if postpone literal then ; immediate
( String -> word hashes )
create wdbuf 256 allot
\ hehehe, blch
: blch? bl <= ;
: andch? dup [char] , = swap [char] & = or ;
: thenc? dup [char] ; = swap [char] . = or ;
: punct? dup andch? swap thenc? or ;
: wdch? dup blch? swap punct? or 0= ;
: more? ( a u xt -- ) >r over c@ r> execute over 0> and ;
: /bl begin ['] blch? more? while 1 /string repeat ;
: >word wdbuf count + c! wdbuf c@ 1+ wdbuf c! ;
: punctw ( a u -- a u ) over c@
dup andch? if drop s" and" exit then
dup thenc? if drop s" then" exit then
drop s" unknown" ;
\ side effects taste nice!
: /word ( a u -- a' u' ) /bl 0 wdbuf c!
['] punct? more?
if punctw wdbuf place 1 /string exit then
begin ['] wdch? more? while
over c@ >word 1 /string repeat ;
( Chain words )
\ basically words which can be modified later to have
\ new bits of code running before or after older versions,
\ stored as a linked list of execution tokens. this makes
\ it easy to, for instance, define the body of a word and
\ then narrow it down with special cases and sanity checks
\ later on down the line. or, in our case here, to define
\ actions simply, and only afterwards specify "can't do
\ that, stupid!" under different conditions.
variable ;xt variable chcont? variable curch
: ; postpone ; ;xt @ if ;xt @ execute ;xt off then ; immediate
: ch>head ; : ch>tail cell+ ; : ch>next ; : ch>xt cell+ ;
: instead chcont? off ;
: (chain) chcont? @ >r chcont? on ch>head @
begin dup 0<> chcont? @ and
while dup >r ch>xt @ execute r> ch>next @
repeat drop r> chcont? ! ;
: ;chain here curch @ 2dup ch>head ! ch>tail ! 0 , , ;
: (:chain) create here curch ! 0 , 0 , does> (chain) ;
: :chain (:chain) ['] ;chain ;xt ! :noname ;
: ch< ch>tail @ ch>next ! ch>tail ! ;
: >ch ch>head @ swap ch>next ! ch>head ! ;
: ;ere here 0 , swap , swap 2dup >ch ;
: ;aft here 0 , swap , swap 2dup ch< ;
: :ere ' >body ['] ;ere ;xt ! :noname ;
: :aft ' >body ['] ;aft ;xt ! :noname ;
: ?deny{ postpone if postpone .{ postpone instead
postpone exit postpone then ; immediate
: ?or{ postpone 0= postpone ?deny{ ; immediate
( Actions )
\ action buffers are null-terminated, like c strings
\ for ease of iteration etc
-1 constant argwd
create lastact 0 ,
: act>prev ;
: act>proc cell+ ;
: act>buf [ 2 cells ], + ;
\ i think this is a term real people use?
: glob? 1 = swap c@ [char] * = and ;
: actwd 2dup glob? if 2drop argwd else hash then ;
: s>act, ( a u -- )
begin /word wdbuf c@ 0>
while wdbuf count actwd ,
repeat 0 , 2drop ;
\ i love parsing words
: act: ( xt -- ) >r
begin [char] / parse dup 0>
while here lastact @ , lastact ! r@ , s>act,
repeat 2drop r> drop ;
( Structures )
\ state-smart fields to yield ... efficiency, maybe?
\ in this non-realtime text adventure, throttled by the
\ speed of SSH connections, not the FORTH program?
\ at the cost of filling SEE with magic numbers?
\ that part's kinda funny, so i'm keeping it.
\ opaque byte offsets, fuck yeah
\ in a native code forth you could have FIELD directly
\ compile an inline addition instruction, even w/o a
\ peephole optimizer to that kinda thing automatically
\ -- just poke opcodes right into fuckin memory, hell
\ yeah.
\ this is for gforth, though, which is lame and boring
\ and traditional and Good Enough
: :recd create here 0 , 0 does> @ ;
: field create immediate over , + does> @
state @ if postpone literal postpone + else + then ;
: enum dup constant 1+ ;
: recd; swap ! ;
( Exits )
:recd exits
enum north
enum northeast
enum east
enum southeast
enum south
enum southwest
enum west
enum northwest
enum up
enum down
recd;
create oppotbl south , southwest , west , northwest ,
north , northeast , east , southeast , down , up ,
: opposite cells oppotbl + @ ;
( Game objects )
:recd gob
cell field >last
cell field >flags
cell field >rel
cell field >env
cell field >inv \ latest child object
cell field >pob \ previous sibling
cell field >nob \ next sibling
exits cells field >exits
0 field >name
recd;
: >keys >name count + ; \ name hash, null-terminated, count-preceded
: >desc >keys count 1+ cells + ;
: >exit cells + >exits ;
: zallot here over 0 fill allot ;
variable desc#
variable curob
\ gob zallot! gob zallot!
\ sounds like a magic spell
: id: create here gob zallot curob @ over >last ! curob ! ;
: desc, dup desc# +! 0 do count c, loop drop ;
: ?desc, dup 0> if desc, else 2drop then ;
: keys, curob @ >name count here >r 0 c,
begin /word wdbuf c@ 0>
while wdbuf count hash , r@ c@ 1+ r@ c!
repeat 2drop r> drop 0 , ;
: name: $0A parse tuck here place 1+ allot keys, ;
: syn: $0A parse -1 cells allot
begin /word wdbuf c@ 0> while wdbuf count hash ,
curob @ >keys dup c@ 1+ swap c! repeat 0 , 2drop ;
: nl s" " ;
: desc: desc# off here 0 ,
begin source >in @ /string /bl
?desc, nl desc, refill 0= #tib @ 0= or
until desc# @ swap ! ;
: $@ dup cell+ swap @ ;
: excise ( obj -- )
dup dup >env @ >inv @ = if dup >nob @ over >env @ >inv ! then
dup >pob @ ?dup if over >nob @ swap >nob ! then
dup >nob @ ?dup if over >pob @ swap >pob ! then
dup >pob off dup >nob off >env off ;
: inv+ ( c to -- )
over >nob over >inv @ swap !
dup >inv @ if 2dup >inv @ >pob ! then
>inv ! ;
: env! dup >env @ if dup excise then
2dup swap inv+ >env ! ;
variable player
variable intang
: you player @ ;
( Flags )
create flag# 1 ,
: !flag! create , does> @ curob @ >flags tuck @ or swap ! ;
: !flag? create , does> @ swap >flags @ and 0<> ;
: flag flag# @ dup !flag! dup !flag? 2* flag# ! ;
flag room room?
flag item item?
flag contr contr?
flag suprt suprt?
flag scen scen?
( Gender and grammar )
\ yaay, my favorite...
flag an an?
flag the the?
flag proper proper?
flag plur plur?
flag ucnt ucnt?
flag masc masc?
flag fem fem?
flag neut neut?
: you? you = ;
: .you drop bold ." you" ;bold ;
: .name >name count bold type ;bold ;
: when postpone dup ' compile, postpone if
$0A parse evaluate postpone exit postpone then ; immediate
: .the
when you? .name
when proper? .name
." the " .name ;
: .a
when you? .you
when the? .the
when proper? .name
when ucnt? .name
when plur? ." some " .name
when an? ." an " .name
." a " .name ;
: .nom
when you? drop ." you"
when masc? drop ." he"
when fem? drop ." she"
when neut? drop ." they"
when plur? drop ." they"
drop ." it" ;
: .acc
when you? drop ." you"
when masc? drop ." him"
when fem? drop ." her"
when neut? drop ." them"
when plur? drop ." them"
drop ." it" ;
: .gen
when you? drop ." your"
when masc? drop ." his"
when fem? drop ." her"
when neut? drop ." their"
when plur? drop ." their"
drop ." its" ;
: .gens
when you? drop ." yours"
when masc? drop ." his"
when fem? drop ." hers"
when neut? drop ." theirs"
when plur? drop ." theirs"
drop ." its" ;
: .ref
when you? drop ." yourself"
when masc? drop ." himself"
when fem? drop ." herself"
when neut? drop ." themself"
when plur? drop ." themselves"
drop ." itself" ;
: .that
when you? drop ." yourself"
when masc? drop ." him"
when fem? drop ." her"
when neut? drop ." them"
when plur? drop ." those"
drop ." that" ;
: are? dup plur? over neut? or swap you? or ;
: .is are? if ." are" else ." is" then ;
: (s) are? 0= if ." s" then ;
( Relations )
1 constant in
2 constant atop
3 constant under
4 constant heldby
: now ( ob rel env -- ) rot tuck env! >rel ! ;
: is? ( ob rel env -- ? ) rot tuck >env @ = -rot >rel @ = and ;
: isn't? ( ob rel env -- ? ) rot tuck >env @ <> -rot >rel @ <> or ;
: first ( rel env -- ) curob @ -rot now ;
: .rel case
in of ." inside" endof
atop of ." on top of" endof
under of ." underneath" endof
heldby of ." held by" endof
." child of"
endcase ;
( Object definition shortcuts )
variable lastroom
: room room curob @ lastroom ! ;
: item item in lastroom @ first ;
: scen scen in lastroom @ first ;
( Object debug info display )
8 constant o#max
create o# 0 ,
: o.cr cr o# @ 2* spaces ;
: o.type bounds do i c@ $0A = if o.cr else i c@ emit then loop ;
: o.f flag# @ begin dup 0> while
2dup and if dup ." $" u. then 2/ repeat 2drop ;
: .o o# @ o#max > if drop exit then
base @ >r hex
o.cr dup >pob @ u. ." <- " dup u. ." -> " dup >nob @ u.
o.cr dup >name count type ." ( " dup >flags @ o.f ." ) "
dup >rel @ .rel space dup >env @ u.
dup >env @ ?dup if ." (" >name count type ." )" then
o.cr dup >desc $@ o.type
>inv @ begin ?dup while
1 o# +! dup recurse -1 o# +! >nob @ repeat
r> base ! ;
: .o cr .o cr ;
: .e you >env @ ?dup if .o then ;
( Object resolution )
: self-word?
dup hash" me" =
over hash" self" = or
swap hash" myself" = or ;
: called-word? ( hash obj -- )
\ over self-word? over you = and if 2drop true exit then
false -rot
>keys count cells bounds do
i @ over = if nip true swap leave then
cell +loop drop ;
: called? ( ha u obj -- )
>r true -rot r>
-rot cells bounds do
i @ over called-word? 0=
if nip false swap leave then
cell +loop drop ;
: inv{ postpone >inv postpone @ postpone begin postpone ?dup
postpone while postpone >r ; immediate
: inv; postpone r@ postpone r> postpone xor postpone >r ; immediate
: }inv postpone r@ postpone if postpone r> postpone >nob
postpone @ postpone then postpone repeat ; immediate
: child postpone r@ ; immediate
8 constant maxro# variable ro# 2variable ron ( resob name )
: (resob)
dup ron 2@ rot called? if exit then
ro# @ maxro# >= if drop exit then
ro# @ dup >r 1+ ro# !
0 swap inv{
child recurse ?dup if nip then
}inv r> ro# ! ;
: resob ro# off ron 2! you >env @ (resob) ;
: rob parse-word hash pad ! pad 1 resob ;
( Action matching )
16 constant #args
16 constant #argb
variable arg#
variable argb#
create argb #argb cells allot
create args #args cells allot
: argz args #args cells 0 fill ;
( Most of these words act on a pair of pointers to word hashes,
TOS the pattern to check against. )
\ this took me so long to get working, and the main bug
\ was in the loop condition the whole time, not any of
\ the code i was looking at.
variable inarg?
: advance cell+ swap cell+ swap ;
variable argend
: arg arg# @ cells args + ;
: arg? argb# @ 0> ;
\ EOMATCH gets reset for each action we search, so the highest
\ priority error to occur during FINDACT is used for error msgs
variable matcherr
variable eomatch
: eomatch: create , does> @ eomatch ! ;
1 dup eomatch: succ
1+ dup constant #nomatch
1+ dup constant #notfound
1+ dup constant #noarg
drop
#notfound eomatch: notfound
#nomatch eomatch: nomatch
#noarg eomatch: noarg
: badmatch cr matcherr @ case
#noarg of ." Regarding what?" endof
#nomatch of ." I didn't understand that." endof
#notfound of ." I don't know what that is." endof
endcase ;
: :arg dup @ argend ! inarg? on argb# off ;
: article?
dup hash" the" = over hash" an" = or
over hash" a" = or swap hash" some" = or ;
: >arg
dup article? if drop exit then
argb# @ #argb 1- >= if drop exit then
argb# @ cells argb + ! 1 argb# +! ;
: ;arg argb argb# @ resob
?dup if arg ! 1 arg# +!
else notfound then inarg? off ;
: argend? dup argend @ = swap 0= or ;
: ?;arg arg? if ;arg else noarg then ;
: >arg+ over @ >arg swap cell+ swap ;
: inarg over @ argend? if ?;arg else >arg+ then ;
: same? over @ over @ = ;
: atend? dup @ 0= ;
: grammar
eomatch @ if exit then
dup @ argwd =
if cell+ :arg
else same?
if atend? if succ else advance then
else nomatch then
then ;
: match? ( b1 b2 -- ? )
eomatch off arg# off inarg? off argz
begin inarg? @ if inarg else grammar then
eomatch @ arg# @ #args >= or
until 2drop eomatch @ dup matcherr @ >
if dup matcherr ! then 1 = ;
: findact matcherr off
lastact
begin @ dup
while 2dup act>buf match?
if nip exit then
repeat nip ;
( Command execution )
64 constant #cmd
variable cmd#
create cmdbuf #cmd cells allot
: >cmd cmd# @ cells cmdbuf + ! 1 cmd# +! ;
: cmd>buf cmd# off
begin /word wdbuf c@ 0>
cmd# @ #cmd 1- < and ( leave room for final 0 )
while wdbuf count hash >cmd
repeat 0 >cmd 2drop ;
\ confusion stolen from t2tmud.org:9999
: perform act>proc @ execute ;
: nostk depth 0 ?do drop loop ;
: .exc bold red fg! ." EXCEPTION #" . norm ;
: .ok bold green fg! ." ok" norm ;
: cmd4 cr ['] evaluate catch ?dup if .exc nostk else .ok then ;
: cmd over c@ [char] ; = over 0> and
if 1 /string cmd4 exit then
cmd>buf cmdbuf findact
?dup if perform else badmatch then ;
( Verb procedures )
: dob args @ ;
: iob args cell+ @ ;
: .inv
inv{ child .a child >nob @
if child >nob @ >nob @
if ." , " else ." , and " then
then }inv ;
\ Universal verbs
:noname you >inv @
if cr ." You hold " you .inv ." ."
else cr ." You are empty-handed." then ;
act: inventory / inv / i
:noname cr ." Goodbye." cr playing? off ;
act: quit
\ Chain verbs (modifiable)
\ these end in underscores because it looks like a text cursor
: grammar ' act: ;
: in-desc? dup you <> swap scen? 0= and ;
: #others 0 over inv{ child in-desc? if 1+ then }inv ;
: has-others? #others 0> ;
:chain look_
you >env @
capslock on cr bold dup .name norm cr capslock off
cr dup >desc $@ type
has-others? if cr cyan fg! then
inv{ child in-desc?
if .{ [child .a] [child .is] here.}
then }inv norm ;
:chain exam_ cr dob >desc $@ type
dob >inv @ if cyan fg! cr
.{ [dob .the] hold[dob (s)] [dob .inv].}
norm then ;
:chain take_ dob heldby you now
.{ You pick up [dob .the].} ;
:chain drop_ dob you >rel @ you >env @ now
.{ You drop [dob .the].} ;
:chain put_in_ dob in iob now
.{ You put [dob .the] in [iob .the].} ;
:chain put_on_ dob in iob now
.{ You put [dob .the] on [iob .the].} ;
grammar look_ look / l
grammar exam_ look * / l * / examine * / x * / look at * / l at *
grammar take_ get * / take * / pick up * / pick * up
grammar drop_ drop * / put * down / put down *
grammar put_in_ put * in */put * into */put * inside */put * inside of */put * within *
grammar put_on_ put * on * / put * onto */put * atop */put * on top of *
: held heldby you is? ;
:ere drop_ dob held ?or{ You'd need to pick [dob .nom] up first.} ;
:ere take_ dob item? ?or{ You can't take [dob .the].} ;
:ere take_ dob held if instead
cr .{ You're already holding [dob .that].} then ;
:ere take_ dob you = ?deny{ You've always been self-possessed.} ;
variable putrel
create relatbl ' contr? , ' suprt? ,
: relacc? iob putrel @ 1- cells relatbl + @ execute ;
: ?put-rel putrel !
dob held ?or{ You're not holding [dob .that].}
dob iob = ?deny{ [putrel @ .rel] [dob .ref]?}
dob you = iob you = or ?deny{ Yourself?}
relacc? ?or{ You can't put anything [putrel @ .rel] [dob .that]!} ;
:ere put_in_ in ?put-rel ;
:ere put_on_ atop ?put-rel ;
: ?go-to ?dup
if you in rot now look_
else .{ You can't go that way.}
then ;
: exit: >r :chain s" you >env @ >exits" evaluate
r> cells postpone literal s" + @ ?go-to" evaluate
postpone ; ;
north exit: n_
northeast exit: ne_
east exit: e_
southeast exit: se_
south exit: s_
southwest exit: sw_
west exit: w_
northwest exit: nw_
up exit: u_
down exit: d_
grammar d_ d / down / go d / go down
grammar n_ n / north / go n / go north
grammar ne_ ne / northeast / go ne / go northeast
grammar e_ e / east / go e / go east
grammar se_ se / southeast / go se / go southeast
grammar s_ s / south / go s / go south
grammar sw_ sw / southwest / go sw / go southwest
grammar w_ w / west / go w / go west
grammar nw_ nw / northwest / go nw / go northwest
grammar u_ u / up / go u / go up
grammar d_ d / down / go d / go down
: -> ' execute swap cells curob @ >exits + ! ;
: <- curob @ swap cells ' execute >exits + ! ;
: <-> >r curob @ ' execute 2dup
>exits r@ opposite cells + !
swap >exits r> cells + ! ;
: :diob curob @ postpone literal postpone <>
postpone if postpone exit postpone then postpone instead ;
\ doesn't work in VFX Forth?
: :iob :ere postpone iob :diob ;
: :dob :ere postpone dob :diob ;
\ Grammar
( User interface )
256 constant #inbuf
create inbuf #inbuf allot
: prompt ." > " ;
: input green fg! cr bold prompt ;bold
inbuf #inbuf accept inbuf swap norm ;
: intro page look_ cr ;
: play init intro begin playing? @ while input cmd cr repeat fini ;
( Game world )
: room: id: room ;
: item: id: item ;
: scen: id: scen ;
: you: id: in lastroom @ first curob @ player ! ;
: to: >r lastroom @ room: curob @
2dup r@ opposite >exit ! swap r> >exit ! ;
include gam.f
( Play )
play bye
|