| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/TADS2/EXECMD.C,v 1.5 1999/07/11 00:46:29 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1987, 1990 by Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | execmd - TADS Interpreter Execute user Command |
| | 15 | Function |
| | 16 | Executes a user command after it has been parsed |
| | 17 | Notes |
| | 18 | TADS 2.0 version |
| | 19 | |
| | 20 | This module contains the implementation of the entire "turn" sequence, |
| | 21 | which is: |
| | 22 | |
| | 23 | preCommand(actor, verb, dobj-list, prep, iobj) |
| | 24 | verb.verbAction(actor, do, prep, io) |
| | 25 | actor.actorAction( verb, do, prep, io ) |
| | 26 | actor.location.roomAction( actor, verb, do, prep, io ) |
| | 27 | if ( io ) |
| | 28 | { |
| | 29 | io.iobjCheck(actor, verb, dobj, prep) |
| | 30 | if (io does not define verIo<Verb> directly) |
| | 31 | io.iobjGen(actor, verb, dobj, prep) |
| | 32 | do.dobjCheck(actor, verb, iobj, prep) |
| | 33 | if (do does not define do<Verb> directly) |
| | 34 | do.dobjGen(actor, verb, iobj, prep) |
| | 35 | io.verIo<Verb>( actor, do ) |
| | 36 | if ( noOutput ) |
| | 37 | { |
| | 38 | do.verDo<Verb>( actor, io ) |
| | 39 | if ( noOutput ) io.io<Verb>( actor, do ) |
| | 40 | } |
| | 41 | } |
| | 42 | else if ( do ) |
| | 43 | { |
| | 44 | do.dobjCheck(actor, verb, nil, nil) |
| | 45 | if (do does not define do<Verb> directly) |
| | 46 | do.dobjGen(actor, verb, nil, nil) |
| | 47 | do.verDo<Verb>( actor ) |
| | 48 | if ( noOutput )do.do<Verb>( actor ) |
| | 49 | } |
| | 50 | else |
| | 51 | { |
| | 52 | verb.action( actor ) |
| | 53 | } |
| | 54 | postAction(actor, verb, dobj, prep, iobj, error_code) |
| | 55 | daemons |
| | 56 | fuses |
| | 57 | endCommand(actor, verb, dobj-list, prep, iobj, error_code) |
| | 58 | |
| | 59 | If an 'exit' or 'exitobj' is encountered, we skip straight to the |
| | 60 | daemons. If an abort is encountered, we skip to endCommand. If |
| | 61 | askio, or askdo is encountered, we skip everything remaining. Under |
| | 62 | any of these exit scenarios, we return success to our caller. |
| | 63 | |
| | 64 | This module also contains code to set and remove fuses and daemons, |
| | 65 | since they are part of the player turn sequence. |
| | 66 | Returns |
| | 67 | 0 for success, other for failure. |
| | 68 | Modified |
| | 69 | 03/25/92 MJRoberts - TADS 2.0 |
| | 70 | 08/13/91 MJRoberts - add him/her support |
| | 71 | 11/30/90 MJRoberts - moved main execmd loop here from vocab, moved |
| | 72 | fuses/daemon stuff to fuses.c |
| | 73 | 04/23/90 MJRoberts - clear alarms (notify's) in clrdaemons() |
| | 74 | 07/07/89 MJRoberts - add fuse/daemon context value |
| | 75 | 06/28/89 MJRoberts - default message if objects don't handle the verb |
| | 76 | 11/06/88 MJRoberts - provide error messages in setfuse, setdaemon, etc. |
| | 77 | 11/06/88 MJRoberts - be careful not to send doX message on ask?o |
| | 78 | 11/05/88 MJRoberts - save tpldef with "again" |
| | 79 | 10/30/88 MJRoberts - new "version 6" parser interface |
| | 80 | 12/28/87 MJRoberts - created |
| | 81 | */ |
| | 82 | |
| | 83 | #include <stdio.h> |
| | 84 | #include <ctype.h> |
| | 85 | #include <string.h> |
| | 86 | #include <stdlib.h> |
| | 87 | |
| | 88 | #include "os.h" |
| | 89 | #include "err.h" |
| | 90 | #include "voc.h" |
| | 91 | #include "tio.h" |
| | 92 | #include "mch.h" |
| | 93 | #include "mcm.h" |
| | 94 | #include "obj.h" |
| | 95 | #include "prp.h" |
| | 96 | #include "run.h" |
| | 97 | #include "lst.h" |
| | 98 | #include "bif.h" |
| | 99 | |
| | 100 | /* allocate and initialize a fuse/daemon/notifier array */ |
| | 101 | void vocinialo(voccxdef *ctx, vocddef **what, int cnt) |
| | 102 | { |
| | 103 | vocddef *p; |
| | 104 | |
| | 105 | *what = (vocddef *)mchalo(ctx->voccxerr, |
| | 106 | (ushort)(cnt * sizeof(vocddef)), "vocinialo"); |
| | 107 | |
| | 108 | /* set all object/function entries to MCMONINV to indicate not-in-use */ |
| | 109 | for (p = *what ; cnt ; ++p, --cnt) |
| | 110 | p->vocdfn = MCMONINV; |
| | 111 | } |
| | 112 | |
| | 113 | /* internal service routine to clear one set of fuses/deamons/alerters */ |
| | 114 | static void vocdmn1clr(vocddef *dmn, uint cnt) |
| | 115 | { |
| | 116 | for ( ; cnt ; --cnt, ++dmn) dmn->vocdfn = MCMONINV; |
| | 117 | } |
| | 118 | |
| | 119 | /* delete all fuses/daemons/alerters */ |
| | 120 | void vocdmnclr(voccxdef *ctx) |
| | 121 | { |
| | 122 | vocdmn1clr(ctx->voccxfus, ctx->voccxfuc); |
| | 123 | vocdmn1clr(ctx->voccxdmn, ctx->voccxdmc); |
| | 124 | vocdmn1clr(ctx->voccxalm, ctx->voccxalc); |
| | 125 | } |
| | 126 | |
| | 127 | /* save undo information for a daemon/fuse/notifier */ |
| | 128 | static void vocdusav(voccxdef *ctx, vocddef *what) |
| | 129 | { |
| | 130 | uchar *p; |
| | 131 | objucxdef *uc = ctx->voccxundo; |
| | 132 | ushort siz = sizeof(what) + sizeof(*what) + 1; |
| | 133 | |
| | 134 | /* if we don't need to save undo, quit now */ |
| | 135 | if (uc == 0 || !objuok(uc)) |
| | 136 | return; |
| | 137 | |
| | 138 | /* reserve space for our record */ |
| | 139 | p = objures(uc, OBJUCLI, siz); |
| | 140 | |
| | 141 | /* set up our undo record */ |
| | 142 | *p = VOC_UNDO_DAEMON; |
| | 143 | memcpy(p + 1, &what, (size_t)sizeof(what)); |
| | 144 | memcpy(p + 1 + sizeof(what), what, (size_t)sizeof(*what)); |
| | 145 | |
| | 146 | uc->objucxhead += siz; |
| | 147 | } |
| | 148 | |
| | 149 | /* apply undo information for a daemon/fuse/notifier */ |
| | 150 | void vocdundo(void *ctx0, uchar *data) |
| | 151 | { |
| | 152 | voccxdef *ctx = (voccxdef *)ctx0; |
| | 153 | vocddef *daemon; |
| | 154 | objnum objn; |
| | 155 | ushort siz; |
| | 156 | ushort wrdsiz; |
| | 157 | uchar *p; |
| | 158 | int sccnt; |
| | 159 | objnum sc; |
| | 160 | int len1, len2; |
| | 161 | prpnum prp; |
| | 162 | int flags; |
| | 163 | uchar *wrd; |
| | 164 | |
| | 165 | switch(*data) |
| | 166 | { |
| | 167 | case VOC_UNDO_DAEMON: |
| | 168 | memcpy(&daemon, data + 1, (size_t)sizeof(daemon)); |
| | 169 | memcpy(daemon, data + 1 + sizeof(daemon), (size_t)sizeof(*daemon)); |
| | 170 | break; |
| | 171 | |
| | 172 | case VOC_UNDO_NEWOBJ: |
| | 173 | /* get the object number */ |
| | 174 | objn = osrp2(data + 1); |
| | 175 | |
| | 176 | /* delete the object's inheritance and vocabulary records */ |
| | 177 | vocdel(ctx, objn); |
| | 178 | vocidel(ctx, objn); |
| | 179 | |
| | 180 | /* delete the object */ |
| | 181 | mcmfre(ctx->voccxmem, (mcmon)objn); |
| | 182 | break; |
| | 183 | |
| | 184 | case VOC_UNDO_DELOBJ: |
| | 185 | /* get the object's number and size */ |
| | 186 | objn = osrp2(data + 1); |
| | 187 | siz = osrp2(data + 3); |
| | 188 | wrdsiz = osrp2(data + 5); |
| | 189 | |
| | 190 | /* allocate the object with its original number */ |
| | 191 | p = mcmalonum(ctx->voccxmem, siz, (mcmon)objn); |
| | 192 | |
| | 193 | /* copy the contents back to the object */ |
| | 194 | memcpy(p, data + 7, (size_t)siz); |
| | 195 | |
| | 196 | /* get its superclass if it has one */ |
| | 197 | sccnt = objnsc(p); |
| | 198 | if (sccnt) sc = osrp2(objsc(p)); |
| | 199 | |
| | 200 | /* unlock the object, and create its inheritance records */ |
| | 201 | mcmunlck(ctx->voccxmem, (mcmon)objn); |
| | 202 | vociadd(ctx, objn, MCMONINV, sccnt, &sc, VOCIFNEW | VOCIFVOC); |
| | 203 | |
| | 204 | /* restore the words as well */ |
| | 205 | data += 7 + siz; |
| | 206 | while (wrdsiz) |
| | 207 | { |
| | 208 | /* get the lengths from the buffer */ |
| | 209 | len1 = osrp2(data + 2); |
| | 210 | len2 = osrp2(data + 4); |
| | 211 | |
| | 212 | /* add the word */ |
| | 213 | vocadd2(ctx, data[0], objn, data[1], data+6, len1, |
| | 214 | data+6+len1, len2); |
| | 215 | |
| | 216 | /* remove this object from the word size */ |
| | 217 | wrdsiz -= 6 + len1 + len2; |
| | 218 | data += 6 + len1 + len2; |
| | 219 | } |
| | 220 | break; |
| | 221 | |
| | 222 | case VOC_UNDO_ADDVOC: |
| | 223 | case VOC_UNDO_DELVOC: |
| | 224 | flags = *(data + 1); |
| | 225 | prp = *(data + 2); |
| | 226 | objn = osrp2(data + 3); |
| | 227 | wrd = data + 5; |
| | 228 | if (*data == VOC_UNDO_ADDVOC) |
| | 229 | vocdel1(ctx, objn, (char *)wrd, prp, FALSE, FALSE, FALSE); |
| | 230 | else |
| | 231 | vocadd(ctx, prp, objn, flags, (char *)wrd); |
| | 232 | break; |
| | 233 | |
| | 234 | case VOC_UNDO_SETME: |
| | 235 | ctx->voccxme = osrp2(data + 1); |
| | 236 | break; |
| | 237 | } |
| | 238 | } |
| | 239 | |
| | 240 | /* determine size of one of our client undo records */ |
| | 241 | ushort OS_LOADDS vocdusz(void *ctx0, uchar *data) |
| | 242 | { |
| | 243 | VARUSED(ctx0); |
| | 244 | |
| | 245 | switch(*data) |
| | 246 | { |
| | 247 | case VOC_UNDO_DAEMON: |
| | 248 | /* it's the size of the structures, plus one for the header */ |
| | 249 | return (ushort)((sizeof(vocddef *) + sizeof(vocddef)) + 1); |
| | 250 | |
| | 251 | case VOC_UNDO_NEWOBJ: |
| | 252 | /* 2 bytes for the objnum plus 1 for the header */ |
| | 253 | return 2 + 1; |
| | 254 | |
| | 255 | case VOC_UNDO_DELOBJ: |
| | 256 | /* |
| | 257 | * 1 (header) + 2 (objnum) + 2 (size) + 2 (word size) + object |
| | 258 | * data size + word size |
| | 259 | */ |
| | 260 | return osrp2(data+3) + osrp2(data+5) + 1+2+2+2; |
| | 261 | |
| | 262 | case VOC_UNDO_ADDVOC: |
| | 263 | case VOC_UNDO_DELVOC: |
| | 264 | /* 1 (header) + 2 (objnum) + 1 (flags) + 1 (type) + word size */ |
| | 265 | return osrp2(data + 5) + 5; |
| | 266 | |
| | 267 | default: |
| | 268 | return 0; |
| | 269 | } |
| | 270 | } |
| | 271 | |
| | 272 | /* save undo for object creation */ |
| | 273 | void vocdusave_newobj(voccxdef *ctx, objnum objn) |
| | 274 | { |
| | 275 | objucxdef *uc = ctx->voccxundo; |
| | 276 | uchar *p; |
| | 277 | |
| | 278 | p = objures(uc, OBJUCLI, 3); |
| | 279 | *p = VOC_UNDO_NEWOBJ; |
| | 280 | oswp2(p+1, objn); |
| | 281 | |
| | 282 | uc->objucxhead += 3; |
| | 283 | } |
| | 284 | |
| | 285 | /* save undo information for a change in the "Me" object */ |
| | 286 | void vocdusave_me(voccxdef *ctx, objnum old_me) |
| | 287 | { |
| | 288 | uchar *p; |
| | 289 | objucxdef *uc = ctx->voccxundo; |
| | 290 | |
| | 291 | /* if we don't need to save undo, there's nothing to do */ |
| | 292 | if (uc == 0 || !objuok(uc)) |
| | 293 | return; |
| | 294 | |
| | 295 | /* reserve space for our record */ |
| | 296 | p = objures(uc, OBJUCLI, 3); |
| | 297 | *p = VOC_UNDO_SETME; |
| | 298 | oswp2(p+1, old_me); |
| | 299 | |
| | 300 | /* absorb the space */ |
| | 301 | uc->objucxhead += 3; |
| | 302 | } |
| | 303 | |
| | 304 | /* callback context structure */ |
| | 305 | struct delobj_cb_ctx |
| | 306 | { |
| | 307 | uchar *p; |
| | 308 | }; |
| | 309 | |
| | 310 | /* |
| | 311 | * Iteration callback to write vocabulary words for an object being |
| | 312 | * deleted to an undo stream, so that they can be restored if the |
| | 313 | * deletion is undone. |
| | 314 | */ |
| | 315 | static void delobj_cb(void *ctx0, vocdef *voc, vocwdef *vocw) |
| | 316 | { |
| | 317 | struct delobj_cb_ctx *ctx = (struct delobj_cb_ctx *)ctx0; |
| | 318 | uchar *p = ctx->p; |
| | 319 | |
| | 320 | /* write this object's header to the stream */ |
| | 321 | p[0] = vocw->vocwtyp; |
| | 322 | p[1] = vocw->vocwflg; |
| | 323 | oswp2(p+2, voc->voclen); |
| | 324 | oswp2(p+4, voc->vocln2); |
| | 325 | |
| | 326 | /* write the words as well */ |
| | 327 | memcpy(p+6, voc->voctxt, (size_t)(voc->voclen + voc->vocln2)); |
| | 328 | |
| | 329 | /* advance the pointer */ |
| | 330 | ctx->p += 6 + voc->voclen + voc->vocln2; |
| | 331 | } |
| | 332 | |
| | 333 | /* save undo for object deletion */ |
| | 334 | void vocdusave_delobj(voccxdef *ctx, objnum objn) |
| | 335 | { |
| | 336 | objucxdef *uc = ctx->voccxundo; |
| | 337 | uchar *p; |
| | 338 | uchar *objp; |
| | 339 | uint siz; |
| | 340 | int wrdsiz; |
| | 341 | int wrdcnt; |
| | 342 | struct delobj_cb_ctx fnctx; |
| | 343 | |
| | 344 | /* figure out how much we need to save */ |
| | 345 | objp = mcmlck(ctx->voccxmem, (mcmon)objn); |
| | 346 | siz = objfree(objp); |
| | 347 | |
| | 348 | /* figure the word size */ |
| | 349 | voc_count(ctx, objn, 0, &wrdcnt, &wrdsiz); |
| | 350 | |
| | 351 | /* |
| | 352 | * we need to store an additional 6 bytes (2-length1, 2-length2, |
| | 353 | * 1-type, 1-flags) for each word |
| | 354 | */ |
| | 355 | wrdsiz += wrdcnt*6; |
| | 356 | |
| | 357 | /* set up the undo header */ |
| | 358 | p = objures(uc, OBJUCLI, (ushort)(7 + siz + wrdsiz)); |
| | 359 | *p = VOC_UNDO_DELOBJ; |
| | 360 | oswp2(p+1, objn); |
| | 361 | oswp2(p+3, siz); |
| | 362 | oswp2(p+5, wrdsiz); |
| | 363 | |
| | 364 | /* save the object's data */ |
| | 365 | memcpy(p+7, objp, (size_t)siz); |
| | 366 | |
| | 367 | /* write the words */ |
| | 368 | fnctx.p = p+7 + siz; |
| | 369 | voc_iterate(ctx, objn, delobj_cb, &fnctx); |
| | 370 | |
| | 371 | /* unlock the object and advance the undo pointer */ |
| | 372 | mcmunlck(ctx->voccxmem, (mcmon)objn); |
| | 373 | uc->objucxhead += 7 + siz + wrdsiz; |
| | 374 | } |
| | 375 | |
| | 376 | /* save undo for word creation */ |
| | 377 | void vocdusave_addwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, |
| | 378 | char *wrd) |
| | 379 | { |
| | 380 | ushort wrdsiz; |
| | 381 | uchar *p; |
| | 382 | objucxdef *uc = ctx->voccxundo; |
| | 383 | |
| | 384 | /* figure out how much space we need, and reserve it */ |
| | 385 | wrdsiz = osrp2(wrd); |
| | 386 | p = objures(uc, OBJUCLI, (ushort)(5 + wrdsiz)); |
| | 387 | |
| | 388 | *p = VOC_UNDO_ADDVOC; |
| | 389 | *(p+1) = flags; |
| | 390 | *(p+2) = (uchar)typ; |
| | 391 | oswp2(p+3, objn); |
| | 392 | memcpy(p+5, wrd, (size_t)wrdsiz); |
| | 393 | |
| | 394 | uc->objucxhead += 5 + wrdsiz; |
| | 395 | } |
| | 396 | |
| | 397 | /* save undo for word deletion */ |
| | 398 | void vocdusave_delwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, |
| | 399 | char *wrd) |
| | 400 | { |
| | 401 | ushort wrdsiz; |
| | 402 | uchar *p; |
| | 403 | objucxdef *uc = ctx->voccxundo; |
| | 404 | |
| | 405 | /* figure out how much space we need, and reserve it */ |
| | 406 | wrdsiz = osrp2(wrd); |
| | 407 | p = objures(uc, OBJUCLI, (ushort)(5 + wrdsiz)); |
| | 408 | |
| | 409 | *p = VOC_UNDO_DELVOC; |
| | 410 | *(p+1) = flags; |
| | 411 | *(p+2) = (uchar)typ; |
| | 412 | oswp2(p+3, objn); |
| | 413 | memcpy(p+5, wrd, (size_t)wrdsiz); |
| | 414 | |
| | 415 | uc->objucxhead += 5 + wrdsiz; |
| | 416 | } |
| | 417 | |
| | 418 | |
| | 419 | |
| | 420 | /* set a fuse/daemon/notifier */ |
| | 421 | void vocsetfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, |
| | 422 | uint tm, runsdef *val, int err) |
| | 423 | { |
| | 424 | int slots; |
| | 425 | |
| | 426 | if (what == ctx->voccxdmn) |
| | 427 | slots = ctx->voccxdmc; |
| | 428 | else if (what == ctx->voccxalm) |
| | 429 | slots = ctx->voccxalc; |
| | 430 | else if (what == ctx->voccxfus) |
| | 431 | slots = ctx->voccxfuc; |
| | 432 | else |
| | 433 | errsig(ctx->voccxerr, ERR_BADSETF); |
| | 434 | |
| | 435 | /* find a free slot, and set up our fuse/daemon */ |
| | 436 | for ( ; slots ; ++what, --slots) |
| | 437 | { |
| | 438 | if (what->vocdfn == MCMONINV) |
| | 439 | { |
| | 440 | /* save an undo record for this slot before changing */ |
| | 441 | vocdusav(ctx, what); |
| | 442 | |
| | 443 | /* record the information */ |
| | 444 | what->vocdfn = func; |
| | 445 | if (val != 0) |
| | 446 | OSCPYSTRUCT(what->vocdarg, *val); |
| | 447 | what->vocdprp = prop; |
| | 448 | what->vocdtim = tm; |
| | 449 | |
| | 450 | /* |
| | 451 | * the fuse/notifier/daemon is set - no need to look further |
| | 452 | * for an open slot |
| | 453 | */ |
| | 454 | return; |
| | 455 | } |
| | 456 | } |
| | 457 | |
| | 458 | /* we didn't find an open slot - signal the appropriate error */ |
| | 459 | errsig(ctx->voccxerr, err); |
| | 460 | } |
| | 461 | |
| | 462 | /* remove a fuse/daemon/notifier */ |
| | 463 | void vocremfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, |
| | 464 | runsdef *val, int err) |
| | 465 | { |
| | 466 | int slots; |
| | 467 | |
| | 468 | if (what == ctx->voccxdmn) slots = ctx->voccxdmc; |
| | 469 | else if (what == ctx->voccxalm) slots = ctx->voccxalc; |
| | 470 | else if (what == ctx->voccxfus) slots = ctx->voccxfuc; |
| | 471 | else errsig(ctx->voccxerr, ERR_BADREMF); |
| | 472 | |
| | 473 | /* find the slot with this same fuse/daemon/notifier, and remove it */ |
| | 474 | for ( ; slots ; ++what, --slots) |
| | 475 | { |
| | 476 | if (what->vocdfn == func |
| | 477 | && what->vocdprp == prop |
| | 478 | && (!val || (val->runstyp == what->vocdarg.runstyp |
| | 479 | && !memcmp(&val->runsv, &what->vocdarg.runsv, |
| | 480 | (size_t)datsiz(val->runstyp, |
| | 481 | &val->runsv))))) |
| | 482 | { |
| | 483 | /* save an undo record for this slot before changing */ |
| | 484 | vocdusav(ctx, what); |
| | 485 | |
| | 486 | what->vocdfn = MCMONINV; |
| | 487 | return; |
| | 488 | } |
| | 489 | } |
| | 490 | |
| | 491 | /* errsig(ctx->voccxerr, err); <<<harmless - don't signal it>>> */ |
| | 492 | } |
| | 493 | |
| | 494 | /* |
| | 495 | * Count one or more turns - burn all fuses down by the given number of |
| | 496 | * turns. Execute any fuses that expire within the given interval, but |
| | 497 | * not any that expire at the end of the last turn counted here. (If |
| | 498 | * incrementing by one turn only, no fuses will be executed.) If the |
| | 499 | * do_fuses flag is false, fuses are simply deleted if they burn down |
| | 500 | * within the interval. |
| | 501 | */ |
| | 502 | void vocturn(voccxdef *ctx, int turncnt, int do_fuses) |
| | 503 | { |
| | 504 | vocddef *p; |
| | 505 | int i; |
| | 506 | int do_exe; |
| | 507 | |
| | 508 | while (turncnt--) |
| | 509 | { |
| | 510 | /* presume we won't find anything to execute */ |
| | 511 | do_exe = FALSE; |
| | 512 | |
| | 513 | /* go through notifiers, looking for fuse-type notifiers */ |
| | 514 | for (i = ctx->voccxalc, p = ctx->voccxalm ; i ; ++p, --i) |
| | 515 | { |
| | 516 | if (p->vocdfn != MCMONINV |
| | 517 | && p->vocdtim != VOCDTIM_EACH_TURN |
| | 518 | && p->vocdtim != 0) |
| | 519 | { |
| | 520 | /* save an undo record for this slot before changing */ |
| | 521 | vocdusav(ctx, p); |
| | 522 | |
| | 523 | if (--(p->vocdtim) == 0) |
| | 524 | do_exe = TRUE; |
| | 525 | } |
| | 526 | } |
| | 527 | |
| | 528 | /* now go through the fuses */ |
| | 529 | for (i = ctx->voccxfuc, p = ctx->voccxfus ; i ; ++p, --i) |
| | 530 | { |
| | 531 | if (p->vocdfn != MCMONINV && p->vocdtim != 0) |
| | 532 | { |
| | 533 | /* save an undo record for this slot before changing */ |
| | 534 | vocdusav(ctx, p); |
| | 535 | |
| | 536 | if (--(p->vocdtim) == 0) |
| | 537 | do_exe = TRUE; |
| | 538 | } |
| | 539 | } |
| | 540 | |
| | 541 | /* |
| | 542 | * if we'll be doing more, and anything burned down, run |
| | 543 | * current fuses before going on to the next turn |
| | 544 | */ |
| | 545 | if ((!do_fuses || turncnt) && do_exe) |
| | 546 | exefuse(ctx, do_fuses); |
| | 547 | } |
| | 548 | } |
| | 549 | |
| | 550 | /* |
| | 551 | * display a default error message for a verb/dobj/iobj combo. |
| | 552 | * The message is "I don't know how to <verb.sdesc> <dobj.thedesc>" if |
| | 553 | * the dobj is present, and "I don't know how to <verb.sdesc> anything |
| | 554 | * <prep.sdesc> <iobj.thedesc>" if the iobj is present. Such a message |
| | 555 | * is displayed when the objects in the command don't handle the verb |
| | 556 | * (i.e., don't have any methods for verification of the verb: they |
| | 557 | * lack verDo<verb> or verIo<verb>). |
| | 558 | */ |
| | 559 | static void exeperr(voccxdef *ctx, objnum verb, objnum dobj, |
| | 560 | objnum prep, objnum iobj) |
| | 561 | { |
| | 562 | if (ctx->voccxper2 != MCMONINV) |
| | 563 | { |
| | 564 | runpobj(ctx->voccxrun, iobj); |
| | 565 | runpobj(ctx->voccxrun, prep); |
| | 566 | runpobj(ctx->voccxrun, dobj); |
| | 567 | runpobj(ctx->voccxrun, verb); |
| | 568 | runfn(ctx->voccxrun, ctx->voccxper2, 4); |
| | 569 | return; |
| | 570 | } |
| | 571 | |
| | 572 | vocerr(ctx, VOCERR(110), "I don't know how to "); |
| | 573 | runppr(ctx->voccxrun, verb, PRP_SDESC, 0); |
| | 574 | |
| | 575 | if (dobj != MCMONINV) |
| | 576 | { |
| | 577 | vocerr(ctx, VOCERR(111), " "); |
| | 578 | runppr(ctx->voccxrun, dobj, PRP_THEDESC, 0); |
| | 579 | } |
| | 580 | else |
| | 581 | { |
| | 582 | vocerr(ctx, VOCERR(112), " anything "); |
| | 583 | if (prep != MCMONINV) |
| | 584 | runppr(ctx->voccxrun, prep, PRP_SDESC, 0); |
| | 585 | else |
| | 586 | vocerr(ctx, VOCERR(113), "to"); |
| | 587 | vocerr(ctx, VOCERR(114), " "); |
| | 588 | runppr(ctx->voccxrun, iobj, PRP_THEDESC, 0); |
| | 589 | } |
| | 590 | vocerr(ctx, VOCERR(115), "."); |
| | 591 | } |
| | 592 | |
| | 593 | |
| | 594 | /* |
| | 595 | * Execute daemons |
| | 596 | */ |
| | 597 | void exedaem(voccxdef *ctx) |
| | 598 | { |
| | 599 | runcxdef *rcx = ctx->voccxrun; |
| | 600 | vocddef *daemon; |
| | 601 | int i; |
| | 602 | runsdef val; |
| | 603 | int err; |
| | 604 | |
| | 605 | for (i = ctx->voccxdmc, daemon = ctx->voccxdmn ; i ; ++daemon, --i) |
| | 606 | { |
| | 607 | if (daemon->vocdfn != MCMONINV) |
| | 608 | { |
| | 609 | objnum thisd = daemon->vocdfn; |
| | 610 | |
| | 611 | ERRBEGIN(ctx->voccxerr) |
| | 612 | |
| | 613 | OSCPYSTRUCT(val, daemon->vocdarg); |
| | 614 | runpush(rcx, val.runstyp, &val); |
| | 615 | runfn(rcx, thisd, 1); |
| | 616 | |
| | 617 | ERRCATCH(ctx->voccxerr, err) |
| | 618 | if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) |
| | 619 | errrse(ctx->voccxerr); |
| | 620 | ERREND(ctx->voccxerr) |
| | 621 | } |
| | 622 | } |
| | 623 | for (i = ctx->voccxalc, daemon = ctx->voccxalm ; i ; ++daemon, --i) |
| | 624 | { |
| | 625 | if (daemon->vocdfn != MCMONINV |
| | 626 | && daemon->vocdtim == VOCDTIM_EACH_TURN) |
| | 627 | { |
| | 628 | ERRBEGIN(ctx->voccxerr) |
| | 629 | |
| | 630 | runppr(rcx, daemon->vocdfn, daemon->vocdprp, 0); |
| | 631 | |
| | 632 | ERRCATCH(ctx->voccxerr, err) |
| | 633 | if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) |
| | 634 | errrse(ctx->voccxerr); |
| | 635 | ERREND(ctx->voccxerr) |
| | 636 | } |
| | 637 | } |
| | 638 | } |
| | 639 | |
| | 640 | /* |
| | 641 | * Execute any pending fuses. Return TRUE if any fuses were executed, |
| | 642 | * FALSE otherwise. |
| | 643 | */ |
| | 644 | int exefuse(voccxdef *ctx, int do_run) |
| | 645 | { |
| | 646 | runcxdef *rcx = ctx->voccxrun; |
| | 647 | vocddef *daemon; |
| | 648 | int i; |
| | 649 | int found = FALSE; |
| | 650 | runsdef val; |
| | 651 | int err; |
| | 652 | |
| | 653 | /* first, execute any expired function-based fuses */ |
| | 654 | for (i = ctx->voccxfuc, daemon = ctx->voccxfus ; i ; ++daemon, --i) |
| | 655 | { |
| | 656 | if (daemon->vocdfn != MCMONINV && daemon->vocdtim == 0) |
| | 657 | { |
| | 658 | objnum thisf = daemon->vocdfn; |
| | 659 | |
| | 660 | found = TRUE; |
| | 661 | ERRBEGIN(ctx->voccxerr) |
| | 662 | |
| | 663 | /* save an undo record for this slot before changing */ |
| | 664 | vocdusav(ctx, daemon); |
| | 665 | |
| | 666 | /* remove the fuse prior to running */ |
| | 667 | daemon->vocdfn = MCMONINV; |
| | 668 | |
| | 669 | if (do_run) |
| | 670 | { |
| | 671 | OSCPYSTRUCT(val, daemon->vocdarg); |
| | 672 | runpush(rcx, val.runstyp, &val); |
| | 673 | runfn(rcx, thisf, 1); |
| | 674 | } |
| | 675 | |
| | 676 | ERRCATCH(ctx->voccxerr, err) |
| | 677 | if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) |
| | 678 | errrse(ctx->voccxerr); |
| | 679 | ERREND(ctx->voccxerr) |
| | 680 | } |
| | 681 | } |
| | 682 | |
| | 683 | /* next, execute any expired method-based notifier fuses */ |
| | 684 | for (i = ctx->voccxalc, daemon = ctx->voccxalm ; i ; ++daemon, --i) |
| | 685 | { |
| | 686 | if (daemon->vocdfn != MCMONINV && daemon->vocdtim == 0) |
| | 687 | { |
| | 688 | objnum thisa = daemon->vocdfn; |
| | 689 | |
| | 690 | found = TRUE; |
| | 691 | ERRBEGIN(ctx->voccxerr) |
| | 692 | |
| | 693 | /* save an undo record for this slot before changing */ |
| | 694 | vocdusav(ctx, daemon); |
| | 695 | |
| | 696 | /* delete it prior to running it */ |
| | 697 | daemon->vocdfn = MCMONINV; |
| | 698 | |
| | 699 | if (do_run) |
| | 700 | runppr(rcx, thisa, daemon->vocdprp, 0); |
| | 701 | |
| | 702 | ERRCATCH(ctx->voccxerr, err) |
| | 703 | if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) |
| | 704 | errrse(ctx->voccxerr); |
| | 705 | ERREND(ctx->voccxerr) |
| | 706 | } |
| | 707 | } |
| | 708 | |
| | 709 | /* return true if we found any expired fuses */ |
| | 710 | return found; |
| | 711 | } |
| | 712 | |
| | 713 | /* ------------------------------------------------------------------------ */ |
| | 714 | /* |
| | 715 | * Find the action routine template for a verb. Fills in *tplofs with |
| | 716 | * the offset of the template property within the verb object, and fills |
| | 717 | * in actofs with the offset of the "action" property within the verb |
| | 718 | * object. Sets *tplofs to zero if there's no template, and sets |
| | 719 | * *actofs to zero if there's no action routine. |
| | 720 | */ |
| | 721 | static void exe_get_tpl(voccxdef *ctx, objnum verb, |
| | 722 | uint *tplofs, uint *actofs) |
| | 723 | { |
| | 724 | /* look up the new-style template first */ |
| | 725 | *tplofs = objgetap(ctx->voccxmem, verb, PRP_TPL2, (objnum *)0, FALSE); |
| | 726 | |
| | 727 | /* if there's no new-style template, look up the old-style template */ |
| | 728 | if (*tplofs == 0) |
| | 729 | *tplofs = objgetap(ctx->voccxmem, verb, PRP_TPL, (objnum *)0, FALSE); |
| | 730 | |
| | 731 | /* also look to see if the verb has an Action method */ |
| | 732 | *actofs = objgetap(ctx->voccxmem, verb, PRP_ACTION, (objnum *)0, FALSE); |
| | 733 | } |
| | 734 | |
| | 735 | |
| | 736 | /* ------------------------------------------------------------------------ */ |
| | 737 | /* |
| | 738 | * Execute fuses and daemons. Returns zero on success, or ERR_ABORT if |
| | 739 | * 'abort' was thrown during execution. |
| | 740 | */ |
| | 741 | int exe_fuses_and_daemons(voccxdef *ctx, int err, int do_fuses, |
| | 742 | objnum actor, objnum verb, |
| | 743 | vocoldef *dobj_list, int dobj_cnt, |
| | 744 | objnum prep, objnum iobj) |
| | 745 | { |
| | 746 | int err2; |
| | 747 | |
| | 748 | /* presume no error */ |
| | 749 | err2 = 0; |
| | 750 | |
| | 751 | /* execute fuses and daemons if desired - trap any errors that occur */ |
| | 752 | if (do_fuses) |
| | 753 | { |
| | 754 | ERRBEGIN(ctx->voccxerr) |
| | 755 | { |
| | 756 | /* execute daemons */ |
| | 757 | exedaem(ctx); |
| | 758 | |
| | 759 | /* execute fuses */ |
| | 760 | (void)exefuse(ctx, TRUE); |
| | 761 | } |
| | 762 | ERRCATCH(ctx->voccxerr, err2) |
| | 763 | { |
| | 764 | /* |
| | 765 | * if 'abort' was invoked, ignore it, since it's now had the |
| | 766 | * desired effect of skipping any remaining fuses and |
| | 767 | * daemons; resignal any other error |
| | 768 | */ |
| | 769 | if (err2 != ERR_RUNABRT) |
| | 770 | errrse(ctx->voccxerr); |
| | 771 | |
| | 772 | /* replace any previous error with the new error code */ |
| | 773 | err = err2; |
| | 774 | } |
| | 775 | ERREND(ctx->voccxerr); |
| | 776 | } |
| | 777 | |
| | 778 | /* execute endCommand if it's defined */ |
| | 779 | if (ctx->voccxendcmd != MCMONINV) |
| | 780 | { |
| | 781 | /* push the arguments */ |
| | 782 | runpnum(ctx->voccxrun, err); |
| | 783 | runpobj(ctx->voccxrun, iobj); |
| | 784 | runpobj(ctx->voccxrun, prep); |
| | 785 | voc_push_vocoldef_list(ctx, dobj_list, dobj_cnt); |
| | 786 | runpobj(ctx->voccxrun, verb); |
| | 787 | runpobj(ctx->voccxrun, actor); |
| | 788 | |
| | 789 | /* call endCommand */ |
| | 790 | runfn(ctx->voccxrun, ctx->voccxendcmd, 6); |
| | 791 | } |
| | 792 | |
| | 793 | /* return the error status */ |
| | 794 | return err; |
| | 795 | } |
| | 796 | |
| | 797 | /* ------------------------------------------------------------------------ */ |
| | 798 | /* |
| | 799 | * execute iobjGen/dobjGen methods, if appropriate |
| | 800 | */ |
| | 801 | static int exegen(voccxdef *ctx, objnum obj, prpnum genprop, |
| | 802 | prpnum verprop, prpnum actprop) |
| | 803 | { |
| | 804 | int hasgen; /* has xobjGen property */ |
| | 805 | objnum genobj; /* object with xobjGen property */ |
| | 806 | int hasver; /* has verXoVerb property */ |
| | 807 | objnum verobj; /* object with verXoVerb property */ |
| | 808 | int hasact; /* has xoVerb property */ |
| | 809 | objnum actobj; /* object with xoVerb property */ |
| | 810 | |
| | 811 | /* ignore it if there's no object here */ |
| | 812 | if (obj == MCMONINV) return(FALSE); |
| | 813 | |
| | 814 | /* look up the xobjGen property, and ignore if not present */ |
| | 815 | hasgen = objgetap(ctx->voccxmem, obj, genprop, &genobj, FALSE); |
| | 816 | if (!hasgen) return(FALSE); |
| | 817 | |
| | 818 | /* look up the verXoVerb and xoVerb properties */ |
| | 819 | hasver = objgetap(ctx->voccxmem, obj, verprop, &verobj, FALSE); |
| | 820 | hasact = objgetap(ctx->voccxmem, obj, actprop, &actobj, FALSE); |
| | 821 | |
| | 822 | /* ignore if verXoVerb or xoVerb "overrides" xobjGen */ |
| | 823 | if ((hasver && !bifinh(ctx, vocinh(ctx, genobj), verobj)) |
| | 824 | || (hasact && !bifinh(ctx, vocinh(ctx, genobj), actobj))) |
| | 825 | return FALSE; |
| | 826 | |
| | 827 | /* all conditions are met - execute dobjGen */ |
| | 828 | return TRUE; |
| | 829 | } |
| | 830 | |
| | 831 | /* ------------------------------------------------------------------------ */ |
| | 832 | /* |
| | 833 | * Save "again" information for a direct or indirect object |
| | 834 | */ |
| | 835 | static void exe_save_again_obj(vocoldef *againv, const vocoldef *objv, |
| | 836 | char **bufp) |
| | 837 | { |
| | 838 | /* if there's an object, save it */ |
| | 839 | if (objv != 0) |
| | 840 | { |
| | 841 | /* copy the object information structure */ |
| | 842 | memcpy(againv, objv, sizeof(*againv)); |
| | 843 | |
| | 844 | /* copy the original command words to the "again" buffer */ |
| | 845 | if (objv->vocolfst != 0 && objv->vocollst != 0) |
| | 846 | { |
| | 847 | size_t copylen; |
| | 848 | |
| | 849 | /* |
| | 850 | * Compute the length of the entire list. The words are |
| | 851 | * arranged consecutively in the buffer, separated by null |
| | 852 | * bytes, so we must copy everything from the first word to |
| | 853 | * the start of the last word, plus the length of the last |
| | 854 | * word, plus the last word's trailing null byte. |
| | 855 | */ |
| | 856 | copylen = objv->vocollst - objv->vocolfst |
| | 857 | + strlen(objv->vocollst) + 1; |
| | 858 | |
| | 859 | /* copy the text */ |
| | 860 | memcpy(*bufp, objv->vocolfst, copylen); |
| | 861 | |
| | 862 | /* |
| | 863 | * set the new structure to point into the copy, not the |
| | 864 | * original |
| | 865 | */ |
| | 866 | againv->vocolfst = *bufp; |
| | 867 | againv->vocollst = *bufp + (objv->vocollst - objv->vocolfst); |
| | 868 | |
| | 869 | /* skip past the space we've consumed in the buffer */ |
| | 870 | *bufp += copylen; |
| | 871 | } |
| | 872 | } |
| | 873 | else |
| | 874 | { |
| | 875 | /* there's nothing to save - just set the object ID to invalid */ |
| | 876 | againv->vocolobj = MCMONINV; |
| | 877 | } |
| | 878 | } |
| | 879 | |
| | 880 | /* |
| | 881 | * Restore an "again" object previously saved. Note that we must copy |
| | 882 | * the saved data to our 2-element arrays so that we can insert a |
| | 883 | * terminating element after each restored element - other code |
| | 884 | * occasionally expects these structures to be stored in the standard |
| | 885 | * object list array format. Returns a pointer to the restored object |
| | 886 | * list, which is the same as the first argument. |
| | 887 | */ |
| | 888 | static vocoldef *exe_restore_again_obj(vocoldef again_array[2], |
| | 889 | const vocoldef *saved_obj) |
| | 890 | { |
| | 891 | /* copy the saved object into the first array element */ |
| | 892 | memcpy(&again_array[0], saved_obj, sizeof(again_array[0])); |
| | 893 | |
| | 894 | /* clear the second element to indicate the end of the object list */ |
| | 895 | again_array[1].vocolobj = MCMONINV; |
| | 896 | again_array[1].vocolflg = 0; |
| | 897 | |
| | 898 | /* return a pointer to the first array element */ |
| | 899 | return &again_array[0]; |
| | 900 | } |
| | 901 | |
| | 902 | /* ------------------------------------------------------------------------ */ |
| | 903 | /* |
| | 904 | * Execute a single command. 'recursive' indicates whether the routine |
| | 905 | * is being called for normal command processing or as a recursive call |
| | 906 | * from within the game; if this flag is true, we'll bypass certain |
| | 907 | * operations that are only appropriate for normal direct player |
| | 908 | * commands: we won't remember the command for "again" processing, we |
| | 909 | * won't do end-of-turn processing, and we won't reset the system stack |
| | 910 | * before each function invocation. |
| | 911 | */ |
| | 912 | static int exe1cmd(voccxdef *ctx, objnum actor, objnum verb, vocoldef *dobjv, |
| | 913 | objnum *prepptr, vocoldef *iobjv, int endturn, uchar *tpl, |
| | 914 | int newstyle, int recursive, |
| | 915 | int validate_dobj, int validate_iobj, |
| | 916 | vocoldef *dobj_list, int cur_dobj_idx, int dobj_cnt, |
| | 917 | int show_multi_prefix, int multi_flags) |
| | 918 | { |
| | 919 | objnum loc; |
| | 920 | int err; |
| | 921 | runcxdef *rcx = ctx->voccxrun; |
| | 922 | objnum prep = *prepptr; |
| | 923 | objnum dobj = (dobjv != 0 ? dobjv->vocolobj : MCMONINV); |
| | 924 | objnum iobj = (iobjv != 0 ? iobjv->vocolobj : MCMONINV); |
| | 925 | int tplflags; |
| | 926 | int dobj_first; |
| | 927 | objnum old_tio_actor; |
| | 928 | vocoldef *old_ctx_dobj; |
| | 929 | vocoldef *old_ctx_iobj; |
| | 930 | objnum old_verb; |
| | 931 | objnum old_actor; |
| | 932 | objnum old_prep; |
| | 933 | int do_fuses; |
| | 934 | int do_postact; |
| | 935 | vocoldef again_dobj[2]; |
| | 936 | vocoldef again_iobj[2]; |
| | 937 | |
| | 938 | /* presume no error will occur */ |
| | 939 | err = 0; |
| | 940 | |
| | 941 | /* |
| | 942 | * Presume we'll run fuses and daemons if this is the end of the |
| | 943 | * turn. We only do fuses and daemons once per command, even if the |
| | 944 | * command contains multiple objects; 'endturn' will be true only |
| | 945 | * when this is the last object of the command. |
| | 946 | */ |
| | 947 | do_fuses = endturn; |
| | 948 | |
| | 949 | /* presume we will call postAction */ |
| | 950 | do_postact = TRUE; |
| | 951 | |
| | 952 | /* remember the original tio-level actor setting */ |
| | 953 | old_tio_actor = tiogetactor(ctx->voccxtio); |
| | 954 | |
| | 955 | /* remember the original command settings (in case this is recursive) */ |
| | 956 | old_actor = ctx->voccxactor; |
| | 957 | old_verb = ctx->voccxverb; |
| | 958 | old_prep = ctx->voccxprep; |
| | 959 | old_ctx_dobj = ctx->voccxdobj; |
| | 960 | old_ctx_iobj = ctx->voccxiobj; |
| | 961 | |
| | 962 | /* the default actor is Me */ |
| | 963 | if (actor == MCMONINV) |
| | 964 | actor = ctx->voccxme; |
| | 965 | |
| | 966 | /* if command is "again", get information from previous command */ |
| | 967 | if (verb == ctx->voccxvag) |
| | 968 | { |
| | 969 | /* it's "again" - repeat the last command */ |
| | 970 | actor = ctx->voccxlsa; |
| | 971 | verb = ctx->voccxlsv; |
| | 972 | dobj = ctx->voccxlsd.vocolobj; |
| | 973 | iobj = ctx->voccxlsi.vocolobj; |
| | 974 | prep = ctx->voccxlsp; |
| | 975 | tpl = ctx->voccxlst; |
| | 976 | newstyle = ctx->voccxlssty; |
| | 977 | |
| | 978 | /* |
| | 979 | * If we have a direct or indirect object, restore the full |
| | 980 | * object information structure pointers (in particular, this |
| | 981 | * restores the word lists). |
| | 982 | */ |
| | 983 | if (dobj != MCMONINV) |
| | 984 | dobjv = exe_restore_again_obj(again_dobj, &ctx->voccxlsd); |
| | 985 | if (iobj != MCMONINV) |
| | 986 | iobjv = exe_restore_again_obj(again_iobj, &ctx->voccxlsi); |
| | 987 | |
| | 988 | /* |
| | 989 | * make sure the command is repeatable: there must have been a |
| | 990 | * verb, and the objects specified must still be accessible |
| | 991 | */ |
| | 992 | if (verb == MCMONINV) |
| | 993 | { |
| | 994 | /* |
| | 995 | * if the last command was lost due to an object deletion, |
| | 996 | * show the message "you can't repeat that command"; |
| | 997 | * otherwise, show the message "there's no command to |
| | 998 | * repeat" |
| | 999 | */ |
| | 1000 | if ((ctx->voccxflg & VOCCXAGAINDEL) != 0) |
| | 1001 | vocerr(ctx, VOCERR(27), "You can't repeat that command."); |
| | 1002 | else |
| | 1003 | vocerr(ctx, VOCERR(26), "There's no command to repeat."); |
| | 1004 | |
| | 1005 | /* flush the output and return failure */ |
| | 1006 | tioflush(ctx->voccxtio); |
| | 1007 | return 0; |
| | 1008 | } |
| | 1009 | else if ((dobj != MCMONINV && |
| | 1010 | !vocchkaccess(ctx, dobj, PRP_VALIDDO, 0, actor, verb)) |
| | 1011 | || (iobj != MCMONINV && |
| | 1012 | !vocchkaccess(ctx, iobj, PRP_VALIDIO, 0, actor, verb)) |
| | 1013 | || !vocchkaccess(ctx, actor, PRP_VALIDACTOR, 0, actor, verb)) |
| | 1014 | { |
| | 1015 | vocerr(ctx, VOCERR(27), "You can't repeat that command."); |
| | 1016 | tioflush(ctx->voccxtio); |
| | 1017 | return(0); |
| | 1018 | } |
| | 1019 | } |
| | 1020 | else |
| | 1021 | { |
| | 1022 | /* verify the direct object if present */ |
| | 1023 | if (validate_dobj |
| | 1024 | && dobj != MCMONINV |
| | 1025 | && !vocchkaccess(ctx, dobj, PRP_VALIDDO, 0, actor, verb)) |
| | 1026 | { |
| | 1027 | /* generate an appropriate message */ |
| | 1028 | if (vocchkvis(ctx, dobj, actor)) |
| | 1029 | { |
| | 1030 | /* it's visible but not accessible */ |
| | 1031 | vocnoreach(ctx, &dobj, 1, actor, verb, prep, |
| | 1032 | PRP_DODEFAULT, FALSE, 0, 0, 1); |
| | 1033 | } |
| | 1034 | else |
| | 1035 | { |
| | 1036 | /* it's not even visible */ |
| | 1037 | if (recursive) |
| | 1038 | vocerr(ctx, VOCERR(39), "You don't see that here."); |
| | 1039 | else |
| | 1040 | vocerr(ctx, VOCERR(38), |
| | 1041 | "You don't see that here any more."); |
| | 1042 | } |
| | 1043 | |
| | 1044 | /* indicate the error */ |
| | 1045 | return ERR_PRS_VAL_DO_FAIL; |
| | 1046 | } |
| | 1047 | |
| | 1048 | /* verify the indirect object if present */ |
| | 1049 | if (validate_iobj |
| | 1050 | && iobj != MCMONINV |
| | 1051 | && !vocchkaccess(ctx, iobj, PRP_VALIDIO, 0, actor, verb)) |
| | 1052 | { |
| | 1053 | /* generate the error message */ |
| | 1054 | if (vocchkvis(ctx, iobj, actor)) |
| | 1055 | { |
| | 1056 | /* it's visible but not accessible */ |
| | 1057 | vocnoreach(ctx, &iobj, 1, actor, verb, prep, |
| | 1058 | PRP_IODEFAULT, FALSE, 0, 0, 1); |
| | 1059 | } |
| | 1060 | else |
| | 1061 | { |
| | 1062 | /* it's not even visible */ |
| | 1063 | if (recursive) |
| | 1064 | vocerr(ctx, VOCERR(39), "You don't see that here."); |
| | 1065 | else |
| | 1066 | vocerr(ctx, VOCERR(38), |
| | 1067 | "You don't see that here any more."); |
| | 1068 | } |
| | 1069 | |
| | 1070 | /* indicate the error */ |
| | 1071 | return ERR_PRS_VAL_IO_FAIL; |
| | 1072 | } |
| | 1073 | |
| | 1074 | /* |
| | 1075 | * save the command, unless this is a recursive call from the |
| | 1076 | * game, so that we can repeat this command if the next is |
| | 1077 | * "again" |
| | 1078 | */ |
| | 1079 | if (!recursive) |
| | 1080 | { |
| | 1081 | char *dst; |
| | 1082 | |
| | 1083 | /* save the command parameters */ |
| | 1084 | ctx->voccxlsa = actor; |
| | 1085 | ctx->voccxlsv = verb; |
| | 1086 | ctx->voccxlsp = prep; |
| | 1087 | ctx->voccxlssty = newstyle; |
| | 1088 | if (tpl != 0) |
| | 1089 | memcpy(ctx->voccxlst, tpl, |
| | 1090 | (size_t)(newstyle ? VOCTPL2SIZ : VOCTPLSIZ)); |
| | 1091 | |
| | 1092 | /* set up to write into the "again" word buffer */ |
| | 1093 | dst = ctx->voccxagainbuf; |
| | 1094 | |
| | 1095 | /* save the direct object information */ |
| | 1096 | exe_save_again_obj(&ctx->voccxlsd, dobjv, &dst); |
| | 1097 | |
| | 1098 | /* save the indirect object information */ |
| | 1099 | exe_save_again_obj(&ctx->voccxlsi, iobjv, &dst); |
| | 1100 | |
| | 1101 | /* |
| | 1102 | * clear the flag indicating that "again" was lost due to |
| | 1103 | * object deletion, because we obviously have a valid |
| | 1104 | * "again" at this point |
| | 1105 | */ |
| | 1106 | ctx->voccxflg &= ~VOCCXAGAINDEL; |
| | 1107 | } |
| | 1108 | } |
| | 1109 | |
| | 1110 | /* remember the flags */ |
| | 1111 | tplflags = (tpl != 0 && newstyle ? voctplflg(tpl) : 0); |
| | 1112 | dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); |
| | 1113 | |
| | 1114 | /* set up actor for tio subsystem - format strings need to know */ |
| | 1115 | tiosetactor(ctx->voccxtio, actor); |
| | 1116 | |
| | 1117 | /* store current dobj and iobj vocoldef's for later reference */ |
| | 1118 | ctx->voccxdobj = dobjv; |
| | 1119 | ctx->voccxiobj = iobjv; |
| | 1120 | |
| | 1121 | /* store the rest of the current command objects for reference */ |
| | 1122 | ctx->voccxactor = actor; |
| | 1123 | ctx->voccxverb = verb; |
| | 1124 | ctx->voccxprep = prep; |
| | 1125 | |
| | 1126 | ERRBEGIN(ctx->voccxerr) |
| | 1127 | |
| | 1128 | /* reset the run-time context if this is a top-level call */ |
| | 1129 | if (!recursive) |
| | 1130 | runrst(rcx); |
| | 1131 | |
| | 1132 | /* |
| | 1133 | * if this is the first object, invoke the game's preCommand |
| | 1134 | * function, passing the list of all of the direct objects |
| | 1135 | */ |
| | 1136 | if (cur_dobj_idx == 0 && ctx->voccxprecmd != MCMONINV) |
| | 1137 | { |
| | 1138 | /* push the arguments: actor, verb, dobj-list, prep, iobj */ |
| | 1139 | runpobj(rcx, iobj); |
| | 1140 | runpobj(rcx, prep); |
| | 1141 | voc_push_vocoldef_list(ctx, dobj_list, dobj_cnt); |
| | 1142 | runpobj(rcx, verb); |
| | 1143 | runpobj(rcx, actor); |
| | 1144 | |
| | 1145 | /* catch errors specially for preCommand */ |
| | 1146 | ERRBEGIN(ctx->voccxerr) |
| | 1147 | { |
| | 1148 | /* invoke preCommand */ |
| | 1149 | runfn(rcx, ctx->voccxprecmd, 5); |
| | 1150 | } |
| | 1151 | ERRCATCH(ctx->voccxerr, err) |
| | 1152 | { |
| | 1153 | /* |
| | 1154 | * if the error was 'exit', translate it to EXITPRECMD so |
| | 1155 | * that we handle the outer loop correctly (exiting from |
| | 1156 | * preCommand skips execution for all subsequent objects, |
| | 1157 | * but doesn't skip fuses and daemons) |
| | 1158 | */ |
| | 1159 | if (err == ERR_RUNEXIT) |
| | 1160 | errsig(ctx->voccxerr, ERR_RUNEXITPRECMD); |
| | 1161 | |
| | 1162 | /* no special handling - just resignal the error */ |
| | 1163 | errrse(ctx->voccxerr); |
| | 1164 | } |
| | 1165 | ERREND(ctx->voccxerr); |
| | 1166 | } |
| | 1167 | |
| | 1168 | /* show the pre-object prefix if the caller instructed us to do so */ |
| | 1169 | voc_multi_prefix(ctx, dobj, show_multi_prefix, multi_flags, |
| | 1170 | cur_dobj_idx, dobj_cnt); |
| | 1171 | |
| | 1172 | /* |
| | 1173 | * check to see if the verb has verbAction defined - if so, invoke |
| | 1174 | * the method |
| | 1175 | */ |
| | 1176 | if (objgetap(ctx->voccxmem, verb, PRP_VERBACTION, (objnum *)0, FALSE)) |
| | 1177 | { |
| | 1178 | /* call verb.verbAction(actor, dobj, prep, iobj) */ |
| | 1179 | runpobj(rcx, iobj); |
| | 1180 | runpobj(rcx, prep); |
| | 1181 | runpobj(rcx, dobj); |
| | 1182 | runpobj(rcx, actor); |
| | 1183 | runppr(rcx, verb, PRP_VERBACTION, 4); |
| | 1184 | } |
| | 1185 | |
| | 1186 | /* invoke cmdActor.actorAction(verb, dobj, prep, iobj) */ |
| | 1187 | runpobj(rcx, iobj); |
| | 1188 | runpobj(rcx, prep); |
| | 1189 | runpobj(rcx, dobj); |
| | 1190 | runpobj(rcx, verb); |
| | 1191 | runppr(rcx, actor, PRP_ACTORACTION, 4); |
| | 1192 | |
| | 1193 | /* reset the run-time context if this is a top-level call */ |
| | 1194 | if (!recursive) |
| | 1195 | runrst(rcx); |
| | 1196 | |
| | 1197 | /* invoke actor.location.roomAction(actor, verb, dobj, prep, iobj) */ |
| | 1198 | runppr(rcx, actor, PRP_LOCATION, 0); |
| | 1199 | if (runtostyp(rcx) == DAT_OBJECT) |
| | 1200 | { |
| | 1201 | loc = runpopobj(rcx); |
| | 1202 | |
| | 1203 | /* reset the run-time context if this is a top-level call */ |
| | 1204 | if (!recursive) |
| | 1205 | runrst(rcx); |
| | 1206 | |
| | 1207 | /* call roomAction */ |
| | 1208 | runpobj(rcx, iobj); |
| | 1209 | runpobj(rcx, prep); |
| | 1210 | runpobj(rcx, dobj); |
| | 1211 | runpobj(rcx, verb); |
| | 1212 | runpobj(rcx, actor); |
| | 1213 | runppr(rcx, loc, PRP_ROOMACTION, 5); |
| | 1214 | } |
| | 1215 | else |
| | 1216 | { |
| | 1217 | /* the location isn't an object, so discard it */ |
| | 1218 | rundisc(rcx); |
| | 1219 | } |
| | 1220 | |
| | 1221 | /* if there's an indirect object, execute iobjCheck */ |
| | 1222 | if (iobj != MCMONINV) |
| | 1223 | { |
| | 1224 | /* reset the run-time context if this is a top-level call */ |
| | 1225 | if (!recursive) |
| | 1226 | runrst(rcx); |
| | 1227 | |
| | 1228 | /* invoke iobjCheck */ |
| | 1229 | runpobj(rcx, prep); |
| | 1230 | runpobj(rcx, dobj); |
| | 1231 | runpobj(rcx, verb); |
| | 1232 | runpobj(rcx, actor); |
| | 1233 | runppr(rcx, iobj, PRP_IOBJCHECK, 4); |
| | 1234 | } |
| | 1235 | |
| | 1236 | /* |
| | 1237 | * If there's an indirect object, and the indirect object doesn't |
| | 1238 | * directly define io<Verb>, call iobj.iobjGen(actor, verb, dobj, |
| | 1239 | * prep) |
| | 1240 | */ |
| | 1241 | if (iobj != MCMONINV |
| | 1242 | && exegen(ctx, iobj, PRP_IOBJGEN, voctplvi(tpl), voctplio(tpl))) |
| | 1243 | { |
| | 1244 | /* reset the run-time context if this is a top-level call */ |
| | 1245 | if (!recursive) |
| | 1246 | runrst(rcx); |
| | 1247 | |
| | 1248 | /* invoke iobjGen */ |
| | 1249 | runpobj(rcx, prep); |
| | 1250 | runpobj(rcx, dobj); |
| | 1251 | runpobj(rcx, verb); |
| | 1252 | runpobj(rcx, actor); |
| | 1253 | runppr(rcx, iobj, PRP_IOBJGEN, 4); |
| | 1254 | } |
| | 1255 | |
| | 1256 | /* if there's an direct object, execute dobjCheck */ |
| | 1257 | if (dobj != MCMONINV) |
| | 1258 | { |
| | 1259 | /* reset the run-time context if this is a top-level call */ |
| | 1260 | if (!recursive) |
| | 1261 | runrst(rcx); |
| | 1262 | |
| | 1263 | /* invoke dobjCheck */ |
| | 1264 | runpobj(rcx, prep); |
| | 1265 | runpobj(rcx, iobj); |
| | 1266 | runpobj(rcx, verb); |
| | 1267 | runpobj(rcx, actor); |
| | 1268 | runppr(rcx, dobj, PRP_DOBJCHECK, 4); |
| | 1269 | } |
| | 1270 | |
| | 1271 | /* |
| | 1272 | * If there's a direct object, and the direct object doesn't |
| | 1273 | * directly define do<Verb>, call dobj.dobjGen(actor, verb, iobj, |
| | 1274 | * prep) |
| | 1275 | */ |
| | 1276 | if (dobj != MCMONINV |
| | 1277 | && exegen(ctx, dobj, PRP_DOBJGEN, voctplvd(tpl), voctpldo(tpl))) |
| | 1278 | { |
| | 1279 | /* reset the run-time context if this is a top-level call */ |
| | 1280 | if (!recursive) |
| | 1281 | runrst(rcx); |
| | 1282 | |
| | 1283 | /* invoke dobjGen */ |
| | 1284 | runpobj(rcx, prep); |
| | 1285 | runpobj(rcx, iobj); |
| | 1286 | runpobj(rcx, verb); |
| | 1287 | runpobj(rcx, actor); |
| | 1288 | runppr(rcx, dobj, PRP_DOBJGEN, 4); |
| | 1289 | } |
| | 1290 | |
| | 1291 | /* reset the hidden-text flag */ |
| | 1292 | tiohide(ctx->voccxtio); |
| | 1293 | tioshow(ctx->voccxtio); |
| | 1294 | |
| | 1295 | /* |
| | 1296 | * Now do what needs to be done, depending on the sentence structure: |
| | 1297 | * |
| | 1298 | * No objects ==> cmdVerb.action( cmdActor ) |
| | 1299 | * |
| | 1300 | * Direct object only ==> cmdDobj.verDo<Verb>( actor ) |
| | 1301 | *. cmdDobj.do<Verb>( actor ) |
| | 1302 | * |
| | 1303 | * Indirect + direct ==> cmdDobj.verDo<Verb>( actor, cmdIobj ) |
| | 1304 | *. cmdIobj.verIo<Verb>( actor, cmdDobj ) |
| | 1305 | *. cmdIobj.io<Verb>( actor, cmdDobj ) |
| | 1306 | */ |
| | 1307 | if (dobj == MCMONINV) |
| | 1308 | { |
| | 1309 | /* reset the stack for top-level calls */ |
| | 1310 | if (!recursive) |
| | 1311 | runrst(rcx); |
| | 1312 | |
| | 1313 | /* invoke verb.action */ |
| | 1314 | runpobj(rcx, actor); |
| | 1315 | runppr(rcx, verb, PRP_ACTION, 1); |
| | 1316 | } |
| | 1317 | else if (iobj == MCMONINV) |
| | 1318 | { |
| | 1319 | if (!objgetap(ctx->voccxmem, dobj, voctplvd(tpl), (objnum *)0, FALSE)) |
| | 1320 | { |
| | 1321 | /* display the error */ |
| | 1322 | exeperr(ctx, verb, dobj, MCMONINV, MCMONINV); |
| | 1323 | |
| | 1324 | /* note that verDoVerb failed */ |
| | 1325 | err = ERR_PRS_NO_VERDO; |
| | 1326 | |
| | 1327 | /* we're done with this command */ |
| | 1328 | goto skipToFuses; |
| | 1329 | } |
| | 1330 | |
| | 1331 | /* reset the stack for top-level calls */ |
| | 1332 | if (!recursive) |
| | 1333 | runrst(rcx); |
| | 1334 | |
| | 1335 | /* invoke dobj.verDoVerb */ |
| | 1336 | runpobj(rcx, actor); |
| | 1337 | runppr(rcx, dobj, voctplvd(tpl), 1); |
| | 1338 | |
| | 1339 | /* check for an error message from verDoVerb */ |
| | 1340 | if (!tioshow(ctx->voccxtio)) |
| | 1341 | { |
| | 1342 | /* reset the stack for top-level calls */ |
| | 1343 | if (!recursive) |
| | 1344 | runrst(rcx); |
| | 1345 | |
| | 1346 | /* dobj.verDoVerb displayed no output - process dobj.doVerb */ |
| | 1347 | runpobj(rcx, actor); |
| | 1348 | runppr(rcx, dobj, voctpldo(tpl), 1); |
| | 1349 | } |
| | 1350 | else |
| | 1351 | { |
| | 1352 | /* note that verDoVerb failed */ |
| | 1353 | err = ERR_PRS_VERDO_FAIL; |
| | 1354 | } |
| | 1355 | } |
| | 1356 | else |
| | 1357 | { |
| | 1358 | /* check to see if the verDoVerb and verIoVerb methods exist */ |
| | 1359 | if (!objgetap(ctx->voccxmem, dobj, voctplvd(tpl), (objnum *)0, FALSE)) |
| | 1360 | { |
| | 1361 | /* no verDoVerb method - show a default message */ |
| | 1362 | exeperr(ctx, verb, dobj, MCMONINV, MCMONINV); |
| | 1363 | |
| | 1364 | /* note the error */ |
| | 1365 | err = ERR_PRS_NO_VERDO; |
| | 1366 | |
| | 1367 | /* skip to the end of the turn */ |
| | 1368 | goto skipToFuses; |
| | 1369 | } |
| | 1370 | else if (!objgetap(ctx->voccxmem, iobj, voctplvi(tpl), (objnum *)0, |
| | 1371 | FALSE)) |
| | 1372 | { |
| | 1373 | /* no verIoVerb method - show a default mesage */ |
| | 1374 | exeperr(ctx, verb, MCMONINV, prep, iobj); |
| | 1375 | |
| | 1376 | /* note the error */ |
| | 1377 | err = ERR_PRS_NO_VERIO; |
| | 1378 | |
| | 1379 | /* skip to the end of the turn */ |
| | 1380 | goto skipToFuses; |
| | 1381 | } |
| | 1382 | |
| | 1383 | /* reset the stack for top-level calls */ |
| | 1384 | if (!recursive) |
| | 1385 | runrst(rcx); |
| | 1386 | |
| | 1387 | /* call verDoVerb(actor [,iobj]) */ |
| | 1388 | if (!dobj_first) |
| | 1389 | runpobj(rcx, iobj); |
| | 1390 | runpobj(rcx, actor); |
| | 1391 | runppr(rcx, dobj, voctplvd(tpl), (dobj_first ? 1 : 2)); |
| | 1392 | |
| | 1393 | /* check for error output from verDoVerb */ |
| | 1394 | if (!tioshow(ctx->voccxtio)) |
| | 1395 | { |
| | 1396 | /* reset the stack for top-level calls */ |
| | 1397 | if (!recursive) |
| | 1398 | runrst(rcx); |
| | 1399 | |
| | 1400 | /* no error from verDoVerb - call verIoVerb(actor [,dobj]) */ |
| | 1401 | if (dobj_first) |
| | 1402 | runpobj(rcx, dobj); |
| | 1403 | runpobj(rcx, actor); |
| | 1404 | runppr(rcx, iobj, voctplvi(tpl), (dobj_first ? 2 : 1)); |
| | 1405 | |
| | 1406 | /* check for error output from verIoVerb */ |
| | 1407 | if (!tioshow(ctx->voccxtio)) |
| | 1408 | { |
| | 1409 | /* reset the stack for top-level calls */ |
| | 1410 | if (!recursive) |
| | 1411 | runrst(rcx); |
| | 1412 | |
| | 1413 | /* no error from verDoVerb or verIoVerb - call ioVerb */ |
| | 1414 | runpobj(rcx, dobj); |
| | 1415 | runpobj(rcx, actor); |
| | 1416 | runppr(rcx, iobj, voctplio(tpl), 2); |
| | 1417 | } |
| | 1418 | else |
| | 1419 | { |
| | 1420 | /* note the error */ |
| | 1421 | err = ERR_PRS_VERIO_FAIL; |
| | 1422 | } |
| | 1423 | } |
| | 1424 | else |
| | 1425 | { |
| | 1426 | /* note the error */ |
| | 1427 | err = ERR_PRS_VERDO_FAIL; |
| | 1428 | } |
| | 1429 | } |
| | 1430 | |
| | 1431 | skipToFuses: |
| | 1432 | ERRCATCH(ctx->voccxerr, err) |
| | 1433 | { |
| | 1434 | /* if askIo was invoked, get the preposition from the error stack */ |
| | 1435 | if (err == ERR_RUNASKI) |
| | 1436 | *prepptr = errargint(0); |
| | 1437 | |
| | 1438 | /* |
| | 1439 | * If we executed 'abort', we'll skip straight to endCommand. |
| | 1440 | * |
| | 1441 | * If we executed askDo or askIo, we won't execute anything |
| | 1442 | * more, because the command is being interrupted. |
| | 1443 | * |
| | 1444 | * If 'exit' or 'exitobj' was executed, proceed through |
| | 1445 | * postAction and subsequent steps. |
| | 1446 | * |
| | 1447 | * If any error occurred other than 'exit' or 'exitobj' being |
| | 1448 | * invoked, resignal the error. |
| | 1449 | * |
| | 1450 | * We don't need to do anything more at this point if 'exit' was |
| | 1451 | * invoked, because 'exit' merely skips to the end-of-turn |
| | 1452 | * phase, which is where we'll go next from here. |
| | 1453 | * |
| | 1454 | * If 'exitobj' was invoked, we don't want to return an error at |
| | 1455 | * all, since we just want to skip the remainder of the normal |
| | 1456 | * processing for the current object and proceed to the next |
| | 1457 | * object (in a command with multiple direct objects). |
| | 1458 | */ |
| | 1459 | if (err == ERR_RUNABRT) |
| | 1460 | { |
| | 1461 | /* |
| | 1462 | * aborting - we're going to call postAction, but we're not |
| | 1463 | * going to execute fuses and daemons |
| | 1464 | */ |
| | 1465 | do_fuses = FALSE; |
| | 1466 | endturn = TRUE; |
| | 1467 | } |
| | 1468 | else if (err == ERR_RUNASKD || err == ERR_RUNASKI) |
| | 1469 | { |
| | 1470 | /* we're going to skip all end-of-turn action */ |
| | 1471 | do_fuses = FALSE; |
| | 1472 | do_postact = FALSE; |
| | 1473 | endturn = FALSE; |
| | 1474 | } |
| | 1475 | else if (err == ERR_RUNEXIT) |
| | 1476 | { |
| | 1477 | /* |
| | 1478 | * Proceed with the remainder of the processing for this |
| | 1479 | * turn, but retain the error code to return to our caller, |
| | 1480 | * so they know that the rest of the turn is to be skipped. |
| | 1481 | * |
| | 1482 | * In addition, set 'do_fuses' to true, since we want to go |
| | 1483 | * directly to the fuse and daemon processing for this turn, |
| | 1484 | * regardless of whether any other objects are present |
| | 1485 | * (because we'll skip any remaining objects). |
| | 1486 | */ |
| | 1487 | endturn = TRUE; |
| | 1488 | do_fuses = TRUE; |
| | 1489 | } |
| | 1490 | else if (err == ERR_RUNEXITPRECMD) |
| | 1491 | { |
| | 1492 | /* |
| | 1493 | * exited from preCommand - end the turn, but do not run the |
| | 1494 | * postAction routine |
| | 1495 | */ |
| | 1496 | do_fuses = TRUE; |
| | 1497 | do_postact = FALSE; |
| | 1498 | endturn = TRUE; |
| | 1499 | } |
| | 1500 | else if (err == ERR_RUNEXITOBJ) |
| | 1501 | { |
| | 1502 | /* |
| | 1503 | * Proceed with the remainder of processing for this turn - |
| | 1504 | * we want to proceed to the next object, if any, and |
| | 1505 | * process it as normal. We don't need to update 'endturn' |
| | 1506 | * or 'do_fuses', since we want to do all of those in the |
| | 1507 | * normal fashion. |
| | 1508 | */ |
| | 1509 | } |
| | 1510 | else |
| | 1511 | { |
| | 1512 | /* |
| | 1513 | * We can't handle any other errors. Restore the enclosing |
| | 1514 | * command context, and resignal the error. |
| | 1515 | */ |
| | 1516 | |
| | 1517 | /* restore the previous tio actor setting */ |
| | 1518 | tiosetactor(ctx->voccxtio, old_tio_actor); |
| | 1519 | |
| | 1520 | /* restore the original context iobj and dobj settings */ |
| | 1521 | ctx->voccxdobj = old_ctx_dobj; |
| | 1522 | ctx->voccxiobj = old_ctx_iobj; |
| | 1523 | |
| | 1524 | /* restore the original context command objects */ |
| | 1525 | ctx->voccxactor = old_actor; |
| | 1526 | ctx->voccxverb = old_verb; |
| | 1527 | ctx->voccxprep = old_prep; |
| | 1528 | |
| | 1529 | /* resignal the error */ |
| | 1530 | errrse(ctx->voccxerr); |
| | 1531 | } |
| | 1532 | } |
| | 1533 | ERREND(ctx->voccxerr); |
| | 1534 | |
| | 1535 | /* |
| | 1536 | * If desired, call postAction(actor, verb, dobj, prep, iobj, |
| | 1537 | * error_status). |
| | 1538 | */ |
| | 1539 | if (do_postact && ctx->voccxpostact != MCMONINV) |
| | 1540 | { |
| | 1541 | int err2; |
| | 1542 | |
| | 1543 | ERRBEGIN(ctx->voccxerr) |
| | 1544 | { |
| | 1545 | /* push the arguments */ |
| | 1546 | runpnum(rcx, err); |
| | 1547 | runpobj(rcx, iobj); |
| | 1548 | runpobj(rcx, prep); |
| | 1549 | runpobj(rcx, dobj); |
| | 1550 | runpobj(rcx, verb); |
| | 1551 | runpobj(rcx, actor); |
| | 1552 | |
| | 1553 | /* invoke postAction */ |
| | 1554 | runfn(rcx, ctx->voccxpostact, 6); |
| | 1555 | } |
| | 1556 | ERRCATCH(ctx->voccxerr, err2) |
| | 1557 | { |
| | 1558 | /* remember the new error condition */ |
| | 1559 | err = err2; |
| | 1560 | |
| | 1561 | /* if we're aborting, skip fuses and daemons */ |
| | 1562 | if (err == ERR_RUNABRT) |
| | 1563 | { |
| | 1564 | endturn = TRUE; |
| | 1565 | do_fuses = FALSE; |
| | 1566 | } |
| | 1567 | } |
| | 1568 | ERREND(ctx->voccxerr); |
| | 1569 | } |
| | 1570 | |
| | 1571 | /* restore the original context iobj and dobj settings */ |
| | 1572 | ctx->voccxdobj = old_ctx_dobj; |
| | 1573 | ctx->voccxiobj = old_ctx_iobj; |
| | 1574 | |
| | 1575 | /* restore the original context command objects */ |
| | 1576 | ctx->voccxverb = old_verb; |
| | 1577 | ctx->voccxprep = old_prep; |
| | 1578 | |
| | 1579 | /* reset the stack for top-level calls */ |
| | 1580 | if (!recursive) |
| | 1581 | runrst(rcx); |
| | 1582 | |
| | 1583 | /* |
| | 1584 | * If this is the end of the turn, execute fuses and daemons. Skip |
| | 1585 | * fuses on recursive calls, since we want to count them as part of |
| | 1586 | * the enclosing turn. |
| | 1587 | */ |
| | 1588 | if (endturn && !recursive) |
| | 1589 | { |
| | 1590 | /* catch errors so that we can restore the actor globals */ |
| | 1591 | ERRBEGIN(ctx->voccxerr) |
| | 1592 | { |
| | 1593 | /* run fuses, daemons, and endCommand */ |
| | 1594 | err = exe_fuses_and_daemons(ctx, err, do_fuses, actor, verb, |
| | 1595 | dobj_list, dobj_cnt, prep, iobj); |
| | 1596 | } |
| | 1597 | ERRCLEAN(ctx->voccxerr) |
| | 1598 | { |
| | 1599 | /* restore the previous actor globals */ |
| | 1600 | ctx->voccxactor = old_actor; |
| | 1601 | tiosetactor(ctx->voccxtio, old_tio_actor); |
| | 1602 | } |
| | 1603 | ERRENDCLN(ctx->voccxerr); |
| | 1604 | } |
| | 1605 | |
| | 1606 | /* restore the previous actor globals */ |
| | 1607 | ctx->voccxactor = old_actor; |
| | 1608 | tiosetactor(ctx->voccxtio, old_tio_actor); |
| | 1609 | |
| | 1610 | /* success */ |
| | 1611 | return err; |
| | 1612 | } |
| | 1613 | |
| | 1614 | |
| | 1615 | /* |
| | 1616 | * saveit stores the current direct object list in 'it' or 'them'. |
| | 1617 | */ |
| | 1618 | static void exesaveit(voccxdef *ctx, vocoldef *dolist) |
| | 1619 | { |
| | 1620 | int cnt; |
| | 1621 | int i; |
| | 1622 | int dbg = ctx->voccxflg & VOCCXFDBG; |
| | 1623 | runcxdef *rcx = ctx->voccxrun; |
| | 1624 | |
| | 1625 | cnt = voclistlen(dolist); |
| | 1626 | if (cnt == 1) |
| | 1627 | { |
| | 1628 | /* |
| | 1629 | * check to make sure they're not referring to a number or a |
| | 1630 | * string; if so, it doesn't make any sense to save it |
| | 1631 | */ |
| | 1632 | if (dolist[0].vocolflg == VOCS_STR |
| | 1633 | || dolist[0].vocolflg == VOCS_NUM) |
| | 1634 | { |
| | 1635 | /* |
| | 1636 | * As of 2.5.11, don't clear 'it' on a number or string; |
| | 1637 | * rather, just leave it as it was from the prior command. |
| | 1638 | * Players will almost never expect a number or string to have |
| | 1639 | * anything to do with pronoun antecedents, and in fact some |
| | 1640 | * players reported finding it confusing to have the antecedant |
| | 1641 | * implied by the second-most-recent command disappear when the |
| | 1642 | * most recent command used a number of string. |
| | 1643 | */ |
| | 1644 | #if 0 |
| | 1645 | /* save a nil 'it' */ |
| | 1646 | ctx->voccxit = MCMONINV; |
| | 1647 | if (dbg) |
| | 1648 | tioputs(ctx->voccxtio, |
| | 1649 | ".. setting 'it' to nil (strObj/numObj)\\n"); |
| | 1650 | #endif |
| | 1651 | |
| | 1652 | /* we're done */ |
| | 1653 | return; |
| | 1654 | } |
| | 1655 | |
| | 1656 | /* save 'it' */ |
| | 1657 | ctx->voccxit = dolist[0].vocolobj; |
| | 1658 | ctx->voccxthc = 0; |
| | 1659 | |
| | 1660 | if (dbg) |
| | 1661 | { |
| | 1662 | tioputs(ctx->voccxtio, ".. setting it: "); |
| | 1663 | runppr(rcx, ctx->voccxit, PRP_SDESC, 0); |
| | 1664 | tioputs(ctx->voccxtio, "\\n"); |
| | 1665 | } |
| | 1666 | |
| | 1667 | /* set "him" if appropriate */ |
| | 1668 | runppr(rcx, ctx->voccxit, PRP_ISHIM, 0); |
| | 1669 | if (runtostyp(rcx) == DAT_TRUE) |
| | 1670 | { |
| | 1671 | ctx->voccxhim = ctx->voccxit; |
| | 1672 | if (dbg) |
| | 1673 | tioputs(ctx->voccxtio, |
| | 1674 | "... [setting \"him\" to same object]\\n"); |
| | 1675 | } |
| | 1676 | rundisc(rcx); |
| | 1677 | |
| | 1678 | /* set "her" if appropriate */ |
| | 1679 | runppr(rcx, ctx->voccxit, PRP_ISHER, 0); |
| | 1680 | if (runtostyp(rcx) == DAT_TRUE) |
| | 1681 | { |
| | 1682 | ctx->voccxher = ctx->voccxit; |
| | 1683 | if (dbg) |
| | 1684 | tioputs(ctx->voccxtio, |
| | 1685 | "... [setting \"her\" to same object]\\n"); |
| | 1686 | } |
| | 1687 | rundisc(rcx); |
| | 1688 | } |
| | 1689 | else if (cnt > 1) |
| | 1690 | { |
| | 1691 | ctx->voccxthc = cnt; |
| | 1692 | ctx->voccxit = MCMONINV; |
| | 1693 | if (dbg) tioputs(ctx->voccxtio, ".. setting \"them\": ["); |
| | 1694 | for (i = 0 ; i < cnt ; ++i) |
| | 1695 | { |
| | 1696 | ctx->voccxthm[i] = dolist[i].vocolobj; |
| | 1697 | if (dbg) |
| | 1698 | { |
| | 1699 | runppr(rcx, dolist[i].vocolobj, PRP_SDESC, 0); |
| | 1700 | tioputs(ctx->voccxtio, i+1 < cnt ? ", " : "]\\n"); |
| | 1701 | } |
| | 1702 | } |
| | 1703 | } |
| | 1704 | } |
| | 1705 | |
| | 1706 | /* display a multiple-object prefix */ |
| | 1707 | void voc_multi_prefix(voccxdef *ctx, objnum objn, |
| | 1708 | int show_prefix, int multi_flags, |
| | 1709 | int cur_index, int count) |
| | 1710 | { |
| | 1711 | runcxdef *rcx = ctx->voccxrun; |
| | 1712 | |
| | 1713 | /* if the object is invalid, ignore it */ |
| | 1714 | if (objn == MCMONINV) |
| | 1715 | return; |
| | 1716 | |
| | 1717 | /* |
| | 1718 | * if there's a prefixdesc method defined, call it rather than the |
| | 1719 | * older multisdesc (or even older sdesc) approach |
| | 1720 | */ |
| | 1721 | if (objgetap(ctx->voccxmem, objn, PRP_PREFIXDESC, |
| | 1722 | (objnum *)0, FALSE) != 0) |
| | 1723 | { |
| | 1724 | runsdef val; |
| | 1725 | |
| | 1726 | /* push the word flags */ |
| | 1727 | runpnum(rcx, multi_flags); |
| | 1728 | |
| | 1729 | /* |
| | 1730 | * push the object count and the current index (adjusted to a |
| | 1731 | * 1-based value) |
| | 1732 | */ |
| | 1733 | runpnum(rcx, count); |
| | 1734 | runpnum(rcx, cur_index + 1); |
| | 1735 | |
| | 1736 | /* push the 'show' flag */ |
| | 1737 | val.runstyp = runclog(show_prefix); |
| | 1738 | runpush(rcx, val.runstyp, &val); |
| | 1739 | |
| | 1740 | /* call the method */ |
| | 1741 | runppr(rcx, objn, PRP_PREFIXDESC, 4); |
| | 1742 | |
| | 1743 | /* we're done */ |
| | 1744 | return; |
| | 1745 | } |
| | 1746 | |
| | 1747 | /* |
| | 1748 | * if we're not showing the prefix, don't use the multisdesc/sdesc |
| | 1749 | * display |
| | 1750 | */ |
| | 1751 | if (!show_prefix) |
| | 1752 | return; |
| | 1753 | |
| | 1754 | /* |
| | 1755 | * use multisdesc if defined (for compatibility with older games, |
| | 1756 | * use sdesc if multisdesc doesn't exist for this object) |
| | 1757 | */ |
| | 1758 | if (objgetap(ctx->voccxmem, objn, PRP_MULTISDESC, |
| | 1759 | (objnum *)0, FALSE) == 0) |
| | 1760 | { |
| | 1761 | /* there's no multisdesc defined - use the plain sdesc */ |
| | 1762 | runppr(rcx, objn, PRP_SDESC, 0); |
| | 1763 | } |
| | 1764 | else |
| | 1765 | { |
| | 1766 | /* multisdesc is defined - use it */ |
| | 1767 | runppr(rcx, objn, PRP_MULTISDESC, 0); |
| | 1768 | } |
| | 1769 | |
| | 1770 | /* show the colon */ |
| | 1771 | vocerr_info(ctx, VOCERR(120), ": "); |
| | 1772 | } |
| | 1773 | |
| | 1774 | /* execute command for each object in direct object list */ |
| | 1775 | static int exeloop(voccxdef *ctx, objnum actor, objnum verb, |
| | 1776 | vocoldef *dolist, objnum *prep, vocoldef *iobj, |
| | 1777 | int multi_flags, uchar *tpl, int newstyle) |
| | 1778 | { |
| | 1779 | runcxdef *rcx = ctx->voccxrun; |
| | 1780 | int err; |
| | 1781 | int i; |
| | 1782 | int dobj_cnt; |
| | 1783 | int exec_cnt; |
| | 1784 | vocoldef *dobj; |
| | 1785 | |
| | 1786 | /* |
| | 1787 | * count the direct objects; we'll iterate over the direct objects, |
| | 1788 | * so we execute the command once per direct object |
| | 1789 | */ |
| | 1790 | exec_cnt = dobj_cnt = (dolist != 0 ? voclistlen(dolist) : 0); |
| | 1791 | |
| | 1792 | /* |
| | 1793 | * if there are no direct objects, we still must execute the command |
| | 1794 | * once |
| | 1795 | */ |
| | 1796 | if (exec_cnt < 1) |
| | 1797 | exec_cnt = 1; |
| | 1798 | |
| | 1799 | /* |
| | 1800 | * If we have multiple direct objects, or we're using "all" with |
| | 1801 | * just one direct object, check with the verb to see if multiple |
| | 1802 | * words are acceptable: call verb.rejectMultiDobj, and see what it |
| | 1803 | * returns; if it returns true, don't allow multiple words, and |
| | 1804 | * expect that rejectMultiDobj displayed an error message. |
| | 1805 | * Otherwise, proceed. |
| | 1806 | */ |
| | 1807 | if (((multi_flags & VOCS_ALL) != 0 || dobj_cnt > 1) |
| | 1808 | && dolist && dolist[0].vocolobj != MCMONINV) |
| | 1809 | { |
| | 1810 | int typ; |
| | 1811 | |
| | 1812 | ERRBEGIN(ctx->voccxerr) |
| | 1813 | runrst(rcx); |
| | 1814 | if (!prep || *prep == MCMONINV) |
| | 1815 | runpnil(rcx); |
| | 1816 | else |
| | 1817 | runpobj(rcx, *prep); |
| | 1818 | runppr(rcx, verb, PRP_REJECTMDO, 1); |
| | 1819 | typ = runtostyp(rcx); |
| | 1820 | rundisc(rcx); |
| | 1821 | ERRCATCH(ctx->voccxerr, err) |
| | 1822 | if (err == ERR_RUNEXIT || err == ERR_RUNEXITOBJ |
| | 1823 | || err == ERR_RUNABRT) |
| | 1824 | return err; |
| | 1825 | else |
| | 1826 | errrse(ctx->voccxerr); |
| | 1827 | ERREND(ctx->voccxerr) |
| | 1828 | |
| | 1829 | /* if they returned 'true', don't bother continuing */ |
| | 1830 | if (typ == DAT_TRUE) |
| | 1831 | return 0; |
| | 1832 | } |
| | 1833 | |
| | 1834 | /* |
| | 1835 | * execute the command the required number of times |
| | 1836 | */ |
| | 1837 | for (i = 0 ; i < exec_cnt ; ++i) |
| | 1838 | { |
| | 1839 | int show_multi_prefix; |
| | 1840 | |
| | 1841 | /* get the current direct object, if we have one */ |
| | 1842 | dobj = (dolist != 0 ? &dolist[i] : 0); |
| | 1843 | |
| | 1844 | /* |
| | 1845 | * If we have a number or string, set the current one in |
| | 1846 | * numObj/strObj |
| | 1847 | */ |
| | 1848 | if (dolist != 0) |
| | 1849 | { |
| | 1850 | if (dolist[i].vocolflg == VOCS_STR) |
| | 1851 | { |
| | 1852 | /* it's a string - set strObj.value */ |
| | 1853 | vocsetobj(ctx, ctx->voccxstr, DAT_SSTRING, |
| | 1854 | dolist[i].vocolfst + 1, &dolist[i], &dolist[i]); |
| | 1855 | } |
| | 1856 | else if (dolist[i].vocolflg == VOCS_NUM) |
| | 1857 | { |
| | 1858 | long v1, v2; |
| | 1859 | |
| | 1860 | /* it's a number - set numObj.value */ |
| | 1861 | v1 = atol(dolist[i].vocolfst); |
| | 1862 | oswp4(&v2, v1); |
| | 1863 | vocsetobj(ctx, ctx->voccxnum, DAT_NUMBER, &v2, |
| | 1864 | &dolist[i], &dolist[i]); |
| | 1865 | } |
| | 1866 | } |
| | 1867 | |
| | 1868 | /* |
| | 1869 | * For cases where we have a bunch of direct objects (or even |
| | 1870 | * one when "all" was used), we want to preface the output from |
| | 1871 | * each iteration with the name of the object we're acting on |
| | 1872 | * currently. In other cases, there is no prefix. |
| | 1873 | */ |
| | 1874 | show_multi_prefix = ((multi_flags != 0 || dobj_cnt > 1) && dobj != 0); |
| | 1875 | |
| | 1876 | /* |
| | 1877 | * Execute the command for this object. For every object except |
| | 1878 | * the first, re-validate the direct and indirect objects. |
| | 1879 | * There's no need to re-validate the objects on the first |
| | 1880 | * object in a command, because that will already have been done |
| | 1881 | * during object resolution. |
| | 1882 | */ |
| | 1883 | err = exe1cmd(ctx, actor, verb, dobj, prep, iobj, |
| | 1884 | (i + 1 == exec_cnt), tpl, newstyle, FALSE, |
| | 1885 | i != 0, i != 0, dolist, i, dobj_cnt, |
| | 1886 | show_multi_prefix, multi_flags); |
| | 1887 | |
| | 1888 | /* check the error - ignore any verification failures */ |
| | 1889 | switch(err) |
| | 1890 | { |
| | 1891 | case ERR_PRS_VERDO_FAIL: |
| | 1892 | case ERR_PRS_VERIO_FAIL: |
| | 1893 | case ERR_PRS_NO_VERDO: |
| | 1894 | case ERR_PRS_NO_VERIO: |
| | 1895 | case ERR_RUNEXITOBJ: |
| | 1896 | case ERR_RUNEXIT: |
| | 1897 | /* ignore the error and continue */ |
| | 1898 | err = 0; |
| | 1899 | break; |
| | 1900 | |
| | 1901 | case ERR_RUNEXITPRECMD: |
| | 1902 | /* |
| | 1903 | * exited from preCommand - skip execution of subsequent |
| | 1904 | * objects, but return success |
| | 1905 | */ |
| | 1906 | return 0; |
| | 1907 | |
| | 1908 | case 0: |
| | 1909 | /* no error; continue */ |
| | 1910 | break; |
| | 1911 | |
| | 1912 | default: |
| | 1913 | /* anything else stops this command */ |
| | 1914 | return err; |
| | 1915 | } |
| | 1916 | |
| | 1917 | /* flush output */ |
| | 1918 | tioflush(ctx->voccxtio); |
| | 1919 | } |
| | 1920 | |
| | 1921 | /* success */ |
| | 1922 | return 0; |
| | 1923 | } |
| | 1924 | |
| | 1925 | /* |
| | 1926 | * Execute a command recursively. Game code can call this routine |
| | 1927 | * (indirectly through a built-in function) to execute a command, using |
| | 1928 | * all of the same steps that would be applied for the command if the |
| | 1929 | * player had typed it. |
| | 1930 | */ |
| | 1931 | int execmd_recurs(voccxdef *ctx, objnum actor, objnum verb, |
| | 1932 | objnum dobj, objnum prep, objnum iobj, |
| | 1933 | int validate_dobj, int validate_iobj) |
| | 1934 | { |
| | 1935 | int err; |
| | 1936 | int newstyle; |
| | 1937 | uchar tpl[VOCTPL2SIZ]; |
| | 1938 | vocoldef dobjv; |
| | 1939 | vocoldef iobjv; |
| | 1940 | voccxdef ctx_copy; |
| | 1941 | runsdef *orig_sp; |
| | 1942 | runsdef *orig_bp; |
| | 1943 | |
| | 1944 | /* |
| | 1945 | * Save the stack and base pointers as they are on entry. Since |
| | 1946 | * exe1cmd() is being called recursively, it won't automatically clear |
| | 1947 | * the stack after it's done as it would at the top level; this means |
| | 1948 | * that an aborted frame can be left on the stack if we throw an |
| | 1949 | * 'exit' or 'abort' in the course of executing the command. To make |
| | 1950 | * sure we don't leave any aborted frames on the stack before |
| | 1951 | * returning to our caller, we simply need to restore the stack and |
| | 1952 | * frame pointers on the way out as they were on the way in. |
| | 1953 | */ |
| | 1954 | orig_sp = ctx->voccxrun->runcxsp; |
| | 1955 | orig_bp = ctx->voccxrun->runcxbp; |
| | 1956 | |
| | 1957 | /* make a copy of the voc context, so that changes aren't permanent */ |
| | 1958 | ctx_copy = *ctx; |
| | 1959 | ctx = &ctx_copy; |
| | 1960 | |
| | 1961 | /* |
| | 1962 | * there are no unknown words in the recursive command, since the |
| | 1963 | * command was prepared directly from resolved objects |
| | 1964 | */ |
| | 1965 | ctx->voccxunknown = 0; |
| | 1966 | |
| | 1967 | /* set up the vocoldef structure for the direct object, if present */ |
| | 1968 | if (dobj != MCMONINV) |
| | 1969 | { |
| | 1970 | dobjv.vocolobj = dobj; |
| | 1971 | dobjv.vocolfst = dobjv.vocollst = ""; |
| | 1972 | dobjv.vocolflg = 0; |
| | 1973 | } |
| | 1974 | |
| | 1975 | /* set up the vocoldef structure for the indirect object, if present */ |
| | 1976 | if (iobj != MCMONINV) |
| | 1977 | { |
| | 1978 | iobjv.vocolobj = iobj; |
| | 1979 | iobjv.vocolfst = iobjv.vocollst = ""; |
| | 1980 | iobjv.vocolflg = 0; |
| | 1981 | } |
| | 1982 | |
| | 1983 | /* figure out which template we need, based on the objects provided */ |
| | 1984 | if (dobj == MCMONINV) |
| | 1985 | { |
| | 1986 | uint actofs; |
| | 1987 | uint tplofs; |
| | 1988 | |
| | 1989 | /* |
| | 1990 | * No objects were provided - use the verb's "action" method. |
| | 1991 | * Make sure that there is in fact an "action" method. |
| | 1992 | */ |
| | 1993 | exe_get_tpl(ctx, verb, &tplofs, &actofs); |
| | 1994 | if (actofs != 0) |
| | 1995 | { |
| | 1996 | /* execute the "action" method */ |
| | 1997 | err = exe1cmd(ctx, actor, verb, 0, &prep, 0, FALSE, |
| | 1998 | 0, FALSE, TRUE, validate_dobj, validate_iobj, |
| | 1999 | 0, 0, 0, FALSE, 0); |
| | 2000 | } |
| | 2001 | else |
| | 2002 | { |
| | 2003 | /* indicate that the sentence structure wasn't understood */ |
| | 2004 | err = ERR_PRS_SENT_UNK; |
| | 2005 | } |
| | 2006 | } |
| | 2007 | else if (iobj == MCMONINV) |
| | 2008 | { |
| | 2009 | /* |
| | 2010 | * No indirect object was provided, but a direct object is |
| | 2011 | * present - use the one-object template. First, look up the |
| | 2012 | * template. |
| | 2013 | */ |
| | 2014 | if (voctplfnd(ctx, verb, MCMONINV, tpl, &newstyle)) |
| | 2015 | { |
| | 2016 | /* execute the command */ |
| | 2017 | err = exe1cmd(ctx, actor, verb, &dobjv, &prep, 0, FALSE, |
| | 2018 | tpl, newstyle, TRUE, validate_dobj, validate_iobj, |
| | 2019 | &dobjv, 0, 1, FALSE, 0); |
| | 2020 | } |
| | 2021 | else |
| | 2022 | { |
| | 2023 | /* indicate that the sentence structure wasn't understood */ |
| | 2024 | err = ERR_PRS_SENT_UNK; |
| | 2025 | } |
| | 2026 | } |
| | 2027 | else |
| | 2028 | { |
| | 2029 | /* |
| | 2030 | * Both a direct and indirect object were provided - find the |
| | 2031 | * two-object template for the given preposition. |
| | 2032 | */ |
| | 2033 | if (voctplfnd(ctx, verb, prep, tpl, &newstyle)) |
| | 2034 | { |
| | 2035 | /* execute the command */ |
| | 2036 | err = exe1cmd(ctx, actor, verb, &dobjv, &prep, &iobjv, FALSE, |
| | 2037 | tpl, newstyle, TRUE, validate_dobj, validate_iobj, |
| | 2038 | &dobjv, 0, 1, FALSE, 0); |
| | 2039 | } |
| | 2040 | else |
| | 2041 | { |
| | 2042 | /* indicate that the sentence structure wasn't understood */ |
| | 2043 | err = ERR_PRS_SENT_UNK; |
| | 2044 | } |
| | 2045 | } |
| | 2046 | |
| | 2047 | /* |
| | 2048 | * if the error was EXITPRECMD, change it to EXIT - EXITPRECMD is a |
| | 2049 | * special flag indicating that we exited from a preCommand |
| | 2050 | * function, which is different than normal exiting internally but |
| | 2051 | * not to the game |
| | 2052 | */ |
| | 2053 | if (err == ERR_RUNEXITPRECMD) |
| | 2054 | err = ERR_RUNEXIT; |
| | 2055 | |
| | 2056 | /* |
| | 2057 | * restore the original stack and base pointers, to ensure that we |
| | 2058 | * don't leave any aborted frames on the stack |
| | 2059 | */ |
| | 2060 | ctx->voccxrun->runcxsp = orig_sp; |
| | 2061 | ctx->voccxrun->runcxbp = orig_bp; |
| | 2062 | |
| | 2063 | /* return the result code */ |
| | 2064 | return err; |
| | 2065 | } |
| | 2066 | |
| | 2067 | |
| | 2068 | /* |
| | 2069 | * Check for ALL, ANY, or THEM in the list - use multi-mode if found, |
| | 2070 | * even if we have only one object. Returns a combination of any of the |
| | 2071 | * VOCS_ALL, VOCS_ANY, or VOCS_THEM flags that we find. |
| | 2072 | */ |
| | 2073 | static int check_for_multi(vocoldef *dolist) |
| | 2074 | { |
| | 2075 | int dolen; |
| | 2076 | int i; |
| | 2077 | int result; |
| | 2078 | |
| | 2079 | /* presume we won't find any flags */ |
| | 2080 | result = 0; |
| | 2081 | |
| | 2082 | /* |
| | 2083 | * scan the list for ALL, ANY, or THEM flags, combining any such |
| | 2084 | * flags we find into the result |
| | 2085 | */ |
| | 2086 | dolen = voclistlen(dolist); |
| | 2087 | for (i = 0 ; i < dolen ; ++i) |
| | 2088 | result |= (dolist[i].vocolflg & (VOCS_ALL | VOCS_ANY | VOCS_THEM)); |
| | 2089 | |
| | 2090 | /* return the result */ |
| | 2091 | return result; |
| | 2092 | } |
| | 2093 | |
| | 2094 | /* ------------------------------------------------------------------------ */ |
| | 2095 | /* |
| | 2096 | * Try running the preparseCmd user function. Returns 0 if the |
| | 2097 | * function doesn't exist or returns 'true', ERR_PREPRSCMDCAN if it |
| | 2098 | * returns 'nil' (and thus wants to cancel the command), and |
| | 2099 | * ERR_PREPRSCMDREDO if it returns a list (and thus wants to redo the |
| | 2100 | * command). |
| | 2101 | */ |
| | 2102 | int try_preparse_cmd(voccxdef *ctx, char **cmd, int wrdcnt, |
| | 2103 | uchar **preparse_list) |
| | 2104 | { |
| | 2105 | uchar listbuf[VOCBUFSIZ + 2 + 3*VOCBUFSIZ]; |
| | 2106 | int i; |
| | 2107 | uchar *p; |
| | 2108 | size_t len; |
| | 2109 | runsdef val; |
| | 2110 | int typ; |
| | 2111 | int err; |
| | 2112 | |
| | 2113 | /* if there's no preparseCmd, keep processing */ |
| | 2114 | if (ctx->voccxppc == MCMONINV) |
| | 2115 | return 0; |
| | 2116 | |
| | 2117 | /* build a list of the words */ |
| | 2118 | for (p = listbuf + 2, i = 0 ; i < wrdcnt ; ++i) |
| | 2119 | { |
| | 2120 | char *src; |
| | 2121 | int add_quote; |
| | 2122 | |
| | 2123 | /* check for strings - they require special handling */ |
| | 2124 | if (cmd[i][0] == '"') |
| | 2125 | { |
| | 2126 | /* |
| | 2127 | * it's a string - what follows is a run-time style string, |
| | 2128 | * with a length prefix followed by the text of the string |
| | 2129 | */ |
| | 2130 | len = osrp2(cmd[i] + 1) - 2; |
| | 2131 | src = cmd[i] + 3; |
| | 2132 | |
| | 2133 | /* add quotes to the result */ |
| | 2134 | add_quote = TRUE; |
| | 2135 | } |
| | 2136 | else |
| | 2137 | { |
| | 2138 | /* ordinary word - copy directly */ |
| | 2139 | src = (char *)cmd[i]; |
| | 2140 | |
| | 2141 | /* it's a null-terminated string */ |
| | 2142 | len = strlen(src); |
| | 2143 | |
| | 2144 | /* don't add quotes to the result */ |
| | 2145 | add_quote = FALSE; |
| | 2146 | } |
| | 2147 | |
| | 2148 | /* write the type prefix */ |
| | 2149 | *p++ = DAT_SSTRING; |
| | 2150 | |
| | 2151 | /* write the length prefix */ |
| | 2152 | oswp2(p, len + 2 + (add_quote ? 2 : 0)); |
| | 2153 | p += 2; |
| | 2154 | |
| | 2155 | /* add an open quote if necessary */ |
| | 2156 | if (add_quote) |
| | 2157 | *p++ = '"'; |
| | 2158 | |
| | 2159 | /* copy the text */ |
| | 2160 | memcpy(p, src, len); |
| | 2161 | p += len; |
| | 2162 | |
| | 2163 | /* add the closing quote if necessary */ |
| | 2164 | if (add_quote) |
| | 2165 | *p++ = '"'; |
| | 2166 | } |
| | 2167 | |
| | 2168 | /* set the length of the whole list */ |
| | 2169 | len = p - listbuf; |
| | 2170 | oswp2(listbuf, len); |
| | 2171 | |
| | 2172 | /* push the list as the argument, and call the user's preparseCmd */ |
| | 2173 | val.runstyp = DAT_LIST; |
| | 2174 | val.runsv.runsvstr = listbuf; |
| | 2175 | runpush(ctx->voccxrun, DAT_LIST, &val); |
| | 2176 | |
| | 2177 | /* presume that no error will occur */ |
| | 2178 | err = 0; |
| | 2179 | |
| | 2180 | /* catch errors that occur within preparseCmd */ |
| | 2181 | ERRBEGIN(ctx->voccxerr) |
| | 2182 | { |
| | 2183 | /* call preparseCmd */ |
| | 2184 | runfn(ctx->voccxrun, ctx->voccxppc, 1); |
| | 2185 | } |
| | 2186 | ERRCATCH(ctx->voccxerr, err) |
| | 2187 | { |
| | 2188 | /* |
| | 2189 | * if it's abort/exit/exitobj, just return it; for any other |
| | 2190 | * errors, just re-throw the same error |
| | 2191 | */ |
| | 2192 | switch(err) |
| | 2193 | { |
| | 2194 | case ERR_RUNABRT: |
| | 2195 | case ERR_RUNEXIT: |
| | 2196 | case ERR_RUNEXITOBJ: |
| | 2197 | /* simply return these errors to the caller */ |
| | 2198 | break; |
| | 2199 | |
| | 2200 | default: |
| | 2201 | /* re-throw anything else */ |
| | 2202 | errrse(ctx->voccxerr); |
| | 2203 | } |
| | 2204 | } |
| | 2205 | ERREND(ctx->voccxerr); |
| | 2206 | |
| | 2207 | /* if an error occurred, return the error code */ |
| | 2208 | if (err != 0) |
| | 2209 | return err; |
| | 2210 | |
| | 2211 | /* get the result */ |
| | 2212 | typ = runtostyp(ctx->voccxrun); |
| | 2213 | |
| | 2214 | /* if they returned a list, it's a new command to execute */ |
| | 2215 | if (typ == DAT_LIST) |
| | 2216 | { |
| | 2217 | /* get the list and give it to the caller */ |
| | 2218 | *preparse_list = runpoplst(ctx->voccxrun); |
| | 2219 | |
| | 2220 | /* |
| | 2221 | * indicate that the command is to be reparsed with the new word |
| | 2222 | * list |
| | 2223 | */ |
| | 2224 | return ERR_PREPRSCMDREDO; |
| | 2225 | } |
| | 2226 | |
| | 2227 | /* for any other type, we don't need the value, so discard it */ |
| | 2228 | rundisc(ctx->voccxrun); |
| | 2229 | |
| | 2230 | /* if the result is nil, don't process this command further */ |
| | 2231 | if (typ == DAT_NIL) |
| | 2232 | return ERR_PREPRSCMDCAN; |
| | 2233 | else |
| | 2234 | return 0; |
| | 2235 | } |
| | 2236 | |
| | 2237 | |
| | 2238 | /* ------------------------------------------------------------------------ */ |
| | 2239 | /* |
| | 2240 | * Call parseAskobjIndirect |
| | 2241 | */ |
| | 2242 | static void voc_askobj_indirect(voccxdef *ctx, vocoldef *dolist, |
| | 2243 | objnum actor, objnum verb, objnum prep) |
| | 2244 | { |
| | 2245 | int cnt; |
| | 2246 | int i; |
| | 2247 | size_t len; |
| | 2248 | uchar *lstp; |
| | 2249 | |
| | 2250 | /* |
| | 2251 | * Generate the direct object list argument. This argument is a |
| | 2252 | * list of lists. For each noun phrase, we generate one sublist in |
| | 2253 | * the main list. Each sublist itself consists of three |
| | 2254 | * sub-sublists: first, a list of strings giving the words in the |
| | 2255 | * noun phrase; second, a list of the objects matching the noun |
| | 2256 | * phrase; third, a list of the flags for the matching objects. |
| | 2257 | * |
| | 2258 | * So, if the player typed "put red box and blue ball", we might |
| | 2259 | * generate a list something like this: |
| | 2260 | * |
| | 2261 | * [ [ ['red', 'box'], [redBox1, redBox2], [0, 0] ], [ ['blue', |
| | 2262 | * 'ball'], [blueBall], [0, 0] ] ] |
| | 2263 | */ |
| | 2264 | |
| | 2265 | /* |
| | 2266 | * First, figure out how much space we need for this list of lists |
| | 2267 | * of lists. Scan the direct object list for distinct noun phrases |
| | 2268 | * - we need one sublist for each distinct noun phrase. |
| | 2269 | */ |
| | 2270 | cnt = voclistlen(dolist); |
| | 2271 | for (len = 0, i = 0 ; i < cnt ; ) |
| | 2272 | { |
| | 2273 | char *p; |
| | 2274 | size_t curlen; |
| | 2275 | int j; |
| | 2276 | |
| | 2277 | /* |
| | 2278 | * we need the sublist type prefix (one byte) plus the sublist |
| | 2279 | * length prefix (two bytes), plus the type and length prefixes |
| | 2280 | * (one plus two bytes) for each of the three sub-sublist |
| | 2281 | */ |
| | 2282 | len += (1+2) + 3*(1+2); |
| | 2283 | |
| | 2284 | /* |
| | 2285 | * we need space to store the strings for the words in this noun |
| | 2286 | * phrase |
| | 2287 | */ |
| | 2288 | for (p = dolist[i].vocolfst ; p != 0 && p <= dolist[i].vocollst ; |
| | 2289 | p += curlen + 1) |
| | 2290 | { |
| | 2291 | /* |
| | 2292 | * add in the space needed for this string element in the |
| | 2293 | * sub-sublist - we need one byte for the type prefix, two |
| | 2294 | * bytes for the length prefix, and the bytes for the string |
| | 2295 | * itself |
| | 2296 | */ |
| | 2297 | curlen = strlen(p); |
| | 2298 | len += (1+2) + curlen; |
| | 2299 | } |
| | 2300 | |
| | 2301 | /* |
| | 2302 | * scan each object for this same noun phrase (i.e., for which |
| | 2303 | * the vocabulary words are the same) |
| | 2304 | */ |
| | 2305 | for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; |
| | 2306 | ++j) |
| | 2307 | { |
| | 2308 | /* |
| | 2309 | * Add in space for this object in the sub-sublist for the |
| | 2310 | * current noun phrase. If this object is nil, we need only |
| | 2311 | * one byte for the type; otherwise, we need one byte for |
| | 2312 | * the type prefix plus two bytes for the object ID. |
| | 2313 | */ |
| | 2314 | if (dolist[i].vocolobj == MCMONINV) |
| | 2315 | len += 1; |
| | 2316 | else |
| | 2317 | len += (1 + 2); |
| | 2318 | |
| | 2319 | /* |
| | 2320 | * Add in space for the flags sub-sublist for the current |
| | 2321 | * object. We need one byte for the type and four for the |
| | 2322 | * integer value. |
| | 2323 | */ |
| | 2324 | len += (1 + 4); |
| | 2325 | } |
| | 2326 | |
| | 2327 | /* skip to the next distinct noun phrase */ |
| | 2328 | i = j; |
| | 2329 | } |
| | 2330 | |
| | 2331 | /* allocate the list */ |
| | 2332 | lstp = voc_push_list_siz(ctx, len); |
| | 2333 | |
| | 2334 | /* |
| | 2335 | * Go through our object array again, and this time actually build |
| | 2336 | * the list. |
| | 2337 | */ |
| | 2338 | for (i = 0 ; i < cnt ; ) |
| | 2339 | { |
| | 2340 | char *p; |
| | 2341 | uchar *subp; |
| | 2342 | uchar *subsubp; |
| | 2343 | size_t curlen; |
| | 2344 | int j; |
| | 2345 | |
| | 2346 | /* start the sublist with the type prefix */ |
| | 2347 | *lstp++ = DAT_LIST; |
| | 2348 | |
| | 2349 | /* leave a placeholder for our length prefix */ |
| | 2350 | subp = lstp; |
| | 2351 | lstp += 2; |
| | 2352 | |
| | 2353 | /* start the sub-sublist with the word strings */ |
| | 2354 | *lstp++ = DAT_LIST; |
| | 2355 | subsubp = lstp; |
| | 2356 | lstp += 2; |
| | 2357 | |
| | 2358 | /* store the word strings in the sub-sublist */ |
| | 2359 | for (p = dolist[i].vocolfst ; p != 0 && p <= dolist[i].vocollst ; |
| | 2360 | p += curlen + 1) |
| | 2361 | { |
| | 2362 | /* get this string's length */ |
| | 2363 | curlen = strlen(p); |
| | 2364 | |
| | 2365 | /* store the type and length prefixes */ |
| | 2366 | *lstp++ = DAT_SSTRING; |
| | 2367 | oswp2(lstp, curlen + 2); |
| | 2368 | lstp += 2; |
| | 2369 | |
| | 2370 | /* store the string */ |
| | 2371 | memcpy(lstp, p, curlen); |
| | 2372 | lstp += curlen; |
| | 2373 | } |
| | 2374 | |
| | 2375 | /* fix up the string sub-sublist length */ |
| | 2376 | oswp2(subsubp, lstp - subsubp); |
| | 2377 | |
| | 2378 | /* start the second sub-sublist, for the objects */ |
| | 2379 | *lstp++ = DAT_LIST; |
| | 2380 | subsubp = lstp; |
| | 2381 | lstp += 2; |
| | 2382 | |
| | 2383 | /* write each object */ |
| | 2384 | for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; |
| | 2385 | ++j) |
| | 2386 | { |
| | 2387 | /* |
| | 2388 | * if this object isn't nil, write it to the sub-sublist; |
| | 2389 | * otherwise, just put nil in the sub-sublist |
| | 2390 | */ |
| | 2391 | if (dolist[j].vocolobj != MCMONINV) |
| | 2392 | { |
| | 2393 | *lstp++ = DAT_OBJECT; |
| | 2394 | oswp2(lstp, dolist[j].vocolobj); |
| | 2395 | lstp += 2; |
| | 2396 | } |
| | 2397 | else |
| | 2398 | { |
| | 2399 | /* no object - just store nil */ |
| | 2400 | *lstp++ = DAT_NIL; |
| | 2401 | } |
| | 2402 | } |
| | 2403 | |
| | 2404 | /* fix up the object sub-sublist length */ |
| | 2405 | oswp2(subsubp, lstp - subsubp); |
| | 2406 | |
| | 2407 | /* start the third sub-sublist, for the flags */ |
| | 2408 | *lstp++ = DAT_LIST; |
| | 2409 | subsubp = lstp; |
| | 2410 | lstp += 2; |
| | 2411 | |
| | 2412 | /* write each object's flags */ |
| | 2413 | for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; |
| | 2414 | ++j) |
| | 2415 | { |
| | 2416 | /* write the flags */ |
| | 2417 | *lstp++ = DAT_NUMBER; |
| | 2418 | oswp4(lstp, dolist[j].vocolflg); |
| | 2419 | lstp += 4; |
| | 2420 | } |
| | 2421 | |
| | 2422 | /* fix up the flag sub-sublist length */ |
| | 2423 | oswp2(subsubp, lstp - subsubp); |
| | 2424 | |
| | 2425 | /* skip to the start of the next distinct noun phrase */ |
| | 2426 | i = j; |
| | 2427 | |
| | 2428 | /* fix up the sublist length */ |
| | 2429 | oswp2(subp, lstp - subp); |
| | 2430 | } |
| | 2431 | |
| | 2432 | /* push the prep, verb, and actor arguments */ |
| | 2433 | runpobj(ctx->voccxrun, prep); |
| | 2434 | runpobj(ctx->voccxrun, verb); |
| | 2435 | runpobj(ctx->voccxrun, |
| | 2436 | (objnum)(actor == MCMONINV ? ctx->voccxme : actor)); |
| | 2437 | |
| | 2438 | /* call the function */ |
| | 2439 | runfn(ctx->voccxrun, ctx->voccxpask3, 4); |
| | 2440 | } |
| | 2441 | |
| | 2442 | |
| | 2443 | /* ------------------------------------------------------------------------ */ |
| | 2444 | /* |
| | 2445 | * execmd() - executes a user's command given the verb's verb and |
| | 2446 | * preposition words, a list of nouns to be used as indirect objects, |
| | 2447 | * and a list to be used for direct objects. The globals cmdActor and |
| | 2448 | * cmdPrep should already be set. This routine tries to find a template |
| | 2449 | * for the verb which matches the player's command. If no template |
| | 2450 | * matches, we try (using default objects and, if that fails, requests |
| | 2451 | * to the player for objects) to fill in any missing information in the |
| | 2452 | * player's command. If that still fails, we will say we don't |
| | 2453 | * understand the sentence and leave it at that. |
| | 2454 | */ |
| | 2455 | int execmd(voccxdef *ctx, objnum actor, objnum prep, |
| | 2456 | char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist, |
| | 2457 | char **cmd, int *typelist, |
| | 2458 | char *cmdbuf, int wrdcnt, uchar **preparse_list, int *next_word) |
| | 2459 | { |
| | 2460 | objnum verb; |
| | 2461 | objnum iobj; |
| | 2462 | int multi_flags = 0; |
| | 2463 | vocwdef *n; |
| | 2464 | int cnt; |
| | 2465 | vocoldef *newnoun; |
| | 2466 | int next; |
| | 2467 | char *exenewcmd; |
| | 2468 | char *donewcmd; |
| | 2469 | char *ionewcmd; |
| | 2470 | char *exenewbuf; |
| | 2471 | char *donewbuf; |
| | 2472 | char *ionewbuf; |
| | 2473 | char **exenewlist; |
| | 2474 | char **donewlist; |
| | 2475 | char **ionewlist; |
| | 2476 | int *exenewtype; |
| | 2477 | int *donewtype; |
| | 2478 | int *ionewtype; |
| | 2479 | vocoldef *dolist1; |
| | 2480 | vocoldef *iolist1; |
| | 2481 | uchar tpl[VOCTPL2SIZ]; |
| | 2482 | int foundtpl; /* used to determine success of tpl searches */ |
| | 2483 | runcxdef *rcx = ctx->voccxrun; |
| | 2484 | uint tplofs; /* offset of template object */ |
| | 2485 | uint actofs; /* offset of 'action' property */ |
| | 2486 | int askflags; /* flag for what we need to ask user */ |
| | 2487 | int newstyle; /* flag indicating new-style template definitions */ |
| | 2488 | int tplflags; |
| | 2489 | int err; |
| | 2490 | uchar *save_sp; |
| | 2491 | |
| | 2492 | /* run preparseCmd */ |
| | 2493 | switch(try_preparse_cmd(ctx, cmd, wrdcnt, preparse_list)) |
| | 2494 | { |
| | 2495 | case 0: |
| | 2496 | /* proceed with the command */ |
| | 2497 | break; |
| | 2498 | |
| | 2499 | case ERR_PREPRSCMDCAN: |
| | 2500 | /* command cancelled */ |
| | 2501 | return 0; |
| | 2502 | |
| | 2503 | case ERR_RUNEXIT: |
| | 2504 | case ERR_RUNABRT: |
| | 2505 | case ERR_RUNEXITOBJ: |
| | 2506 | /* abort/exit/exitobj - treat this the same as command cancellation */ |
| | 2507 | return 0; |
| | 2508 | |
| | 2509 | case ERR_PREPRSCMDREDO: |
| | 2510 | /* redo the command - so indicate to the caller */ |
| | 2511 | return ERR_PREPRSCMDREDO; |
| | 2512 | } |
| | 2513 | |
| | 2514 | /* look up the verb based on the verb and verb-prep */ |
| | 2515 | n = vocffw(ctx, vverb, (int)strlen(vverb), |
| | 2516 | vprep, (vprep ? (int)strlen(vprep) : 0), PRP_VERB, |
| | 2517 | (vocseadef *)0); |
| | 2518 | |
| | 2519 | /* if we didn't find a verb template, we can't process the sentence */ |
| | 2520 | if (n == 0) |
| | 2521 | { |
| | 2522 | /* try parseUnknownVerb, and show an error if that doesn't handle it */ |
| | 2523 | if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, |
| | 2524 | TRUE, VOCERR(18), |
| | 2525 | "I don't understand that sentence.")) |
| | 2526 | { |
| | 2527 | /* they handled it successfully - end the command with success */ |
| | 2528 | return 0; |
| | 2529 | } |
| | 2530 | else |
| | 2531 | { |
| | 2532 | /* |
| | 2533 | * parseUnknownVerb failed or aborted - end the command with |
| | 2534 | * an error |
| | 2535 | */ |
| | 2536 | return 1; |
| | 2537 | } |
| | 2538 | } |
| | 2539 | |
| | 2540 | /* get the deepverb object */ |
| | 2541 | verb = n->vocwobj; |
| | 2542 | |
| | 2543 | /* default actor is "Me" */ |
| | 2544 | if (actor == MCMONINV) |
| | 2545 | actor = ctx->voccxme; |
| | 2546 | |
| | 2547 | /* set a savepoint, if we're keeping undo information */ |
| | 2548 | if (ctx->voccxundo) |
| | 2549 | objusav(ctx->voccxundo); |
| | 2550 | |
| | 2551 | /* |
| | 2552 | * Check that the room will allow this command -- it may not |
| | 2553 | * due to darkness or other ailment. We can find out with the |
| | 2554 | * roomCheck(verb) message, sent to the meobj. |
| | 2555 | */ |
| | 2556 | { |
| | 2557 | int t; |
| | 2558 | |
| | 2559 | /* call roomCheck */ |
| | 2560 | runrst(rcx); |
| | 2561 | runpobj(rcx, verb); |
| | 2562 | runppr(rcx, ctx->voccxme, PRP_ROOMCHECK, 1); |
| | 2563 | t = runpoplog(rcx); |
| | 2564 | |
| | 2565 | /* if they returned nil, stop the command, but indicate success */ |
| | 2566 | if (!t) |
| | 2567 | return 0; |
| | 2568 | } |
| | 2569 | |
| | 2570 | /* look for a new-style template first, then the old-style template */ |
| | 2571 | exe_get_tpl(ctx, verb, &tplofs, &actofs); |
| | 2572 | |
| | 2573 | /* make sure we found a verb */ |
| | 2574 | if (tplofs == 0 && actofs == 0 && verb != ctx->voccxvag) |
| | 2575 | { |
| | 2576 | /* try parseUnknownVerb, and show an error if that doesn't handle it */ |
| | 2577 | if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, |
| | 2578 | TRUE, VOCERR(23), |
| | 2579 | "internal error: verb has no action, doAction, or ioAction")) |
| | 2580 | return 0; |
| | 2581 | else |
| | 2582 | return 1; |
| | 2583 | } |
| | 2584 | |
| | 2585 | /* |
| | 2586 | * Check to see if we have an "all" - if we do, we'll need to |
| | 2587 | * display the direct object's name even if only one direct object |
| | 2588 | * comes of it. |
| | 2589 | */ |
| | 2590 | multi_flags = check_for_multi(dolist); |
| | 2591 | |
| | 2592 | /* |
| | 2593 | * set up dobj word list in case objwords is used in doDefault (the |
| | 2594 | * game may want to check for "all" and disallow it, for example) |
| | 2595 | */ |
| | 2596 | ctx->voccxdobj = dolist; |
| | 2597 | |
| | 2598 | /* set up our stack allocations, which we may need from now on */ |
| | 2599 | voc_enter(ctx, &save_sp); |
| | 2600 | VOC_STK_ARRAY(ctx, char, donewcmd, VOCBUFSIZ); |
| | 2601 | VOC_STK_ARRAY(ctx, char, ionewcmd, VOCBUFSIZ); |
| | 2602 | VOC_STK_ARRAY(ctx, char, donewbuf, 2*VOCBUFSIZ); |
| | 2603 | VOC_STK_ARRAY(ctx, char, ionewbuf, 2*VOCBUFSIZ); |
| | 2604 | VOC_STK_ARRAY(ctx, char *, donewlist, VOCBUFSIZ); |
| | 2605 | VOC_STK_ARRAY(ctx, char *, ionewlist, VOCBUFSIZ); |
| | 2606 | VOC_MAX_ARRAY(ctx, int, donewtype); |
| | 2607 | VOC_MAX_ARRAY(ctx, int, ionewtype); |
| | 2608 | VOC_MAX_ARRAY(ctx, vocoldef, dolist1); |
| | 2609 | VOC_MAX_ARRAY(ctx, vocoldef, iolist1); |
| | 2610 | |
| | 2611 | /* keep going until we're done with the sentence */ |
| | 2612 | for ( ;; ) |
| | 2613 | { |
| | 2614 | askflags = err = 0; |
| | 2615 | |
| | 2616 | ERRBEGIN(ctx->voccxerr) |
| | 2617 | |
| | 2618 | /* |
| | 2619 | * Now see what kind of sentence we have. If we have no |
| | 2620 | * objects and an action, use the action. If we have a direct |
| | 2621 | * object and a doAction, use the doAction. If we have an |
| | 2622 | * indirect object and an ioAction with a matching preposition, |
| | 2623 | * use the ioAction. If we have an indirect object and no |
| | 2624 | * matching ioAction, complain. If we have a direct object and |
| | 2625 | * no doAction or ioAction, complain. If we have fewer objects |
| | 2626 | * than we really want, ask the user for more of them. |
| | 2627 | */ |
| | 2628 | if (voclistlen(dolist) == 0 && voclistlen(iolist) == 0) |
| | 2629 | { |
| | 2630 | if (actofs || verb == ctx->voccxvag) |
| | 2631 | { |
| | 2632 | if ((err = exeloop(ctx, actor, verb, (vocoldef *)0, &prep, |
| | 2633 | (vocoldef *)0, multi_flags, |
| | 2634 | (uchar *)0, 0)) != 0) |
| | 2635 | goto exit_error; |
| | 2636 | } |
| | 2637 | else |
| | 2638 | { |
| | 2639 | /* |
| | 2640 | * The player has not specified any objects, but the |
| | 2641 | * verb seems to require one. See if there's a unique |
| | 2642 | * default. |
| | 2643 | */ |
| | 2644 | runrst(rcx); |
| | 2645 | runpnil(rcx); |
| | 2646 | runpobj(rcx, prep); |
| | 2647 | runpobj(rcx, actor); |
| | 2648 | runppr(rcx, verb, PRP_DODEFAULT, 3); |
| | 2649 | |
| | 2650 | if (runtostyp(rcx) == DAT_LIST) |
| | 2651 | { |
| | 2652 | uchar *l = runpoplst(rcx); |
| | 2653 | uint lstsiz; |
| | 2654 | objnum defobj; |
| | 2655 | int objcnt; |
| | 2656 | objnum newprep; |
| | 2657 | runsdef val; |
| | 2658 | objnum o; |
| | 2659 | |
| | 2660 | /* push list back on stack, to keep in heap */ |
| | 2661 | val.runsv.runsvstr = l; |
| | 2662 | val.runstyp = DAT_LIST; |
| | 2663 | runrepush(rcx, &val); |
| | 2664 | |
| | 2665 | /* get list size out of list */ |
| | 2666 | lstsiz = osrp2(l) - 2; |
| | 2667 | l += 2; |
| | 2668 | |
| | 2669 | /* find default preposition for verb, if any */ |
| | 2670 | runppr(rcx, verb, PRP_PREPDEFAULT, 0); |
| | 2671 | if (runtostyp(rcx) == DAT_OBJECT) |
| | 2672 | newprep = runpopobj(rcx); |
| | 2673 | else |
| | 2674 | { |
| | 2675 | newprep = MCMONINV; |
| | 2676 | rundisc(rcx); |
| | 2677 | } |
| | 2678 | |
| | 2679 | if (!voctplfnd(ctx, verb, newprep, tpl, &newstyle)) |
| | 2680 | { |
| | 2681 | for (objcnt = 0 ; lstsiz && objcnt < 2 |
| | 2682 | ; lstadv(&l, &lstsiz)) |
| | 2683 | { |
| | 2684 | if (*l == DAT_OBJECT) |
| | 2685 | { |
| | 2686 | ++objcnt; |
| | 2687 | defobj = osrp2(l + 1); |
| | 2688 | } |
| | 2689 | } |
| | 2690 | } |
| | 2691 | else |
| | 2692 | { |
| | 2693 | int dobj_first; |
| | 2694 | |
| | 2695 | /* |
| | 2696 | * Get the template flags. If we must |
| | 2697 | * disambiguate the direct object first for this |
| | 2698 | * verb, do so now. |
| | 2699 | */ |
| | 2700 | tplflags = (newstyle ? voctplflg(tpl) : 0); |
| | 2701 | dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); |
| | 2702 | |
| | 2703 | for (objcnt = 0 ; lstsiz && objcnt < 2 |
| | 2704 | ; lstadv(&l, &lstsiz)) |
| | 2705 | { |
| | 2706 | if (*l == DAT_OBJECT) |
| | 2707 | { |
| | 2708 | o = osrp2(l + 1); |
| | 2709 | if (!objgetap(ctx->voccxmem, o, voctplvd(tpl), |
| | 2710 | (objnum *)0, FALSE)) |
| | 2711 | continue; |
| | 2712 | |
| | 2713 | tiohide(ctx->voccxtio); |
| | 2714 | if (newprep != MCMONINV && !dobj_first) |
| | 2715 | runpnil(rcx); |
| | 2716 | runpobj(rcx, actor); |
| | 2717 | runppr(rcx, o, voctplvd(tpl), |
| | 2718 | ((newprep != MCMONINV && !dobj_first) |
| | 2719 | ? 2 : 1)); |
| | 2720 | |
| | 2721 | if (!tioshow(ctx->voccxtio)) |
| | 2722 | { |
| | 2723 | ++objcnt; |
| | 2724 | defobj = o; |
| | 2725 | } |
| | 2726 | } |
| | 2727 | } |
| | 2728 | |
| | 2729 | /* no longer need list in heap, so discard it */ |
| | 2730 | rundisc(rcx); |
| | 2731 | |
| | 2732 | /* use default object if there's exactly one */ |
| | 2733 | if (objcnt == 1) |
| | 2734 | { |
| | 2735 | dolist[0].vocolobj = defobj; |
| | 2736 | dolist[0].vocolflg = 0; |
| | 2737 | dolist[0].vocolfst = dolist[0].vocollst = 0; |
| | 2738 | dolist[1].vocolobj = MCMONINV; |
| | 2739 | dolist[1].vocolflg = 0; |
| | 2740 | dolist[1].vocolfst = dolist[1].vocollst = 0; |
| | 2741 | |
| | 2742 | runrst(rcx); |
| | 2743 | if (ctx->voccxpdef2 != MCMONINV) |
| | 2744 | { |
| | 2745 | runpnil(rcx); |
| | 2746 | runpobj(rcx, defobj); |
| | 2747 | runpobj(rcx, verb); |
| | 2748 | runpobj(rcx, actor); |
| | 2749 | runfn(rcx, ctx->voccxpdef2, 4); |
| | 2750 | } |
| | 2751 | else if (ctx->voccxpdef != MCMONINV) |
| | 2752 | { |
| | 2753 | runpnil(rcx); |
| | 2754 | runpobj(rcx, defobj); |
| | 2755 | runfn(rcx, ctx->voccxpdef, 2); |
| | 2756 | } |
| | 2757 | else |
| | 2758 | { |
| | 2759 | /* tell the player what we're doing */ |
| | 2760 | vocerr_info(ctx, VOCERR(130), "("); |
| | 2761 | runppr(rcx, defobj, PRP_THEDESC, 0); |
| | 2762 | vocerr_info(ctx, VOCERR(131), ")"); |
| | 2763 | tioflush(ctx->voccxtio); |
| | 2764 | } |
| | 2765 | err = -2; /* "continue" */ |
| | 2766 | goto exit_error; |
| | 2767 | } |
| | 2768 | } |
| | 2769 | } |
| | 2770 | else |
| | 2771 | rundisc(rcx); |
| | 2772 | |
| | 2773 | /* |
| | 2774 | * No unique default; ask the player for a direct |
| | 2775 | * object, and try the command again if he is kind |
| | 2776 | * enough to provide one. |
| | 2777 | */ |
| | 2778 | askflags = ERR_RUNASKD; |
| | 2779 | } |
| | 2780 | } |
| | 2781 | else if (voclistlen(iolist) == 0) |
| | 2782 | { |
| | 2783 | /* direct object(s), but no indirect object -- find doAction */ |
| | 2784 | if (voctplfnd(ctx, verb, MCMONINV, tpl, &newstyle)) |
| | 2785 | { |
| | 2786 | /* disambiguate the direct object list, now that we can */ |
| | 2787 | if (vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, |
| | 2788 | PRP_VALIDDO, voctplvd(tpl), cmd, MCMONINV, |
| | 2789 | actor, verb, prep, cmdbuf, FALSE)) |
| | 2790 | { |
| | 2791 | err = -1; |
| | 2792 | goto exit_error; |
| | 2793 | } |
| | 2794 | iobj = MCMONINV; |
| | 2795 | |
| | 2796 | /* |
| | 2797 | * save the disambiguated direct object list, in case |
| | 2798 | * we hit an askio in the course of processing it |
| | 2799 | */ |
| | 2800 | memcpy(dolist, dolist1, |
| | 2801 | (size_t)(voclistlen(dolist1) + 1)*sizeof(dolist[0])); |
| | 2802 | |
| | 2803 | /* re-check for multi-mode */ |
| | 2804 | if (multi_flags == 0) |
| | 2805 | multi_flags = check_for_multi(dolist1); |
| | 2806 | |
| | 2807 | /* save it/them/him/her, and execute the command */ |
| | 2808 | exesaveit(ctx, dolist1); |
| | 2809 | if ((err = exeloop(ctx, actor, verb, dolist1, &prep, |
| | 2810 | (vocoldef *)0, multi_flags, |
| | 2811 | tpl, newstyle)) != 0) |
| | 2812 | goto exit_error; |
| | 2813 | } |
| | 2814 | else |
| | 2815 | { |
| | 2816 | /* no doAction - we'll need to find an indirect object */ |
| | 2817 | runrst(rcx); |
| | 2818 | runppr(rcx, verb, PRP_PREPDEFAULT, 0); |
| | 2819 | if (runtostyp(rcx) != DAT_OBJECT) |
| | 2820 | { |
| | 2821 | /* discard the result */ |
| | 2822 | rundisc(rcx); |
| | 2823 | |
| | 2824 | /* call parseUnknownVerb to handle it */ |
| | 2825 | if (try_unknown_verb(ctx, actor, cmd, typelist, |
| | 2826 | wrdcnt, next_word, TRUE, VOCERR(24), |
| | 2827 | "I don't recognize that sentence.")) |
| | 2828 | { |
| | 2829 | /* handled - end the command successfully */ |
| | 2830 | err = 0; |
| | 2831 | } |
| | 2832 | else |
| | 2833 | { |
| | 2834 | /* not handled - indicate failure */ |
| | 2835 | err = -1; |
| | 2836 | } |
| | 2837 | goto exit_error; |
| | 2838 | } |
| | 2839 | prep = runpopobj(rcx); |
| | 2840 | |
| | 2841 | runrst(rcx); |
| | 2842 | runpobj(rcx, prep); |
| | 2843 | runpobj(rcx, actor); |
| | 2844 | runppr(rcx, verb, PRP_IODEFAULT, 2); |
| | 2845 | |
| | 2846 | if (runtostyp(rcx) == DAT_LIST) |
| | 2847 | { |
| | 2848 | uchar *l = runpoplst(rcx); |
| | 2849 | uint lstsiz; |
| | 2850 | objnum defobj; |
| | 2851 | int objcnt; |
| | 2852 | runsdef val; |
| | 2853 | objnum o; |
| | 2854 | |
| | 2855 | /* push list back on stack, to keep in heap */ |
| | 2856 | val.runsv.runsvstr = l; |
| | 2857 | val.runstyp = DAT_LIST; |
| | 2858 | runrepush(rcx, &val); |
| | 2859 | |
| | 2860 | /* get list size out of list */ |
| | 2861 | lstsiz = osrp2(l) - 2; |
| | 2862 | l += 2; |
| | 2863 | |
| | 2864 | if (!voctplfnd(ctx, verb, prep, tpl, &newstyle)) |
| | 2865 | { |
| | 2866 | for (objcnt = 0 ; lstsiz && objcnt < 2 |
| | 2867 | ; lstadv(&l, &lstsiz)) |
| | 2868 | { |
| | 2869 | if (*l == DAT_OBJECT) |
| | 2870 | { |
| | 2871 | objcnt++; |
| | 2872 | defobj = osrp2(l + 1); |
| | 2873 | } |
| | 2874 | } |
| | 2875 | } |
| | 2876 | else |
| | 2877 | { |
| | 2878 | int dobj_first; |
| | 2879 | |
| | 2880 | /* |
| | 2881 | * Get the template flags. If we must |
| | 2882 | * disambiguate the direct object first for this |
| | 2883 | * verb, do so now. |
| | 2884 | */ |
| | 2885 | tplflags = (newstyle ? voctplflg(tpl) : 0); |
| | 2886 | dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); |
| | 2887 | if (dobj_first) |
| | 2888 | { |
| | 2889 | if (vocdisambig(ctx, dolist1, dolist, |
| | 2890 | PRP_DODEFAULT, PRP_VALIDDO, |
| | 2891 | voctplvd(tpl), cmd, MCMONINV, |
| | 2892 | actor, verb, prep, cmdbuf, |
| | 2893 | FALSE)) |
| | 2894 | { |
| | 2895 | err = -1; |
| | 2896 | goto exit_error; |
| | 2897 | } |
| | 2898 | |
| | 2899 | /* only one direct object is allowed here */ |
| | 2900 | if (voclistlen(dolist1) > 1) |
| | 2901 | { |
| | 2902 | vocerr(ctx, VOCERR(28), |
| | 2903 | "You can't use multiple objects with this command."); |
| | 2904 | err = -1; |
| | 2905 | goto exit_error; |
| | 2906 | } |
| | 2907 | |
| | 2908 | /* save the object in the original list */ |
| | 2909 | memcpy(dolist, dolist1, |
| | 2910 | (size_t)(2 * sizeof(dolist[0]))); |
| | 2911 | } |
| | 2912 | |
| | 2913 | for (objcnt = 0 ; lstsiz && objcnt < 2 |
| | 2914 | ; lstadv(&l, &lstsiz)) |
| | 2915 | { |
| | 2916 | if (*l == DAT_OBJECT) |
| | 2917 | { |
| | 2918 | o = osrp2(l + 1); |
| | 2919 | if (!objgetap(ctx->voccxmem, o, voctplvi(tpl), |
| | 2920 | (objnum *)0, FALSE)) |
| | 2921 | continue; |
| | 2922 | |
| | 2923 | tiohide(ctx->voccxtio); |
| | 2924 | if (dobj_first) |
| | 2925 | runpobj(rcx, dolist[0].vocolobj); |
| | 2926 | runpobj(rcx, actor); |
| | 2927 | runppr(rcx, o, voctplvi(tpl), |
| | 2928 | (dobj_first ? 2 : 1)); |
| | 2929 | if (!tioshow(ctx->voccxtio)) |
| | 2930 | { |
| | 2931 | objcnt++; |
| | 2932 | defobj = o; |
| | 2933 | } |
| | 2934 | } |
| | 2935 | } |
| | 2936 | } |
| | 2937 | |
| | 2938 | /* no longer need list in heap, so discard it */ |
| | 2939 | rundisc(rcx); |
| | 2940 | |
| | 2941 | /* if there's exactly one default object, use it */ |
| | 2942 | if (objcnt == 1) |
| | 2943 | { |
| | 2944 | iolist[0].vocolobj = defobj; |
| | 2945 | iolist[0].vocolflg = 0; |
| | 2946 | iolist[0].vocolfst = iolist[0].vocollst = 0; |
| | 2947 | iolist[1].vocolobj = MCMONINV; |
| | 2948 | iolist[1].vocolflg = 0; |
| | 2949 | iolist[1].vocolfst = iolist[1].vocollst = 0; |
| | 2950 | |
| | 2951 | /* tell the user what we're assuming */ |
| | 2952 | runrst(rcx); |
| | 2953 | if (ctx->voccxpdef2 != MCMONINV) |
| | 2954 | { |
| | 2955 | runpobj(rcx, prep); |
| | 2956 | runpobj(rcx, defobj); |
| | 2957 | runpobj(rcx, verb); |
| | 2958 | runpobj(rcx, actor); |
| | 2959 | runfn(rcx, ctx->voccxpdef2, 4); |
| | 2960 | } |
| | 2961 | else if (ctx->voccxpdef != MCMONINV) |
| | 2962 | { |
| | 2963 | runpobj(rcx, prep); |
| | 2964 | runpobj(rcx, defobj); |
| | 2965 | runfn(rcx, ctx->voccxpdef, 2); |
| | 2966 | } |
| | 2967 | else |
| | 2968 | { |
| | 2969 | vocerr_info(ctx, VOCERR(130), "("); |
| | 2970 | runppr(rcx, prep, PRP_SDESC, 0); |
| | 2971 | vocerr_info(ctx, VOCERR(132), " "); |
| | 2972 | runppr(rcx, defobj, PRP_THEDESC, 0); |
| | 2973 | vocerr_info(ctx, VOCERR(131), ")"); |
| | 2974 | } |
| | 2975 | tioflush(ctx->voccxtio); |
| | 2976 | err = -2; /* "continue" */ |
| | 2977 | goto exit_error; |
| | 2978 | } |
| | 2979 | } |
| | 2980 | else |
| | 2981 | rundisc(rcx); |
| | 2982 | |
| | 2983 | /* |
| | 2984 | * We didn't get a unique default indirect object, so |
| | 2985 | * we should ask the player for an indirct object, and |
| | 2986 | * repeat the command should he provide one. |
| | 2987 | */ |
| | 2988 | askflags = ERR_RUNASKI; |
| | 2989 | } |
| | 2990 | } |
| | 2991 | else |
| | 2992 | { |
| | 2993 | objnum otherobj; |
| | 2994 | |
| | 2995 | /* find the template for this verb/prep combination */ |
| | 2996 | if (!voctplfnd(ctx, verb, prep, tpl, &newstyle)) |
| | 2997 | { |
| | 2998 | vocoldef *np1; |
| | 2999 | |
| | 3000 | /* |
| | 3001 | * If we could have used the preposition in the first noun |
| | 3002 | * phrase rather than in the verb, and this would have |
| | 3003 | * yielded a valid verb phrase, the error is "I don't see |
| | 3004 | * any <noun phrase> here". |
| | 3005 | * |
| | 3006 | * Otherwise, it's a verb phrasing error. In this case, |
| | 3007 | * call parseUnknownVerb to handle the error; the default |
| | 3008 | * error is "I don't recognize that sentence". |
| | 3009 | */ |
| | 3010 | np1 = dolist[0].vocolfst < iolist[0].vocolfst |
| | 3011 | ? dolist : iolist; |
| | 3012 | if ((np1->vocolflg & VOCS_TRIMPREP) != 0) |
| | 3013 | { |
| | 3014 | char namebuf[VOCBUFSIZ]; |
| | 3015 | |
| | 3016 | /* |
| | 3017 | * it's a trimmed prep phrase, so we actually have an |
| | 3018 | * unmatched object - report the error |
| | 3019 | */ |
| | 3020 | voc_make_obj_name_from_list( |
| | 3021 | ctx, namebuf, cmd, np1->vocolfst, np1->vocolhlst); |
| | 3022 | vocerr(ctx, VOCERR(9), "I don't see any %s here.", |
| | 3023 | namebuf); |
| | 3024 | |
| | 3025 | /* terminate the command with an error */ |
| | 3026 | err = -1; |
| | 3027 | } |
| | 3028 | else if (try_unknown_verb(ctx, actor, cmd, typelist, |
| | 3029 | wrdcnt, next_word, TRUE, |
| | 3030 | VOCERR(24), |
| | 3031 | "I don't recognize that sentence.")) |
| | 3032 | { |
| | 3033 | /* they handled it - terminate command successfully */ |
| | 3034 | err = 0; |
| | 3035 | } |
| | 3036 | else |
| | 3037 | { |
| | 3038 | /* that failed - terminate the command with an error */ |
| | 3039 | err = -1; |
| | 3040 | } |
| | 3041 | |
| | 3042 | /* terminate the command */ |
| | 3043 | goto exit_error; |
| | 3044 | } |
| | 3045 | |
| | 3046 | /* |
| | 3047 | * We have both direct and indirect objects. If we don't |
| | 3048 | * yet have the direct object, go ask for it |
| | 3049 | */ |
| | 3050 | if (voclistlen(dolist) == 0) |
| | 3051 | { |
| | 3052 | askflags = ERR_RUNASKD; |
| | 3053 | goto exit_error; |
| | 3054 | } |
| | 3055 | |
| | 3056 | /* get the flags (if old-style, flags are always zero) */ |
| | 3057 | tplflags = (newstyle ? voctplflg(tpl) : 0); |
| | 3058 | |
| | 3059 | /* |
| | 3060 | * the "other" object (dobj if doing iobj, iobj if doing |
| | 3061 | * dobj) is not known when the first object is disambiguated |
| | 3062 | */ |
| | 3063 | otherobj = MCMONINV; |
| | 3064 | |
| | 3065 | /* disambiguate the objects in the proper order */ |
| | 3066 | if (tplflags & VOCTPLFLG_DOBJ_FIRST) |
| | 3067 | { |
| | 3068 | /* disambiguate the direct object list */ |
| | 3069 | if (vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, |
| | 3070 | PRP_VALIDDO, voctplvd(tpl), cmd, otherobj, |
| | 3071 | actor, verb, prep, cmdbuf, FALSE)) |
| | 3072 | { |
| | 3073 | err = -1; |
| | 3074 | goto exit_error; |
| | 3075 | } |
| | 3076 | |
| | 3077 | /* |
| | 3078 | * only one direct object is allowed if it's |
| | 3079 | * disambiguated first |
| | 3080 | */ |
| | 3081 | if (voclistlen(dolist1) > 1) |
| | 3082 | { |
| | 3083 | vocerr(ctx, VOCERR(28), |
| | 3084 | "You can't use multiple objects with this command."); |
| | 3085 | err = -1; |
| | 3086 | goto exit_error; |
| | 3087 | } |
| | 3088 | |
| | 3089 | /* the other object is now known for iboj disambiguation */ |
| | 3090 | otherobj = dolist1[0].vocolobj; |
| | 3091 | } |
| | 3092 | |
| | 3093 | /* disambiguate the indirect object list */ |
| | 3094 | if (vocdisambig(ctx, iolist1, iolist, PRP_IODEFAULT, |
| | 3095 | PRP_VALIDIO, voctplvi(tpl), cmd, otherobj, |
| | 3096 | actor, verb, prep, cmdbuf, FALSE)) |
| | 3097 | { |
| | 3098 | err = -1; |
| | 3099 | goto exit_error; |
| | 3100 | } |
| | 3101 | |
| | 3102 | /* only one indirect object is allowed */ |
| | 3103 | if (voclistlen(iolist1) > 1) |
| | 3104 | { |
| | 3105 | vocerr(ctx, VOCERR(25), |
| | 3106 | "You can't use multiple indirect objects."); |
| | 3107 | err = -1; |
| | 3108 | goto exit_error; |
| | 3109 | } |
| | 3110 | otherobj = iobj = iolist1[0].vocolobj; |
| | 3111 | |
| | 3112 | /* |
| | 3113 | * disambiguate the direct object list if we haven't |
| | 3114 | * already done so (we might have disambiguated it first due |
| | 3115 | * to the DisambigDobjFirst flag being set in the template) |
| | 3116 | */ |
| | 3117 | if (!(tplflags & VOCTPLFLG_DOBJ_FIRST) |
| | 3118 | && vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, |
| | 3119 | PRP_VALIDDO, voctplvd(tpl), cmd, otherobj, |
| | 3120 | actor, verb, prep, cmdbuf, FALSE)) |
| | 3121 | { |
| | 3122 | err = -1; |
| | 3123 | goto exit_error; |
| | 3124 | } |
| | 3125 | |
| | 3126 | /* re-check for multi-mode */ |
| | 3127 | if (multi_flags == 0) |
| | 3128 | multi_flags = check_for_multi(dolist1); |
| | 3129 | |
| | 3130 | /* save it/them/him/her, and execute the command */ |
| | 3131 | exesaveit(ctx, dolist1); |
| | 3132 | if ((err = exeloop(ctx, actor, verb, dolist1, &prep, iolist1, |
| | 3133 | multi_flags, tpl, newstyle)) != 0) |
| | 3134 | goto exit_error; |
| | 3135 | } |
| | 3136 | |
| | 3137 | exit_error: ; |
| | 3138 | |
| | 3139 | ERRCATCH(ctx->voccxerr, err) |
| | 3140 | if (err == ERR_RUNASKI) prep = errargint(0); |
| | 3141 | if (err != ERR_RUNASKD && err != ERR_RUNASKI) |
| | 3142 | errrse(ctx->voccxerr); |
| | 3143 | ERREND(ctx->voccxerr) |
| | 3144 | |
| | 3145 | switch(err) |
| | 3146 | { |
| | 3147 | case 0: |
| | 3148 | break; |
| | 3149 | |
| | 3150 | case ERR_RUNABRT: |
| | 3151 | /* "abort" executed - return the ABORT code */ |
| | 3152 | VOC_RETVAL(ctx, save_sp, err); |
| | 3153 | |
| | 3154 | case ERR_RUNEXIT: |
| | 3155 | /* |
| | 3156 | * "exit" executed - terminate the command, but return |
| | 3157 | * success, since we want to process any additional commands |
| | 3158 | */ |
| | 3159 | VOC_RETVAL(ctx, save_sp, 0); |
| | 3160 | |
| | 3161 | case ERR_RUNEXITOBJ: |
| | 3162 | /* |
| | 3163 | * "exitobj" executed - indicate success, since this merely |
| | 3164 | * indicates that the game decided it was done processing an |
| | 3165 | * object early |
| | 3166 | */ |
| | 3167 | VOC_RETVAL(ctx, save_sp, 0); |
| | 3168 | |
| | 3169 | case ERR_RUNASKI: |
| | 3170 | case ERR_RUNASKD: |
| | 3171 | askflags = err; |
| | 3172 | break; |
| | 3173 | |
| | 3174 | case -2: /* special code: continue with main loop */ |
| | 3175 | continue; |
| | 3176 | |
| | 3177 | case -1: /* special code: return an error */ |
| | 3178 | default: |
| | 3179 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3180 | } |
| | 3181 | |
| | 3182 | /* |
| | 3183 | * If we got this far, we probably want more information. The |
| | 3184 | * askflags can tell us what to do from here. |
| | 3185 | */ |
| | 3186 | if (askflags) |
| | 3187 | { |
| | 3188 | int old_unknown; |
| | 3189 | int exenewpos; |
| | 3190 | |
| | 3191 | /* |
| | 3192 | * if we had unknown words, don't ask for more information |
| | 3193 | * at this point; simply give up and report the unknown word |
| | 3194 | */ |
| | 3195 | if (ctx->voccxunknown != 0) |
| | 3196 | { |
| | 3197 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3198 | } |
| | 3199 | |
| | 3200 | /* find new template indicated by the additional object */ |
| | 3201 | foundtpl = voctplfnd(ctx, verb, prep, tpl, &newstyle); |
| | 3202 | tplflags = (newstyle ? voctplflg(tpl) : 0); |
| | 3203 | |
| | 3204 | /* find a default object of the type requested */ |
| | 3205 | runrst(rcx); |
| | 3206 | if (askflags == ERR_RUNASKD) runpnil(rcx); |
| | 3207 | runpobj(rcx, prep); |
| | 3208 | runpobj(rcx, actor); |
| | 3209 | runppr(rcx, verb, |
| | 3210 | (prpnum)(askflags == ERR_RUNASKD |
| | 3211 | ? PRP_DODEFAULT : PRP_IODEFAULT), |
| | 3212 | (askflags == ERR_RUNASKD ? 3 : 2)); |
| | 3213 | |
| | 3214 | /* |
| | 3215 | * If we got a list back from ?oDefault, and we have a new |
| | 3216 | * template for the command, process the list normally with |
| | 3217 | * the object verification routine for this template. If we |
| | 3218 | * end up with exactly one object, we will assume it is the |
| | 3219 | * object to be used; otherwise, make no assumption and ask |
| | 3220 | * the user for guidance. |
| | 3221 | */ |
| | 3222 | if (runtostyp(rcx) == DAT_LIST && foundtpl) |
| | 3223 | { |
| | 3224 | uchar *l = runpoplst(rcx); |
| | 3225 | uint lstsiz; |
| | 3226 | int objcnt; |
| | 3227 | objnum defobj; |
| | 3228 | objnum o; |
| | 3229 | runsdef val; |
| | 3230 | |
| | 3231 | /* push list back on stack, to keep it in the heap */ |
| | 3232 | val.runsv.runsvstr = l; |
| | 3233 | val.runstyp = DAT_LIST; |
| | 3234 | runrepush(rcx, &val); |
| | 3235 | |
| | 3236 | /* get list size out of list */ |
| | 3237 | lstsiz = osrp2(l) - 2; |
| | 3238 | l += 2; |
| | 3239 | |
| | 3240 | for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) |
| | 3241 | { |
| | 3242 | if (*l == DAT_OBJECT) |
| | 3243 | { |
| | 3244 | prpnum verprop; |
| | 3245 | int argc = 1; |
| | 3246 | |
| | 3247 | o = osrp2(l + 1); |
| | 3248 | verprop = (askflags == ERR_RUNASKD ? voctplvd(tpl) |
| | 3249 | : voctplvi(tpl)); |
| | 3250 | |
| | 3251 | if (!objgetap(ctx->voccxmem, o, verprop, |
| | 3252 | (objnum *)0, FALSE)) |
| | 3253 | continue; |
| | 3254 | |
| | 3255 | tiohide(ctx->voccxtio); |
| | 3256 | |
| | 3257 | /* |
| | 3258 | * In the unlikely event that we have an |
| | 3259 | * indirect object but no direct object, push |
| | 3260 | * the iobj. This can happen when the player |
| | 3261 | * types a sentence such as "verb prep iobj". |
| | 3262 | */ |
| | 3263 | if (voclistlen(iolist) != 0 |
| | 3264 | && askflags == ERR_RUNASKD |
| | 3265 | && !(tplflags & VOCTPLFLG_DOBJ_FIRST)) |
| | 3266 | { |
| | 3267 | /* push the indirect object */ |
| | 3268 | runpobj(rcx, iolist[0].vocolobj); |
| | 3269 | |
| | 3270 | /* note the second argument */ |
| | 3271 | argc = 2; |
| | 3272 | } |
| | 3273 | |
| | 3274 | /* |
| | 3275 | * If this is a disambigDobjFirst verb, and |
| | 3276 | * we're validating an indirect object list, |
| | 3277 | * then we must push the direct object argument |
| | 3278 | * to the indirect object validation routine. |
| | 3279 | */ |
| | 3280 | if (askflags == ERR_RUNASKI |
| | 3281 | && (tplflags & VOCTPLFLG_DOBJ_FIRST) != 0) |
| | 3282 | { |
| | 3283 | /* push the diret object */ |
| | 3284 | runpobj(rcx, dolist[0].vocolobj); |
| | 3285 | |
| | 3286 | /* note the second argument */ |
| | 3287 | argc = 2; |
| | 3288 | } |
| | 3289 | |
| | 3290 | /* push the actor and call the verXoVerb routine */ |
| | 3291 | runpobj(rcx, actor); |
| | 3292 | runppr(rcx, o, verprop, argc); |
| | 3293 | if (!tioshow(ctx->voccxtio)) |
| | 3294 | { |
| | 3295 | ++objcnt; |
| | 3296 | defobj = o; |
| | 3297 | } |
| | 3298 | |
| | 3299 | } |
| | 3300 | } |
| | 3301 | |
| | 3302 | /* no longer need list in heap, so discard it */ |
| | 3303 | rundisc(rcx); |
| | 3304 | |
| | 3305 | /* if we found exactly one object, it's the default */ |
| | 3306 | if (objcnt == 1) |
| | 3307 | { |
| | 3308 | if (askflags == ERR_RUNASKD) |
| | 3309 | { |
| | 3310 | dolist[0].vocolobj = defobj; |
| | 3311 | dolist[0].vocolflg = 0; |
| | 3312 | dolist[0].vocolfst = dolist[0].vocollst = 0; |
| | 3313 | dolist[1].vocolobj = MCMONINV; |
| | 3314 | dolist[1].vocolflg = 0; |
| | 3315 | dolist[1].vocolfst = dolist[1].vocollst = 0; |
| | 3316 | } |
| | 3317 | else |
| | 3318 | { |
| | 3319 | iolist[0].vocolobj = defobj; |
| | 3320 | iolist[0].vocolflg = 0; |
| | 3321 | iolist[0].vocolfst = iolist[0].vocollst = 0; |
| | 3322 | iolist[1].vocolobj = MCMONINV; |
| | 3323 | iolist[1].vocolflg = 0; |
| | 3324 | iolist[1].vocolfst = iolist[1].vocollst = 0; |
| | 3325 | } |
| | 3326 | |
| | 3327 | /* tell the user what we're assuming */ |
| | 3328 | if (ctx->voccxpdef2 != MCMONINV) |
| | 3329 | { |
| | 3330 | if (askflags == ERR_RUNASKI) |
| | 3331 | runpobj(rcx, prep); |
| | 3332 | else |
| | 3333 | runpnil(rcx); |
| | 3334 | runpobj(rcx, defobj); |
| | 3335 | runpobj(rcx, verb); |
| | 3336 | runpobj(rcx, actor); |
| | 3337 | runfn(rcx, ctx->voccxpdef2, 4); |
| | 3338 | } |
| | 3339 | else if (ctx->voccxpdef != MCMONINV) |
| | 3340 | { |
| | 3341 | if (askflags == ERR_RUNASKI) |
| | 3342 | runpobj(rcx, prep); |
| | 3343 | else |
| | 3344 | runpnil(rcx); |
| | 3345 | runpobj(rcx, defobj); |
| | 3346 | runfn(rcx, ctx->voccxpdef, 2); |
| | 3347 | } |
| | 3348 | else |
| | 3349 | { |
| | 3350 | vocerr_info(ctx, VOCERR(130), "("); |
| | 3351 | if (askflags == ERR_RUNASKI) |
| | 3352 | { |
| | 3353 | runppr(rcx, prep, PRP_SDESC, 0); |
| | 3354 | vocerr_info(ctx, VOCERR(132), " "); |
| | 3355 | } |
| | 3356 | runppr(rcx, defobj, PRP_THEDESC, 0); |
| | 3357 | vocerr_info(ctx, VOCERR(131), ")"); |
| | 3358 | } |
| | 3359 | tioflush(ctx->voccxtio); |
| | 3360 | continue; /* try the command again */ |
| | 3361 | } |
| | 3362 | } |
| | 3363 | else |
| | 3364 | rundisc(rcx); |
| | 3365 | |
| | 3366 | /* make sure output capturing is off for the prompt */ |
| | 3367 | tiocapture(ctx->voccxtio, (mcmcxdef *)0, FALSE); |
| | 3368 | tioclrcapture(ctx->voccxtio); |
| | 3369 | |
| | 3370 | /* |
| | 3371 | * If we're asking for an indirect object, and we have a |
| | 3372 | * list of direct objects, and parseAskobjIndirect is |
| | 3373 | * defined, call it. Otherwise, if there's a |
| | 3374 | * parseAskobjActor routine, call it. Otherwise, if there's |
| | 3375 | * a parseAskobj routine, use that. Finally, if none of |
| | 3376 | * those are defined, generate the default phrasing. |
| | 3377 | */ |
| | 3378 | if (ctx->voccxpask3 != MCMONINV |
| | 3379 | && askflags == ERR_RUNASKI |
| | 3380 | && voclistlen(dolist) != 0) |
| | 3381 | { |
| | 3382 | /* call parseAskobjIndirect */ |
| | 3383 | voc_askobj_indirect(ctx, dolist, actor, verb, prep); |
| | 3384 | } |
| | 3385 | else if (ctx->voccxpask2 != MCMONINV) |
| | 3386 | { |
| | 3387 | if (askflags == ERR_RUNASKI) |
| | 3388 | runpobj(ctx->voccxrun, prep); |
| | 3389 | runpobj(ctx->voccxrun, verb); |
| | 3390 | runpobj(ctx->voccxrun, |
| | 3391 | (objnum)(actor == MCMONINV ? ctx->voccxme : actor)); |
| | 3392 | runfn(ctx->voccxrun, ctx->voccxpask2, |
| | 3393 | askflags == ERR_RUNASKI ? 3 : 2); |
| | 3394 | } |
| | 3395 | else if (ctx->voccxpask != MCMONINV) |
| | 3396 | { |
| | 3397 | if (askflags == ERR_RUNASKI) |
| | 3398 | runpobj(ctx->voccxrun, prep); |
| | 3399 | runpobj(ctx->voccxrun, verb); |
| | 3400 | runfn(ctx->voccxrun, ctx->voccxpask, |
| | 3401 | askflags == ERR_RUNASKI ? 2 : 1); |
| | 3402 | } |
| | 3403 | else |
| | 3404 | { |
| | 3405 | /* |
| | 3406 | * Phrase the question: askDo: "What do you want |
| | 3407 | * <actor> to <verb>?" askIo: "What do you want <actor> |
| | 3408 | * to <verb> it <prep>?" If the actor is Me, leave the |
| | 3409 | * actor out of it. |
| | 3410 | */ |
| | 3411 | if (actor != MCMONINV && actor != ctx->voccxme) |
| | 3412 | { |
| | 3413 | vocerr_info(ctx, VOCERR(148), "What do you want "); |
| | 3414 | runppr(rcx, actor, PRP_THEDESC, 0); |
| | 3415 | vocerr_info(ctx, VOCERR(149), " to "); |
| | 3416 | } |
| | 3417 | else |
| | 3418 | { |
| | 3419 | /* no actor - don't mention one */ |
| | 3420 | vocerr_info(ctx, VOCERR(140), "What do you want to "); |
| | 3421 | } |
| | 3422 | |
| | 3423 | /* add the verb */ |
| | 3424 | runppr(rcx, verb, PRP_SDESC, 0); |
| | 3425 | |
| | 3426 | /* |
| | 3427 | * add an appropriate pronoun for the direct object, |
| | 3428 | * and the preposition, if we're asking for an indirect |
| | 3429 | * object |
| | 3430 | */ |
| | 3431 | if (askflags == ERR_RUNASKI) |
| | 3432 | { |
| | 3433 | int i; |
| | 3434 | int cnt; |
| | 3435 | int distinct; |
| | 3436 | char *lastfst; |
| | 3437 | |
| | 3438 | /* |
| | 3439 | * If possible, tailor the pronoun to the situation |
| | 3440 | * rather than using "it"; if we have multiple |
| | 3441 | * objects, use "them", and if we have agreement |
| | 3442 | * with the possible single objects about "him" or |
| | 3443 | * "her", use that. Otherwise, use "it". If "all" |
| | 3444 | * was specified for any word, automatically assume |
| | 3445 | * multiple distinct objects were specified. |
| | 3446 | */ |
| | 3447 | cnt = voclistlen(dolist); |
| | 3448 | for (distinct = 0, i = 0, lastfst = 0 ; i < cnt ; ++i) |
| | 3449 | { |
| | 3450 | /* if the first word is different here, note it */ |
| | 3451 | if (lastfst != dolist[i].vocolfst) |
| | 3452 | { |
| | 3453 | /* this is a different word - count it */ |
| | 3454 | ++distinct; |
| | 3455 | lastfst = dolist[i].vocolfst; |
| | 3456 | } |
| | 3457 | |
| | 3458 | /* always assume multiple distinct objects on "all" */ |
| | 3459 | if (dolist[i].vocolflg & VOCS_ALL) |
| | 3460 | { |
| | 3461 | distinct = 2; |
| | 3462 | break; |
| | 3463 | } |
| | 3464 | } |
| | 3465 | |
| | 3466 | /* |
| | 3467 | * If we have multiple words, use "them"; |
| | 3468 | * otherwise, see if we can find agreement about |
| | 3469 | * using "him" or "her". |
| | 3470 | */ |
| | 3471 | if (distinct > 1) |
| | 3472 | { |
| | 3473 | /* multiple words specified by user - use "them" */ |
| | 3474 | vocerr_info(ctx, VOCERR(144), " them "); |
| | 3475 | } |
| | 3476 | else |
| | 3477 | { |
| | 3478 | int is_him; |
| | 3479 | int is_her; |
| | 3480 | int is_them; |
| | 3481 | |
| | 3482 | /* run through the objects and check him/her */ |
| | 3483 | for (i = 0 ; i < cnt ; ++i) |
| | 3484 | { |
| | 3485 | int him1, her1, them1; |
| | 3486 | |
| | 3487 | /* if it's special (number, string), use "it" */ |
| | 3488 | if (dolist[i].vocolobj == MCMONINV) |
| | 3489 | { |
| | 3490 | him1 = FALSE; |
| | 3491 | her1 = FALSE; |
| | 3492 | them1 = FALSE; |
| | 3493 | } |
| | 3494 | else |
| | 3495 | { |
| | 3496 | /* check for "him" */ |
| | 3497 | runppr(rcx, dolist[i].vocolobj, PRP_ISHIM, 0); |
| | 3498 | him1 = (runtostyp(rcx) == DAT_TRUE); |
| | 3499 | rundisc(rcx); |
| | 3500 | |
| | 3501 | /* check for "her" */ |
| | 3502 | runppr(rcx, dolist[i].vocolobj, PRP_ISHER, 0); |
| | 3503 | her1 = (runtostyp(rcx) == DAT_TRUE); |
| | 3504 | rundisc(rcx); |
| | 3505 | |
| | 3506 | /* check for "them" */ |
| | 3507 | runppr(rcx, dolist[i].vocolobj, |
| | 3508 | PRP_ISTHEM, 0); |
| | 3509 | them1 = (runtostyp(rcx) == DAT_TRUE); |
| | 3510 | rundisc(rcx); |
| | 3511 | } |
| | 3512 | |
| | 3513 | /* |
| | 3514 | * if this is the first object, it |
| | 3515 | * definitely agrees; otherwise, keep going |
| | 3516 | * only if it agrees with what we found on |
| | 3517 | * the last pass |
| | 3518 | */ |
| | 3519 | if (i == 0) |
| | 3520 | { |
| | 3521 | is_him = him1; |
| | 3522 | is_her = her1; |
| | 3523 | is_them = them1; |
| | 3524 | } |
| | 3525 | else |
| | 3526 | { |
| | 3527 | /* turn off either that is no longer true */ |
| | 3528 | if (!him1) is_him = FALSE; |
| | 3529 | if (!her1) is_her = FALSE; |
| | 3530 | if (!them1) is_them = FALSE; |
| | 3531 | } |
| | 3532 | |
| | 3533 | /* if all are false, stop now */ |
| | 3534 | if (!is_him && !is_her && !is_them) |
| | 3535 | break; |
| | 3536 | } |
| | 3537 | |
| | 3538 | /* |
| | 3539 | * If we could agree on "him", "her", or "them", |
| | 3540 | * use that pronoun; otherwise, use "it". If we |
| | 3541 | * found both "him" and "her" are acceptable for |
| | 3542 | * all objects, use "them". |
| | 3543 | */ |
| | 3544 | if ((is_him && is_her) || is_them) |
| | 3545 | vocerr_info(ctx, VOCERR(147), " them "); |
| | 3546 | else if (is_him) |
| | 3547 | vocerr_info(ctx, VOCERR(145), " him "); |
| | 3548 | else if (is_her) |
| | 3549 | vocerr_info(ctx, VOCERR(146), " her "); |
| | 3550 | else |
| | 3551 | vocerr_info(ctx, VOCERR(141), " it "); |
| | 3552 | } |
| | 3553 | |
| | 3554 | /* finish off the question with the prep and a "?" */ |
| | 3555 | if (prep != MCMONINV) |
| | 3556 | runppr(rcx, prep, PRP_SDESC, 0); |
| | 3557 | else |
| | 3558 | vocerr_info(ctx, VOCERR(142), "to"); |
| | 3559 | } |
| | 3560 | vocerr_info(ctx, VOCERR(143), "?"); |
| | 3561 | } |
| | 3562 | tioflush(ctx->voccxtio); |
| | 3563 | |
| | 3564 | /* |
| | 3565 | * Get a new command line. If the player gives us |
| | 3566 | * something that looks like a noun list, and nothing more, |
| | 3567 | * he anwered our question; otherwise, he's typing a new |
| | 3568 | * command, so we must return to the caller with the reparse |
| | 3569 | * flag set. |
| | 3570 | */ |
| | 3571 | if (askflags == ERR_RUNASKD) |
| | 3572 | { |
| | 3573 | exenewbuf = donewbuf; |
| | 3574 | exenewcmd = donewcmd; |
| | 3575 | exenewlist = donewlist; |
| | 3576 | exenewtype = donewtype; |
| | 3577 | } |
| | 3578 | else |
| | 3579 | { |
| | 3580 | exenewbuf = ionewbuf; |
| | 3581 | exenewcmd = ionewcmd; |
| | 3582 | exenewlist = ionewlist; |
| | 3583 | exenewtype = ionewtype; |
| | 3584 | } |
| | 3585 | |
| | 3586 | /* read the new command */ |
| | 3587 | if (vocread(ctx, actor, verb, exenewcmd, VOCBUFSIZ, |
| | 3588 | askflags == ERR_RUNASKD ? 3 : 4) == VOCREAD_REDO) |
| | 3589 | { |
| | 3590 | /* |
| | 3591 | * we got an input line, but we want to treat it as a brand |
| | 3592 | * new command line - copy the new text to the command |
| | 3593 | * buffer, set the 'redo' flag, and give up |
| | 3594 | */ |
| | 3595 | strcpy(cmdbuf, exenewcmd); |
| | 3596 | ctx->voccxredo = TRUE; |
| | 3597 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3598 | } |
| | 3599 | |
| | 3600 | if (!(cnt = voctok(ctx, exenewcmd, exenewbuf, exenewlist, |
| | 3601 | TRUE, FALSE, TRUE))) |
| | 3602 | { |
| | 3603 | runrst(rcx); |
| | 3604 | runfn(rcx, ctx->voccxprd, 0); |
| | 3605 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3606 | } |
| | 3607 | if (cnt < 0) |
| | 3608 | { |
| | 3609 | ctx->voccxunknown = 0; |
| | 3610 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3611 | } |
| | 3612 | |
| | 3613 | /* |
| | 3614 | * Save the unknown word count while getting types, and set |
| | 3615 | * the count to a non-zero value - this will force the type |
| | 3616 | * checker to generate an error on an unknown word. This |
| | 3617 | * removes a little control from the game (since |
| | 3618 | * parseUnknownXobj won't be called), but there's not much |
| | 3619 | * else we can do here. |
| | 3620 | */ |
| | 3621 | old_unknown = ctx->voccxunknown; |
| | 3622 | ctx->voccxunknown = 1; |
| | 3623 | |
| | 3624 | /* get the types */ |
| | 3625 | exenewlist[cnt] = 0; |
| | 3626 | if (vocgtyp(ctx, exenewlist, exenewtype, cmdbuf)) |
| | 3627 | { |
| | 3628 | /* |
| | 3629 | * clear the unknown word count so that we fail with |
| | 3630 | * this error rather than trying to deal with unknown |
| | 3631 | * words |
| | 3632 | */ |
| | 3633 | ctx->voccxunknown = 0; |
| | 3634 | |
| | 3635 | /* return failure */ |
| | 3636 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3637 | } |
| | 3638 | |
| | 3639 | /* restore the unknown word count */ |
| | 3640 | ctx->voccxunknown = old_unknown; |
| | 3641 | |
| | 3642 | /* start at the first word */ |
| | 3643 | exenewpos = 0; |
| | 3644 | |
| | 3645 | /* |
| | 3646 | * if we're asking for an indirect object, and the first |
| | 3647 | * word is a preposition, and matches the preposition that |
| | 3648 | * we supplied to precede the indirect object, skip the |
| | 3649 | * preposition |
| | 3650 | */ |
| | 3651 | if (askflags == ERR_RUNASKI |
| | 3652 | && prep != MCMONINV |
| | 3653 | && (exenewtype[0] & VOCT_PREP) != 0) |
| | 3654 | { |
| | 3655 | vocwdef *vp; |
| | 3656 | |
| | 3657 | /* get the preposition */ |
| | 3658 | vp = vocffw(ctx, exenewlist[0], (int)strlen(exenewlist[0]), |
| | 3659 | (char *)0, 0, PRP_PREP, (vocseadef *)0); |
| | 3660 | if (vp != 0 && vp->vocwobj == prep) |
| | 3661 | ++exenewpos; |
| | 3662 | } |
| | 3663 | |
| | 3664 | /* check for a noun */ |
| | 3665 | newnoun = (askflags == ERR_RUNASKD ? dolist : iolist); |
| | 3666 | cnt = vocchknoun(ctx, exenewlist, exenewtype, exenewpos, &next, |
| | 3667 | newnoun, FALSE); |
| | 3668 | |
| | 3669 | if (cnt < 0) { VOC_RETVAL(ctx, save_sp, 1); } /* invalid syntax */ |
| | 3670 | if (cnt == 0 |
| | 3671 | || (exenewlist[next] && !vocspec(exenewlist[next], VOCW_THEN) |
| | 3672 | && *exenewlist[next] != '\0')) |
| | 3673 | { |
| | 3674 | strcpy(cmdbuf, exenewcmd); |
| | 3675 | ctx->voccxredo = TRUE; |
| | 3676 | VOC_RETVAL(ctx, save_sp, 1); |
| | 3677 | } |
| | 3678 | |
| | 3679 | /* re-check the 'multi' flags */ |
| | 3680 | multi_flags = check_for_multi(newnoun); |
| | 3681 | |
| | 3682 | /* give it another go by going back to the top of the loop */ |
| | 3683 | } |
| | 3684 | else |
| | 3685 | { |
| | 3686 | /* normal exit flags - return success */ |
| | 3687 | VOC_RETVAL(ctx, save_sp, 0); |
| | 3688 | } |
| | 3689 | } |
| | 3690 | } |
| | 3691 | |