summary refs log tree commit diff
path: root/adv.f
blob: e8dc6fe13faca219de20b0434440138ef33a4d23 (plain)
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