├── LICENSE ├── Makefile ├── README.md ├── advavl.c ├── advavl.h ├── advcio.c ├── advcom.c ├── advcom.h ├── advdbs.c ├── advdbs.h ├── advexe.c ├── advexp.c ├── advfcn.c ├── adviio.c ├── advint.c ├── advint.h ├── advmsg.c ├── advprs.c ├── advscn.c ├── advsys.doc ├── advtrm.c ├── objects.adi └── osample.adv /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 dbetz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC?=propeller-elf-gcc 2 | 3 | CFLAGS=-Wall -Os 4 | 5 | ifeq ($(CC),cc) 6 | EXT= 7 | endif 8 | 9 | ifeq ($(CC),propeller-elf-gcc) 10 | CFLAGS += -mcmm 11 | EXT=.elf 12 | endif 13 | 14 | COBJS=\ 15 | advcom.o \ 16 | advavl.o \ 17 | advcio.o \ 18 | advexp.o \ 19 | advfcn.o \ 20 | advscn.o 21 | 22 | IOBJS=\ 23 | advexe.o \ 24 | advdbs.o \ 25 | adviio.o \ 26 | advint.o \ 27 | advmsg.o \ 28 | advprs.o \ 29 | advtrm.o 30 | 31 | all: advcom$(EXT) advint$(EXT) 32 | 33 | advcom$(EXT): $(COBJS) 34 | $(CC) $(CFLAGS) -o $@ $(COBJS) 35 | 36 | advint$(EXT): $(IOBJS) 37 | $(CC) $(CFLAGS) -o $@ $(IOBJS) 38 | 39 | %.o: %.c 40 | $(CC) $(CFLAGS) -c -o $@ $< 41 | 42 | clean: 43 | rm -rf *.o advcom$(EXT) advint$(EXT) *.dat 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # advsys 2 | AdvSys - A Language for Writing Text Adventure Games 3 | -------------------------------------------------------------------------------- /advavl.c: -------------------------------------------------------------------------------- 1 | /* advavl.c - avl tree manipulation routines */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advavl.h" 10 | #include "advcom.h" 11 | #include "advdbs.h" 12 | 13 | #define TRUE 1 14 | #define FALSE 0 15 | 16 | /* external variables */ 17 | extern char *data; 18 | extern int curwrd; 19 | extern int dptr; 20 | 21 | /* local variables */ 22 | static TREE *curtree; 23 | static char thiskey[WRDSIZE+1]; 24 | 25 | /* prototypes */ 26 | static int tenter1(TNODE **pnode,int *ph); 27 | static int tfind1(TNODE *node); 28 | 29 | /* tnew - allocate a new avl tree */ 30 | TREE *tnew(void) 31 | { 32 | TREE *tree; 33 | 34 | /* allocate the tree structure */ 35 | if ((tree = (TREE *)malloc(sizeof(TREE))) == NULL) 36 | return (NULL); 37 | 38 | /* initialize the new tree */ 39 | tree->tr_root = NULL; 40 | tree->tr_cnt = 0; 41 | 42 | /* return the new tree */ 43 | return (tree); 44 | } 45 | 46 | /* tenter - add an entry to an avl tree */ 47 | int tenter(TREE *tree,char *key) 48 | { 49 | int h; 50 | curtree = tree; 51 | strncpy(thiskey,key,WRDSIZE); thiskey[WRDSIZE] = 0; 52 | return (tenter1(&tree->tr_root,&h)); 53 | } 54 | 55 | /* tenter1 - internal insertion routine */ 56 | static int tenter1(TNODE **pnode,int *ph) 57 | { 58 | TNODE *p,*q,*r; 59 | int val,c; 60 | 61 | /* check for the subtree being empty */ 62 | if ((p = *pnode) == NULL) { 63 | if ((p = (TNODE *)malloc(sizeof(TNODE))) != NULL) { 64 | curtree->tr_cnt++; 65 | KEY(p) = save(thiskey); 66 | WORD(p) = curwrd; 67 | LLINK(p) = RLINK(p) = NULL; 68 | B(p) = 0; 69 | *pnode = p; 70 | *ph = TRUE; 71 | return (WORD(p)); 72 | } 73 | else { 74 | *ph = FALSE; 75 | return (NIL); 76 | } 77 | } 78 | 79 | /* otherwise, check for a match at this node */ 80 | else if ((c = strcmp(thiskey,KEY(p))) == 0) { 81 | *ph = FALSE; 82 | return (WORD(p)); 83 | } 84 | 85 | /* otherwise, check the left subtree */ 86 | else if (c < 0) { 87 | val = tenter1(&LLINK(p),ph); 88 | if (*ph) 89 | switch (B(p)) { 90 | case 1: 91 | B(p) = 0; 92 | *ph = FALSE; 93 | break; 94 | case 0: 95 | B(p) = -1; 96 | break; 97 | case -1: 98 | q = LLINK(p); 99 | if (B(q) == -1) { 100 | LLINK(p) = RLINK(q); 101 | RLINK(q) = p; 102 | B(p) = 0; 103 | p = q; 104 | } 105 | else { 106 | r = RLINK(q); 107 | RLINK(q) = LLINK(r); 108 | LLINK(r) = q; 109 | LLINK(p) = RLINK(r); 110 | RLINK(r) = p; 111 | B(p) = (B(r) == -1 ? 1 : 0); 112 | B(q) = (B(r) == 1 ? -1 : 0); 113 | p = r; 114 | } 115 | B(p) = 0; 116 | *pnode = p; 117 | *ph = FALSE; 118 | break; 119 | } 120 | } 121 | 122 | /* otherwise, check the right subtree */ 123 | else { 124 | val = tenter1(&RLINK(p),ph); 125 | if (*ph) 126 | switch (B(p)) { 127 | case -1: 128 | B(p) = 0; 129 | *ph = FALSE; 130 | break; 131 | case 0: 132 | B(p) = 1; 133 | break; 134 | case 1: 135 | q = RLINK(p); 136 | if (B(q) == 1) { 137 | RLINK(p) = LLINK(q); 138 | LLINK(q) = p; 139 | B(p) = 0; 140 | p = q; 141 | } 142 | else { 143 | r = LLINK(q); 144 | LLINK(q) = RLINK(r); 145 | RLINK(r) = q; 146 | RLINK(p) = LLINK(r); 147 | LLINK(r) = p; 148 | B(p) = (B(r) == 1 ? -1 : 0); 149 | B(q) = (B(r) == -1 ? 1 : 0); 150 | p = r; 151 | } 152 | B(p) = 0; 153 | *pnode = p; 154 | *ph = FALSE; 155 | break; 156 | } 157 | } 158 | 159 | /* return the node found or inserted */ 160 | return (val); 161 | } 162 | 163 | /* tfind - find an entry in an avl tree */ 164 | int tfind(TREE *tree,char *key) 165 | { 166 | strncpy(thiskey,key,WRDSIZE); thiskey[WRDSIZE] = 0; 167 | return (tfind1(tree->tr_root)); 168 | } 169 | 170 | /* tfind1 - internal lookup routine */ 171 | static int tfind1(TNODE *node) 172 | { 173 | int c; 174 | 175 | /* check for the subtree being empty */ 176 | if (node == NULL) 177 | return (NIL); 178 | 179 | /* otherwise, check for a match at this node */ 180 | else if ((c = strcmp(thiskey,KEY(node))) == 0) 181 | return (WORD(node)); 182 | 183 | /* otherwise, check the left subtree */ 184 | else if (c < 0) 185 | return (tfind1(LLINK(node))); 186 | 187 | /* otherwise, check the right subtree */ 188 | else 189 | return (tfind1(RLINK(node))); 190 | } 191 | -------------------------------------------------------------------------------- /advavl.h: -------------------------------------------------------------------------------- 1 | /* advavl.h - avl tree definitions */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #ifndef __ADVAVL_H__ 8 | #define __ADVAVL_H__ 9 | 10 | typedef struct tree { 11 | struct tnode *tr_root; /* root node */ 12 | int tr_cnt; /* count of entries */ 13 | } TREE; 14 | 15 | typedef struct tnode { 16 | int tn_b; /* balance flag */ 17 | struct tnode *tn_llink; /* left subtree */ 18 | struct tnode *tn_rlink; /* right subtree */ 19 | char *tn_key; /* word */ 20 | int tn_word; /* word number */ 21 | } TNODE; 22 | 23 | #define LLINK(n) ((n)->tn_llink) 24 | #define RLINK(n) ((n)->tn_rlink) 25 | #define KEY(n) ((n)->tn_key) 26 | #define WORD(n) ((n)->tn_word) 27 | #define B(n) ((n)->tn_b) 28 | #define tentries(t) ((t)->tr_cnt) 29 | 30 | /* function prototypes */ 31 | TREE *tnew(void); 32 | int tenter(TREE *tree,char *key); 33 | int tfind(TREE *tree,char *key); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /advcio.c: -------------------------------------------------------------------------------- 1 | /* advcio.c - adventure compiler i/o routines */ 2 | 3 | #include 4 | #include 5 | #include "advcom.h" 6 | 7 | #define BSIZE 8192 8 | 9 | /* global variables */ 10 | long ad_foff; 11 | 12 | /* local variables */ 13 | static char buf[BSIZE]; 14 | static int boff; 15 | static FILE *fp; 16 | 17 | void macinit(char *iname,char *oname) 18 | { 19 | printf("File name? "); fflush(stdout); 20 | gets(iname); strcpy(oname,iname); 21 | strcat(iname,".adv"); 22 | strcat(oname,".dat"); 23 | } 24 | 25 | void macpause(void) 26 | { 27 | } 28 | 29 | void ad_create(char *name) 30 | { 31 | /* open the output file */ 32 | if (!(fp = fopen(name,"wb"))) 33 | fail("can't create data file"); 34 | 35 | /* initialize the buffer and file offset */ 36 | ad_foff = 0L; 37 | boff = 0; 38 | } 39 | 40 | void ad_close(void) 41 | { 42 | ad_flush(); 43 | fclose(fp); 44 | } 45 | 46 | void ad_putc(int ch) 47 | { 48 | buf[boff++] = ch; ad_foff++; 49 | if (boff >= BSIZE) 50 | ad_flush(); 51 | } 52 | 53 | void ad_seek(long pos) 54 | { 55 | ad_flush(); 56 | if (fseek(fp,pos,SEEK_SET) != pos) 57 | fail("error positioning output file"); 58 | ad_foff = pos; 59 | } 60 | 61 | void ad_flush(void) 62 | { 63 | if (boff) { 64 | if (fwrite(buf,1, boff, fp) != boff) 65 | fail("error writing to output file"); 66 | boff = 0; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /advcom.c: -------------------------------------------------------------------------------- 1 | /* advcom.c - a compiler for adventure games */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advcom.h" 10 | #include "advdbs.h" 11 | 12 | /* symbol tables */ 13 | SYMBOL *symbols; 14 | SYMBOL *exfunctions; 15 | ARGUMENT *arguments; 16 | ARGUMENT *temporaries; 17 | 18 | /* adventure id information */ 19 | char aname[19]; 20 | int aversion; 21 | 22 | /* word table */ 23 | int wtable[WMAX+1],wcnt; 24 | 25 | /* object table */ 26 | int otable[OMAX+1],ocnt; 27 | 28 | /* action table */ 29 | int atable[AMAX+1],acnt; 30 | 31 | /* constant, variable and property symbol counts */ 32 | int ccnt,vcnt,pcnt; 33 | 34 | /* data and code space */ 35 | char *data,*code; 36 | int dptr,cptr; 37 | 38 | /* buffer for building an object */ 39 | int objbuf[OSIZE]; 40 | int nprops; 41 | 42 | /* global variables */ 43 | char ifile[FMAX]; /* input file name */ 44 | char ofile[FMAX]; /* output file name */ 45 | FILE *ifp; /* input file pointer */ 46 | unsigned int msgoff; /* message section offset */ 47 | TREE *words; /* word tree */ 48 | int curwrd; /* current word number */ 49 | int curobj; /* current object */ 50 | int curact; /* current action */ 51 | int def_flag; /* default action flag value */ 52 | int def_mask; /* default action mask value */ 53 | 54 | /* header information variables */ 55 | int h_main; /* main code */ 56 | 57 | /* external variables */ 58 | extern int errcount; /* error count */ 59 | extern int t_value; /* token value */ 60 | extern char t_token[]; /* token string */ 61 | extern char *t_names[]; /* token names */ 62 | extern long ad_foff; /* data file offset */ 63 | 64 | /* forward declarations */ 65 | SYMBOL *sfind(); 66 | SYMBOL *senter(); 67 | char *save(); 68 | 69 | /* main - the main routine */ 70 | int main(int argc,char *argv[]) 71 | { 72 | int tkn,obj,i; 73 | 74 | /* initialize */ 75 | printf("ADVCOM v1.2 - Copyright (c) 1986, by David Betz\n"); 76 | wcnt = ocnt = acnt = ccnt = vcnt = pcnt = msgoff = 0; 77 | symbols = exfunctions = NULL; 78 | arguments = temporaries = NULL; 79 | def_flag = def_mask = 0; 80 | aname[0] = '\0'; 81 | h_main = NIL; 82 | sinit(); 83 | 84 | /* setup the code and data space */ 85 | if ((data = calloc(1,DMAX)) == 0) 86 | fail("insufficient memory"); 87 | if ((code = calloc(1,CMAX)) == 0) 88 | fail("insufficient memory"); 89 | dptr = cptr = 1; /* make sure nothing has a zero offset */ 90 | 91 | /* get the file name */ 92 | if (argc < 2) 93 | fail("usage: advcom [ ]"); 94 | strcpy(ifile,argv[1]); strcat(ifile,".adv"); 95 | strcpy(ofile,(argc < 3 ? argv[1] : argv[2])); strcat(ofile,".dat"); 96 | 97 | /* open the input file */ 98 | if ((ifp = fopen(ifile,"r")) == NULL) 99 | fail("can't open input file"); 100 | 101 | /* create and initialize the output file */ 102 | ad_create(ofile); 103 | for (i = 0; i++ < 512; ad_putc('\0')) 104 | ; 105 | 106 | /* create the word tree */ 107 | words = tnew(); 108 | 109 | /* enter builtin constants */ 110 | center("t",-1); 111 | center("nil",0); 112 | 113 | /* enter the builtin variables */ 114 | venter("$actor"); 115 | venter("$action"); 116 | venter("$dobject"); 117 | venter("$ndobjects"); 118 | venter("$iobject"); 119 | venter("$ocount"); 120 | 121 | /* enter the preposition "to" */ 122 | add_word("to",WT_PREPOSITION); 123 | 124 | /* process statements until end of file */ 125 | while ((tkn = token()) == T_OPEN) { 126 | frequire(T_IDENTIFIER); 127 | 128 | /* identification statement */ 129 | if (match("adventure")) 130 | do_adventure(); 131 | 132 | /* vocabulary statements */ 133 | else if (match("adjective")) 134 | do_word(WT_ADJECTIVE); 135 | else if (match("preposition")) 136 | do_word(WT_PREPOSITION); 137 | else if (match("conjunction")) 138 | do_word(WT_CONJUNCTION); 139 | else if (match("article")) 140 | do_word(WT_ARTICLE); 141 | else if (match("synonym")) 142 | do_synonym(); 143 | 144 | /* constant, variable, function and default definition statements */ 145 | else if (match("define")) 146 | do_define(); 147 | else if (match("variable")) 148 | do_variable(); 149 | else if (match("default")) 150 | do_default(); 151 | 152 | /* extended function definition statement */ 153 | else if (match("extended")) 154 | do_extended(); 155 | 156 | /* property definition statement */ 157 | else if (match("property")) 158 | do_defproperty(); 159 | 160 | /* handle the main code statement */ 161 | else if (match("main")) 162 | h_main = do_code(t_token); 163 | 164 | /* action definition statement */ 165 | else if (match("action")) 166 | do_action(); 167 | 168 | /* object definition statements */ 169 | else if (match("object")) 170 | do_object(t_token,NIL); 171 | 172 | /* object instance definition statements */ 173 | else if ((obj = ofind(t_token)) != 0) 174 | do_object(t_token,obj); 175 | 176 | /* error, unknown statement */ 177 | else 178 | error("Unknown statement type"); 179 | } 180 | require(tkn,T_EOF); 181 | 182 | /* close the input file */ 183 | fclose(ifp); 184 | 185 | /* output the data structures */ 186 | output(); 187 | 188 | /* close the output file */ 189 | ad_close(); 190 | 191 | /* return successfully */ 192 | return 0; 193 | } 194 | 195 | /* getvalue - get a value */ 196 | int getvalue(void) 197 | { 198 | SYMBOL *sym; 199 | 200 | switch (token()) { 201 | case T_IDENTIFIER: if ((sym = sfind(symbols,t_token)) != 0) 202 | return (sym->s_value); 203 | return (oenter(t_token)); 204 | case T_NUMBER: return (t_value); 205 | case T_STRING: return (t_value); 206 | default: error("Expecting identifier, number or string"); 207 | return (0); 208 | } 209 | } 210 | 211 | /* dalloc - allocate data space */ 212 | int dalloc(int size) 213 | { 214 | if ((dptr += size) > DMAX) 215 | fail("out of data space"); 216 | return (dptr - size); 217 | } 218 | 219 | /* add_word - add a word to the dictionary */ 220 | int add_word(char *str,int type) 221 | { 222 | if ((curwrd = tfind(words,str)) == NIL) { 223 | if (wcnt < WMAX) { 224 | curwrd = ++wcnt; 225 | wtable[curwrd] = type; 226 | tenter(words,str); 227 | } 228 | else { 229 | error("too many words"); 230 | curwrd = 0; 231 | } 232 | } 233 | else if (wtable[curwrd] == WT_UNKNOWN) 234 | wtable[curwrd] = type; 235 | else if (type != WT_UNKNOWN && type != wtable[curwrd]) 236 | error("Ambiguous word type"); 237 | return (curwrd); 238 | } 239 | 240 | /* add_synonym - add a synonym to a word */ 241 | int add_synonym(char *str,int wrd) 242 | { 243 | curwrd = wrd; 244 | return (tenter(words,str)); 245 | } 246 | 247 | /* getword - get a word from an object field */ 248 | int getword(int off) 249 | { 250 | return ((data[off] & 0xFF) | (data[off+1] << 8)); 251 | } 252 | 253 | /* putword - put a word into an object field */ 254 | void putword(int off,int dat) 255 | { 256 | data[off] = dat; 257 | data[off+1] = dat >> 8; 258 | } 259 | 260 | /* getbyte - get a byte from an object field */ 261 | int getbyte(int off) 262 | { 263 | return (data[off]); 264 | } 265 | 266 | /* putbyte - put a byte into an object field */ 267 | void putbyte(int off,int dat) 268 | { 269 | data[off] = dat; 270 | } 271 | 272 | /* output - output the binary data structures */ 273 | void output(void) 274 | { 275 | int woff,wsize; /* word table offset and size */ 276 | int ooff,osize; /* object table offset and size */ 277 | int aoff,asize; /* action table offset and size */ 278 | int toff,tsize; /* word type table offset and size */ 279 | int voff,vsize; /* variable table offset and size */ 280 | int soff,ssize; /* save area offset and size */ 281 | int dsize; /* data size without dictionary */ 282 | int dbase,cbase,size,mblk,dblk,i; 283 | 284 | /* make sure the adventure id information is present */ 285 | if (aname[0] == 0) { 286 | xerror("no adventure identification information"); 287 | strcpy(aname,"ADVENTURE"); 288 | aversion = 0; 289 | } 290 | 291 | /* pad the remainder of this message block */ 292 | while (msgoff & 0x007F) 293 | { ad_putc('\0'); ad_putc('\0'); ad_putc('\0'); ad_putc('\0'); msgoff++; } 294 | 295 | /* save the size of the data area before the dictionary */ 296 | dsize = dptr; 297 | 298 | /* insert the vocabulary into the data array */ 299 | woutput(words->tr_root); 300 | 301 | /* compute table offsets */ 302 | woff = 0; wsize = tentries(words) * 2 + 2; 303 | toff = woff + wsize; tsize = wcnt; 304 | ooff = toff + tsize; osize = ocnt * 2 + 2; 305 | aoff = ooff + osize; asize = acnt * 2 + 2; 306 | voff = aoff + asize; vsize = vcnt * 2 + 2; 307 | dbase = voff + vsize; 308 | cbase = dbase + dptr; 309 | 310 | /* compute the resident structure size */ 311 | size = wsize+tsize+osize+asize+vsize+dptr+cptr; 312 | 313 | /* set the save area parameters */ 314 | soff = voff; ssize = vsize + dsize; 315 | 316 | /* compute the first block for message text */ 317 | mblk = 1; 318 | dblk = (int)(ad_foff >> 9); 319 | 320 | /* output the word table */ 321 | word_out(tentries(words)); 322 | wtoutput(words->tr_root); 323 | 324 | /* output the word type table */ 325 | for (i = 1; i <= wcnt; i++) 326 | byte_out(wtable[i]); 327 | 328 | /* output the object table */ 329 | word_out(ocnt); 330 | for (i = 1; i <= ocnt; i++) { 331 | if (otable[i] == NIL) 332 | undef_object(i); 333 | word_out(otable[i]); 334 | } 335 | 336 | /* output the action table */ 337 | word_out(acnt); 338 | for (i = 1; i <= acnt; i++) 339 | word_out(atable[i]); 340 | 341 | /* beginning of saveable data */ 342 | 343 | /* output the variable table */ 344 | word_out(vcnt); 345 | for (i = 1; i <= vcnt; i++) 346 | word_out(NIL); 347 | 348 | /* output the data space */ 349 | for (i = 0; i < dptr; ) 350 | byte_out(data[i++]); 351 | 352 | /* end of saveable data */ 353 | 354 | /* output the code space */ 355 | for (i = 0; i < cptr; ) 356 | byte_out(code[i++]); 357 | 358 | /* output the file header */ 359 | ad_seek(0L); 360 | word_out(size); /* resident structure size */ 361 | str_out("ADVSYS",6);/* magic information */ 362 | word_out(VERSION); /* data structure version number */ 363 | str_out(aname,18); /* adventure name */ 364 | word_out(aversion); /* adventure version number */ 365 | word_out(woff); /* word table offset */ 366 | word_out(toff); /* word type table offset */ 367 | word_out(ooff); /* object table offset */ 368 | word_out(aoff); /* action table offset */ 369 | word_out(voff); /* variable table offset */ 370 | word_out(dbase); /* base of data */ 371 | word_out(cbase); /* base of code */ 372 | word_out(dblk); /* first data block */ 373 | word_out(mblk); /* first message text block */ 374 | word_out(h_main); /* main code */ 375 | word_out(soff); /* save area offset */ 376 | word_out(ssize); /* save area size */ 377 | 378 | /* show statistics */ 379 | printf("[ words: %d ]\n",tentries(words)); 380 | printf("[ word types: %d ]\n",wcnt); 381 | printf("[ objects: %d ]\n",ocnt); 382 | printf("[ actions: %d ]\n",acnt); 383 | printf("[ variables: %d ]\n",vcnt); 384 | printf("[ data: %d ]\n",dsize); 385 | printf("[ code: %d ]\n",cptr); 386 | printf("[ dictionary: %d ]\n",dptr-dsize); 387 | printf("[ text: %ld ]\n",(long) msgoff * 4L); 388 | printf("[ save area: %d ]\n",ssize); 389 | printf("[ errors: %d ]\n",errcount); 390 | } 391 | 392 | /* woutput - output the word data */ 393 | void woutput(TNODE *node) 394 | { 395 | int wnum,wrd; 396 | 397 | if (node) { 398 | woutput(LLINK(node)); 399 | wnum = WORD(node); 400 | wrd = WORD(node) = dalloc(strlen(KEY(node))+3); 401 | putword(wrd,wnum); 402 | strcpy(data+wrd+2,KEY(node)); 403 | if (wtable[wnum] == WT_UNKNOWN) 404 | printf("Type of word %s is unknown\n",KEY(node)); 405 | woutput(RLINK(node)); 406 | } 407 | } 408 | 409 | /* wtoutput - output the word table */ 410 | void wtoutput(TNODE *node) 411 | { 412 | if (node) { 413 | wtoutput(LLINK(node)); 414 | word_out(WORD(node)); 415 | wtoutput(RLINK(node)); 416 | } 417 | } 418 | 419 | /* undef_object - complain about an undefined object */ 420 | void undef_object(int n) 421 | { 422 | char msg[100]; 423 | SYMBOL *sym; 424 | 425 | for (sym = symbols; sym != NULL; sym = sym->s_next) 426 | if (sym->s_type == ST_OBJECT && n == sym->s_value) { 427 | sprintf(msg,"Object %s is undefined",sym->s_name); 428 | xerror(msg); 429 | break; 430 | } 431 | } 432 | 433 | /* str_out - output a string */ 434 | void str_out(char *str,int len) 435 | { 436 | while (len--) 437 | byte_out(*str++); 438 | } 439 | 440 | /* word_out - output a word */ 441 | void word_out(int dat) 442 | { 443 | byte_out(dat); 444 | byte_out(dat >> 8); 445 | } 446 | 447 | /* byte_out - output a byte */ 448 | void byte_out(int dat) 449 | { 450 | ad_putc((~dat - 30) & 0xFF); 451 | } 452 | 453 | /* oenter - enter an object into the symbol table */ 454 | int oenter(char *name) 455 | { 456 | SYMBOL *sym; 457 | 458 | if ((sym = sfind(symbols,name)) != 0) { 459 | if (sym->s_type != ST_OBJECT) 460 | error("Not an object"); 461 | return (sym->s_value); 462 | } 463 | if (ocnt < OMAX) { 464 | senter(&symbols,name,ST_OBJECT,++ocnt); 465 | otable[ocnt] = NIL; 466 | } 467 | else 468 | error("too many objects"); 469 | return (ocnt); 470 | } 471 | 472 | /* ofind - find an object in the symbol table */ 473 | int ofind(char *name) 474 | { 475 | SYMBOL *sym; 476 | 477 | if ((sym = sfind(symbols,name)) != 0) { 478 | if (sym->s_type != ST_OBJECT) 479 | return (NIL); 480 | return (sym->s_value); 481 | } 482 | return (NIL); 483 | } 484 | 485 | /* aenter - enter an action into the symbol table */ 486 | int aenter(char *name) 487 | { 488 | SYMBOL *sym; 489 | 490 | if ((sym = sfind(symbols,name)) != 0) { 491 | if (sym->s_type != ST_ACTION) 492 | error("Not an action"); 493 | return (sym->s_value); 494 | } 495 | if (acnt < AMAX) { 496 | senter(&symbols,name,ST_ACTION,++acnt); 497 | atable[acnt] = NIL; 498 | } 499 | else 500 | error("too many actions"); 501 | return (acnt); 502 | } 503 | 504 | /* venter - enter a variable into the symbol table */ 505 | int venter(char *name) 506 | { 507 | SYMBOL *sym; 508 | 509 | if ((sym = sfind(symbols,name)) != 0) { 510 | if (sym->s_type != ST_VARIABLE) 511 | error("Not a variable"); 512 | return (sym->s_value); 513 | } 514 | senter(&symbols,name,ST_VARIABLE,++vcnt); 515 | return (vcnt); 516 | } 517 | 518 | /* penter - enter a property into the symbol table */ 519 | int penter(char *name) 520 | { 521 | SYMBOL *sym; 522 | 523 | if ((sym = sfind(symbols,name)) != 0) { 524 | if (sym->s_type != ST_PROPERTY) 525 | error("Not a property"); 526 | return (sym->s_value); 527 | } 528 | senter(&symbols,name,ST_PROPERTY,++pcnt); 529 | return (pcnt); 530 | } 531 | 532 | /* center - enter a constant into the symbol table */ 533 | void center(char *name,int value) 534 | { 535 | if (sfind(symbols,name)) { 536 | error("Already defined"); 537 | return; 538 | } 539 | senter(&symbols,name,ST_CONSTANT,value); 540 | } 541 | 542 | /* sfind - find a symbol in the symbol table */ 543 | SYMBOL *sfind(SYMBOL *head,char *name) 544 | { 545 | SYMBOL *sym; 546 | 547 | for (sym = head; sym != NULL; sym = sym->s_next) 548 | if (strcmp(name,sym->s_name) == 0) 549 | break; 550 | return (sym); 551 | } 552 | 553 | /* senter - enter a symbol into the symbol table */ 554 | SYMBOL *senter(SYMBOL **phead,char *name,int type,int value) 555 | { 556 | SYMBOL *sym; 557 | 558 | if ((sym = (SYMBOL *)malloc(sizeof(SYMBOL))) == NULL) 559 | fail("out of memory"); 560 | sym->s_name = save(name); 561 | sym->s_type = type; 562 | sym->s_value = value; 563 | sym->s_next = *phead; 564 | *phead = sym; 565 | return (sym); 566 | } 567 | 568 | /* frequire - fetch a token and check it */ 569 | void frequire(int rtkn) 570 | { 571 | require(token(),rtkn); 572 | } 573 | 574 | /* require - check for a required token */ 575 | void require(int tkn,int rtkn) 576 | { 577 | char msg[100]; 578 | if (tkn != rtkn) { 579 | sprintf(msg,"Expecting %s",t_names[rtkn]); 580 | error(msg); 581 | } 582 | } 583 | 584 | /* save - allocate memory for a string */ 585 | char *save(char *str) 586 | { 587 | char *new; 588 | 589 | if ((new = malloc(strlen(str)+1)) == NULL) 590 | fail("out of memory"); 591 | strcpy(new,str); 592 | return (new); 593 | } 594 | 595 | /* match - compare a string with the current token */ 596 | int match(char *str) 597 | { 598 | return (strcmp(str,t_token) == 0); 599 | } 600 | 601 | /* fail - print an error message and exit */ 602 | void fail(char *msg) 603 | { 604 | printf("%s\n",msg); 605 | exit(0); 606 | } 607 | -------------------------------------------------------------------------------- /advcom.h: -------------------------------------------------------------------------------- 1 | /* advcom.h - adventure compiler definitions */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advavl.h" 10 | 11 | /* limits */ 12 | #define TKNSIZE 50 /* maximum token size */ 13 | #define OSIZE 104 /* maximum object size (O_SIZE/2 + OPMAX*2) */ 14 | #define OPMAX 50 /* maximum # properties/object */ 15 | #define WMAX 800 /* maximum number of words */ 16 | #define OMAX 800 /* maximum number of objects */ 17 | #define AMAX 500 /* maximum number of actions */ 18 | #define DMAX 32767 /* maximum data space */ 19 | #define CMAX 32767 /* maximum code space */ 20 | #define FMAX 20 /* file name maximum */ 21 | 22 | /* useful definitions */ 23 | #define TRUE 1 24 | #define FALSE 0 25 | #define EOS '\0' 26 | 27 | /* token definitions */ 28 | #define T_OPEN 1 29 | #define T_CLOSE 2 30 | #define T_STRING 3 31 | #define T_IDENTIFIER 4 32 | #define T_NUMBER 5 33 | #define T_EOF 6 34 | 35 | /* symbol types */ 36 | #define ST_OBJECT 1 37 | #define ST_ACTION 2 38 | #define ST_VARIABLE 3 39 | #define ST_CONSTANT 4 40 | #define ST_PROPERTY 5 41 | 42 | /* symbol structure */ 43 | typedef struct symbol { 44 | char *s_name; /* symbol name */ 45 | int s_type; /* symbol type */ 46 | int s_value; /* symbol value */ 47 | struct symbol *s_next; /* next symbol in table */ 48 | } SYMBOL; 49 | 50 | /* function argument structure */ 51 | typedef struct argument { 52 | char *arg_name; /* argument name */ 53 | struct argument *arg_next; /* next argument */ 54 | } ARGUMENT; 55 | 56 | /* advcom.c prototypes */ 57 | int main(int argc,char *argv[]); 58 | int getvalue(void); 59 | int dalloc(int size); 60 | int add_word(char *str,int type); 61 | int add_synonym(char *str,int wrd); 62 | int getword(int off); 63 | void putword(int off,int dat); 64 | int getbyte(int off); 65 | void putbyte(int off,int dat); 66 | void output(void); 67 | void woutput(TNODE *node); 68 | void wtoutput(TNODE *node); 69 | void undef_object(int n); 70 | void str_out(char *str,int len); 71 | void word_out(int dat); 72 | void byte_out(int dat); 73 | int oenter(char *name); 74 | int ofind(char *name); 75 | int aenter(char *name); 76 | int venter(char *name); 77 | int penter(char *name); 78 | void center(char *name,int value); 79 | SYMBOL *sfind(SYMBOL *head,char *name); 80 | SYMBOL *senter(SYMBOL **phead,char *name,int type,int value); 81 | void frequire(int rtkn); 82 | void require(int tkn,int rtkn); 83 | char *save(char *str); 84 | int match(char *str); 85 | void fail(char *msg); 86 | 87 | /* advexp.c prototypes */ 88 | void do_expr(void); 89 | int in_ntab(void); 90 | int in_ftab(void); 91 | int in_etab(void); 92 | void do_cond(void); 93 | void do_and(void); 94 | void do_or(void); 95 | void do_if(void); 96 | void do_while(void); 97 | void do_progn(void); 98 | void do_setq(void); 99 | void do_return(void); 100 | void do_send(void); 101 | void do_sndsuper(void); 102 | void sender(void); 103 | void do_catch(void); 104 | void do_call(void); 105 | void do_nary(int op,int n); 106 | void do_literal(void); 107 | void do_identifier(void); 108 | void code_argument(int n); 109 | void cd_setargument(int n); 110 | void code_temporary(int n); 111 | void cd_settemporary(int n); 112 | void code_variable(int n); 113 | void cd_setvariable(int n); 114 | void code_literal(int n); 115 | void do_op(int op); 116 | int putcbyte(int b); 117 | int putcword(int w); 118 | void fixup(int chn,int val); 119 | 120 | /* advfcn.c prototypes */ 121 | void do_adventure(void); 122 | void do_word(int type); 123 | void do_synonym(void); 124 | void do_define(void); 125 | void do_extended(void); 126 | void do_variable(void); 127 | void do_defproperty(void); 128 | void do_default(void); 129 | void do_dflag(int flag); 130 | int do_object(char *cname,int class); 131 | void do_noun(void); 132 | void do_adjective(void); 133 | void do_property(int flags); 134 | void do_method(void); 135 | void setprop(int prop,int flags,int value); 136 | void addprop(int prop,int flags,int value); 137 | int do_code(char *type); 138 | void do_action(void); 139 | void do_flag(int flag); 140 | void do_verb(void); 141 | void do_preposition(void); 142 | void do_function(void); 143 | void addargument(ARGUMENT **list,char *name); 144 | void freelist(ARGUMENT *arg); 145 | int findarg(char *name); 146 | int findtmp(char *name); 147 | 148 | /* advscn.c prototypes */ 149 | void sinit(void); 150 | int token(void); 151 | void stoken(int tkn); 152 | int rtoken(void); 153 | int getstring(void); 154 | int getid(int ch); 155 | int isnum(char *str,int *pval); 156 | void wputc(int ch); 157 | void strdone(void); 158 | int skipspaces(void); 159 | int isidchar(int ch); 160 | int getchr(void); 161 | int encode(int ch); 162 | void error(char *msg); 163 | void xerror(char *msg); 164 | 165 | /* advcstuff.c prototypes */ 166 | void macinit(char *iname,char *oname); 167 | void macpause(void); 168 | void ad_create(char *name); 169 | void ad_close(void); 170 | void ad_putc(int ch); 171 | void ad_seek(long pos); 172 | void ad_flush(void); 173 | -------------------------------------------------------------------------------- /advdbs.c: -------------------------------------------------------------------------------- 1 | /* advdbs.c - adventure database access routines */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "advint.h" 11 | #include "advdbs.h" 12 | 13 | /* global variables */ 14 | int h_main; /* main code */ 15 | FILE *datafp; /* data file pointer */ 16 | 17 | /* external variables */ 18 | extern jmp_buf restart; 19 | 20 | /* table base addresses */ 21 | char *wtable; /* word table */ 22 | char *wtypes; /* word type table */ 23 | int wcount; /* number of words */ 24 | char *otable; /* object table */ 25 | int ocount; /* number of objects */ 26 | char *atable; /* action table */ 27 | int acount; /* number of actions */ 28 | char *vtable; /* variable table */ 29 | int vcount; /* number of variables */ 30 | char *data; /* base of data tables */ 31 | char *base; /* current base address */ 32 | char *dbase; /* base of the data space */ 33 | char *cbase; /* base of the code space */ 34 | int length; /* length of resident data structures */ 35 | 36 | /* data file header */ 37 | static char hdr[HDR_SIZE]; 38 | 39 | /* save parameters */ 40 | static long saveoff; /* save data file offset */ 41 | static char *save; /* save area base address */ 42 | static int slen; /* save area length */ 43 | 44 | /* prototypes */ 45 | 46 | /* db_init - read and decode the data file header */ 47 | void db_init(char *name) 48 | { 49 | int woff,ooff,aoff,voff,n; 50 | char fname[50]; 51 | 52 | /* get the data file name */ 53 | strcpy(fname,name); 54 | strcat(fname,".dat"); 55 | 56 | /* open the data file */ 57 | if (!(datafp = fopen(fname,"rb"))) 58 | error("can't open data file: %s",fname); 59 | 60 | /* read the header */ 61 | if (fread(hdr,1,HDR_SIZE,datafp) != HDR_SIZE) 62 | error("bad data file - header"); 63 | complement(hdr,HDR_SIZE); 64 | base = hdr; 65 | 66 | /* check the magic information */ 67 | if (strncmp(&hdr[HDR_MAGIC],"ADVSYS",6) != 0) 68 | error("not an adventure data file"); 69 | 70 | /* check the version number */ 71 | if ((n = getword(HDR_VERSION)) < 103 || n > VERSION) 72 | error("wrong version number: %d %d",n,VERSION); 73 | 74 | /* decode the resident data length header field */ 75 | length = getword(HDR_LENGTH); 76 | 77 | /* allocate space for the resident data structure */ 78 | if ((data = malloc(length)) == 0) 79 | error("insufficient memory"); 80 | 81 | /* compute the offset to the data */ 82 | saveoff = (long)getword(HDR_DATBLK) * 512L; 83 | 84 | /* read the resident data structure */ 85 | fseek(datafp,saveoff,SEEK_SET); 86 | if (fread(data,1,length,datafp) != length) 87 | error("bad data file - resident data"); 88 | complement(data,length); 89 | 90 | /* get the table base addresses */ 91 | wtable = data + (woff = getword(HDR_WTABLE)); 92 | wtypes = data + getword(HDR_WTYPES) - 1; 93 | otable = data + (ooff = getword(HDR_OTABLE)); 94 | atable = data + (aoff = getword(HDR_ATABLE)); 95 | vtable = data + (voff = getword(HDR_VTABLE)); 96 | 97 | /* get the save data area */ 98 | saveoff += (long)getword(HDR_SAVE); 99 | save = data + getword(HDR_SAVE); 100 | slen = getword(HDR_SLEN); 101 | 102 | /* get the base of the data and code spaces */ 103 | dbase = data + getword(HDR_DBASE); 104 | cbase = data + getword(HDR_CBASE); 105 | 106 | /* initialize the message routines */ 107 | msg_init(datafp,getword(HDR_MSGBLK)); 108 | 109 | /* get the main code pointer */ 110 | h_main = getword(HDR_MAIN); 111 | 112 | /* get the table lengths */ 113 | base = data; 114 | wcount = getword(woff); 115 | ocount = getword(ooff); 116 | acount = getword(aoff); 117 | vcount = getword(voff); 118 | 119 | /* setup the base of the resident data */ 120 | base = dbase; 121 | 122 | /* set the object count */ 123 | setvalue(V_OCOUNT,ocount); 124 | } 125 | 126 | /* db_save - save the current database */ 127 | int db_save(void) 128 | { 129 | return (advsave(&hdr[HDR_ANAME],20,save,slen) ? T : NIL); 130 | } 131 | 132 | /* db_restore - restore a saved database */ 133 | int db_restore(void) 134 | { 135 | return (advrestore(&hdr[HDR_ANAME],20,save,slen) ? T : NIL); 136 | } 137 | 138 | /* db_restart - restart the current game */ 139 | int db_restart(void) 140 | { 141 | fseek(datafp,saveoff,SEEK_SET); 142 | if (fread(save,1,slen,datafp) != slen) 143 | return (NIL); 144 | complement(save,slen); 145 | setvalue(V_OCOUNT,ocount); 146 | longjmp(restart,1); 147 | } 148 | 149 | /* complement - complement a block of memory */ 150 | void complement(char *adr,int len) 151 | { 152 | for (; len--; adr++) 153 | *adr = ~(*adr + 30); 154 | } 155 | 156 | /* findword - find a word in the dictionary */ 157 | int findword(char *word) 158 | { 159 | char sword[WRDSIZE+1]; 160 | int wrd,i; 161 | 162 | /* shorten the word */ 163 | strncpy(sword,word,WRDSIZE); sword[WRDSIZE] = 0; 164 | 165 | /* look up the word */ 166 | for (i = 1; i <= wcount; i++) { 167 | wrd = getwloc(i); 168 | if (strcmp(base+wrd+2,sword) == 0) 169 | return (getword(wrd)); 170 | } 171 | return (NIL); 172 | } 173 | 174 | /* wtype - return the type of a word */ 175 | int wtype(int wrd) 176 | { 177 | return (wtypes[wrd]); 178 | } 179 | 180 | /* match - match an object against a name and list of adjectives */ 181 | int match(int obj,int noun,int *adjs) 182 | { 183 | int *aptr; 184 | 185 | if (!hasnoun(obj,noun)) 186 | return (FALSE); 187 | for (aptr = adjs; *aptr != NIL; aptr++) 188 | if (!hasadjective(obj,*aptr)) 189 | return (FALSE); 190 | return (TRUE); 191 | } 192 | 193 | /* checkverb - check to see if this is a valid verb */ 194 | int checkverb(int *verbs) 195 | { 196 | int act; 197 | 198 | /* look up the action */ 199 | for (act = 1; act <= acount; act++) 200 | if (hasverb(act,verbs)) 201 | return (act); 202 | return (NIL); 203 | } 204 | 205 | /* findaction - find an action matching a description */ 206 | int findaction(int *verbs,int preposition,int flag) 207 | { 208 | int act,mask; 209 | 210 | /* look up the action */ 211 | for (act = 1; act <= acount; act++) { 212 | if (preposition && !haspreposition(act,preposition)) 213 | continue; 214 | if (!hasverb(act,verbs)) 215 | continue; 216 | mask = ~getabyte(act,A_MASK); 217 | if ((flag & mask) == (getabyte(act,A_FLAG) & mask)) 218 | return (act); 219 | } 220 | return (NIL); 221 | } 222 | 223 | /* getp - get the value of an object property */ 224 | int getp(int obj,int prop) 225 | { 226 | int p; 227 | 228 | for (; obj; obj = getofield(obj,O_CLASS)) 229 | if ((p = findprop(obj,prop)) != 0) 230 | return (getofield(obj,p)); 231 | return (NIL); 232 | } 233 | 234 | /* setp - set the value of an object property */ 235 | int setp(int obj,int prop,int val) 236 | { 237 | int p; 238 | 239 | for (; obj; obj = getofield(obj,O_CLASS)) 240 | if ((p = findprop(obj,prop)) != 0) 241 | return (putofield(obj,p,val)); 242 | return (NIL); 243 | } 244 | 245 | /* findprop - find a property */ 246 | int findprop(int obj,int prop) 247 | { 248 | int n,i,p; 249 | 250 | n = getofield(obj,O_NPROPERTIES); 251 | for (i = p = 0; i < n; i++, p += 4) 252 | if ((getofield(obj,O_PROPERTIES+p) & 0x7FFF) == prop) 253 | return (O_PROPERTIES+p+2); 254 | return (NIL); 255 | } 256 | 257 | /* hasnoun - check to see if an object has a specified noun */ 258 | int hasnoun(int obj,int noun) 259 | { 260 | while (obj) { 261 | if (inlist(getofield(obj,O_NOUNS),noun)) 262 | return (TRUE); 263 | obj = getofield(obj,O_CLASS); 264 | } 265 | return (FALSE); 266 | } 267 | 268 | /* hasadjective - check to see if an object has a specified adjective */ 269 | int hasadjective(int obj,int adjective) 270 | { 271 | while (obj) { 272 | if (inlist(getofield(obj,O_ADJECTIVES),adjective)) 273 | return (TRUE); 274 | obj = getofield(obj,O_CLASS); 275 | } 276 | return (FALSE); 277 | } 278 | 279 | /* hasverb - check to see if this action has this verb */ 280 | int hasverb(int act,int *verbs) 281 | { 282 | int link,word,*verb; 283 | 284 | /* get the list of verbs */ 285 | link = getafield(act,A_VERBS); 286 | 287 | /* look for this verb */ 288 | while (link != NIL) { 289 | verb = verbs; 290 | word = getword(link+L_DATA); 291 | while (*verb != NIL && word != NIL) { 292 | if (*verb != getword(word+L_DATA)) 293 | break; 294 | verb++; 295 | word = getword(word+L_NEXT); 296 | } 297 | if (*verb == NIL && word == NIL) 298 | return (TRUE); 299 | link = getword(link+L_NEXT); 300 | } 301 | return (FALSE); 302 | } 303 | 304 | /* haspreposition - check to see if an action has a specified preposition */ 305 | int haspreposition(int act,int preposition) 306 | { 307 | return (inlist(getafield(act,A_PREPOSITIONS),preposition)); 308 | } 309 | 310 | /* inlist - check to see if a word is an element of a list */ 311 | int inlist(int link,int word) 312 | { 313 | while (link != NIL) { 314 | if (word == getword(link+L_DATA)) 315 | return (TRUE); 316 | link = getword(link+L_NEXT); 317 | } 318 | return (FALSE); 319 | } 320 | 321 | /* getofield - get a field from an object */ 322 | int getofield(int obj,int off) 323 | { 324 | return (getword(getoloc(obj)+off)); 325 | } 326 | 327 | /* putofield - put a field into an object */ 328 | int putofield(int obj,int off,int val) 329 | { 330 | return (putword(getoloc(obj)+off,val)); 331 | } 332 | 333 | /* getafield - get a field from an action */ 334 | int getafield(int act,int off) 335 | { 336 | return (getword(getaloc(act)+off)); 337 | } 338 | 339 | /* getabyte - get a byte field from an action */ 340 | int getabyte(int act,int off) 341 | { 342 | return (getbyte(getaloc(act)+off)); 343 | } 344 | 345 | /* getoloc - get an object from the object table */ 346 | int getoloc(int n) 347 | { 348 | if (n < 1 || n > ocount) 349 | error("object number out of range: %d",n); 350 | return (getdword(otable+n+n)); 351 | } 352 | 353 | /* getaloc - get an action from the action table */ 354 | int getaloc(int n) 355 | { 356 | if (n < 1 || n > acount) 357 | error("action number out of range: %d",n); 358 | return (getdword(atable+n+n)); 359 | } 360 | 361 | /* getvalue - get the value of a variable from the variable table */ 362 | int getvalue(int n) 363 | { 364 | if (n < 1 || n > vcount) 365 | error("variable number out of range: %d",n); 366 | return (getdword(vtable+n+n)); 367 | } 368 | 369 | /* setvalue - set the value of a variable in the variable table */ 370 | int setvalue(int n,int v) 371 | { 372 | if (n < 1 || n > vcount) 373 | error("variable number out of range: %d",n); 374 | return (putdword(vtable+n+n,v)); 375 | } 376 | 377 | /* getwloc - get a word from the word table */ 378 | int getwloc(int n) 379 | { 380 | if (n < 1 || n > wcount) 381 | error("word number out of range: %d",n); 382 | return (getdword(wtable+n+n)); 383 | } 384 | 385 | /* getword - get a word from the data array */ 386 | int getword(int n) 387 | { 388 | return (getdword(base+n)); 389 | } 390 | 391 | /* putword - put a word into the data array */ 392 | int putword(int n,int w) 393 | { 394 | return (putdword(base+n,w)); 395 | } 396 | 397 | /* getbyte - get a byte from the data array */ 398 | int getbyte(int n) 399 | { 400 | return (*(base+n) & 0xFF); 401 | } 402 | 403 | /* getcbyte - get a code byte */ 404 | int getcbyte(int n) 405 | { 406 | return (*(cbase+n) & 0xFF); 407 | } 408 | 409 | /* getcword - get a code word */ 410 | int getcword(int n) 411 | { 412 | return (getdword(cbase+n)); 413 | } 414 | 415 | /* getdword - get a word from the data array */ 416 | int getdword(char *p) 417 | { 418 | return ((*p & 0xFF) | (*(p+1) << 8)); 419 | } 420 | 421 | /* putdword - put a word into the data array */ 422 | int putdword(char *p,int w) 423 | { 424 | *p = w; *(p+1) = w >> 8; 425 | return (w); 426 | } 427 | 428 | -------------------------------------------------------------------------------- /advdbs.h: -------------------------------------------------------------------------------- 1 | /* advdbs.h - adventure database definitions */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | /* useful constants */ 8 | #define T -1 9 | #define NIL 0 10 | #define WRDSIZE 6 11 | 12 | /* data structure version number */ 13 | #define VERSION 103 14 | 15 | /* file header offsets */ 16 | #define HDR_LENGTH 0 /* length of header in bytes */ 17 | #define HDR_MAGIC 2 /* magic information (6 bytes) */ 18 | #define HDR_VERSION 8 /* data structure version number */ 19 | #define HDR_ANAME 10 /* adventure name (18 bytes) */ 20 | #define HDR_AVERSION 28 /* adventure version number */ 21 | #define HDR_WTABLE 30 /* offset to word table */ 22 | #define HDR_WTYPES 32 /* offset to word type table */ 23 | #define HDR_OTABLE 34 /* offset to object table */ 24 | #define HDR_ATABLE 36 /* offset to action table */ 25 | #define HDR_VTABLE 38 /* offset to variable table */ 26 | #define HDR_DBASE 40 /* offset to base of data space */ 27 | #define HDR_CBASE 42 /* offset to base of code space */ 28 | #define HDR_DATBLK 44 /* first data block */ 29 | #define HDR_MSGBLK 46 /* first message text block */ 30 | #define HDR_MAIN 48 /* main code */ 31 | #define HDR_SAVE 50 /* save area offset */ 32 | #define HDR_SLEN 52 /* save area length */ 33 | #define HDR_SIZE 54 /* size of header */ 34 | 35 | /* word types */ 36 | #define WT_UNKNOWN 0 37 | #define WT_VERB 1 38 | #define WT_NOUN 2 39 | #define WT_ADJECTIVE 3 40 | #define WT_PREPOSITION 4 41 | #define WT_CONJUNCTION 5 42 | #define WT_ARTICLE 6 43 | 44 | /* object fields */ 45 | #define O_CLASS 0 46 | #define O_NOUNS 2 47 | #define O_ADJECTIVES 4 48 | #define O_NPROPERTIES 6 49 | #define O_PROPERTIES 8 50 | #define O_SIZE 8 51 | 52 | /* action fields */ 53 | #define A_VERBS 0 54 | #define A_PREPOSITIONS 2 55 | #define A_FLAG 4 56 | #define A_MASK 5 57 | #define A_CODE 6 58 | #define A_SIZE 8 59 | 60 | /* link fields */ 61 | #define L_DATA 0 62 | #define L_NEXT 2 63 | #define L_SIZE 4 64 | 65 | /* property flags */ 66 | #define P_CLASS 0x8000 /* class property */ 67 | 68 | /* action flags */ 69 | #define A_ACTOR 0x01 /* actor */ 70 | #define A_DOBJECT 0x02 /* direct object */ 71 | #define A_IOBJECT 0x04 /* indirect object */ 72 | 73 | /* opcodes */ 74 | #define OP_BRT 0x01 /* branch on true */ 75 | #define OP_BRF 0x02 /* branch on false */ 76 | #define OP_BR 0x03 /* branch unconditionally */ 77 | #define OP_T 0x04 /* load top of stack with t */ 78 | #define OP_NIL 0x05 /* load top of stack with nil */ 79 | #define OP_PUSH 0x06 /* push nil onto stack */ 80 | #define OP_NOT 0x07 /* logical negate top of stack */ 81 | #define OP_ADD 0x08 /* add two numeric expressions */ 82 | #define OP_SUB 0x09 /* subtract two numeric expressions */ 83 | #define OP_MUL 0x0A /* multiply two numeric expressions */ 84 | #define OP_DIV 0x0B /* divide two numeric expressions */ 85 | #define OP_REM 0x0C /* remainder of two numeric expressions */ 86 | #define OP_BAND 0x0D /* bitwise and of two numeric expressions */ 87 | #define OP_BOR 0x0E /* bitwise or of two numeric expressions */ 88 | #define OP_BNOT 0x0F /* bitwise not of two numeric expressions */ 89 | #define OP_LT 0x10 /* less than */ 90 | #define OP_EQ 0x11 /* equal to */ 91 | #define OP_GT 0x12 /* greater than */ 92 | #define OP_LIT 0x13 /* load literal */ 93 | #define OP_VAR 0x14 /* load a variable value */ 94 | #define OP_GETP 0x15 /* get the value of an object property */ 95 | #define OP_SETP 0x16 /* set the value of an object property */ 96 | #define OP_SET 0x17 /* set the value of a variable */ 97 | #define OP_PRINT 0x18 /* print messages */ 98 | #define OP_TERPRI 0x19 /* terminate the print line */ 99 | #define OP_PNUMBER 0x1A /* print a number */ 100 | #define OP_EXIT 0x1B /* exit the program */ 101 | #define OP_RETURN 0x1C /* return from interpreter */ 102 | #define OP_CALL 0x1D /* call a function */ 103 | #define OP_SVAR 0x1E /* short load a variable */ 104 | #define OP_SSET 0x1F /* short set a variable */ 105 | #define OP_SPLIT 0x20 /* short load a positive literal */ 106 | #define OP_SNLIT 0x21 /* short load a negative literal */ 107 | #define OP_YORN 0x22 /* yes-or-no predicate */ 108 | #define OP_SAVE 0x23 /* save data structures */ 109 | #define OP_RESTORE 0x24 /* restore data structures */ 110 | #define OP_ARG 0x25 /* load an argument value */ 111 | #define OP_ASET 0x26 /* set an argument value */ 112 | #define OP_TMP 0x27 /* load a temporary variable value */ 113 | #define OP_TSET 0x28 /* set a temporary variable */ 114 | #define OP_TSPACE 0x29 /* allocate temporary variable space */ 115 | #define OP_CLASS 0x2A /* get the class of an object */ 116 | #define OP_MATCH 0x2B /* match a noun phrase with an object */ 117 | #define OP_PNOUN 0x2C /* print a noun phrase */ 118 | #define OP_RESTART 0x2D /* restart the current game */ 119 | /* (opcodes 2E and 2F are unused) */ 120 | #define OP_SEND 0x30 /* send a message to an object */ 121 | #define OP_CATCH 0x31 /* catch a signal (establish a catch frame) */ 122 | #define OP_CDONE 0x32 /* remove a catch frame from the stack */ 123 | #define OP_THROW 0x33 /* throw a signal */ 124 | #define OP_PARSE 0x34 /* parse the next input line */ 125 | #define OP_NEXT 0x35 /* get the next command */ 126 | #define OP_EXTEND 0x36 /* call an extended function */ 127 | 128 | #define OP_XVAR 0x40 /* extra short load a variable */ 129 | #define OP_XSET 0x60 /* extra short set a variable */ 130 | #define OP_XPLIT 0x80 /* extra short load a positive literal */ 131 | #define OP_XNLIT 0xC0 /* extra short load a negative literal */ 132 | 133 | /* builtin variables */ 134 | #define V_ACTOR 1 /* actor noun phrase number */ 135 | #define V_ACTION 2 /* action from parse */ 136 | #define V_DOBJECT 3 /* first direct object noun phrase number */ 137 | #define V_NDOBJECTS 4 /* number of direct object noun phrases */ 138 | #define V_IOBJECT 5 /* indirect object noun phrase number */ 139 | #define V_OCOUNT 6 /* total object count */ 140 | 141 | -------------------------------------------------------------------------------- /advexe.c: -------------------------------------------------------------------------------- 1 | /* advexe.c - adventure code executer */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include "advint.h" 9 | #include "advdbs.h" 10 | 11 | /* external variables */ 12 | extern int nouns[],*adjectives[]; 13 | 14 | /* local variables */ 15 | static int *sp,*fp,*efp,*top,pc,opcode; 16 | static int stack[STKSIZE]; 17 | static jmp_buf trap; 18 | 19 | /* external routines */ 20 | extern char *trm_get(); 21 | 22 | /* forward declarations */ 23 | extern void (*optab[])(void); 24 | 25 | static void opCALL(void); 26 | static void opSEND(void); 27 | static void opRETURN(void); 28 | static void opTSPACE(void); 29 | static void opTMP(void); 30 | static void opTSET(void); 31 | static void opARG(void); 32 | static void opASET(void); 33 | static void opBRT(void); 34 | static void opBRF(void); 35 | static void opBR(void); 36 | static void opT(void); 37 | static void opNIL(void); 38 | static void opPUSH(void); 39 | static void opNOT(void); 40 | static void opADD(void); 41 | static void opSUB(void); 42 | static void opMUL(void); 43 | static void opDIV(void); 44 | static void opREM(void); 45 | static void opBAND(void); 46 | static void opBOR(void); 47 | static void opBNOT(void); 48 | static void opLT(void); 49 | static void opEQ(void); 50 | static void opGT(void); 51 | static void opLIT(void); 52 | static void opSPLIT(void); 53 | static void opSNLIT(void); 54 | static void opVAR(void); 55 | static void opSVAR(void); 56 | static void opSET(void); 57 | static void opSSET(void); 58 | static void opGETP(void); 59 | static void opSETP(void); 60 | static void opPRINT(void); 61 | static void opPNUMBER(void); 62 | static void opPNOUN(void); 63 | static void opTERPRI(void); 64 | static void opEXIT(void); 65 | static void opYORN(void); 66 | static void opCLASS(void); 67 | static void opMATCH(void); 68 | static void opSAVE(void); 69 | static void opRESTORE(void); 70 | static void opRESTART(void); 71 | static void opCATCH(void); 72 | static void opCDONE(void); 73 | static void opTHROW(void); 74 | static void opPARSE(void); 75 | static void opNEXT(void); 76 | static void opXVAR(void); 77 | static void opXSET(void); 78 | static void opXPLIT(void); 79 | static void opXNLIT(void); 80 | static void badopcode(void); 81 | static int getboperand(void); 82 | static int getwoperand(void); 83 | 84 | /* execute - execute adventure code */ 85 | void execute(int code) 86 | { 87 | /* initialize */ 88 | sp = fp = top = stack + STKSIZE; 89 | efp = NULL; 90 | pc = code; 91 | 92 | /* trap exits */ 93 | if (setjmp(trap)) 94 | return; 95 | 96 | /* execute the code */ 97 | for (;;) { 98 | 99 | /* get the opcode */ 100 | opcode = getcbyte(pc); ++pc; 101 | /* printf("%04x: %02x\n",pc-1,opcode); */ 102 | 103 | /* execute the instruction */ 104 | ((*optab[opcode]))(); 105 | } 106 | } 107 | 108 | static void opCALL(void) 109 | { 110 | *--sp = getboperand(); 111 | *--sp = pc; 112 | *--sp = (int)(top - fp); 113 | fp = sp; 114 | pc = getafield(fp[fp[2]+3],A_CODE); 115 | } 116 | 117 | static void opSEND(void) 118 | { 119 | register int p2; 120 | *--sp = getboperand(); 121 | *--sp = pc; 122 | *--sp = (int)(top - fp); 123 | fp = sp; 124 | p2 = ((p2 = fp[fp[2]+3]) ? getofield(p2,O_CLASS) : fp[fp[2]+2]); 125 | if (p2 && (p2 = getp(p2,fp[fp[2]+1]))) 126 | pc = getafield(p2,A_CODE); 127 | else { 128 | *sp = NIL; 129 | opRETURN(); 130 | } 131 | } 132 | 133 | static void opRETURN(void) 134 | { 135 | register int p2,p3; 136 | if (fp == top) 137 | longjmp(trap,1); 138 | else { 139 | p2 = *sp; 140 | sp = fp; 141 | fp = top - *sp++; 142 | pc = *sp++; 143 | p3 = *sp++; 144 | sp += p3; 145 | *sp = p2; 146 | } 147 | } 148 | 149 | static void opTSPACE(void) 150 | { 151 | sp -= getboperand(); 152 | } 153 | 154 | static void opTMP(void) 155 | { 156 | *sp = fp[-getboperand()-1]; 157 | } 158 | 159 | static void opTSET(void) 160 | { 161 | fp[-getboperand()-1] = *sp; 162 | } 163 | 164 | static void opARG(void) 165 | { 166 | register int p2; 167 | p2 = getboperand(); 168 | if (p2 >= fp[2]) 169 | error("too few arguments: %d %d",p2+1,fp[2]); 170 | *sp = fp[p2+3]; 171 | } 172 | 173 | static void opASET(void) 174 | { 175 | register int p2; 176 | p2 = getboperand(); 177 | if (p2 >= fp[2]) 178 | error("too few arguments: %d %d",p2+1,fp[2]); 179 | fp[p2+3] = *sp; 180 | } 181 | 182 | static void opBRT(void) 183 | { 184 | pc = (*sp ? getwoperand() : pc+2); 185 | } 186 | 187 | static void opBRF(void) 188 | { 189 | pc = (*sp ? pc+2 : getwoperand()); 190 | } 191 | 192 | static void opBR(void) 193 | { 194 | pc = getwoperand(); 195 | } 196 | 197 | static void opT(void) 198 | { 199 | *sp = T; 200 | } 201 | 202 | static void opNIL(void) 203 | { 204 | *sp = NIL; 205 | } 206 | 207 | static void opPUSH(void) 208 | { 209 | *--sp = NIL; 210 | } 211 | 212 | static void opNOT(void) 213 | { 214 | *sp = (*sp ? NIL : T); 215 | } 216 | 217 | static void opADD(void) 218 | { 219 | register int p2; 220 | p2 = *sp++; 221 | *sp += p2; 222 | } 223 | 224 | static void opSUB(void) 225 | { 226 | register int p2; 227 | p2 = *sp++; 228 | *sp -= p2; 229 | } 230 | 231 | static void opMUL(void) 232 | { 233 | register int p2; 234 | p2 = *sp++; 235 | *sp *= p2; 236 | } 237 | 238 | static void opDIV(void) 239 | { 240 | register int p2; 241 | p2 = *sp++; 242 | *sp = (p2 == 0 ? 0 : *sp / p2); 243 | } 244 | 245 | static void opREM(void) 246 | { 247 | register int p2; 248 | p2 = *sp++; 249 | *sp = (p2 == 0 ? 0 : *sp % p2); 250 | } 251 | 252 | static void opBAND(void) 253 | { 254 | register int p2; 255 | p2 = *sp++; 256 | *sp &= p2; 257 | } 258 | 259 | static void opBOR(void) 260 | { 261 | register int p2; 262 | p2 = *sp++; 263 | *sp |= p2; 264 | } 265 | 266 | static void opBNOT(void) 267 | { 268 | *sp = ~*sp; 269 | } 270 | 271 | static void opLT(void) 272 | { 273 | register int p2; 274 | p2 = *sp++; 275 | *sp = (*sp < p2 ? T : NIL); 276 | } 277 | 278 | static void opEQ(void) 279 | { 280 | register int p2; 281 | p2 = *sp++; 282 | *sp = (*sp == p2 ? T : NIL); 283 | } 284 | 285 | static void opGT(void) 286 | { 287 | register int p2; 288 | p2 = *sp++; 289 | *sp = (*sp > p2 ? T : NIL); 290 | } 291 | 292 | static void opLIT(void) 293 | { 294 | *sp = getwoperand(); 295 | } 296 | 297 | static void opSPLIT(void) 298 | { 299 | *sp = getboperand(); 300 | } 301 | 302 | static void opSNLIT(void) 303 | { 304 | *sp = -getboperand(); 305 | } 306 | 307 | static void opVAR(void) 308 | { 309 | *sp = getvalue(getwoperand()); 310 | } 311 | 312 | static void opSVAR(void) 313 | { 314 | *sp = getvalue(getboperand()); 315 | } 316 | 317 | static void opSET(void) 318 | { 319 | setvalue(getwoperand(),*sp); 320 | } 321 | 322 | static void opSSET(void) 323 | { 324 | setvalue(getboperand(),*sp); 325 | } 326 | 327 | static void opGETP(void) 328 | { 329 | register int p2; 330 | p2 = *sp++; 331 | *sp = getp(*sp,p2); 332 | } 333 | 334 | static void opSETP(void) 335 | { 336 | register int p2,p3; 337 | p3 = *sp++; 338 | p2 = *sp++; 339 | *sp = setp(*sp,p2,p3); 340 | } 341 | 342 | static void opPRINT(void) 343 | { 344 | register int ch; 345 | msg_open(*sp); 346 | while ((ch = msg_byte()) != 0) 347 | trm_chr(ch); 348 | } 349 | 350 | static void opPNUMBER(void) 351 | { 352 | char buf[10]; 353 | sprintf(buf,"%d",*sp); 354 | trm_str(buf); 355 | } 356 | 357 | static void opPNOUN(void) 358 | { 359 | show_noun(*sp); 360 | } 361 | 362 | static void opTERPRI(void) 363 | { 364 | trm_chr('\n'); 365 | } 366 | 367 | static void opEXIT(void) 368 | { 369 | longjmp(trap,1); 370 | } 371 | 372 | static void opYORN(void) 373 | { 374 | char *p; 375 | p = trm_get(); 376 | *sp = (p && (*p == 'Y' || *p == 'y') ? T : NIL); 377 | } 378 | 379 | static void opCLASS(void) 380 | { 381 | *sp = getofield(*sp,O_CLASS); 382 | } 383 | 384 | static void opMATCH(void) 385 | { 386 | register int p2; 387 | p2 = *sp++; 388 | *sp = (match(*sp,nouns[p2-1],adjectives[p2-1]) ? T : NIL); 389 | } 390 | 391 | static void opSAVE(void) 392 | { 393 | *sp = db_save(); 394 | } 395 | 396 | static void opRESTORE(void) 397 | { 398 | *sp = db_restore(); 399 | } 400 | 401 | static void opRESTART(void) 402 | { 403 | *sp = db_restart(); 404 | } 405 | 406 | static void opCATCH(void) 407 | { 408 | *--sp = pc; 409 | *--sp = (int)(top - fp); 410 | *--sp = (int)(top - efp); 411 | efp = sp; 412 | *--sp = NIL; 413 | pc += 2; 414 | } 415 | 416 | static void opCDONE(void) 417 | { 418 | register int p2; 419 | p2 = *sp++; 420 | efp = top - *sp++; 421 | fp = top - *sp++; 422 | *++sp = p2; 423 | } 424 | 425 | static void opTHROW(void) 426 | { 427 | register int p2; 428 | for (; efp != NULL; efp = top - *efp) 429 | if (sp[1] == efp[3]) 430 | break; 431 | if (efp) { 432 | p2 = *sp; 433 | sp = efp; 434 | efp = top - *sp++; 435 | fp = top - *sp++; 436 | pc = *sp++; 437 | pc = getwoperand(); 438 | *sp = p2; 439 | } 440 | else 441 | error("no target for throw: %d",sp[1]); 442 | } 443 | 444 | static void opPARSE(void) 445 | { 446 | *sp = parse(); 447 | } 448 | 449 | static void opNEXT(void) 450 | { 451 | *sp = next(); 452 | } 453 | 454 | static void opXVAR(void) 455 | { 456 | *sp = getvalue(opcode - OP_XVAR); 457 | } 458 | 459 | static void opXSET(void) 460 | { 461 | setvalue(opcode - OP_XSET,*sp); 462 | } 463 | 464 | static void opXPLIT(void) 465 | { 466 | *sp = opcode - OP_XPLIT; 467 | } 468 | 469 | static void opXNLIT(void) 470 | { 471 | *sp = OP_XNLIT - opcode; 472 | } 473 | 474 | static void badopcode(void) 475 | { 476 | error("bad opcode: %d",opcode); 477 | } 478 | 479 | /* getboperand - get data byte */ 480 | static int getboperand(void) 481 | { 482 | int data; 483 | data = getcbyte(pc); pc += 1; 484 | return (data); 485 | } 486 | 487 | /* getwoperand - get data word */ 488 | static int getwoperand(void) 489 | { 490 | int data; 491 | data = getcword(pc); pc += 2; 492 | return (data); 493 | } 494 | 495 | /* opcode dispatch table */ 496 | void (*optab[])(void) = { 497 | badopcode, /* 00 (undefined) */ 498 | opBRT, /* 01 branch on true */ 499 | opBRF, /* 02 branch on false */ 500 | opBR, /* 03 branch unconditionally */ 501 | opT, /* 04 load top of stack with t */ 502 | opNIL, /* 05 load top of stack with nil */ 503 | opPUSH, /* 06 push nil onto stack */ 504 | opNOT, /* 07 logical negate top of stack */ 505 | opADD, /* 08 add two numeric expressions */ 506 | opSUB, /* 09 subtract two numeric expressions */ 507 | opMUL, /* 0A multiply two numeric expressions */ 508 | opDIV, /* 0B divide two numeric expressions */ 509 | opREM, /* 0C remainder of two numeric expressions */ 510 | opBAND, /* 0D bitwise and of two numeric expressions */ 511 | opBOR, /* 0E bitwise or of two numeric expressions */ 512 | opBNOT, /* 0F bitwise not of two numeric expressions */ 513 | opLT, /* 10 less than */ 514 | opEQ, /* 11 equal to */ 515 | opGT, /* 12 greater than */ 516 | opLIT, /* 13 load literal */ 517 | opVAR, /* 14 load a variable value */ 518 | opGETP, /* 15 get the value of an object property */ 519 | opSETP, /* 16 set the value of an object property */ 520 | opSET, /* 17 set the value of a variable */ 521 | opPRINT, /* 18 print messages */ 522 | opTERPRI, /* 19 terminate the print line */ 523 | opPNUMBER, /* 1A print a number */ 524 | opEXIT, /* 1B exit the program */ 525 | opRETURN, /* 1C return from interpreter */ 526 | opCALL, /* 1D call a function */ 527 | opSVAR, /* 1E short load a variable */ 528 | opSSET, /* 1F short set a variable */ 529 | opSPLIT, /* 20 short load a positive literal */ 530 | opSNLIT, /* 21 short load a negative literal */ 531 | opYORN, /* 22 yes-or-no predicate */ 532 | opSAVE, /* 23 save data structures */ 533 | opRESTORE, /* 24 restore data structures */ 534 | opARG, /* 25 load an argument value */ 535 | opASET, /* 26 set an argument value */ 536 | opTMP, /* 27 load a temporary variable value */ 537 | opTSET, /* 28 set a temporary variable */ 538 | opTSPACE, /* 29 allocate temporary variable space */ 539 | opCLASS, /* 2A get the class of an object */ 540 | opMATCH, /* 2B match a noun phrase with an object */ 541 | opPNOUN, /* 2C print a noun phrase */ 542 | opRESTART, /* 2D restart the current game */ 543 | badopcode, /* 2E (undefined) */ 544 | badopcode, /* 2F (undefined) */ 545 | opSEND, /* 30 send a message to an object */ 546 | opCATCH, /* 31 catch a signal (establish a catch frame) */ 547 | opCDONE, /* 32 remove a catch frame from the stack */ 548 | opTHROW, /* 33 throw a signal */ 549 | opPARSE, /* 34 parse the next input line */ 550 | opNEXT, /* 35 get the next command */ 551 | badopcode, /* 36 (undefined) */ 552 | badopcode, /* 37 (undefined) */ 553 | badopcode, /* 38 (undefined) */ 554 | badopcode, /* 39 (undefined) */ 555 | badopcode, /* 3A (undefined) */ 556 | badopcode, /* 3B (undefined) */ 557 | badopcode, /* 3C (undefined) */ 558 | badopcode, /* 3D (undefined) */ 559 | badopcode, /* 3E (undefined) */ 560 | badopcode, /* 3F (undefined) */ 561 | 562 | opXVAR, /* 40 extra short load a variable */ 563 | opXVAR, /* 41 extra short load a variable */ 564 | opXVAR, /* 42 extra short load a variable */ 565 | opXVAR, /* 43 extra short load a variable */ 566 | opXVAR, /* 44 extra short load a variable */ 567 | opXVAR, /* 45 extra short load a variable */ 568 | opXVAR, /* 46 extra short load a variable */ 569 | opXVAR, /* 47 extra short load a variable */ 570 | opXVAR, /* 48 extra short load a variable */ 571 | opXVAR, /* 49 extra short load a variable */ 572 | opXVAR, /* 4A extra short load a variable */ 573 | opXVAR, /* 4B extra short load a variable */ 574 | opXVAR, /* 4C extra short load a variable */ 575 | opXVAR, /* 4D extra short load a variable */ 576 | opXVAR, /* 4E extra short load a variable */ 577 | opXVAR, /* 4F extra short load a variable */ 578 | opXVAR, /* 50 extra short load a variable */ 579 | opXVAR, /* 51 extra short load a variable */ 580 | opXVAR, /* 52 extra short load a variable */ 581 | opXVAR, /* 53 extra short load a variable */ 582 | opXVAR, /* 54 extra short load a variable */ 583 | opXVAR, /* 55 extra short load a variable */ 584 | opXVAR, /* 56 extra short load a variable */ 585 | opXVAR, /* 57 extra short load a variable */ 586 | opXVAR, /* 58 extra short load a variable */ 587 | opXVAR, /* 59 extra short load a variable */ 588 | opXVAR, /* 5A extra short load a variable */ 589 | opXVAR, /* 5B extra short load a variable */ 590 | opXVAR, /* 5C extra short load a variable */ 591 | opXVAR, /* 5D extra short load a variable */ 592 | opXVAR, /* 5E extra short load a variable */ 593 | opXVAR, /* 5F extra short load a variable */ 594 | 595 | opXSET, /* 60 extra short set a variable */ 596 | opXSET, /* 61 extra short set a variable */ 597 | opXSET, /* 62 extra short set a variable */ 598 | opXSET, /* 63 extra short set a variable */ 599 | opXSET, /* 64 extra short set a variable */ 600 | opXSET, /* 65 extra short set a variable */ 601 | opXSET, /* 66 extra short set a variable */ 602 | opXSET, /* 67 extra short set a variable */ 603 | opXSET, /* 68 extra short set a variable */ 604 | opXSET, /* 69 extra short set a variable */ 605 | opXSET, /* 6A extra short set a variable */ 606 | opXSET, /* 6B extra short set a variable */ 607 | opXSET, /* 6C extra short set a variable */ 608 | opXSET, /* 6D extra short set a variable */ 609 | opXSET, /* 6E extra short set a variable */ 610 | opXSET, /* 6F extra short set a variable */ 611 | opXSET, /* 70 extra short set a variable */ 612 | opXSET, /* 71 extra short set a variable */ 613 | opXSET, /* 72 extra short set a variable */ 614 | opXSET, /* 73 extra short set a variable */ 615 | opXSET, /* 74 extra short set a variable */ 616 | opXSET, /* 75 extra short set a variable */ 617 | opXSET, /* 76 extra short set a variable */ 618 | opXSET, /* 77 extra short set a variable */ 619 | opXSET, /* 78 extra short set a variable */ 620 | opXSET, /* 79 extra short set a variable */ 621 | opXSET, /* 7A extra short set a variable */ 622 | opXSET, /* 7B extra short set a variable */ 623 | opXSET, /* 7C extra short set a variable */ 624 | opXSET, /* 7D extra short set a variable */ 625 | opXSET, /* 7E extra short set a variable */ 626 | opXSET, /* 7F extra short set a variable */ 627 | 628 | opXPLIT, /* 80 extra short load a positive literal */ 629 | opXPLIT, /* 81 extra short load a positive literal */ 630 | opXPLIT, /* 82 extra short load a positive literal */ 631 | opXPLIT, /* 83 extra short load a positive literal */ 632 | opXPLIT, /* 84 extra short load a positive literal */ 633 | opXPLIT, /* 85 extra short load a positive literal */ 634 | opXPLIT, /* 86 extra short load a positive literal */ 635 | opXPLIT, /* 87 extra short load a positive literal */ 636 | opXPLIT, /* 88 extra short load a positive literal */ 637 | opXPLIT, /* 89 extra short load a positive literal */ 638 | opXPLIT, /* 8A extra short load a positive literal */ 639 | opXPLIT, /* 8B extra short load a positive literal */ 640 | opXPLIT, /* 8C extra short load a positive literal */ 641 | opXPLIT, /* 8D extra short load a positive literal */ 642 | opXPLIT, /* 8E extra short load a positive literal */ 643 | opXPLIT, /* 8F extra short load a positive literal */ 644 | opXPLIT, /* 90 extra short load a positive literal */ 645 | opXPLIT, /* 91 extra short load a positive literal */ 646 | opXPLIT, /* 92 extra short load a positive literal */ 647 | opXPLIT, /* 93 extra short load a positive literal */ 648 | opXPLIT, /* 94 extra short load a positive literal */ 649 | opXPLIT, /* 95 extra short load a positive literal */ 650 | opXPLIT, /* 96 extra short load a positive literal */ 651 | opXPLIT, /* 97 extra short load a positive literal */ 652 | opXPLIT, /* 98 extra short load a positive literal */ 653 | opXPLIT, /* 99 extra short load a positive literal */ 654 | opXPLIT, /* 9A extra short load a positive literal */ 655 | opXPLIT, /* 9B extra short load a positive literal */ 656 | opXPLIT, /* 9C extra short load a positive literal */ 657 | opXPLIT, /* 9D extra short load a positive literal */ 658 | opXPLIT, /* 9E extra short load a positive literal */ 659 | opXPLIT, /* 9F extra short load a positive literal */ 660 | opXPLIT, /* A0 extra short load a positive literal */ 661 | opXPLIT, /* A1 extra short load a positive literal */ 662 | opXPLIT, /* A2 extra short load a positive literal */ 663 | opXPLIT, /* A3 extra short load a positive literal */ 664 | opXPLIT, /* A4 extra short load a positive literal */ 665 | opXPLIT, /* A5 extra short load a positive literal */ 666 | opXPLIT, /* A6 extra short load a positive literal */ 667 | opXPLIT, /* A7 extra short load a positive literal */ 668 | opXPLIT, /* A8 extra short load a positive literal */ 669 | opXPLIT, /* A9 extra short load a positive literal */ 670 | opXPLIT, /* AA extra short load a positive literal */ 671 | opXPLIT, /* AB extra short load a positive literal */ 672 | opXPLIT, /* AC extra short load a positive literal */ 673 | opXPLIT, /* AD extra short load a positive literal */ 674 | opXPLIT, /* AE extra short load a positive literal */ 675 | opXPLIT, /* AF extra short load a positive literal */ 676 | opXPLIT, /* B0 extra short load a positive literal */ 677 | opXPLIT, /* B1 extra short load a positive literal */ 678 | opXPLIT, /* B2 extra short load a positive literal */ 679 | opXPLIT, /* B3 extra short load a positive literal */ 680 | opXPLIT, /* B4 extra short load a positive literal */ 681 | opXPLIT, /* B5 extra short load a positive literal */ 682 | opXPLIT, /* B6 extra short load a positive literal */ 683 | opXPLIT, /* B7 extra short load a positive literal */ 684 | opXPLIT, /* B8 extra short load a positive literal */ 685 | opXPLIT, /* B9 extra short load a positive literal */ 686 | opXPLIT, /* BA extra short load a positive literal */ 687 | opXPLIT, /* BB extra short load a positive literal */ 688 | opXPLIT, /* BC extra short load a positive literal */ 689 | opXPLIT, /* BD extra short load a positive literal */ 690 | opXPLIT, /* BE extra short load a positive literal */ 691 | opXPLIT, /* BF extra short load a positive literal */ 692 | 693 | opXNLIT, /* C0 extra short load a negative literal */ 694 | opXNLIT, /* C1 extra short load a negative literal */ 695 | opXNLIT, /* C2 extra short load a negative literal */ 696 | opXNLIT, /* C3 extra short load a negative literal */ 697 | opXNLIT, /* C4 extra short load a negative literal */ 698 | opXNLIT, /* C5 extra short load a negative literal */ 699 | opXNLIT, /* C6 extra short load a negative literal */ 700 | opXNLIT, /* C7 extra short load a negative literal */ 701 | opXNLIT, /* C8 extra short load a negative literal */ 702 | opXNLIT, /* C9 extra short load a negative literal */ 703 | opXNLIT, /* CA extra short load a negative literal */ 704 | opXNLIT, /* CB extra short load a negative literal */ 705 | opXNLIT, /* CC extra short load a negative literal */ 706 | opXNLIT, /* CD extra short load a negative literal */ 707 | opXNLIT, /* CE extra short load a negative literal */ 708 | opXNLIT, /* CF extra short load a negative literal */ 709 | opXNLIT, /* D0 extra short load a negative literal */ 710 | opXNLIT, /* D1 extra short load a negative literal */ 711 | opXNLIT, /* D2 extra short load a negative literal */ 712 | opXNLIT, /* D3 extra short load a negative literal */ 713 | opXNLIT, /* D4 extra short load a negative literal */ 714 | opXNLIT, /* D5 extra short load a negative literal */ 715 | opXNLIT, /* D6 extra short load a negative literal */ 716 | opXNLIT, /* D7 extra short load a negative literal */ 717 | opXNLIT, /* D8 extra short load a negative literal */ 718 | opXNLIT, /* D9 extra short load a negative literal */ 719 | opXNLIT, /* DA extra short load a negative literal */ 720 | opXNLIT, /* DB extra short load a negative literal */ 721 | opXNLIT, /* DC extra short load a negative literal */ 722 | opXNLIT, /* DD extra short load a negative literal */ 723 | opXNLIT, /* DE extra short load a negative literal */ 724 | opXNLIT, /* DF extra short load a negative literal */ 725 | opXNLIT, /* E0 extra short load a negative literal */ 726 | opXNLIT, /* E1 extra short load a negative literal */ 727 | opXNLIT, /* E2 extra short load a negative literal */ 728 | opXNLIT, /* E3 extra short load a negative literal */ 729 | opXNLIT, /* E4 extra short load a negative literal */ 730 | opXNLIT, /* E5 extra short load a negative literal */ 731 | opXNLIT, /* E6 extra short load a negative literal */ 732 | opXNLIT, /* E7 extra short load a negative literal */ 733 | opXNLIT, /* E8 extra short load a negative literal */ 734 | opXNLIT, /* E9 extra short load a negative literal */ 735 | opXNLIT, /* EA extra short load a negative literal */ 736 | opXNLIT, /* EB extra short load a negative literal */ 737 | opXNLIT, /* EC extra short load a negative literal */ 738 | opXNLIT, /* ED extra short load a negative literal */ 739 | opXNLIT, /* EE extra short load a negative literal */ 740 | opXNLIT, /* EF extra short load a negative literal */ 741 | opXNLIT, /* F0 extra short load a negative literal */ 742 | opXNLIT, /* F1 extra short load a negative literal */ 743 | opXNLIT, /* F2 extra short load a negative literal */ 744 | opXNLIT, /* F3 extra short load a negative literal */ 745 | opXNLIT, /* F4 extra short load a negative literal */ 746 | opXNLIT, /* F5 extra short load a negative literal */ 747 | opXNLIT, /* F6 extra short load a negative literal */ 748 | opXNLIT, /* F7 extra short load a negative literal */ 749 | opXNLIT, /* F8 extra short load a negative literal */ 750 | opXNLIT, /* F9 extra short load a negative literal */ 751 | opXNLIT, /* FA extra short load a negative literal */ 752 | opXNLIT, /* FB extra short load a negative literal */ 753 | opXNLIT, /* FC extra short load a negative literal */ 754 | opXNLIT, /* FD extra short load a negative literal */ 755 | opXNLIT, /* FE extra short load a negative literal */ 756 | opXNLIT /* FF extra short load a negative literal */ 757 | }; 758 | -------------------------------------------------------------------------------- /advexp.c: -------------------------------------------------------------------------------- 1 | /* advexp.c - expression compiler for adventure games */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include "advavl.h" 9 | #include "advcom.h" 10 | #include "advdbs.h" 11 | 12 | /* external routines */ 13 | extern SYMBOL *sfind(); 14 | 15 | /* external variables */ 16 | extern SYMBOL *symbols,*exfunctions; 17 | extern char t_token[]; 18 | extern int t_value; 19 | extern int curobj; 20 | extern char *code; 21 | extern int cptr; 22 | 23 | /* opcode tables */ 24 | static struct { char *nt_name; int nt_code,nt_args; } *nptr,ntab[] = { 25 | { "not", OP_NOT, 1 }, 26 | { "+", OP_ADD, 2 }, 27 | { "-", OP_SUB, 2 }, 28 | { "*", OP_MUL, 2 }, 29 | { "/", OP_DIV, 2 }, 30 | { "%", OP_REM, 2 }, 31 | { "&", OP_BAND, 2 }, 32 | { "|", OP_BOR, 2 }, 33 | { "~", OP_BNOT, 1 }, 34 | { "<", OP_LT, 2 }, 35 | { "=", OP_EQ, 2 }, 36 | { ">", OP_GT, 2 }, 37 | { "getp", OP_GETP, 2 }, 38 | { "setp", OP_SETP, 3 }, 39 | { "class", OP_CLASS, 1 }, 40 | { "match", OP_MATCH, 2 }, 41 | { "print", OP_PRINT, 1 }, 42 | { "print-number", OP_PNUMBER, 1 }, 43 | { "print-noun", OP_PNOUN, 1 }, 44 | { "terpri", OP_TERPRI, 0 }, 45 | { "exit", OP_EXIT, 0 }, 46 | { "save", OP_SAVE, 0 }, 47 | { "restore", OP_RESTORE, 0 }, 48 | { "restart", OP_RESTART, 0 }, 49 | { "yes-or-no", OP_YORN, 0 }, 50 | { "throw", OP_THROW, 2 }, 51 | { "parse", OP_PARSE, 0 }, 52 | { "next", OP_NEXT, 0 }, 53 | { 0, 0, 0 } 54 | }; 55 | static struct { char *ft_name; void (*ft_fcn)(void); } *fptr,ftab[] = { 56 | { "cond", do_cond }, 57 | { "and", do_and }, 58 | { "or", do_or }, 59 | { "if", do_if }, 60 | { "while", do_while }, 61 | { "progn", do_progn }, 62 | { "setq", do_setq }, 63 | { "return", do_return }, 64 | { "send", do_send }, 65 | { "send-super", do_sndsuper }, 66 | { "catch", do_catch }, 67 | { 0, 0 } 68 | }; 69 | 70 | /* do_expr - compile a subexpression */ 71 | void do_expr(void) 72 | { 73 | int tkn; 74 | 75 | switch (token()) { 76 | case T_OPEN: 77 | switch (tkn = token()) { 78 | case T_IDENTIFIER: 79 | if (in_ntab() || in_ftab() || in_etab()) 80 | break; 81 | default: 82 | stoken(tkn); 83 | do_call(); 84 | } 85 | break; 86 | case T_NUMBER: 87 | do_literal(); 88 | break; 89 | case T_STRING: 90 | do_literal(); 91 | break; 92 | case T_IDENTIFIER: 93 | do_identifier(); 94 | break; 95 | default: 96 | error("Expecting expression"); 97 | } 98 | } 99 | 100 | /* in_ntab - check for a function in ntab */ 101 | int in_ntab(void) 102 | { 103 | for (nptr = ntab; nptr->nt_name; ++nptr) 104 | if (strcmp(t_token,nptr->nt_name) == 0) { 105 | do_nary(nptr->nt_code,nptr->nt_args); 106 | return (TRUE); 107 | } 108 | return (FALSE); 109 | } 110 | 111 | /* in_ftab - check for a function in ftab */ 112 | int in_ftab(void) 113 | { 114 | for (fptr = ftab; fptr->ft_name; ++fptr) 115 | if (strcmp(t_token,fptr->ft_name) == 0) { 116 | (*fptr->ft_fcn)(); 117 | return (TRUE); 118 | } 119 | return (FALSE); 120 | } 121 | 122 | /* in_etab - compile an extended function call */ 123 | int in_etab(void) 124 | { 125 | SYMBOL *fcn; 126 | int tkn,n; 127 | 128 | /* find the extended function definition */ 129 | if ((fcn = sfind(exfunctions,t_token)) == NULL) 130 | return (FALSE); 131 | 132 | /* compile each argument expression */ 133 | for (n = 0; (tkn = token()) != T_CLOSE; ++n) { 134 | stoken(tkn); 135 | putcbyte(OP_PUSH); 136 | do_expr(); 137 | } 138 | 139 | /* compile the extended function call */ 140 | putcbyte(OP_EXTEND); 141 | putcbyte(fcn->s_value); 142 | putcbyte(n); 143 | 144 | /* return successfully */ 145 | return (TRUE); 146 | } 147 | 148 | /* do_cond - compile the (COND ... ) expression */ 149 | void do_cond(void) 150 | { 151 | int tkn,nxt,end; 152 | 153 | /* initialize the fixup chain */ 154 | end = NIL; 155 | 156 | /* compile each COND clause */ 157 | while ((tkn = token()) != T_CLOSE) { 158 | require(tkn,T_OPEN); 159 | do_expr(); 160 | putcbyte(OP_BRF); 161 | nxt = putcword(NIL); 162 | while ((tkn = token()) != T_CLOSE) { 163 | stoken(tkn); 164 | do_expr(); 165 | } 166 | putcbyte(OP_BR); 167 | end = putcword(end); 168 | fixup(nxt,cptr); 169 | } 170 | 171 | /* fixup references to the end of statement */ 172 | if (end) 173 | fixup(end,cptr); 174 | else 175 | putcbyte(OP_NIL); 176 | } 177 | 178 | /* do_and - compile the (AND ... ) expression */ 179 | void do_and(void) 180 | { 181 | int tkn,end; 182 | 183 | /* initialize the fixup chain */ 184 | end = NIL; 185 | 186 | /* compile each expression */ 187 | while ((tkn = token()) != T_CLOSE) { 188 | stoken(tkn); 189 | do_expr(); 190 | putcbyte(OP_BRF); 191 | end = putcword(end); 192 | } 193 | 194 | /* fixup references to the end of statement */ 195 | if (end) 196 | fixup(end,cptr); 197 | else 198 | putcbyte(OP_NIL); 199 | } 200 | 201 | /* do_or - compile the (OR ... ) expression */ 202 | void do_or(void) 203 | { 204 | int tkn,end; 205 | 206 | /* initialize the fixup chain */ 207 | end = NIL; 208 | 209 | /* compile each expression */ 210 | while ((tkn = token()) != T_CLOSE) { 211 | stoken(tkn); 212 | do_expr(); 213 | putcbyte(OP_BRT); 214 | end = putcword(end); 215 | } 216 | 217 | /* fixup references to the end of statement */ 218 | if (end) 219 | fixup(end,cptr); 220 | else 221 | putcbyte(OP_T); 222 | } 223 | 224 | /* do_if - compile the (IF ... ) expression */ 225 | void do_if(void) 226 | { 227 | int tkn,nxt,end; 228 | 229 | /* compile the test expression */ 230 | do_expr(); 231 | 232 | /* skip around the 'then' clause if the expression is false */ 233 | putcbyte(OP_BRF); 234 | nxt = putcword(NIL); 235 | 236 | /* compile the 'then' clause */ 237 | do_expr(); 238 | 239 | /* compile the 'else' clause */ 240 | if ((tkn = token()) != T_CLOSE) { 241 | putcbyte(OP_BR); 242 | end = putcword(NIL); 243 | fixup(nxt,cptr); 244 | stoken(tkn); 245 | do_expr(); 246 | frequire(T_CLOSE); 247 | nxt = end; 248 | } 249 | 250 | /* handle the end of the statement */ 251 | fixup(nxt,cptr); 252 | } 253 | 254 | /* do_while - compile the (WHILE ... ) expression */ 255 | void do_while(void) 256 | { 257 | int tkn,nxt,end; 258 | 259 | /* compile the test expression */ 260 | nxt = cptr; 261 | do_expr(); 262 | 263 | /* skip around the 'then' clause if the expression is false */ 264 | putcbyte(OP_BRF); 265 | end = putcword(NIL); 266 | 267 | /* compile the loop body */ 268 | while ((tkn = token()) != T_CLOSE) { 269 | stoken(tkn); 270 | do_expr(); 271 | } 272 | 273 | /* branch back to the start of the loop */ 274 | putcbyte(OP_BR); 275 | putcword(nxt); 276 | 277 | /* handle the end of the statement */ 278 | fixup(end,cptr); 279 | } 280 | 281 | /* do_progn - compile the (PROGN ... ) expression */ 282 | void do_progn(void) 283 | { 284 | int tkn,n; 285 | 286 | /* compile each expression */ 287 | for (n = 0; (tkn = token()) != T_CLOSE; ++n) { 288 | stoken(tkn); 289 | do_expr(); 290 | } 291 | 292 | /* check for an empty statement list */ 293 | if (n == 0) 294 | putcbyte(OP_NIL); 295 | } 296 | 297 | /* do_setq - compile the (SETQ v x) expression */ 298 | void do_setq(void) 299 | { 300 | char name[TKNSIZE+1]; 301 | int n; 302 | 303 | /* get the symbol name */ 304 | frequire(T_IDENTIFIER); 305 | strcpy(name,t_token); 306 | 307 | /* compile the value expression */ 308 | do_expr(); 309 | 310 | /* check for this being a local symbol */ 311 | if ((n = findarg(name)) >= 0) 312 | cd_setargument(n); 313 | else if ((n = findtmp(name)) >= 0) 314 | cd_settemporary(n); 315 | else { 316 | n = venter(name); 317 | cd_setvariable(n); 318 | } 319 | frequire(T_CLOSE); 320 | } 321 | 322 | /* do_return - handle the (RETURN [expr]) expression */ 323 | void do_return(void) 324 | { 325 | int tkn; 326 | 327 | /* look for a result expression */ 328 | if ((tkn = token()) != T_CLOSE) { 329 | stoken(tkn); 330 | do_expr(); 331 | frequire(T_CLOSE); 332 | } 333 | 334 | /* otherwise, default the result to nil */ 335 | else 336 | putcbyte(OP_NIL); 337 | 338 | /* insert the return opcode */ 339 | putcbyte(OP_RETURN); 340 | } 341 | 342 | /* do_send - handle the (SEND obj msg [expr]...) expression */ 343 | void do_send(void) 344 | { 345 | /* start searching for the method at the object itself */ 346 | putcbyte(OP_NIL); 347 | 348 | /* compile the object expression */ 349 | putcbyte(OP_PUSH); 350 | do_expr(); 351 | 352 | /* call the general message sender */ 353 | sender(); 354 | } 355 | 356 | /* do_sndsuper - handle the (SEND-SUPER msg [expr]...) expression */ 357 | void do_sndsuper(void) 358 | { 359 | /* start searching for the method at the current class object */ 360 | code_literal(curobj); 361 | 362 | /* pass the message to "self" */ 363 | putcbyte(OP_PUSH); 364 | code_argument(findarg("self")); 365 | 366 | /* call the general message sender */ 367 | sender(); 368 | } 369 | 370 | /* sender - compile an expression to send a message to an object */ 371 | void sender(void) 372 | { 373 | int tkn,n; 374 | 375 | /* compile the selector expression */ 376 | putcbyte(OP_PUSH); 377 | do_expr(); 378 | 379 | /* compile each argument expression */ 380 | for (n = 2; (tkn = token()) != T_CLOSE; ++n) { 381 | stoken(tkn); 382 | putcbyte(OP_PUSH); 383 | do_expr(); 384 | } 385 | putcbyte(OP_SEND); 386 | putcbyte(n); 387 | } 388 | 389 | /* do_catch - comple a (CATCH ...) expression */ 390 | void do_catch(void) 391 | { 392 | int tkn,end; 393 | 394 | /* compile the catch tag */ 395 | do_expr(); 396 | 397 | /* setup the catch frame */ 398 | putcbyte(OP_CATCH); 399 | end = putcword(NIL); 400 | 401 | /* compile the body of the catch */ 402 | while ((tkn = token()) != T_CLOSE) { 403 | stoken(tkn); 404 | do_expr(); 405 | } 406 | 407 | /* remove the catch frame */ 408 | putcbyte(OP_CDONE); 409 | 410 | /* handle the end of the statement */ 411 | fixup(end,cptr); 412 | } 413 | 414 | /* do_call - compile a function call */ 415 | void do_call(void) 416 | { 417 | int tkn,n; 418 | 419 | /* compile the function itself */ 420 | do_expr(); 421 | 422 | /* compile each argument expression */ 423 | for (n = 0; (tkn = token()) != T_CLOSE; ++n) { 424 | stoken(tkn); 425 | putcbyte(OP_PUSH); 426 | do_expr(); 427 | } 428 | putcbyte(OP_CALL); 429 | putcbyte(n); 430 | } 431 | 432 | /* do_nary - compile nary operator expressions */ 433 | void do_nary(int op,int n) 434 | { 435 | while (n--) { 436 | do_expr(); 437 | if (n) putcbyte(OP_PUSH); 438 | } 439 | putcbyte(op); 440 | frequire(T_CLOSE); 441 | } 442 | 443 | /* do_literal - compile a literal */ 444 | void do_literal(void) 445 | { 446 | code_literal(t_value); 447 | } 448 | 449 | /* do_identifier - compile an identifier */ 450 | void do_identifier(void) 451 | { 452 | SYMBOL *sym; 453 | int n; 454 | 455 | if (match("t")) 456 | putcbyte(OP_T); 457 | else if (match("nil")) 458 | putcbyte(OP_NIL); 459 | else if ((n = findarg(t_token)) >= 0) 460 | code_argument(n); 461 | else if ((n = findtmp(t_token)) >= 0) 462 | code_temporary(n); 463 | else if ((sym = sfind(symbols,t_token)) != 0) { 464 | if (sym->s_type == ST_VARIABLE) 465 | code_variable(sym->s_value); 466 | else 467 | code_literal(sym->s_value); 468 | } 469 | else 470 | code_literal(oenter(t_token)); 471 | } 472 | 473 | /* code_argument - compile an argument reference */ 474 | void code_argument(int n) 475 | { 476 | putcbyte(OP_ARG); 477 | putcbyte(n); 478 | } 479 | 480 | /* cd_setargument - compile a set argument reference */ 481 | void cd_setargument(int n) 482 | { 483 | putcbyte(OP_ASET); 484 | putcbyte(n); 485 | } 486 | 487 | /* code_temporary - compile an temporary reference */ 488 | void code_temporary(int n) 489 | { 490 | putcbyte(OP_TMP); 491 | putcbyte(n); 492 | } 493 | 494 | /* cd_settemporary - compile a set temporary reference */ 495 | void cd_settemporary(int n) 496 | { 497 | putcbyte(OP_TSET); 498 | putcbyte(n); 499 | } 500 | 501 | /* code_variable - compile a variable reference */ 502 | void code_variable(int n) 503 | { 504 | if (n < 32) 505 | putcbyte(OP_XVAR+n); 506 | else if (n < 256) 507 | { putcbyte(OP_SVAR); putcbyte(n); } 508 | else 509 | { putcbyte(OP_VAR); putcword(n); } 510 | } 511 | 512 | /* cd_setvariable - compile a set variable reference */ 513 | void cd_setvariable(int n) 514 | { 515 | if (n < 32) 516 | putcbyte(OP_XSET+n); 517 | else if (n < 256) 518 | { putcbyte(OP_SSET); putcbyte(n); } 519 | else 520 | { putcbyte(OP_SET); putcword(n); } 521 | } 522 | 523 | /* code_literal - compile a literal reference */ 524 | void code_literal(int n) 525 | { 526 | if (n >= 0 && n < 64) 527 | putcbyte(OP_XPLIT+n); 528 | else if (n < 0 && n > -64) 529 | putcbyte(OP_XNLIT-n); 530 | else if (n >= 64 && n < 256) 531 | { putcbyte(OP_SPLIT); putcbyte(n); } 532 | else if (n <= -64 && n > -256) 533 | { putcbyte(OP_SNLIT); putcbyte(-n); } 534 | else 535 | { putcbyte(OP_LIT); putcword(n); } 536 | } 537 | 538 | /* do_op - insert an opcode and look for closing paren */ 539 | void do_op(int op) 540 | { 541 | putcbyte(op); 542 | frequire(T_CLOSE); 543 | } 544 | 545 | /* putcbyte - put a code byte into data space */ 546 | int putcbyte(int b) 547 | { 548 | if (cptr < CMAX) 549 | code[cptr++] = b; 550 | else 551 | error("insufficient code space"); 552 | return (cptr-1); 553 | } 554 | 555 | /* putcword - put a code word into data space */ 556 | int putcword(int w) 557 | { 558 | putcbyte(w); 559 | putcbyte(w >> 8); 560 | return (cptr-2); 561 | } 562 | 563 | /* fixup - fixup a reference chain */ 564 | void fixup(int chn,int val) 565 | { 566 | int hval,nxt; 567 | 568 | /* store the value into each location in the chain */ 569 | for (hval = val >> 8; chn != NIL; chn = nxt) { 570 | if (chn < 0 || chn > CMAX-2) 571 | return; 572 | nxt = (code[chn] & 0xFF) | (code[chn+1] << 8); 573 | code[chn] = val; 574 | code[chn+1] = hval; 575 | } 576 | } 577 | 578 | 579 | -------------------------------------------------------------------------------- /advfcn.c: -------------------------------------------------------------------------------- 1 | /* advfcn.c - functions for the adventure compiler */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advavl.h" 10 | #include "advcom.h" 11 | #include "advdbs.h" 12 | 13 | /* external variables */ 14 | extern char aname[]; /* adventure name */ 15 | extern int aversion; /* adventure version number */ 16 | extern int cptr; /* code space pointer */ 17 | extern int objbuf[]; /* object staging buffer */ 18 | extern int nprops; /* number of properties in current object */ 19 | extern int t_value; /* token value */ 20 | extern char t_token[]; /* token string */ 21 | extern char *t_names[]; /* token names */ 22 | extern int otable[]; /* object table */ 23 | extern int curobj; /* current object number */ 24 | extern int curact; /* current action offset */ 25 | extern int atable[],acnt; /* action table and count */ 26 | extern SYMBOL *exfunctions; /* extended function list */ 27 | extern ARGUMENT *arguments; /* function argument list */ 28 | extern ARGUMENT *temporaries; /* function temporary variable list */ 29 | extern int def_flag; /* default action flag value */ 30 | extern int def_mask; /* default action mask value */ 31 | 32 | /* do_adventure - handle the statement */ 33 | void do_adventure(void) 34 | { 35 | /* get the adventure name */ 36 | frequire(T_IDENTIFIER); 37 | strncpy(aname,t_token,18); 38 | aname[18] = 0; 39 | 40 | /* get the adventure version number */ 41 | frequire(T_NUMBER); 42 | aversion = t_value; 43 | 44 | /* check for the closing paren */ 45 | frequire(T_CLOSE); 46 | } 47 | 48 | /* do_word - enter words of a particular type */ 49 | void do_word(int type) 50 | { 51 | int tkn; 52 | 53 | while ((tkn = token()) == T_IDENTIFIER) 54 | add_word(t_token,type); 55 | require(tkn,T_CLOSE); 56 | } 57 | 58 | /* do_synonym - handle the statement */ 59 | void do_synonym(void) 60 | { 61 | int tkn,wrd; 62 | 63 | frequire(T_IDENTIFIER); 64 | wrd = add_word(t_token,WT_UNKNOWN); 65 | while ((tkn = token()) == T_IDENTIFIER) 66 | add_synonym(t_token,wrd); 67 | require(tkn,T_CLOSE); 68 | } 69 | 70 | /* do_define - handle the statement */ 71 | void do_define(void) 72 | { 73 | char name[TKNSIZE+1]; 74 | int tkn; 75 | 76 | if ((tkn = token()) == T_OPEN) 77 | do_function(); 78 | else { 79 | stoken(tkn); 80 | while ((tkn = token()) == T_IDENTIFIER) { 81 | strcpy(name,t_token); 82 | center(name,getvalue()); 83 | } 84 | require(tkn,T_CLOSE); 85 | } 86 | } 87 | 88 | /* do_extended - handle the statement */ 89 | void do_extended(void) 90 | { 91 | char name[TKNSIZE+1]; 92 | 93 | frequire(T_IDENTIFIER); 94 | strcpy(name,t_token); 95 | frequire(T_NUMBER); 96 | senter(&exfunctions,name,0,t_value); 97 | frequire(T_CLOSE); 98 | } 99 | 100 | /* do_variable - handle the statement */ 101 | void do_variable(void) 102 | { 103 | int tkn; 104 | 105 | while ((tkn = token()) == T_IDENTIFIER) 106 | venter(t_token); 107 | require(tkn,T_CLOSE); 108 | } 109 | 110 | /* do_defproperty - handle the statement */ 111 | void do_defproperty(void) 112 | { 113 | int tkn; 114 | 115 | while ((tkn = token()) == T_IDENTIFIER) 116 | penter(t_token); 117 | require(tkn,T_CLOSE); 118 | } 119 | 120 | /* do_default - handle the statement */ 121 | void do_default(void) 122 | { 123 | int tkn; 124 | 125 | /* process statements until end of file */ 126 | while ((tkn = token()) == T_OPEN) { 127 | frequire(T_IDENTIFIER); 128 | if (match("actor")) 129 | do_dflag(A_ACTOR); 130 | else if (match("direct-object")) 131 | do_dflag(A_DOBJECT); 132 | else if (match("indirect-object")) 133 | do_dflag(A_IOBJECT); 134 | else 135 | error("Unknown default definition statement type"); 136 | } 137 | require(tkn,T_CLOSE); 138 | } 139 | 140 | /* do_dflag - handle ACTOR, DIRECT-OBJECT, and INDIRECT-OBJECT statements */ 141 | void do_dflag(int flag) 142 | { 143 | int tkn; 144 | 145 | if ((tkn = token()) == T_IDENTIFIER) { 146 | if (match("required")) { 147 | def_flag |= flag; 148 | def_mask &= ~flag; 149 | } 150 | else if (match("forbidden")) { 151 | def_flag &= ~flag; 152 | def_mask &= ~flag; 153 | } 154 | else if (match("optional")) 155 | def_mask |= flag; 156 | else 157 | error("Expecting: REQUIRED, FORBIDDEN or OPTIONAL"); 158 | tkn = token(); 159 | } 160 | else { 161 | def_flag |= flag; 162 | def_mask &= ~flag; 163 | } 164 | require(tkn,T_CLOSE); 165 | } 166 | 167 | /* do_object - handle object (LOCATION,OBJECT,ACTOR) definitions */ 168 | int do_object(char *cname,int class) 169 | { 170 | int tkn,obj,obase,osize,i,p; 171 | 172 | printf("[ %s: ",cname); 173 | frequire(T_IDENTIFIER); 174 | printf("%s ]\n",t_token); 175 | obj = curobj = oenter(t_token); 176 | 177 | /* initialize the object */ 178 | objbuf[O_CLASS/2] = class; 179 | objbuf[O_NOUNS/2] = NIL; 180 | objbuf[O_ADJECTIVES/2] = NIL; 181 | objbuf[O_NPROPERTIES/2] = nprops = 0; 182 | 183 | /* copy the property list of the class object */ 184 | if (class) { 185 | obase = otable[class]; 186 | osize = getword(obase+O_NPROPERTIES); 187 | for (i = p = 0; i < osize; i++, p += 4) 188 | if ((getword(obase+O_PROPERTIES+p) & P_CLASS) == 0) 189 | addprop(getword(obase+O_PROPERTIES+p),0, 190 | getword(obase+O_PROPERTIES+p+2)); 191 | } 192 | 193 | /* process statements until end of file */ 194 | while ((tkn = token()) == T_OPEN) { 195 | frequire(T_IDENTIFIER); 196 | if (match("noun")) 197 | do_noun(); 198 | else if (match("adjective")) 199 | do_adjective(); 200 | else if (match("property")) 201 | do_property(0); 202 | else if (match("class-property")) 203 | do_property(P_CLASS); 204 | else if (match("method")) 205 | do_method(); 206 | else 207 | error("Unknown object definition statement type"); 208 | } 209 | require(tkn,T_CLOSE); 210 | 211 | /* copy the object to data memory */ 212 | osize = O_SIZE/2 + nprops*2; 213 | obase = dalloc(osize*2); 214 | for (i = p = 0; i < osize; i++, p += 2) 215 | putword(obase+p,objbuf[i]); 216 | otable[obj] = obase; 217 | curobj = NIL; 218 | 219 | /* return the object number */ 220 | return (obj); 221 | } 222 | 223 | /* do_noun - handle the statement */ 224 | void do_noun(void) 225 | { 226 | int tkn,new; 227 | 228 | while ((tkn = token()) == T_IDENTIFIER) { 229 | new = dalloc(L_SIZE); 230 | putword(new+L_DATA,add_word(t_token,WT_NOUN)); 231 | putword(new+L_NEXT,objbuf[O_NOUNS/2]); 232 | objbuf[O_NOUNS/2] = new; 233 | } 234 | require(tkn,T_CLOSE); 235 | } 236 | 237 | /* do_adjective - handle the statement */ 238 | void do_adjective(void) 239 | { 240 | int tkn,new; 241 | 242 | while ((tkn = token()) == T_IDENTIFIER) { 243 | new = dalloc(L_SIZE); 244 | putword(new+L_DATA,add_word(t_token,WT_ADJECTIVE)); 245 | putword(new+L_NEXT,objbuf[O_ADJECTIVES/2]); 246 | objbuf[O_ADJECTIVES/2] = new; 247 | } 248 | require(tkn,T_CLOSE); 249 | } 250 | 251 | /* do_property - handle the statement */ 252 | void do_property(int flags) 253 | { 254 | int tkn,name,value; 255 | 256 | while ((tkn = token()) == T_IDENTIFIER || tkn == T_NUMBER) { 257 | name = (tkn == T_IDENTIFIER ? penter(t_token) : t_value); 258 | value = getvalue(); 259 | setprop(name,flags,value); 260 | } 261 | require(tkn,T_CLOSE); 262 | } 263 | 264 | /* do_method - handle statement */ 265 | void do_method(void) 266 | { 267 | int tkn,name,tcnt; 268 | 269 | /* get the property name */ 270 | frequire(T_OPEN); 271 | frequire(T_IDENTIFIER); 272 | printf("[ method: %s ]\n",t_token); 273 | 274 | /* create a new property */ 275 | name = penter(t_token); 276 | 277 | /* allocate a new (anonymous) action */ 278 | if (acnt < AMAX) 279 | ++acnt; 280 | else 281 | error("too many actions"); 282 | 283 | /* store the action as the value of the property */ 284 | setprop(name,P_CLASS,acnt); 285 | 286 | /* initialize the action */ 287 | curact = atable[acnt] = dalloc(A_SIZE); 288 | putword(curact+A_VERBS,NIL); 289 | putword(curact+A_PREPOSITIONS,NIL); 290 | arguments = temporaries = NULL; 291 | tcnt = 0; 292 | 293 | /* enter the "self" argument */ 294 | addargument(&arguments,"self"); 295 | addargument(&arguments,"(dummy)"); 296 | 297 | /* get the argument list */ 298 | while ((tkn = token()) != T_CLOSE) { 299 | require(tkn,T_IDENTIFIER); 300 | if (match("&aux")) 301 | break; 302 | addargument(&arguments,t_token); 303 | } 304 | 305 | /* check for temporary variable definitions */ 306 | if (tkn == T_IDENTIFIER) 307 | while ((tkn = token()) != T_CLOSE) { 308 | require(tkn,T_IDENTIFIER); 309 | addargument(&temporaries,t_token); 310 | tcnt++; 311 | } 312 | 313 | /* store the code address */ 314 | putword(curact+A_CODE,cptr); 315 | 316 | /* allocate space for temporaries */ 317 | if (temporaries) { 318 | putcbyte(OP_TSPACE); 319 | putcbyte(tcnt); 320 | } 321 | 322 | /* compile the code */ 323 | do_code(NULL); 324 | 325 | /* free the argument and temporary variable symbol tables */ 326 | freelist(arguments); 327 | freelist(temporaries); 328 | arguments = temporaries = NULL; 329 | } 330 | 331 | /* setprop - set the value of a property */ 332 | void setprop(int prop,int flags,int value) 333 | { 334 | int i; 335 | 336 | /* look for the property */ 337 | for (i = 0; i < nprops; i++) 338 | if ((objbuf[O_PROPERTIES/2 + i*2] & ~P_CLASS) == prop) { 339 | objbuf[O_PROPERTIES/2 + i*2 + 1] = value; 340 | return; 341 | } 342 | addprop(prop,flags,value); 343 | } 344 | 345 | /* addprop - add a property to the current object's property list */ 346 | void addprop(int prop,int flags,int value) 347 | { 348 | if (nprops >= OPMAX) { 349 | printf("too many properties for this object\n"); 350 | return; 351 | } 352 | objbuf[O_PROPERTIES/2 + nprops*2] = prop|flags; 353 | objbuf[O_PROPERTIES/2 + nprops*2 + 1] = value; 354 | objbuf[O_NPROPERTIES/2] = ++nprops; 355 | } 356 | 357 | /* do_code - compile code for an expression */ 358 | int do_code(char *type) 359 | { 360 | int adr,tkn; 361 | 362 | if (type) printf("[ compiling %s code ]\n",type); 363 | adr = putcbyte(OP_PUSH); 364 | while ((tkn = token()) != T_CLOSE) { 365 | stoken(tkn); 366 | do_expr(); 367 | } 368 | putcbyte(OP_RETURN); 369 | return (adr); 370 | } 371 | 372 | /* do_action - handle statement */ 373 | void do_action(void) 374 | { 375 | int tkn,act; 376 | 377 | /* get the action name */ 378 | frequire(T_IDENTIFIER); 379 | printf("[ action: %s ]\n",t_token); 380 | 381 | /* create a new action */ 382 | act = aenter(t_token); 383 | if ((curact = atable[act]) == NIL) { 384 | curact = atable[act] = dalloc(A_SIZE); 385 | putword(curact+A_VERBS,NIL); 386 | putword(curact+A_PREPOSITIONS,NIL); 387 | putbyte(curact+A_FLAG,def_flag); 388 | putbyte(curact+A_MASK,def_mask); 389 | putword(curact+A_CODE,NIL); 390 | } 391 | 392 | /* process statements until end of file */ 393 | while ((tkn = token()) == T_OPEN) { 394 | frequire(T_IDENTIFIER); 395 | if (match("actor")) 396 | do_flag(A_ACTOR); 397 | else if (match("verb")) 398 | do_verb(); 399 | else if (match("direct-object")) 400 | do_flag(A_DOBJECT); 401 | else if (match("preposition")) 402 | do_preposition(); 403 | else if (match("indirect-object")) 404 | do_flag(A_IOBJECT); 405 | else if (match("code")) 406 | putword(curact+A_CODE,do_code(NULL)); 407 | else 408 | error("Unknown action definition statement type"); 409 | } 410 | require(tkn,T_CLOSE); 411 | } 412 | 413 | /* do_flag - handle ACTOR, DIRECT-OBJECT, and INDIRECT-OBJECT statements */ 414 | void do_flag(int flag) 415 | { 416 | int tkn; 417 | 418 | if ((tkn = token()) == T_IDENTIFIER) { 419 | if (match("required")) { 420 | putbyte(curact+A_FLAG,getbyte(curact+A_FLAG) | flag); 421 | putbyte(curact+A_MASK,getbyte(curact+A_MASK) & ~flag); 422 | } 423 | else if (match("forbidden")) { 424 | putbyte(curact+A_FLAG,getbyte(curact+A_FLAG) & ~flag); 425 | putbyte(curact+A_MASK,getbyte(curact+A_MASK) & ~flag); 426 | } 427 | else if (match("optional")) 428 | putbyte(curact+A_MASK,getbyte(curact+A_MASK) | flag); 429 | else 430 | error("Expecting: REQUIRED, FORBIDDEN or OPTIONAL"); 431 | tkn = token(); 432 | } 433 | else { 434 | putbyte(curact+A_FLAG,getbyte(curact+A_FLAG) | flag); 435 | putbyte(curact+A_MASK,getbyte(curact+A_MASK) & ~flag); 436 | } 437 | require(tkn,T_CLOSE); 438 | } 439 | 440 | /* do_verb - handle the statement */ 441 | void do_verb(void) 442 | { 443 | int tkn,new,lst; 444 | 445 | while ((tkn = token()) == T_IDENTIFIER || tkn == T_OPEN) { 446 | new = dalloc(L_SIZE); 447 | putword(new+L_NEXT,getword(curact+A_VERBS)); 448 | putword(curact+A_VERBS,new); 449 | lst = dalloc(L_SIZE); 450 | putword(lst+L_NEXT,NIL); 451 | putword(new+L_DATA,lst); 452 | if (tkn == T_IDENTIFIER) 453 | putword(lst+L_DATA,add_word(t_token,WT_VERB)); 454 | else { 455 | if ((tkn = token()) == T_IDENTIFIER) 456 | putword(lst+L_DATA,add_word(t_token,WT_VERB)); 457 | else 458 | error("Expecting verb"); 459 | while ((tkn = token()) == T_IDENTIFIER) { 460 | new = dalloc(L_SIZE); 461 | putword(new+L_DATA,add_word(t_token,WT_UNKNOWN)); 462 | putword(new+L_NEXT,NIL); 463 | putword(lst+L_NEXT,new); 464 | lst = new; 465 | } 466 | require(tkn,T_CLOSE); 467 | } 468 | } 469 | require(tkn,T_CLOSE); 470 | } 471 | 472 | /* do_preposition - handle the statement */ 473 | void do_preposition(void) 474 | { 475 | int tkn,new; 476 | 477 | while ((tkn = token()) == T_IDENTIFIER) { 478 | new = dalloc(L_SIZE); 479 | putword(new+L_DATA,add_word(t_token,WT_PREPOSITION)); 480 | putword(new+L_NEXT,getword(curact+A_PREPOSITIONS)); 481 | putword(curact+A_PREPOSITIONS,new); 482 | } 483 | require(tkn,T_CLOSE); 484 | } 485 | 486 | /* do_function - handle statement */ 487 | void do_function(void) 488 | { 489 | int tkn,act,tcnt; 490 | 491 | /* get the function name */ 492 | frequire(T_IDENTIFIER); 493 | printf("[ function: %s ]\n",t_token); 494 | 495 | /* create a new action */ 496 | act = aenter(t_token); 497 | 498 | /* initialize the action */ 499 | if ((curact = atable[act]) == NIL) { 500 | curact = atable[act] = dalloc(A_SIZE); 501 | putword(curact+A_VERBS,NIL); 502 | putword(curact+A_PREPOSITIONS,NIL); 503 | putbyte(curact+A_FLAG,def_flag); 504 | putbyte(curact+A_MASK,def_mask); 505 | putword(curact+A_CODE,NIL); 506 | } 507 | 508 | /* setup the symbol tables */ 509 | arguments = temporaries = NULL; 510 | tcnt = 0; 511 | 512 | /* get the argument list */ 513 | while ((tkn = token()) != T_CLOSE) { 514 | require(tkn,T_IDENTIFIER); 515 | if (match("&aux")) 516 | break; 517 | addargument(&arguments,t_token); 518 | } 519 | 520 | /* check for temporary variable definitions */ 521 | if (tkn == T_IDENTIFIER) 522 | while ((tkn = token()) != T_CLOSE) { 523 | require(tkn,T_IDENTIFIER); 524 | addargument(&temporaries,t_token); 525 | tcnt++; 526 | } 527 | 528 | /* store the code address */ 529 | putword(curact+A_CODE,cptr); 530 | 531 | /* allocate space for temporaries */ 532 | if (temporaries) { 533 | putcbyte(OP_TSPACE); 534 | putcbyte(tcnt); 535 | } 536 | 537 | /* compile the code */ 538 | do_code(NULL); 539 | 540 | /* free the argument and temporary variable symbol tables */ 541 | freelist(arguments); 542 | freelist(temporaries); 543 | arguments = temporaries = NULL; 544 | } 545 | 546 | /* addargument - add a formal argument */ 547 | void addargument(ARGUMENT **list,char *name) 548 | { 549 | ARGUMENT *arg; 550 | 551 | if ((arg = (ARGUMENT *)malloc(sizeof(ARGUMENT))) == NULL) 552 | fail("out of memory"); 553 | arg->arg_name = save(name); 554 | arg->arg_next = *list; 555 | *list = arg; 556 | } 557 | 558 | /* freelist - free a list of arguments or temporaries */ 559 | void freelist(ARGUMENT *arg) 560 | { 561 | ARGUMENT *nxt; 562 | 563 | while (arg) { 564 | nxt = arg->arg_next; 565 | free(arg->arg_name); 566 | free(arg); 567 | arg = nxt; 568 | } 569 | } 570 | 571 | /* findarg - find an argument offset */ 572 | int findarg(char *name) 573 | { 574 | ARGUMENT *arg; 575 | int n; 576 | 577 | for (n = 0, arg = arguments; arg; n++, arg = arg->arg_next) 578 | if (strcmp(name,arg->arg_name) == 0) 579 | return (n); 580 | return (-1); 581 | } 582 | 583 | /* findtmp - find a temporary variable offset */ 584 | int findtmp(char *name) 585 | { 586 | ARGUMENT *tmp; 587 | int n; 588 | 589 | for (n = 0, tmp = temporaries; tmp; n++, tmp = tmp->arg_next) 590 | if (strcmp(name,tmp->arg_name) == 0) 591 | return (n); 592 | return (-1); 593 | } 594 | 595 | 596 | -------------------------------------------------------------------------------- /adviio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "advint.h" 3 | 4 | int advgetc(void) 5 | { 6 | return getchar(); 7 | } 8 | 9 | void advwaitc(void) 10 | { 11 | getchar(); 12 | } 13 | 14 | int advputc(int ch,FILE *fp) 15 | { 16 | return putc(ch,fp); 17 | } 18 | 19 | int advsave(char *hdr,int hlen,char *save,int slen) 20 | { 21 | char fname[50],*p; 22 | FILE *fp; 23 | 24 | trm_str("File name? "); 25 | if (!(p = trm_get())) 26 | return 0; 27 | 28 | /* add the extension */ 29 | sprintf(fname,"%s.sav",p); 30 | 31 | /* create the data file */ 32 | if ((fp = fopen(fname,"wb")) == NULL) 33 | return 0; 34 | 35 | /* write the header */ 36 | if (fwrite(hdr,hlen,1,fp) != 1) { 37 | fclose(fp); 38 | return 0; 39 | } 40 | 41 | /* write the data */ 42 | if (fwrite(save,slen,1,fp) != 1) { 43 | fclose(fp); 44 | return 0; 45 | } 46 | 47 | /* close the file and return successfully */ 48 | fclose(fp); 49 | return 1; 50 | } 51 | 52 | int advrestore(char *hdr,int hlen,char *save,int slen) 53 | { 54 | char fname[50],hbuf[50],*p; 55 | FILE *fp; 56 | 57 | if (hlen > 50) 58 | error("save file header buffer too small"); 59 | 60 | trm_str("File name? "); 61 | if (!(p = trm_get())) 62 | return 0; 63 | 64 | /* add the extension */ 65 | sprintf(fname,"%s.sav",p); 66 | 67 | /* open the data file */ 68 | if ((fp = fopen(fname,"rb")) == NULL) 69 | return 0; 70 | 71 | /* read the header */ 72 | if (fread(hbuf,hlen,1,fp) != 1) { 73 | fclose(fp); 74 | return 0; 75 | } 76 | 77 | /* compare the headers */ 78 | for (p = hbuf; hlen--; ) 79 | if (*hdr++ != *p++) { 80 | trm_str("This save file does not match the adventure!\n"); 81 | return 0; 82 | } 83 | 84 | /* read the data */ 85 | if (fread(save,slen,1,fp) != 1) { 86 | fclose(fp); 87 | return 0; 88 | } 89 | 90 | /* close the file and return successfully */ 91 | fclose(fp); 92 | return 1; 93 | } 94 | -------------------------------------------------------------------------------- /advint.c: -------------------------------------------------------------------------------- 1 | /* advint.c - an interpreter for adventure games */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "advint.h" 11 | #include "advdbs.h" 12 | 13 | /* global variables */ 14 | jmp_buf restart; 15 | 16 | /* external variables */ 17 | extern int h_main; 18 | 19 | /* prototypes */ 20 | int main(int argc,char *argv[]); 21 | 22 | /* main - the main routine */ 23 | int main(int argc,char *argv[]) 24 | { 25 | char *fname,*lname; 26 | int rows,cols,i; 27 | 28 | printf("ADVINT v1.3 - Copyright (c) 1986, by David Betz\n"); 29 | fname = NULL; 30 | lname = NULL; 31 | rows = 24; 32 | cols = 80; 33 | 34 | /* parse the command line */ 35 | for (i = 1; i < argc; i++) 36 | if (argv[i][0] == '-') 37 | switch (argv[i][1]) { 38 | case 'r': 39 | case 'R': 40 | rows = atoi(&argv[i][2]); 41 | break; 42 | case 'c': 43 | case 'C': 44 | cols = atoi(&argv[i][2]); 45 | break; 46 | case 'l': 47 | case 'L': 48 | lname = &argv[i][2]; 49 | break; 50 | } 51 | else 52 | fname = argv[i]; 53 | if (fname == NULL) { 54 | printf("usage: advint [-r] [-c] [-l] \n"); 55 | exit(1); 56 | } 57 | 58 | /* initialize terminal i/o */ 59 | trm_init(rows,cols,lname); 60 | 61 | /* initialize the database */ 62 | db_init(fname); 63 | 64 | /* come here on restart */ 65 | setjmp(restart); 66 | 67 | /* play the game */ 68 | execute(h_main); 69 | 70 | /* finish with the terminal */ 71 | trm_done(); 72 | 73 | /* return successfully */ 74 | return 0; 75 | } 76 | 77 | /* error - print an error message and exit */ 78 | void error(char *msg,...) 79 | { 80 | char buf[100]; 81 | va_list ap; 82 | va_start(ap,msg); 83 | vsprintf(buf,msg,ap); 84 | trm_str(buf); 85 | trm_chr('\n'); 86 | exit(0); 87 | } 88 | -------------------------------------------------------------------------------- /advint.h: -------------------------------------------------------------------------------- 1 | /* advint.h - adventure interpreter definitions */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | /* useful definitions */ 11 | #define TRUE 1 12 | #define FALSE 0 13 | #define EOS '\0' 14 | 15 | /* program limits */ 16 | #define STKSIZE 500 17 | 18 | /* advdbs.c prototypes */ 19 | void db_init(char *name); 20 | int db_save(void); 21 | int db_restore(void); 22 | int db_restart(void); 23 | void complement(char *adr,int len); 24 | int findword(char *word); 25 | int wtype(int wrd); 26 | int match(int obj,int noun,int *adjs); 27 | int checkverb(int *verbs); 28 | int findaction(int *verbs,int preposition,int flag); 29 | int getp(int obj,int prop); 30 | int setp(int obj,int prop,int val); 31 | int findprop(int obj,int prop); 32 | int hasnoun(int obj,int noun); 33 | int hasadjective(int obj,int adjective); 34 | int hasverb(int act,int *verbs); 35 | int haspreposition(int act,int preposition); 36 | int inlist(int link,int word); 37 | int getofield(int obj,int off); 38 | int putofield(int obj,int off,int val); 39 | int getafield(int act,int off); 40 | int getabyte(int act,int off); 41 | int getoloc(int n); 42 | int getaloc(int n); 43 | int getvalue(int n); 44 | int setvalue(int n,int v); 45 | int getwloc(int n); 46 | int getword(int n); 47 | int putword(int n,int w); 48 | int getbyte(int n); 49 | int getcbyte(int n); 50 | int getcword(int n); 51 | int getdword(char *p); 52 | int putdword(char *p,int w); 53 | 54 | /* advexe.c prototypes */ 55 | void execute(int code); 56 | 57 | /* advint.c prototypes */ 58 | void error(char *msg,...); 59 | 60 | /* advmsg.c prototypes */ 61 | void msg_init(FILE *fp,int base); 62 | void msg_open(unsigned int msg); 63 | int msg_byte(void); 64 | int decode(int ch); 65 | void get_block(unsigned int blk); 66 | 67 | /* advprs.c prototypes */ 68 | int parse(void); 69 | int next(void); 70 | void show_noun(int n); 71 | 72 | /* advtrm.c prototypes */ 73 | void trm_init(int rows,int cols,char *name); 74 | void trm_done(void); 75 | char *trm_get(void); 76 | void trm_str(char *str); 77 | void trm_xstr(char *str); 78 | void trm_chr(int ch); 79 | void trm_word(void); 80 | void trm_eol(void); 81 | void trm_wait(void); 82 | char *trm_line(void); 83 | int getchr(void); 84 | void putchr(int ch); 85 | 86 | /* advistuff.c prototypes */ 87 | void macinit(char *iname,int *prows,int *pcols); 88 | int advopen(char *name); 89 | int advgetc(void); 90 | int advputc(int ch,FILE *fp); 91 | void advwaitc(void); 92 | void advpause(void); 93 | int advsave(char *hdr,int hlen,char *save,int slen); 94 | int advrestore(char *hdr,int hlen,char *save,int slen); 95 | -------------------------------------------------------------------------------- /advmsg.c: -------------------------------------------------------------------------------- 1 | /* advmsg.c - adventure interpreter message routines */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advint.h" 10 | 11 | /* cache size */ 12 | #define CSIZE 8 13 | 14 | /* message block cache */ 15 | static char *mbuffer[CSIZE]; /* message text block cache buffers */ 16 | static int mblock[CSIZE]; /* message text block cache block numbers */ 17 | static int mnext[CSIZE]; /* next most recently used block */ 18 | static int mhead,mtail; /* head and tail of lru list */ 19 | 20 | /* message file variables */ 21 | static int mbase; /* message base block */ 22 | static FILE *mfp; /* message file pointer */ 23 | 24 | /* current message variables */ 25 | static int mblk; /* current block */ 26 | static char *mbuf; /* current buffer */ 27 | static int moff; /* current buffer offset */ 28 | 29 | /* msg_init - initialize the message routines */ 30 | void msg_init(FILE *fp,int base) 31 | { 32 | char *p; 33 | int i; 34 | 35 | /* remember the message file descriptor and base */ 36 | mbase = base; 37 | mfp = fp; 38 | 39 | /* initialize the cache */ 40 | if ((p = malloc(CSIZE * 512)) == NULL) 41 | error("insufficient memory"); 42 | for (i = 0; i < CSIZE; i++) { 43 | mbuffer[i] = p; p += 512; 44 | mblock[i] = -1; 45 | mnext[i] = i+1; 46 | } 47 | mhead = 0; mtail = CSIZE-1; mnext[mtail] = -1; 48 | } 49 | 50 | /* msg_open - open a message */ 51 | void msg_open(unsigned int msg) 52 | { 53 | /* save the current message block */ 54 | mblk = msg >> 7; 55 | 56 | /* make sure the first block is in a buffer */ 57 | get_block(mblk); 58 | 59 | /* setup the initial offset into the block */ 60 | moff = (msg & 0x7F) << 2; 61 | } 62 | 63 | /* msg_byte - get a byte from a message */ 64 | int msg_byte(void) 65 | { 66 | /* check for end of block and get next block */ 67 | if (moff >= 512) { 68 | get_block(++mblk); 69 | moff = 0; 70 | } 71 | 72 | /* return the next message byte */ 73 | return (decode(mbuf[moff++])); 74 | } 75 | 76 | /* decode - decode a character */ 77 | int decode(int ch) 78 | { 79 | return ((ch + 30) & 0xFF); 80 | } 81 | 82 | /* get_block - get a block of message text */ 83 | void get_block(unsigned int blk) 84 | { 85 | int last=0,n; 86 | long loff; 87 | 88 | /* first check the cache */ 89 | for (n = mhead; n != -1; last = n, n = mnext[n]) 90 | if (blk == mblock[n]) { 91 | if (n != mhead) { 92 | if ((mnext[last] = mnext[n]) == -1) 93 | mtail = last; 94 | mnext[n] = mhead; 95 | mhead = n; 96 | } 97 | mbuf = mbuffer[n]; 98 | return; 99 | } 100 | 101 | /* overwrite the least recently used buffer */ 102 | mblock[mtail] = blk; 103 | loff = ((long) mbase + (long) blk) << 9; 104 | fseek(mfp,loff,SEEK_SET); 105 | if (fread(mbuffer[mtail],1,512,mfp) != 512) 106 | error("error reading message text"); 107 | 108 | /* get the block */ 109 | get_block(blk); 110 | } 111 | -------------------------------------------------------------------------------- /advprs.c: -------------------------------------------------------------------------------- 1 | /* advprs.c - adventure parser */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include "advint.h" 8 | #include "advdbs.h" 9 | 10 | /* parser result variables */ 11 | int nouns[20]; 12 | int *adjectives[20]; 13 | static int actor,action,dobject,ndobjects,iobject; 14 | 15 | /* local variables */ 16 | static char *lptr; /* line pointer */ 17 | static int words[100]; /* word table */ 18 | static char *wtext[100];/* word text table */ 19 | static int *wptr; /* word pointer */ 20 | static int wcnt; /* word count */ 21 | 22 | static int verbs[3]; /* words in the verb phrase */ 23 | static int nnums[20]; /* noun word numbers */ 24 | static int nptr; /* noun pointer (actually, an index) */ 25 | static int adjs[100]; /* adjective lists */ 26 | static int anums[100]; /* adjective word numbers */ 27 | static int aptr; /* adjective pointer (actually, an index) */ 28 | 29 | /* prototypes */ 30 | static int getverb(void); 31 | static int getnoun(void); 32 | static int get_line(void); 33 | static int skip_spaces(void); 34 | static int get_word(void); 35 | static int spacep(int ch); 36 | static void parse_error(void); 37 | 38 | /* parse - read and parse an input line */ 39 | int parse(void) 40 | { 41 | int noun1,cnt1,noun2,cnt2; 42 | int preposition,flag; 43 | 44 | /* initialize */ 45 | noun1 = noun2 = NIL; cnt1 = cnt2 = 0; 46 | nptr = aptr = 0; 47 | preposition = 0; 48 | flag = 0; 49 | 50 | /* initialize the parser result variables */ 51 | actor = action = dobject = iobject = NIL; 52 | ndobjects = 0; 53 | 54 | /* get an input line */ 55 | if (!get_line()) 56 | return (NIL); 57 | 58 | /* check for actor */ 59 | if (wtype(*wptr) == WT_ADJECTIVE || wtype(*wptr) == WT_NOUN) { 60 | if ((actor = getnoun()) == NIL) 61 | return (NIL); 62 | flag |= A_ACTOR; 63 | } 64 | 65 | /* get verb phrase */ 66 | if (!getverb()) 67 | return (NIL); 68 | 69 | /* direct object, preposition and indirect object */ 70 | if (*wptr) { 71 | 72 | /* get the first set of noun phrases (direct objects) */ 73 | noun1 = nptr+1; 74 | for (;;) { 75 | 76 | /* get the next direct object */ 77 | if (getnoun() == NIL) 78 | return (NIL); 79 | ++cnt1; 80 | 81 | /* check for more direct objects */ 82 | if (*wptr == NIL || wtype(*wptr) != WT_CONJUNCTION) 83 | break; 84 | wptr++; 85 | } 86 | 87 | /* get the preposition and indirect object */ 88 | if (*wptr) { 89 | 90 | /* get the preposition */ 91 | if (wtype(*wptr) == WT_PREPOSITION) 92 | preposition = *wptr++; 93 | 94 | /* get the second set of noun phrases (indirect object) */ 95 | noun2 = nptr+1; 96 | for (;;) { 97 | 98 | /* get the next direct object */ 99 | if (getnoun() == NIL) 100 | return (NIL); 101 | ++cnt2; 102 | 103 | /* check for more direct objects */ 104 | if (*wptr == NIL || wtype(*wptr) != WT_CONJUNCTION) 105 | break; 106 | wptr++; 107 | } 108 | } 109 | 110 | /* make sure this is the end of the sentence */ 111 | if (*wptr) { 112 | parse_error(); 113 | return (NIL); 114 | } 115 | } 116 | 117 | /* setup the direct and indirect objects */ 118 | if (preposition) { 119 | if (cnt2 > 1) { 120 | parse_error(); 121 | return (NIL); 122 | } 123 | dobject = noun1; 124 | ndobjects = cnt1; 125 | iobject = noun2; 126 | } 127 | else if (noun2) { 128 | if (cnt1 > 1) { 129 | parse_error(); 130 | return (NIL); 131 | } 132 | preposition = findword("to"); 133 | dobject = noun2; 134 | ndobjects = cnt2; 135 | iobject = noun1; 136 | } 137 | else { 138 | dobject = noun1; 139 | ndobjects = cnt1; 140 | } 141 | 142 | /* setup the flags for the action lookup */ 143 | if (dobject) flag |= A_DOBJECT; 144 | if (iobject) flag |= A_IOBJECT; 145 | 146 | /* find the action */ 147 | if ((action = findaction(verbs,preposition,flag)) == NIL) { 148 | parse_error(); 149 | return (NIL); 150 | } 151 | 152 | /* setup the interpreter variables */ 153 | setvalue(V_ACTOR,actor); 154 | setvalue(V_ACTION,action); 155 | setvalue(V_DOBJECT,dobject); 156 | setvalue(V_NDOBJECTS,ndobjects); 157 | setvalue(V_IOBJECT,iobject); 158 | 159 | /* return successfully */ 160 | return (T); 161 | } 162 | 163 | /* next - get the next command (next direct object) */ 164 | int next(void) 165 | { 166 | if (getvalue(V_NDOBJECTS) > 1) { 167 | setvalue(V_ACTOR,actor); 168 | setvalue(V_ACTION,action); 169 | setvalue(V_DOBJECT,getvalue(V_DOBJECT) + 1); 170 | setvalue(V_NDOBJECTS,getvalue(V_NDOBJECTS) - 1); 171 | setvalue(V_IOBJECT,iobject); 172 | return (T); 173 | } 174 | else 175 | return (NIL); 176 | } 177 | 178 | /* show_noun - show a noun phrase */ 179 | void show_noun(int n) 180 | { 181 | int adj,*p; 182 | 183 | /* print the adjectives */ 184 | for (p = adjectives[n-1], adj = FALSE; *p; p++, adj = TRUE) { 185 | if (adj) trm_chr(' '); 186 | trm_str(wtext[anums[p-adjs]]); 187 | } 188 | 189 | /* print the noun */ 190 | if (adj) trm_chr(' '); 191 | trm_str(wtext[nnums[n-1]]); 192 | } 193 | 194 | /* getverb - get a verb phrase and return the action it refers to */ 195 | static int getverb(void) 196 | { 197 | /* get the verb */ 198 | if (*wptr == NIL || wtype(*wptr) != WT_VERB) { 199 | parse_error(); 200 | return (NIL); 201 | } 202 | verbs[0] = *wptr++; 203 | verbs[1] = NIL; 204 | 205 | /* check for a word following the verb */ 206 | if (*wptr) { 207 | verbs[1] = *wptr; 208 | verbs[2] = NIL; 209 | if (checkverb(verbs)) 210 | wptr++; 211 | else { 212 | verbs[1] = words[wcnt-1]; 213 | if (checkverb(verbs)) 214 | words[--wcnt] = NIL; 215 | else { 216 | verbs[1] = NIL; 217 | if (!checkverb(verbs)) { 218 | parse_error(); 219 | return (NIL); 220 | } 221 | } 222 | } 223 | } 224 | return (T); 225 | } 226 | 227 | /* getnoun - get a noun phrase and return the object it refers to */ 228 | static int getnoun(void) 229 | { 230 | /* initialize the adjective list pointer */ 231 | adjectives[nptr] = adjs + aptr; 232 | 233 | /* get the optional article */ 234 | if (*wptr != NIL && wtype(*wptr) == WT_ARTICLE) 235 | wptr++; 236 | 237 | /* get optional adjectives */ 238 | while (*wptr != NIL && wtype(*wptr) == WT_ADJECTIVE) { 239 | adjs[aptr] = *wptr++; 240 | anums[aptr] = wptr - words - 1; 241 | aptr++; 242 | } 243 | adjs[aptr++] = NIL; 244 | 245 | /* get the noun itself */ 246 | if (*wptr == NIL || wtype(*wptr) != WT_NOUN) { 247 | parse_error(); 248 | return (NIL); 249 | } 250 | 251 | /* save the noun */ 252 | nouns[nptr] = *wptr++; 253 | nnums[nptr] = wptr - words - 1; 254 | return (++nptr); 255 | } 256 | 257 | /* get_line - get the input line and lookup each word */ 258 | static int get_line(void) 259 | { 260 | /* read an input line */ 261 | trm_chr(':'); 262 | if ((lptr = trm_get()) == NULL) { 263 | trm_str("Speak up! I can't hear you!\n"); 264 | return (FALSE); 265 | } 266 | 267 | /* get each word on the line */ 268 | for (wcnt = 0; skip_spaces(); wcnt++) 269 | if (get_word() == NIL) 270 | return (FALSE); 271 | words[wcnt] = NIL; 272 | 273 | /* check for a blank line */ 274 | if (wcnt == 0) { 275 | trm_str("Speak up! I can't hear you!\n"); 276 | return (FALSE); 277 | } 278 | 279 | /* point to the first word and return successfully */ 280 | wptr = words; 281 | return (TRUE); 282 | } 283 | 284 | /* skip_spaces - skip leading spaces */ 285 | static int skip_spaces(void) 286 | { 287 | while (spacep(*lptr)) 288 | lptr++; 289 | return (*lptr != EOS); 290 | } 291 | 292 | /* get_word - get the next word */ 293 | static int get_word(void) 294 | { 295 | int ch; 296 | 297 | /* get the next word */ 298 | for (wtext[wcnt] = lptr; (ch = *lptr) != EOS && !spacep(ch); ) 299 | *lptr++ = (isupper(ch) ? tolower(ch) : ch); 300 | if (*lptr != EOS) *lptr++ = EOS; 301 | 302 | /* look up the word */ 303 | if ((words[wcnt] = findword(wtext[wcnt])) != 0) 304 | return (words[wcnt]); 305 | else { 306 | trm_str("I don't know the word \""); 307 | trm_str(wtext[wcnt]); 308 | trm_str("\".\n"); 309 | return (NIL); 310 | } 311 | } 312 | 313 | /* spacep - is this character a space? */ 314 | static int spacep(int ch) 315 | { 316 | return (ch == ' ' || ch == ',' || ch == '.'); 317 | } 318 | 319 | /* parse_error - announce a parsing error */ 320 | static void parse_error(void) 321 | { 322 | trm_str("I don't understand.\n"); 323 | } 324 | 325 | -------------------------------------------------------------------------------- /advscn.c: -------------------------------------------------------------------------------- 1 | /* advscn.c - a lexical scanner for the adventure compiler */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include 9 | #include "advavl.h" 10 | #include "advcom.h" 11 | 12 | /* useful definitions */ 13 | #define maplower(ch) (isupper(ch) ? tolower(ch) : ch) 14 | 15 | /* global variables */ 16 | int errcount=0; /* error count */ 17 | int t_value; /* numeric value */ 18 | char t_token[TKNSIZE+1];/* token string */ 19 | char *t_names[] = { 20 | 0, 21 | "(", 22 | ")", 23 | "STRING", 24 | "IDENTIFIER", 25 | "NUMBER", 26 | "EOF" 27 | }; 28 | 29 | /* external variables */ 30 | extern FILE *ifp; /* input file pointer */ 31 | extern int msgoff; /* message section offset */ 32 | 33 | /* local variables */ 34 | static int savetkn = 0; /* look ahead token */ 35 | static int savech = 0; /* look ahead character */ 36 | static char fname[200]; /* include file name */ 37 | static char line[200]; /* current input line */ 38 | static char *lptr; /* line pointer */ 39 | static int lnum; /* line number */ 40 | static int ieof; /* input end of file flag */ 41 | static int save_lnum; /* saved lnum */ 42 | static FILE *save_ifp; /* saved ifp */ 43 | static int scnt; /* count of characters in string */ 44 | 45 | /* prototypes */ 46 | static int getch(void); 47 | 48 | /* sinit - initialize the scanner */ 49 | void sinit(void) 50 | { 51 | /* setup the line buffer */ 52 | lptr = line; *lptr = 0; 53 | lnum = 0; 54 | 55 | /* no include file yet */ 56 | save_ifp = NULL; 57 | 58 | /* no lookahead yet */ 59 | savech = 0; 60 | savetkn = 0; 61 | 62 | /* not eof yet */ 63 | ieof = FALSE; 64 | } 65 | 66 | /* token - get the next token */ 67 | int token(void) 68 | { 69 | int tkn; 70 | 71 | if ((tkn = savetkn) != 0) 72 | savetkn = 0; 73 | else 74 | tkn = rtoken(); 75 | return (tkn); 76 | } 77 | 78 | /* stoken - save a token */ 79 | void stoken(int tkn) 80 | { 81 | savetkn = tkn; 82 | } 83 | 84 | /* rtoken - read the next token */ 85 | int rtoken(void) 86 | { 87 | int ch; 88 | 89 | /* check the next character */ 90 | for (;;) 91 | switch (ch = skipspaces()) { 92 | case EOF: return (T_EOF); 93 | case '(': strcpy(t_token,"("); return (T_OPEN); 94 | case ')': strcpy(t_token,")"); return (T_CLOSE); 95 | case '"': return (getstring()); 96 | case ';': while (getch() != '\n'); break; 97 | default: return (getid(ch)); 98 | } 99 | } 100 | 101 | /* getstring - get a string */ 102 | int getstring(void) 103 | { 104 | int ch,sflag; 105 | 106 | t_value = msgoff; 107 | sflag = FALSE; 108 | scnt = 0; 109 | while ((ch = getch()) != EOF && ch != '"') 110 | if (isspace(ch)) 111 | sflag = TRUE; 112 | else { 113 | if (ch == '\\') 114 | switch (ch = getch()) { 115 | case 'n': ch = '\n'; break; 116 | case 't': ch = '\t'; break; 117 | } 118 | if (sflag) 119 | { wputc(' '); sflag = FALSE; } 120 | wputc(ch); 121 | } 122 | if (sflag) 123 | wputc(' '); 124 | strdone(); 125 | strcpy(t_token,"{string}"); 126 | return (T_STRING); 127 | } 128 | 129 | /* getid - get an identifier */ 130 | int getid(int ch) 131 | { 132 | char *p; 133 | 134 | p = t_token; *p++ = maplower(ch); 135 | while ((ch = getch()) != EOF && isidchar(ch)) 136 | *p++ = maplower(ch); 137 | *p = EOS; 138 | savech = ch; 139 | return (isnum(t_token,&t_value) ? T_NUMBER : T_IDENTIFIER); 140 | } 141 | 142 | /* isnum - check if this string is a number */ 143 | int isnum(char *str,int *pval) 144 | { 145 | int digits; 146 | char *p; 147 | 148 | /* initialize */ 149 | p = str; digits = 0; 150 | 151 | /* check for a sign */ 152 | if (*p == '+' || *p == '-') 153 | p++; 154 | 155 | /* check for a string of digits */ 156 | while (isdigit(*p)) 157 | p++, digits++; 158 | 159 | /* make sure there was at least one digit and this is the end */ 160 | if (digits == 0 || *p) 161 | return (FALSE); 162 | 163 | /* convert the string to an integer and return successfully */ 164 | if (*str == '+') ++str; 165 | *pval = atoi(str); 166 | return (TRUE); 167 | } 168 | 169 | /* wputc - put a character into the output file */ 170 | void wputc(int ch) 171 | { 172 | ad_putc(encode(ch)); 173 | scnt++; 174 | } 175 | 176 | /* strdone - finish a string */ 177 | void strdone(void) 178 | { 179 | wputc('\0'); 180 | while (scnt & 3) 181 | wputc('\0'); 182 | msgoff += scnt >> 2; 183 | } 184 | 185 | /* skipspaces - skip leading spaces */ 186 | int skipspaces(void) 187 | { 188 | int ch; 189 | 190 | while ((ch = getch()) && isspace(ch)) 191 | ; 192 | return (ch); 193 | } 194 | 195 | /* isidchar - is this an identifier character */ 196 | int isidchar(int ch) 197 | { 198 | return (!isspace(ch) && ch != '(' && ch != ')' && ch != '"'); 199 | } 200 | 201 | /* getch - get the next character */ 202 | static int getch(void) 203 | { 204 | FILE *fp; 205 | int ch; 206 | 207 | /* check for a lookahead character */ 208 | if ((ch = savech) != 0) 209 | savech = 0; 210 | 211 | /* check for a buffered character */ 212 | else if ((ch = *lptr) != 0) 213 | lptr++; 214 | 215 | /* check for end of file */ 216 | else if (ieof) 217 | ch = EOF; 218 | 219 | /* read another line */ 220 | else { 221 | 222 | /* read the line */ 223 | for (lptr = line; (ch = getchr()) != EOF && (*lptr++ = ch) != '\n'; ) 224 | ; 225 | *lptr = 0; 226 | lnum++; 227 | 228 | /* check for an included file */ 229 | if (line[0] == '@') { 230 | 231 | /* open the file */ 232 | strcpy(fname,&line[1]); fname[strlen(fname)-1] = 0; 233 | if ((fp = fopen(fname,"r")) == NULL) { 234 | printf("Can't open include file: %s\n",fname); 235 | exit(0); 236 | } 237 | printf("[ including %s ]\n",fname); 238 | 239 | /* setup input from the file */ 240 | save_lnum = lnum; 241 | save_ifp = ifp; 242 | ifp = fp; 243 | 244 | /* setup for the first line */ 245 | lptr = line; *lptr = 0; 246 | lnum = 0; 247 | } 248 | 249 | /* otherwise this must be an input line */ 250 | else { 251 | 252 | /* terminate the line with a newline */ 253 | *lptr++ = '\n'; *lptr = 0; 254 | 255 | /* check for end of file */ 256 | if (ch == EOF) 257 | ieof = TRUE; 258 | 259 | /* update the line number and setup for the new line */ 260 | lptr = line; 261 | } 262 | 263 | /* get a character */ 264 | ch = getch(); 265 | } 266 | 267 | /* return the current character */ 268 | return (ch); 269 | } 270 | 271 | /* getchr - get a character checking for end of file */ 272 | int getchr(void) 273 | { 274 | int ch; 275 | 276 | if ((ch = getc(ifp)) == EOF || ch == '\032') { 277 | if (save_ifp) { 278 | printf("[ end of %s ]\n",fname); 279 | fclose(ifp); 280 | lnum = save_lnum; 281 | ifp = save_ifp; 282 | save_ifp = NULL; 283 | ch = getchr(); 284 | } 285 | else 286 | ch = EOF; 287 | } 288 | else if (ch == '\r') 289 | ch = getchr(); 290 | return (ch); 291 | } 292 | 293 | /* encode - encode a single character */ 294 | int encode(int ch) 295 | { 296 | return ((ch - 30) & 0xFF); 297 | } 298 | 299 | /* error - report an error in the current line */ 300 | void error(char *msg) 301 | { 302 | char *p; 303 | 304 | printf(">>> %s <<<\n>>> in line %d <<<\n%s",msg,lnum,line); 305 | for (p = line; p < lptr; p++) 306 | if (*p == '\t') 307 | putchar('\t'); 308 | else 309 | putchar(' '); 310 | printf("^\n"); 311 | errcount++; 312 | } 313 | 314 | /* xerror - report an error in the current line */ 315 | void xerror(char *msg) 316 | { 317 | printf(">>> %s <<<\n",msg); 318 | errcount++; 319 | } 320 | -------------------------------------------------------------------------------- /advsys.doc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ADVSYS - An Adventure Writing System 8 | 9 | Version 1.2 10 | 11 | by David Betz 12 | 18 Garrison Drive 13 | Bedford, NH 03110 14 | (603) 472-2389 (home) 15 | 16 | July 14, 1986 17 | 18 | Copyright (c) 1986, by David Betz 19 | All Rights Reserved 20 | Permission is hereby granted for unrestricted non-commercial use 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 | ADVSYS An Adventure Writing System Page 2 71 | 72 | 73 | INTRODUCTION 74 | 75 | ADVSYS is a special purpose programming language that was 76 | specifically designed to be used to write computer text 77 | adventure games. It includes a facility for defining the kinds 78 | of objects that are common in adventures. Some objects 79 | represent locations on the game map, some objects represent 80 | things that the player can find while exploring the adventure 81 | world, and some objects represent other characters that the 82 | adventurer can encounter during his or her journeys. The 83 | adventure language also provides a facility to define actions. 84 | Actions are short sections of code that determine what happens 85 | in response to a command from the player. These two concepts, 86 | "objects" and "actions" form the basis for the adventure 87 | language. 88 | 89 | 90 | ACKNOWLEDGEMENTS 91 | 92 | Although I have written all of the code associated with this 93 | adventure writing system, I must acknowledge the assistance of 94 | one individual without whom this project would probably never 95 | have reached completion. That person is Gary McGath. Gary was 96 | interested in writing a commercial quality adventure game and I 97 | convinced him to write it using my system (which was as yet 98 | almost completely unspecified) instead of using a traditional 99 | programming language. The input that Gary provided during the 100 | development of his game contributed significantly to the overall 101 | design of the system. I would like to thank Gary for that 102 | contribution. 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 | ADVSYS An Adventure Writing System Page 3 137 | 138 | 139 | USING THE SYSTEM TO WRITE AN ADVENTURE 140 | 141 | In order to write an adventure using this system, you need to 142 | write an adventure description. This is an ordinary ASCII text 143 | file containing definitions for all of the objects and actions 144 | in your adventure game. This file is used as input to the 145 | adventure compiler. The compiler takes the adventure 146 | description and compiles it into a set of data structures. 147 | 148 | In order to play an adventure written using this system, you 149 | need the data structure file that was produced by the compiler 150 | and the adventure interpreter program. The interpreter uses the 151 | information produced by the adventure compiler to allow a player 152 | to play the adventure game. Notice that it is not necessary for 153 | the player to have access to the original adventure description 154 | file. All of the information that is necessary to play the 155 | adventure game is contained within the data structure file that 156 | is produced by the compiler. This file is a binary file that 157 | cannot be simply "listed" to reveal the internal workings of the 158 | adventure. 159 | 160 | The adventure compiler is called ADVCOM and the interpreter is 161 | called ADVINT. These two programs in conjunction with this 162 | documentation are all that is required to write and play 163 | adventure games using this system. 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 | ADVSYS An Adventure Writing System Page 4 203 | 204 | 205 | RUNNING THE COMPILER 206 | 207 | If you have created an adventure definition file called 208 | "MYADV.ADV", you can compile it with the command: 209 | 210 | A>advcom myadv 211 | 212 | Typing this command will invoke the adventure compiler and cause 213 | it to compile the file named "MYADV.ADV". The ".ADV" extension 214 | is added to the file name by the compiler. During the process 215 | of compiling the file, many messages will be printed telling 216 | about the progress of the compiler. At the end of the 217 | compilation process, the compiler prints a set of statistics 218 | describing the resulting data structure file. This file will be 219 | called "MYADV.DAT". It contains the data structures needed by 220 | the adventure interpreter to allow a player to play the 221 | adventure game. 222 | 223 | Note: The "A>" in the line above is the MS-DOS prompt and should 224 | not be typed as part of the command. 225 | 226 | 227 | RUNNING THE INTERPRETER 228 | 229 | Assuming that you have a compiled adventure data file called 230 | "MYADV.DAT", you can play the adventure by typing the command: 231 | 232 | A>advint myadv 233 | 234 | This command will start the adventure. There will probably be 235 | some text printed at this point describing the adventure and the 236 | initial situation. You will then be prompted to type a command. 237 | The prompt is the colon character. The format for commands is 238 | described under the section about the parser. After typing a 239 | command, you will be told what happened as a result of your 240 | command, your new situation will be described and you will begin 241 | the loop again. 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 | ADVSYS An Adventure Writing System Page 5 269 | 270 | 271 | ADVENTURE DESCRIPTION FILE FORMAT 272 | 273 | All adventure description files contain a collection of 274 | statements. These statements must be formed according to 275 | the following rules: 276 | 277 | 278 | The adventure definition statement: 279 | 280 | All adventure definitions should have an ADVENTURE 281 | statement. This statement gives the name of the adventure 282 | and the version number of the definition file. Each 283 | adventure should have a unique name. This name is used to 284 | identify "saved position" files and insure that only files 285 | that correspond to the current adventure are restored. The 286 | version number allows the author to have many versions of 287 | the same adventure during development and guarantee that 288 | "save" files from one version aren't restored into another 289 | version. 290 | 291 | (ADVENTURE name version) 292 | 293 | Example: 294 | 295 | (ADVENTURE sample 1) 296 | 297 | 298 | Vocabulary statements: 299 | 300 | These statements add words to the adventure vocabulary. 301 | 302 | (ADJECTIVE word*) 303 | (PREPOSITION word*) 304 | (CONJUNCTION word*) 305 | (ARTICLE word*) 306 | (SYNONYM word synonym*) 307 | 308 | Examples: 309 | 310 | (ADJECTIVE red blue) 311 | (CONJUNCTION and) 312 | (SYNONYM big large) 313 | 314 | Note: 315 | 316 | Words are also added to the vocabulary by the object 317 | and action definitions using the NOUN, ADJECTIVE, VERB 318 | and PREPOSITION statements. 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | ADVSYS An Adventure Writing System Page 6 335 | 336 | 337 | Constant definition statement: 338 | 339 | (DEFINE name value) 340 | 341 | Examples: 342 | 343 | (DEFINE what "I don't understand what you're saying!\n") 344 | (DEFINE max-load 100) 345 | 346 | 347 | Function definition statement: 348 | 349 | (DEFINE (function-name [arg-name]* [&aux tmp-name*]) expr*) 350 | 351 | Example: 352 | 353 | (DEFINE (factorial n) 354 | (IF (< n 2) 355 | 1 356 | (* n (factorial (- n 1))))) 357 | 358 | 359 | Variable definition statement: 360 | 361 | (VARIABLE variable-name*) 362 | 363 | Example: 364 | 365 | (VARIABLE score i j) 366 | 367 | 368 | Property name definition statement: 369 | 370 | (PROPERTY property-name*) 371 | 372 | Example: 373 | 374 | (PROPERTY weight value) 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 | ADVSYS An Adventure Writing System Page 7 401 | 402 | 403 | Comments: 404 | 405 | Comments begin with a semi-colon and end with the end of the 406 | line. 407 | 408 | Example: 409 | 410 | ; this is a comment 411 | 412 | 413 | Include files: 414 | 415 | Any line that begins with a "@" causes the inclusion of 416 | another file. The file name immediately follows the at-sign 417 | and extends to the end of the line. Only one level of 418 | include is supported. 419 | 420 | Example: 421 | 422 | @basic.adv 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 | ADVSYS An Adventure Writing System Page 8 467 | 468 | 469 | Handler definition statements: 470 | 471 | (INIT expr*) 472 | (UPDATE expr*) 473 | (BEFORE expr*) 474 | (AFTER expr*) 475 | (ERROR expr*) 476 | 477 | Example: 478 | 479 | (INIT 480 | (print "Welcome to the sample adventure!\n")) 481 | 482 | 483 | Handlers: 484 | 485 | All activity within an adventure game is controlled by a 486 | built-in handler loop. Each of the handlers in the loop 487 | contains code that is provided by the adventure author. The 488 | sequencing from handler to handler is provided by the 489 | adventure system itself. 490 | 491 | The first handler that is called in an adventure game is the 492 | INIT handler. It prints some sort of introductory text and 493 | initializes all global variables in order to start the 494 | adventure game. 495 | 496 | After the INIT handler has completed, the normal loop is 497 | entered. It starts with the UPDATE handler. The UPDATE 498 | handler prepares for the player's next turn. It should 499 | describe the player's location if it has changed since the 500 | last turn. After the UPDATE handler completes, the parser 501 | is called. It prompts the player for a command, parses the 502 | command, sets the built-in parser varaibles and exits. Then 503 | the BEFORE handler is called. It is called before the 504 | action associated with the command to allow the adventure 505 | author to inspect the parser variables before proceeding to 506 | the action itself. After the BEFORE handler completes, the 507 | action itself is called (or whatever action is stored in the 508 | built-in variable $ACTION when the BEFORE handler 509 | completes). When the action completes, the AFTER handler is 510 | called to give the author a chance to handle events that 511 | happen only at the end of a successful turn. The ERROR 512 | handler is called when the parser detects an error. 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | ADVSYS An Adventure Writing System Page 9 533 | 534 | 535 | The handler loop: 536 | 537 | INIT 538 | | 539 | v 540 | UPDATE<----------+ 541 | | | 542 | v | 543 | parse--->ERROR---+ 544 | | | 545 | v | 546 | BEFORE | 547 | | | 548 | v | 549 | action | 550 | | | 551 | v | 552 | AFTER------------+ 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 | ADVSYS An Adventure Writing System Page 10 599 | 600 | 601 | The parser: 602 | 603 | The parser handles all commands from the player. It prompts 604 | the player when it is ready for a new command. The prompt 605 | is the colon character. When the player has typed a 606 | command, the parser breaks the command into phrases. The 607 | parser recognizes the following command forms: 608 | 609 | [actor,] verb 610 | [actor,] verb dobjects 611 | [actor,] verb dobjects preposition iobject 612 | [actor,] verb iobject dobjects 613 | 614 | Where: 615 | 616 | actor ==> a noun phrase 617 | verb ==> the verb phrase (1 or 2 words) 618 | dobjects ==> dobject [conjunction dobject]* 619 | dobject ==> a noun phrase 620 | preposition ==> a preposition 621 | iobject ==> a noun phrase 622 | noun phrase ==> [article] [adjective]* noun 623 | 624 | Examples: 625 | 626 | Look 627 | Take the red magic sword 628 | Take the red sword and the blue bottle 629 | Give the troll the red sword 630 | Give the red sword to the troll 631 | Troll, give me the sword 632 | 633 | Notes: 634 | 635 | Square brackets enclose optional phrases. An asterisk 636 | indicates zero or more of the preceeding element. 637 | 638 | The fourth form above is treated as if the player had 639 | typed: 640 | 641 | [actor,] verb dobject "to" iobject 642 | 643 | Once the parser has broken the command into phrases, it 644 | assigns each noun phrase a number. It stores the number of 645 | the actor noun phrase in the built-in variable $ACTOR. It 646 | stores the first direct object noun phrase number in the 647 | variable $DOBJECT. It stores the number of direct objects 648 | in the variable $NDOBJECTS. It stores the indirect object 649 | noun phrase number in the variable $IOBJECT. If any of the 650 | noun phrases is missing from the command, the corresponding 651 | variable is set to NIL. The parser saves the verb phrase 652 | and preposition to use when determining which action to use 653 | to handle the command. 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | ADVSYS An Adventure Writing System Page 11 665 | 666 | 667 | Action definition statement: 668 | 669 | Actions are used to handle player commands. Each time the 670 | parser finishes parsing a new command, it uses the verb 671 | phrase and the preposition to locate an action to handle the 672 | command. Each action specifies a kind of template that must 673 | match the command in order for the action to be called. The 674 | template consists of the words used in the verb phrase and 675 | preposition and the existance of the actor, direct object 676 | and indirect object noun phrases. Once the parser finds an 677 | action that matches the command, it stores the action in the 678 | built-in variable $ACTION and exits. 679 | 680 | (ACTION action-name astat*) 681 | astat: 682 | (ACTOR [flag]) 683 | (VERB verb*) 684 | (DIRECT-OBJECT [flag]) 685 | (PREPOSITION word*) 686 | (INDIRECT-OBJECT [flag]) 687 | flag: 688 | REQUIRED must have the corresponding np 689 | OPTIONAL may have the corresponding np 690 | FORBIDDEN must not have the corresponding np 691 | verb: 692 | word 693 | (word word) 694 | 695 | Example: 696 | 697 | (ACTION take 698 | (VERB take (pick up)) 699 | (DIRECT-OBJECT) 700 | (CODE 701 | (print "You can't take the ") 702 | (print-noun $dobject) 703 | (print "!\n"))) 704 | 705 | If the ACTOR, DIRECT-OBJECT or INDIRECT-OBJECT statements 706 | are left out entirely, the settings of the corresponding 707 | flags are taken from the action default definitions. If 708 | there is no action default definition, the value FORBIDDEN 709 | is assumed. If any of these statements is present, but no 710 | flag is specified, it is treated as if the flag REQUIRED was 711 | specified. 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | ADVSYS An Adventure Writing System Page 12 731 | 732 | 733 | Action default definition statement: 734 | 735 | This statement defines default values for the ACTOR, DIRECT- 736 | OBJECT and INDIRECT-OBJECT flags. 737 | 738 | (DEFAULT dstat*) 739 | dstat: 740 | (ACTOR [flag]) 741 | (DIRECT-OBJECT [flag]) 742 | (INDIRECT-OBJECT [flag]) 743 | flag: 744 | REQUIRED 745 | OPTIONAL 746 | FORBIDDEN 747 | 748 | Example: 749 | 750 | (DEFAULT 751 | (ACTOR OPTIONAL)) 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 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | ADVSYS An Adventure Writing System Page 13 797 | 798 | 799 | Object definition statements: 800 | 801 | The object definition statements are used to define 802 | individual objects and classes of objects. The most basic 803 | way of defining an object is using the (OBJECT ...) 804 | statement. This defines an object which has no parent 805 | class. 806 | 807 | It is also possible to create a class of objects that share 808 | information. A class is defined just like a normal object. 809 | It is given nouns, adjectives and properties. In addition, 810 | a class may have class properties. These are properties 811 | that are shared amongst all instances of the class. In 812 | order to create an instance of a class, the (class-name ...) 813 | form is used. This creates an instance of the named class. 814 | An instance will inherit all nouns and adjectives from its 815 | parent class. It will also inherit all class properties 816 | defined in the parent (and its parents). Any normal 817 | properties defined in the parent class will be copied to the 818 | new object. The copies will have the same values that the 819 | parent has, but it is possible for the instance to have 820 | property definitions that override these values. Instances 821 | may also have additional nouns, adjectives and properties. 822 | 823 | (OBJECT object-name ostat*) 824 | (class-name object-name ostat*) 825 | ostat: 826 | (NOUN word*) 827 | (ADJECTIVE word*) 828 | (PROPERTY [property-name value]*) 829 | (CLASS-PROPERTY [property-name value]*) 830 | (METHOD (selector [arg-name]* [&aux tmp-name*]) 831 | expr*) 832 | class-name: 833 | the name of a previously defined object 834 | 835 | Examples: 836 | 837 | (OBJECT sword 838 | (NOUN sword weapon) 839 | (CLASS-PROPERTY 840 | is-weapon T) 841 | (PROPERTY 842 | weight 10 843 | value 5 844 | damage 20)) 845 | 846 | (sword red-sword 847 | (ADJECTIVE red) 848 | (PROPERTY 849 | damage 25)) 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | ADVSYS An Adventure Writing System Page 14 863 | 864 | 865 | Expressions: 866 | 867 | (+ expr expr) add 868 | (- expr expr) subtract 869 | (* expr expr) multiply 870 | (/ expr expr) divide 871 | (% expr expr) remainder 872 | 873 | (& expr expr) bit-wise and 874 | (| expr expr) bit-wise or 875 | (~ expr) bit-wise complement 876 | 877 | These arithmetic functions operate on integers. As it turns 878 | out, every data type in the system is represented by an 879 | integer, so these functions will work with any type of 880 | arguments. They are probably only useful with integers, 881 | however. 882 | 883 | (RANDOMIZE) reset the random number generator 884 | (RAND expr) generate a random number 885 | 886 | These functions enable the generation of pseudo-random 887 | numbers. The (RAND n) function takes a single argument and 888 | generates a random number between zero and n-1. (RANDOMIZE) 889 | resets the seed used by the random number function so that 890 | each invocation of a program results in a new sequence of 891 | random numbers. 892 | 893 | (AND expr*) logical and (short circuits) 894 | (OR expr*) logical or (short circuits) 895 | (NOT expr) logical not 896 | 897 | These functions operate on logical values. In this system, 898 | any value that is not equal to NIL (or zero) is considered 899 | true. NIL and zero are considered false. AND and OR 900 | evaluate their arguments from left to right and stop as soon 901 | as the value of the entire expression can be determined. In 902 | other words, AND stops when it encounters a false value, OR 903 | stops when it encounters a true value. 904 | 905 | (< expr expr) less than 906 | (= expr expr) equal to 907 | (> expr expr) greater than 908 | 909 | These functions compare integers. They cannot be used to 910 | compare strings. 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | ADVSYS An Adventure Writing System Page 15 929 | 930 | 931 | (GETP obj property-name) get the value of a property 932 | (SETP obj property-name value) set the value of a property 933 | 934 | These functions manipulate object properties. They are used 935 | to find the value of a property or to set the value of a 936 | property. They will also find and set the values of 937 | inherited properties. If GETP is used to find the value of 938 | a property that doesn't exist for the specified object, NIL 939 | is returned. If SETP is used to set the value of a property 940 | that doesn't exist, the operation is ignored. 941 | 942 | (CLASS obj) 943 | 944 | This function returns the class of an object. If the object 945 | was defined with an (OBJECT ...) statement, NIL will be 946 | returned. If the object was defined with the (class-name 947 | ...) statement, the class object will be returned. 948 | 949 | (MATCH obj noun-phrase-number) 950 | 951 | This function matches an object with a noun phrase. An 952 | object matches a noun phrase if it includes all of the 953 | adjectives specified in the noun phrase and also includes 954 | the noun mentioned. Both nouns and adjectives can be 955 | inherited. 956 | 957 | (YES-OR-NO) get a yes or no answer from the player 958 | 959 | This function waits for the player to type a line. If the 960 | line begins with a 'Y' or a 'y', the function returns T. If 961 | the line begins with anything else, the function returns 962 | NIL. 963 | 964 | (PRINT expr) print a string 965 | (PRINT-NUMBER expr) print a number 966 | (PRINT-NOUN noun-phrase-number) print a noun phrase 967 | (TERPRI) terminate the print line 968 | 969 | These functions perform various sorts of output. PRINT 970 | prints strings, PRINT-NUMBER prints numbers and PRINT-NOUN 971 | prints a noun phrase. 972 | 973 | (FINISH) exit and continue with the AFTER handler 974 | (CHAIN) exit and continue with the next handler 975 | (ABORT) exit and continue with the UPDATE handler 976 | (RESTART) exit and restart the current game 977 | (EXIT) exit to the operating system 978 | 979 | These functions cause the immediate termination of the 980 | current handler. FINISH causes execution to proceed with 981 | the AFTER handler, CHAIN causes execution to proceed with 982 | the next handler in the normal sequence, ABORT causes 983 | execution to proceed with the UPDATE handler (effectively 984 | aborting the current turn), RESTART restores the game to its 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | ADVSYS An Adventure Writing System Page 16 995 | 996 | 997 | original state and starts over with the INIT handler and 998 | EXIT causes an immediate exit back to the operating system. 999 | 1000 | (SAVE) save the current game position 1001 | (RESTORE) restore a saved game position 1002 | 1003 | These functions allow the player to save and restore 1004 | positions in the game. They prompt the player for a file 1005 | name and either read a saved game position from the file or 1006 | write the current game position to the file. 1007 | 1008 | (function-name expr*) 1009 | 1010 | This expression invokes a user defined function. There 1011 | should be one expression for each of the formal arguments of 1012 | the user function. The value of the expression is the value 1013 | of the last expression in the body of the user function or 1014 | the value passed to a RETURN statement within the function. 1015 | 1016 | (SEND object selector [expr]*) 1017 | 1018 | This expression sends a message to an object. The "object" 1019 | expression should evaluate to an object. The selector 1020 | should match a method selector for that object or one of its 1021 | super-classes. The matching method is invoked with the 1022 | specified expressions as arguments. Also, the implied 1023 | argument SELF will refer to the object receiving the 1024 | message. 1025 | 1026 | (SEND-SUPER selector [expr]*) 1027 | 1028 | This expression sends a message to the super-class of the 1029 | current object. It can only be used within a method and it 1030 | will cause the message to be passed up the class heirarchy 1031 | to the super-class of the object refered to by SELF. 1032 | 1033 | (SETQ variable value) 1034 | 1035 | This expression sets the value of a user variable. 1036 | 1037 | (COND [(test expr*)]*) execute conditionally 1038 | (IF test then-expr [else-expr]) traditional if-then-else 1039 | (WHILE test expr*) conditional iteration 1040 | (PROGN expr*) block construct 1041 | (RETURN [expr]) return from a function 1042 | 1043 | These statements are control constructs. 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | ADVSYS An Adventure Writing System Page 17 1061 | 1062 | 1063 | Primary expressions: 1064 | 1065 | integer (digits preceeded by an optional sign) 1066 | string (characters enclosed in double quotes) 1067 | action-name (an action name) 1068 | object-name (an object or class name) 1069 | property-name (a property name) 1070 | constant-name (a defined constant or function) 1071 | variable-name (a variable name) 1072 | 1073 | Since an adventure description contains a large quantity of 1074 | running text, the format for specifying string constants is 1075 | somewhat extended from normal programming languages. In 1076 | this system, a string is enclosed in double quotes. If the 1077 | end of line occurs before the closing quote within a string, 1078 | it is treated as if it were a space. Any number of 1079 | consecutive spaces is collapsed into a single space. Also, 1080 | the character pair "\n" is used to represent the "end of 1081 | line" character, the pair "\t" is used to represent the tab 1082 | character and the pair "\\" is used to represent the 1083 | backslash character. 1084 | 1085 | Examples: 1086 | 1087 | "This is a string.\n" 1088 | "This 1089 | is 1090 | a 1091 | string.\n" 1092 | 1093 | Both of the examples above represent the same string. 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | ADVSYS An Adventure Writing System Page 18 1127 | 1128 | 1129 | Definitions of symbols used above: 1130 | 1131 | expr an expression 1132 | value an expression 1133 | test an expression (NIL means false, anything else is true) 1134 | then-expr an expression 1135 | else-expr an expression 1136 | obj an expression that evaluates to an object 1137 | property-name an expression that evaluates to a property name 1138 | noun-phrase-number an expression that evaluates to a noun phrase number 1139 | variable a variable name 1140 | T true 1141 | NIL false 1142 | 1143 | 1144 | Built-in variables set by the parser: 1145 | 1146 | $ACTOR (actor noun phrase number) 1147 | $ACTION (action) 1148 | $DOBJECT (first direct object noun phrase number) 1149 | $NDOBJECTS (number of direct object noun phrases) 1150 | $IOBJECT (indirect object noun phrase number) 1151 | 1152 | 1153 | Other built-in variables: 1154 | 1155 | $OCOUNT (total number of objects in the system) 1156 | 1157 | 1158 | CURRENT COMPILER LIMITS 1159 | 1160 | 500 words 1161 | 500 objects 1162 | 20 properties per object 1163 | 200 actions or functions 1164 | 16384 bytes of code 1165 | 16384 bytes of data 1166 | 262144 bytes of text 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | -------------------------------------------------------------------------------- /advtrm.c: -------------------------------------------------------------------------------- 1 | /* advtrm.c - terminal i/o routines */ 2 | /* 3 | Copyright (c) 1993, by David Michael Betz 4 | All rights reserved 5 | */ 6 | 7 | #include 8 | #include "advint.h" 9 | 10 | /* useful definitions */ 11 | #define TRUE 1 12 | #define FALSE 0 13 | #define EOS '\0' 14 | #define LINEMAX 200 15 | #define WORDMAX 100 16 | 17 | /* local variables */ 18 | static char line[LINEMAX+1]; 19 | static int col,maxcol,row,maxrow; 20 | static int scnt,wcnt; 21 | static char word[WORDMAX+1],*wptr; 22 | static FILE *logfp = NULL; 23 | 24 | /* trm_init - initialize the terminal module */ 25 | void trm_init(int rows,int cols,char *name) 26 | { 27 | /* initialize the terminal i/o variables */ 28 | maxcol = cols-1; col = 0; 29 | maxrow = rows-1; row = 0; 30 | wptr = word; wcnt = 0; 31 | scnt = 0; 32 | 33 | /* open the log file */ 34 | if (name && (logfp = fopen(name,"w")) == NULL) 35 | error("can't open log file"); 36 | } 37 | 38 | /* trm_done - finish terminal i/o */ 39 | void trm_done(void) 40 | { 41 | if (wcnt) trm_word(); 42 | if (logfp) fclose(logfp); 43 | } 44 | 45 | /* trm_get - get a line */ 46 | char *trm_get(void) 47 | { 48 | if (wcnt) trm_word(); 49 | while (scnt--) putchr(' '); 50 | row = col = scnt = 0; 51 | return (trm_line()); 52 | } 53 | 54 | /* trm_str - output a string */ 55 | void trm_str(char *str) 56 | { 57 | while (*str) 58 | trm_chr(*str++); 59 | } 60 | 61 | /* trm_xstr - output a string without logging or word wrap */ 62 | void trm_xstr(char *str) 63 | { 64 | while (*str) 65 | advputc(*str++,stdout); 66 | } 67 | 68 | /* trm_chr - output a character */ 69 | void trm_chr(int ch) 70 | { 71 | switch (ch) { 72 | case ' ': 73 | if (wcnt) 74 | trm_word(); 75 | scnt++; 76 | break; 77 | case '\t': 78 | if (wcnt) 79 | trm_word(); 80 | scnt = (col + scnt + 8) & ~7; 81 | break; 82 | case '\n': 83 | if (wcnt) 84 | trm_word(); 85 | trm_eol(); 86 | scnt = 0; 87 | break; 88 | default: 89 | if (wcnt < WORDMAX) { 90 | *wptr++ = ch; 91 | wcnt++; 92 | } 93 | break; 94 | } 95 | } 96 | 97 | /* trm_word - output the current word */ 98 | void trm_word(void) 99 | { 100 | if (col + scnt + wcnt > maxcol) 101 | trm_eol(); 102 | else 103 | while (scnt--) 104 | { putchr(' '); col++; } 105 | for (wptr = word; wcnt--; col++) 106 | putchr(*wptr++); 107 | wptr = word; 108 | wcnt = 0; 109 | scnt = 0; 110 | } 111 | 112 | /* trm_eol - end the current line */ 113 | void trm_eol(void) 114 | { 115 | putchr('\n'); 116 | if (++row >= maxrow) 117 | { trm_wait(); row = 0; } 118 | col = 0; 119 | } 120 | 121 | /* trm_wait - wait for the user to type return */ 122 | void trm_wait(void) 123 | { 124 | trm_xstr(" << MORE >>\r"); 125 | advwaitc(); 126 | trm_xstr(" \r"); 127 | } 128 | 129 | /* trm_line - get an input line */ 130 | char *trm_line(void) 131 | { 132 | char *p; 133 | int ch; 134 | 135 | p = line; 136 | while ((ch = getchr()) != EOF && ch != '\n') 137 | switch (ch) { 138 | case '\177': 139 | case '\010': 140 | if (p != line) { 141 | if (ch != '\010') putchr('\010'); 142 | putchr(' '); 143 | putchr('\010'); 144 | p--; 145 | } 146 | break; 147 | default: 148 | if ((p - line) < LINEMAX) 149 | *p++ = ch; 150 | break; 151 | } 152 | *p = 0; 153 | return (ch == EOF ? NULL : line); 154 | } 155 | 156 | /* getchr - input a single character */ 157 | int getchr(void) 158 | { 159 | int ch; 160 | 161 | if ((ch = advgetc()) != EOF && logfp) 162 | advputc(ch,logfp); 163 | return (ch); 164 | } 165 | 166 | /* putchr - output a single character */ 167 | void putchr(int ch) 168 | { 169 | if (logfp) advputc(ch,logfp); 170 | advputc(ch,stdout); 171 | } 172 | -------------------------------------------------------------------------------- /objects.adi: -------------------------------------------------------------------------------- 1 | ; This is the object-oriented runtime package 2 | ; For AdvSys version 1.3 3 | ; by David Betz 4 | ; May 17, 1987 5 | 6 | ; **************************** 7 | ; HANDLER FORWARD DECLARATIONS 8 | ; **************************** 9 | 10 | (action init) 11 | (action update) 12 | (action before) 13 | (action after) 14 | (action error) 15 | 16 | ; **************************** 17 | ; EXTENDED FUNCTION DEFINTIONS 18 | ; **************************** 19 | 20 | (extended randomize 0) 21 | (extended rand 1) 22 | (extended picture 2) 23 | (extended resource 3) 24 | 25 | ; ******************** 26 | ; PROPERTY DEFINITIONS 27 | ; ******************** 28 | 29 | ; These properties will be used for connections between locations 30 | (property 31 | north ; the location to the north 32 | south ; the location to the south 33 | east ; the location to the east 34 | west ; the location to the west 35 | up ; the location above 36 | down) ; the location below 37 | 38 | ; Basic object properties 39 | (property 40 | initial-location ; the initial location of a "thing" 41 | description ; the "long" description of a location 42 | short-description) ; the "short" description of a location 43 | 44 | ; Connection properties 45 | (property 46 | parent ; the parent of an object 47 | sibling ; the next sibling of an object 48 | child) ; the first child of an object 49 | 50 | ; Location properties 51 | (property 52 | visited) ; true if location has been visited by player 53 | 54 | ; Portal properties 55 | (property 56 | closed ; true if the portal is closed 57 | locked ; true if the portal is locked 58 | key ; key to unlock the portal 59 | other-side) ; the other portal in a pair 60 | 61 | ; ********************** 62 | ; VOCABULARY DEFINITIONS 63 | ; ********************** 64 | 65 | ; Some abbreviations for common commands 66 | (synonym north n) 67 | (synonym south s) 68 | (synonym east e) 69 | (synonym west w) 70 | (synonym inventory i) 71 | 72 | ; Define the basic vocabulary 73 | (conjunction and) 74 | (article the that) 75 | 76 | ; ******************** 77 | ; VARIABLE DEFINITIONS 78 | ; ******************** 79 | 80 | (variable 81 | curloc ; the location of the player character 82 | %actor ; the actor object 83 | %dobject ; the direct object 84 | %iobject) ; the indirect object 85 | 86 | ; ************* 87 | ; THE MAIN LOOP 88 | ; ************* 89 | 90 | ; handler status codes 91 | (define ST-ABORT 1) 92 | (define ST-CHAIN 2) 93 | (define ST-FINISH 3) 94 | 95 | ; Handle a single command 96 | (define (single &aux sts) 97 | (setq sts (catch 1 (before) ST-CHAIN)) 98 | (if (= sts ST-ABORT) 99 | (return NIL) 100 | (if (= sts ST-CHAIN) 101 | (progn 102 | (if (= (catch 1 ($action) ST-CHAIN) ST-ABORT) 103 | (return NIL)) 104 | (catch 1 (after)))))) 105 | 106 | ; The main loop 107 | (main 108 | (catch 1 (init)) 109 | (while t 110 | (catch 1 (update)) 111 | (if (parse) 112 | (if (single) 113 | (while (and (next) (single)))) 114 | (catch 1 (error))))) 115 | 116 | ; **************************** 117 | ; HANDLER SEQUENCING FUNCTIONS 118 | ; **************************** 119 | 120 | ; Abort this turn 121 | (define (abort) 122 | (throw 1 ST-ABORT)) 123 | 124 | ; Exit from this handler, chain to the next 125 | (define (chain) 126 | (throw 1 ST-CHAIN)) 127 | 128 | ; Finish this turn (go directly to the after handler 129 | (define (finish) 130 | (throw 1 ST-FINISH)) 131 | 132 | ; ********************* 133 | ; CONNECTION PRIMITIVES 134 | ; ********************* 135 | 136 | ; Connect an object to a parent 137 | (define (connect p c) 138 | (setp c parent p) 139 | (setp c sibling (getp p child)) 140 | (setp p child c)) 141 | 142 | ; Connect all objects to their initial parents 143 | (define (connect-all &aux obj maxp1 par) 144 | (setq obj 1) 145 | (setq maxp1 (+ $ocount 1)) 146 | (while (< obj maxp1) 147 | (if (setq par (getp obj initial-location)) 148 | (connect par obj)) 149 | (setq obj (+ obj 1)))) 150 | 151 | ; Disconnect an object from its current parent 152 | (define (disconnect obj &aux this prev) 153 | (setq this (getp (getp obj parent) child)) 154 | (setq prev nil) 155 | (while this 156 | (if (= this obj) 157 | (progn 158 | (if prev 159 | (setp prev sibling (getp this sibling)) 160 | (setp (getp this parent) child (getp this sibling))) 161 | (setp this parent nil) 162 | (return))) 163 | (setq prev this) 164 | (setq this (getp this sibling)))) 165 | 166 | ; Print the contents of an object (used by "look") 167 | (define (print-contents obj prop &aux desc) 168 | (setq obj (getp obj child)) 169 | (while obj 170 | (if (setq desc (getp obj prop)) 171 | (progn 172 | (print " ") 173 | (print desc))) 174 | (setq obj (getp obj sibling)))) 175 | 176 | ; List the contents of an object (used for "inventory") 177 | (define (list-contents obj prop &aux desc) 178 | (setq obj (getp obj child)) 179 | (while obj 180 | (if (setq desc (getp obj prop)) 181 | (progn 182 | (print "\t") 183 | (print desc) 184 | (terpri))) 185 | (setq obj (getp obj sibling)))) 186 | 187 | ; ************************ 188 | ; OBJECT CLASS DEFINITIONS 189 | ; ************************ 190 | 191 | ; *********************** 192 | ; The "basic-thing" class 193 | ; *********************** 194 | 195 | (object basic-thing 196 | (property 197 | parent nil ; the parent of this object 198 | sibling nil)) ; the next sibling of this object 199 | 200 | ; *************************** 201 | ; The "location" object class 202 | ; *************************** 203 | 204 | (object location 205 | (property 206 | child nil ; the first object in this location 207 | visited nil) ; has the player been here yet? 208 | (method (knock? obj) 209 | T) 210 | (method (enter obj) 211 | (connect self obj) 212 | T) 213 | (method (leave obj dir &aux loc) 214 | (if (setq loc (getp self dir)) 215 | (if (send loc knock? obj) 216 | (progn 217 | (disconnect obj) 218 | (send loc enter obj))) 219 | (progn 220 | (print "There is no exit in that direction.\n") 221 | nil))) 222 | (method (describe) 223 | (if (getp self visited) 224 | (print (getp self short-description)) 225 | (progn 226 | (print (getp self description)) 227 | (print-contents self description) 228 | (setp self visited t))) 229 | (terpri))) 230 | 231 | 232 | ; ****************** 233 | ; The "portal" class 234 | ; ****************** 235 | 236 | (basic-thing portal 237 | (method (knock? obj) 238 | (if (getp self closed) 239 | (progn 240 | (print "The ") 241 | (print (getp self short-description)) 242 | (print " is closed!\n") 243 | nil) 244 | T)) 245 | (method (enter obj) 246 | (connect (getp (getp self other-side) parent) obj)) 247 | (method (open) 248 | (if (not (getp self closed)) 249 | (progn 250 | (print "The ") 251 | (print (getp self short-description)) 252 | (print " is already open!\n") 253 | nil) 254 | (if (getp self locked) 255 | (progn 256 | (print "The ") 257 | (print (getp self short-description)) 258 | (print " is locked!\n") 259 | nil) 260 | (progn 261 | (setp self closed nil) 262 | T)))) 263 | (method (close) 264 | (if (getp self closed) 265 | (progn 266 | (print "The ") 267 | (print (getp self short-description)) 268 | (print " is already closed!\n") 269 | nil) 270 | (progn 271 | (setp self closed T) 272 | T))) 273 | (method (lock thekey) 274 | (if (not (getp self closed)) 275 | (progn 276 | (print "The ") 277 | (print (getp self short-description)) 278 | (print " is not closed!\n") 279 | nil) 280 | (if (getp self locked) 281 | (progn 282 | (print "The ") 283 | (print (getp self short-description)) 284 | (print " is already locked!\n") 285 | nil) 286 | (if (not (= thekey (getp self key))) 287 | (progn 288 | (print "It doesn't fit the lock!\n") 289 | nil) 290 | (progn 291 | (setp self locked t) 292 | T))))) 293 | (method (unlock thekey) 294 | (if (not (getp self closed)) 295 | (progn 296 | (print "The ") 297 | (print (getp self short-description)) 298 | (print " is already open!\n") 299 | nil) 300 | (if (not (getp self locked)) 301 | (progn 302 | (print "The ") 303 | (print (getp self short-description)) 304 | (print " is not locked!\n") 305 | nil) 306 | (if (not (= thekey (getp self key))) 307 | (progn 308 | (print "It doesn't fit the lock!\n") 309 | nil) 310 | (progn 311 | (setp self locked nil) 312 | T)))))) 313 | 314 | ; ***************** 315 | ; The "actor" class 316 | ; ***************** 317 | 318 | (basic-thing actor 319 | (property 320 | child nil) ; the first "thing" carried by this actor 321 | (method (move dir) 322 | (send (getp self parent) leave self dir)) 323 | (method (take obj) 324 | (disconnect obj) 325 | (connect self obj)) 326 | (method (drop obj) 327 | (disconnect obj) 328 | (connect (getp self parent) obj)) 329 | (method (carrying? obj) 330 | (= (getp obj parent) self)) 331 | (method (inventory) 332 | (cond ((getp %actor child) 333 | (print "You are carrying:\n") 334 | (list-contents %actor short-description)) 335 | (T (print "You are empty-handed.\n"))))) 336 | 337 | ; ***************** 338 | ; The "thing" class (things that can be taken) 339 | ; ***************** 340 | 341 | (basic-thing thing 342 | (class-property 343 | takeable t)) 344 | 345 | ; **************************** 346 | ; The "stationary-thing" class (things that can't be moved) 347 | ; **************************** 348 | 349 | (basic-thing stationary-thing) 350 | 351 | ; *********************** 352 | ; MISCELLANEOUS FUNCTIONS 353 | ; *********************** 354 | 355 | ; Complain about a noun phrase 356 | (define (complain head n tail) 357 | (print head) 358 | (print-noun n) 359 | (print tail) 360 | (abort)) 361 | 362 | ; Find an object in a location 363 | (define (findobject loc n &aux this found) 364 | (setq this (getp loc child)) 365 | (setq found nil) 366 | (while this 367 | (if (match this n) 368 | (if found 369 | (complain "I don't know which " n " you mean!\n") 370 | (setq found this))) 371 | (setq this (getp this sibling))) 372 | found) 373 | 374 | ; Find an object in the player's current location 375 | ; (or in the player's inventory) 376 | (define (in-location n &aux obj) 377 | (if (or (setq obj (findobject curloc n)) 378 | (setq obj (findobject %actor n))) 379 | obj 380 | (complain "I don't see a " n " here!\n"))) 381 | 382 | ; Find an object in the player's inventory 383 | ; (or in the player's current location) 384 | (define (in-pocket n &aux obj) 385 | (if (or (setq obj (findobject %actor n)) 386 | (setq obj (findobject curloc n))) 387 | obj 388 | (complain "You don't have a " n "!\n"))) 389 | 390 | ; *************** 391 | ; ACTION DEFAULTS 392 | ; *************** 393 | 394 | (default 395 | (actor optional)) 396 | 397 | ; ****************** 398 | ; ACTION DEFINITIONS 399 | ; ****************** 400 | 401 | (action look 402 | (verb look) 403 | (code 404 | (setp curloc visited nil) 405 | (send curloc describe))) 406 | 407 | (action a-take 408 | (verb take get (pick up)) 409 | (direct-object) 410 | (code 411 | (setq %dobject (in-location $dobject)) 412 | (if (getp %dobject takeable) 413 | (progn 414 | (if (send %actor carrying? %dobject) 415 | (complain "You are already carrying the " $dobject "!\n")) 416 | (send %actor take %dobject) 417 | (print-noun $dobject) 418 | (print " taken.\n")) 419 | (complain "You can't take the " $dobject "!\n")))) 420 | 421 | (action take-err 422 | (verb take get (pick up)) 423 | (code 424 | (print "Take what?\n"))) 425 | 426 | (action a-drop 427 | (verb drop (put down)) 428 | (direct-object) 429 | (code 430 | (setq %dobject (in-pocket $dobject)) 431 | (if (send %actor carrying? %dobject) 432 | (progn 433 | (send %actor drop %dobject) 434 | (print-noun $dobject) 435 | (print " dropped.\n")) 436 | (complain "You aren't carrying the " $dobject "!\n")))) 437 | 438 | (action drop-err 439 | (verb drop (put down)) 440 | (code 441 | (print "Drop what?\n"))) 442 | 443 | (action give 444 | (verb give) 445 | (direct-object) 446 | (preposition to) 447 | (indirect-object) 448 | (code 449 | (setq %dobject (in-pocket $dobject)) 450 | (setq %iobject (in-location $iobject)) 451 | (if (send %actor carrying? %dobject) 452 | (progn 453 | (send %actor drop %dobject) 454 | (send %iobject take %dobject) 455 | (print-noun $dobject) 456 | (print " given.\n")) 457 | (complain "You aren't carrying the " $dobject "!\n")))) 458 | 459 | (action give-err 460 | (verb give) 461 | (direct-object optional) 462 | (code 463 | (if $dobject 464 | (complain "Give the " $dobject " to who?\n")) 465 | (print "Give what?\n"))) 466 | 467 | (action a-inventory 468 | (verb inventory) 469 | (code 470 | (send %actor inventory))) 471 | 472 | ; *************** 473 | ; PORTAL COMMANDS 474 | ; *************** 475 | 476 | (action a-open 477 | (verb open) 478 | (direct-object) 479 | (code 480 | (setq %dobject (in-location $dobject)) 481 | (send %dobject open))) 482 | 483 | (action open-err 484 | (verb open) 485 | (code 486 | (print "Open what?\n"))) 487 | 488 | (action a-close 489 | (verb close) 490 | (direct-object) 491 | (code 492 | (setq %dobject (in-location $dobject)) 493 | (send %dobject close))) 494 | 495 | (action close-err 496 | (verb close) 497 | (code 498 | (print "Close what?\n"))) 499 | 500 | (action a-lock 501 | (verb lock) 502 | (direct-object) 503 | (preposition with) 504 | (indirect-object) 505 | (code 506 | (setq %dobject (in-location $dobject)) 507 | (setq %iobject (in-pocket $iobject)) 508 | (send %dobject lock %iobject))) 509 | 510 | (action lock-err 511 | (verb lock) 512 | (direct-object optional) 513 | (code 514 | (if $dobject 515 | (complain "Lock the " $dobject " with what?\n")) 516 | (print "Lock what?\n"))) 517 | 518 | (action a-unlock 519 | (verb unlock) 520 | (direct-object) 521 | (preposition with) 522 | (indirect-object) 523 | (code 524 | (setq %dobject (in-location $dobject)) 525 | (setq %iobject (in-pocket $iobject)) 526 | (send %dobject unlock %iobject))) 527 | 528 | (action unlock-err 529 | (verb unlock) 530 | (direct-object optional) 531 | (code 532 | (if $dobject 533 | (complain "Unlock the " $dobject " with what?\n")) 534 | (print "Unlock what?\n"))) 535 | 536 | ; ********************* 537 | ; GAME CONTROL COMMANDS 538 | ; ********************* 539 | 540 | (action save 541 | (verb save) 542 | (code 543 | (save))) 544 | 545 | (action restore 546 | (verb restore) 547 | (code 548 | (restore))) 549 | 550 | (action restart 551 | (verb restart) 552 | (code 553 | (restart))) 554 | 555 | (action quit 556 | (verb quit) 557 | (code 558 | (print "Are you sure you want to quit? ") 559 | (if (yes-or-no) 560 | (exit)))) 561 | 562 | ; ************** 563 | ; TRAVEL ACTIONS 564 | ; ************** 565 | 566 | (action go-north 567 | (verb north (go north)) 568 | (code 569 | (send %actor move north))) 570 | 571 | (action go-south 572 | (verb south (go south)) 573 | (code 574 | (send %actor move south))) 575 | 576 | (action go-east 577 | (verb east (go east)) 578 | (code 579 | (send %actor move east))) 580 | 581 | (action go-west 582 | (verb west (go west)) 583 | (code 584 | (send %actor move west))) 585 | 586 | (action go-up 587 | (verb up (go up)) 588 | (code 589 | (send %actor move up))) 590 | 591 | (action go-down 592 | (verb down (go down)) 593 | (code 594 | (send %actor move down))) 595 | 596 | ; ******************* 597 | ; HANDLER DEFINITIONS 598 | ; ******************* 599 | 600 | (define (init) 601 | (print welcome) 602 | (connect-all) 603 | (setq curloc nil)) 604 | 605 | (define (update) 606 | (if (not (= (getp adventurer parent) curloc)) 607 | (progn 608 | (setq curloc (getp adventurer parent)) 609 | (send curloc describe)))) 610 | 611 | (define (before) 612 | (setq %actor adventurer) 613 | (if $actor 614 | (progn 615 | (setq %actor (in-location $actor)) 616 | (if (not (= (class %actor) actor)) 617 | (complain "You can't talk to a " $actor "!\n"))))) 618 | 619 | (define (after)) 620 | (define (error)) 621 | 622 | 623 | 624 | 625 | -------------------------------------------------------------------------------- /osample.adv: -------------------------------------------------------------------------------- 1 | ; OSAMPLE.ADV 2 | ; 3 | ; This is a VERY simple sample adventure that uses the "OBJECTS.ADI" 4 | ; runtime support code. It isn't interesting to play, but does illustrate 5 | ; most of the features of the adventure authoring system. Try compiling 6 | ; it using the command: 7 | ; 8 | ; A>ADVCOM OSAMPLE 9 | ; 10 | ; When the compile has finished, run the adventure using the command: 11 | ; 12 | ; A>ADVINT OSAMPLE 13 | ; 14 | ; You should then see the initial welcome message and a description of 15 | ; your initial location. You can use the direction names to move from 16 | ; one location to the next (or the abreviations N,S,E,W). You should try 17 | ; manipulating objects using TAKE and DROP. You can manipulate more than 18 | ; one at a time by using the conjunction AND. You can also GIVE an object 19 | ; to another creature like the DOG or CAT. You can instruct another 20 | ; creature to perform an action like: 21 | ; 22 | ; CAT, GIVE THE DOG THE KEY 23 | ; 24 | ; You can also experiment with using adjectives to distinguish between 25 | ; objects (there is more than one KEY in this adventure). 26 | 27 | (adventure sample 1) 28 | 29 | (define welcome "Welcome to the sample adventure.\n") 30 | 31 | @objects.adi 32 | 33 | (actor adventurer 34 | (noun me) 35 | (property 36 | initial-location livingroom)) 37 | 38 | (actor dog 39 | (noun dog) 40 | (adjective small) 41 | (property 42 | description "There is a small dog here." 43 | short-description "a small dog" 44 | initial-location kitchen)) 45 | 46 | (actor cat 47 | (noun cat) 48 | (property 49 | description "There is a cat here." 50 | short-description "a cat" 51 | initial-location kitchen)) 52 | 53 | (location storage-room 54 | (property 55 | description "You are in a small storage room with many empty shelves. 56 | The only exit is a door to the west." 57 | short-description "You are in the storage room." 58 | west hallway) 59 | (method (leave obj dir) 60 | (if (send obj carrying? rusty-key) 61 | (send-super leave obj dir) 62 | (print "You seem to be missing something!\n")))) 63 | 64 | (location hallway 65 | (property 66 | description "You are in a long narrow hallway. There is a door to the 67 | east into a small dark room. There are also exits on both 68 | the north and south ends of the hall." 69 | short-description "You are in the hallway." 70 | east storage-room 71 | north kitchen 72 | south livingroom)) 73 | 74 | (location kitchen 75 | (property 76 | description "This is a rather dusty kitchen. There is a hallway to the 77 | south and a pantry to the west." 78 | short-description "You are in the kitchen." 79 | south hallway 80 | west pantry)) 81 | 82 | (location pantry 83 | (property 84 | description "This is the kitchen pantry. The kitchen is through a 85 | doorway to the east." 86 | short-description "You are in the pantry." 87 | east kitchen) 88 | (method (enter obj) 89 | (send-super enter obj))) 90 | 91 | (location livingroom 92 | (property 93 | description "This appears to be the livingroom. There is a hallway to 94 | the north and a closet to the west." 95 | short-description "You are in the livingroom." 96 | north hallway 97 | west closet 98 | south front-door-1)) 99 | 100 | (location outside 101 | (property 102 | description "You are outside a small house. The front door is to the 103 | north." 104 | short-description "You are outside." 105 | north front-door-2)) 106 | 107 | (portal front-door 108 | (noun door) 109 | (adjective front) 110 | (class-property 111 | short-description "front door" 112 | closed t 113 | locked t 114 | key rusty-key)) 115 | 116 | (front-door front-door-1 117 | (property 118 | initial-location livingroom 119 | other-side front-door-2)) 120 | 121 | (front-door front-door-2 122 | (property 123 | initial-location outside 124 | other-side front-door-1)) 125 | 126 | (location closet 127 | (property 128 | description "This is the livingroom closet. The livingroom is through 129 | a doorway to the east." 130 | short-description "You are in the closet." 131 | east livingroom) 132 | (method (enter obj) 133 | (send-super enter obj))) 134 | 135 | (thing rusty-key 136 | (noun key) 137 | (adjective rusty) 138 | (property 139 | description "There is a rusty key here." 140 | short-description "a rusty key" 141 | initial-location storage-room)) 142 | 143 | (thing silver-key 144 | (noun key) 145 | (adjective small silver) 146 | (property 147 | description "There is a small silver key here." 148 | short-description "a small silver key" 149 | initial-location closet)) 150 | 151 | 152 | 153 | --------------------------------------------------------------------------------