| | 1 | #charset "us-ascii" |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * TADS 3 Library: Pre-Conditions. |
| | 7 | * |
| | 8 | * This module defines the library pre-conditions. A pre-condition is an |
| | 9 | * abstract object that encapsulates a condition that is required to |
| | 10 | * apply before a command can be executed, and optionally an implied |
| | 11 | * command that can bring the condition into effect. Pre-conditions can |
| | 12 | * be associated with actions or with the objects of an action. |
| | 13 | */ |
| | 14 | |
| | 15 | #include "adv3.h" |
| | 16 | |
| | 17 | |
| | 18 | /* ------------------------------------------------------------------------ */ |
| | 19 | /* |
| | 20 | * An action pre-condition object. Each condition of an action is |
| | 21 | * represented by a subclass of this class. |
| | 22 | */ |
| | 23 | class PreCondition: object |
| | 24 | /* |
| | 25 | * Check the condition on the given object (which may be nil, if |
| | 26 | * this condition doesn't apply specifically to one of the objects |
| | 27 | * in the command). If it is possible to meet the condition with an |
| | 28 | * implicit command, and allowImplicit is true, try to execute the |
| | 29 | * command. If the condition cannot be met, report a failure and |
| | 30 | * use 'exit' to terminate the command. |
| | 31 | * |
| | 32 | * If allowImplicit is nil, an implicit command may not be |
| | 33 | * attempted. In this case, if the condition is not met, we must |
| | 34 | * simply report a failure and use 'exit' to terminate the command. |
| | 35 | */ |
| | 36 | checkPreCondition(obj, allowImplicit) { } |
| | 37 | |
| | 38 | /* |
| | 39 | * Verify the condition. This is called during the object |
| | 40 | * verification step so that the pre-condition can add verifications |
| | 41 | * of its own. This can be used, for example, to add likelihood to |
| | 42 | * objects that already meet the condition. Note that it is |
| | 43 | * generally not desirable to report illogical for conditions that |
| | 44 | * checkPreCondition() enforces, because doing so will prevent |
| | 45 | * checkPreCondition() from ever being reached and thus will prevent |
| | 46 | * checkPreCondition() from attempting to carry out implicit actions |
| | 47 | * to meet the condition. |
| | 48 | * |
| | 49 | * 'obj' is the object being checked. Note that because this is |
| | 50 | * called during verification, the explicitly passed-in object must |
| | 51 | * be used in the check rather than the current object in the global |
| | 52 | * current action. |
| | 53 | */ |
| | 54 | verifyPreCondition(obj) { } |
| | 55 | |
| | 56 | /* |
| | 57 | * Precondition execution order. When we execute preconditions for a |
| | 58 | * given action, we'll sort the list of all applicable preconditions |
| | 59 | * in ascending execution order. |
| | 60 | * |
| | 61 | * For the most part, the relative order of two preconditions is |
| | 62 | * arbitrary. In some unusual cases, though, the order is important, |
| | 63 | * such as when applying one precondition can destroy the conditions |
| | 64 | * that the other would try to create but not vice versa. When the |
| | 65 | * order doesn't matter, this can be left at the default setting. |
| | 66 | */ |
| | 67 | preCondOrder = 100 |
| | 68 | ; |
| | 69 | |
| | 70 | /* ------------------------------------------------------------------------ */ |
| | 71 | /* |
| | 72 | * A pre-condition that applies to a specific, pre-determined object, |
| | 73 | * rather than the direct/indirect object of the command. |
| | 74 | */ |
| | 75 | class ObjectPreCondition: PreCondition |
| | 76 | construct(obj, cond) |
| | 77 | { |
| | 78 | /* |
| | 79 | * remember the specific object I act upon, and the underlying |
| | 80 | * precondition to apply to that object |
| | 81 | */ |
| | 82 | obj_ = obj; |
| | 83 | cond_ = cond; |
| | 84 | } |
| | 85 | |
| | 86 | /* route our check to the pre-condition using our specific object */ |
| | 87 | checkPreCondition(obj, allowImplicit) |
| | 88 | { |
| | 89 | /* check the precondition */ |
| | 90 | return cond_.checkPreCondition(obj_, allowImplicit); |
| | 91 | } |
| | 92 | |
| | 93 | /* route our verification check to the pre-condition */ |
| | 94 | verifyPreCondition(obj) |
| | 95 | { |
| | 96 | cond_.verifyPreCondition(obj_); |
| | 97 | } |
| | 98 | |
| | 99 | /* use the same order as our underlying condition */ |
| | 100 | preCondOrder = (cond_.preCondOrder) |
| | 101 | |
| | 102 | /* the object we check with the condition */ |
| | 103 | obj_ = nil |
| | 104 | |
| | 105 | /* the pre-condition we check */ |
| | 106 | cond_ = nil |
| | 107 | ; |
| | 108 | |
| | 109 | |
| | 110 | /* ------------------------------------------------------------------------ */ |
| | 111 | /* |
| | 112 | * Pre-condition: object must be visible. This condition doesn't |
| | 113 | * attempt any implied command to make the object visible, but merely |
| | 114 | * enforces visibility before allowing the command. |
| | 115 | * |
| | 116 | * This condition is useful for commands that rely on visibly inspecting |
| | 117 | * the object, such as "examine" or "look in". It is possible for an |
| | 118 | * object to be in scope without being visible, since an object can be |
| | 119 | * in scope by way of a non-visual sense. |
| | 120 | * |
| | 121 | * We enforce visibility with a verification test, not a precondition |
| | 122 | * check. |
| | 123 | */ |
| | 124 | objVisible: PreCondition |
| | 125 | verifyPreCondition(obj) |
| | 126 | { |
| | 127 | /* if the object isn't visible, disallow the command */ |
| | 128 | if (obj != nil && !gActor.canSee(obj)) |
| | 129 | { |
| | 130 | /* |
| | 131 | * If the actor is in the dark, that must be the problem. |
| | 132 | * Otherwise, if the object can be heard or smelled but not |
| | 133 | * seen, say so. In any other case, issue a generic message |
| | 134 | * that we can't see the object. |
| | 135 | */ |
| | 136 | if (!gActor.isLocationLit()) |
| | 137 | inaccessible(&tooDarkMsg); |
| | 138 | else if (obj.soundPresence && gActor.canHear(obj)) |
| | 139 | inaccessible(&heardButNotSeenMsg, obj); |
| | 140 | else if (obj.smellPresence && gActor.canSmell(obj)) |
| | 141 | inaccessible(&smelledButNotSeenMsg, obj); |
| | 142 | else |
| | 143 | inaccessible(&mustBeVisibleMsg, obj); |
| | 144 | } |
| | 145 | } |
| | 146 | ; |
| | 147 | |
| | 148 | /* ------------------------------------------------------------------------ */ |
| | 149 | /* |
| | 150 | * Pre-condition: object must be audible; that is, it must be within |
| | 151 | * hearing range of the actor. This condition doesn't attempt any |
| | 152 | * implied command to make the object audible, but merely enforces |
| | 153 | * audibility before allowing the command. |
| | 154 | * |
| | 155 | * It is possible for an object to be in scope without being audible, |
| | 156 | * since an object can be inside a container that is transparent to |
| | 157 | * light but blocks all sound. |
| | 158 | * |
| | 159 | * We enforce this condition with a verification test. |
| | 160 | */ |
| | 161 | objAudible: PreCondition |
| | 162 | verifyPreCondition(obj) |
| | 163 | { |
| | 164 | /* if the object isn't audible, disallow the command */ |
| | 165 | if (obj != nil && !gActor.canHear(obj)) |
| | 166 | inaccessible(&cannotHearMsg, obj); |
| | 167 | } |
| | 168 | ; |
| | 169 | |
| | 170 | /* ------------------------------------------------------------------------ */ |
| | 171 | /* |
| | 172 | * Pre-condition: object must be within smelling range of the actor. |
| | 173 | * This condition doesn't attempt any implied command to make the object |
| | 174 | * smellable, but merely enforces the condition before allowing the |
| | 175 | * command. |
| | 176 | * |
| | 177 | * It is possible for an object to be in scope without being smellable, |
| | 178 | * since an object can be inside a container that is transparent to |
| | 179 | * light but blocks all odors. |
| | 180 | * |
| | 181 | * We enforce this condition with a verification test. |
| | 182 | */ |
| | 183 | objSmellable: PreCondition |
| | 184 | verifyPreCondition(obj) |
| | 185 | { |
| | 186 | /* if the object isn't within sense range, disallow the command */ |
| | 187 | if (obj != nil && !gActor.canSmell(obj)) |
| | 188 | inaccessible(&cannotSmellMsg, obj); |
| | 189 | } |
| | 190 | ; |
| | 191 | |
| | 192 | /* ------------------------------------------------------------------------ */ |
| | 193 | /* |
| | 194 | * Pre-condition: actor must be standing. This is useful for travel |
| | 195 | * commands to ensure that the actor is free of any entanglements from |
| | 196 | * nested rooms prior to travel. |
| | 197 | */ |
| | 198 | actorStanding: PreCondition |
| | 199 | checkPreCondition(obj, allowImplicit) |
| | 200 | { |
| | 201 | /* check to see if the actor is standing - if so, we're done */ |
| | 202 | if (gActor.posture == standing) |
| | 203 | return nil; |
| | 204 | |
| | 205 | /* the actor isn't standing - try a "stand up" command */ |
| | 206 | if (allowImplicit && tryImplicitAction(Stand)) |
| | 207 | { |
| | 208 | /* |
| | 209 | * make sure that leaves the actor standing - if not, |
| | 210 | * exit silently, since the reason for failure will have |
| | 211 | * been reported by the "stand up" action |
| | 212 | */ |
| | 213 | if (gActor.posture != standing) |
| | 214 | exit; |
| | 215 | |
| | 216 | /* indicate that we executed an implicit command */ |
| | 217 | return true; |
| | 218 | } |
| | 219 | |
| | 220 | /* we can't stand up implicitly - report the problem and exit */ |
| | 221 | reportFailure(&mustBeStandingMsg); |
| | 222 | exit; |
| | 223 | } |
| | 224 | ; |
| | 225 | |
| | 226 | /* |
| | 227 | * Pre-condition: actor must be "travel ready." The exact meaning of |
| | 228 | * "travel ready" is provided by the actor's immediately container. The |
| | 229 | * 'obj' argument is always the travel connector to be traversed. |
| | 230 | */ |
| | 231 | actorTravelReady: PreCondition |
| | 232 | checkPreCondition(obj, allowImplicit) |
| | 233 | { |
| | 234 | local loc = gActor.location; |
| | 235 | |
| | 236 | /* check to see if the actor is standing - if so, we're done */ |
| | 237 | if (loc.isActorTravelReady(obj)) |
| | 238 | return nil; |
| | 239 | |
| | 240 | /* the actor isn't standing - try a "stand up" command */ |
| | 241 | if (allowImplicit && gActor.location.tryMakingTravelReady(obj)) |
| | 242 | { |
| | 243 | /* |
| | 244 | * make sure that the actor really is travel-ready now - if |
| | 245 | * not, exit silently, since the reason for failure will have |
| | 246 | * been reported by the implicit action |
| | 247 | */ |
| | 248 | if (!loc.isActorTravelReady(obj)) |
| | 249 | exit; |
| | 250 | |
| | 251 | /* indicate that we executed an implicit command */ |
| | 252 | return true; |
| | 253 | } |
| | 254 | |
| | 255 | /* we can't make the actor travel-ready - report failure and exit */ |
| | 256 | reportFailure(loc.notTravelReadyMsg); |
| | 257 | exit; |
| | 258 | } |
| | 259 | ; |
| | 260 | |
| | 261 | /* ------------------------------------------------------------------------ */ |
| | 262 | /* |
| | 263 | * Pre-condition: the traveler is directly in the given room. This will |
| | 264 | * attempt to remove the traveler from any nested rooms within the given |
| | 265 | * room, but cannot perform travel between rooms not related by |
| | 266 | * containment. |
| | 267 | * |
| | 268 | * Note that the traveler is not necessarily the actor, because the actor |
| | 269 | * could be in a vehicle. |
| | 270 | * |
| | 271 | * This is a class, because it has to be instantiated with more |
| | 272 | * parameters than just a single 'obj' passed by default when evaluating |
| | 273 | * preconditions. In particular, we need to know the actor performing |
| | 274 | * the travel, the connector being traversed, and the room we need to be |
| | 275 | * directly in. |
| | 276 | */ |
| | 277 | class TravelerDirectlyInRoom: PreCondition |
| | 278 | construct(actor, conn, loc) |
| | 279 | { |
| | 280 | /* remember the actor, connector, and room */ |
| | 281 | actor_ = actor; |
| | 282 | conn_ = conn; |
| | 283 | loc_ = loc; |
| | 284 | } |
| | 285 | |
| | 286 | checkPreCondition(obj, allowImplicit) |
| | 287 | { |
| | 288 | /* ask the traveler to do the work */ |
| | 289 | return actor_.getTraveler(conn_) |
| | 290 | .checkDirectlyInRoom(loc_, allowImplicit); |
| | 291 | } |
| | 292 | |
| | 293 | /* the actor doing the travel */ |
| | 294 | actor_ = nil |
| | 295 | |
| | 296 | /* the connector being traversed */ |
| | 297 | conn_ = nil |
| | 298 | |
| | 299 | /* the room we need to be directly in */ |
| | 300 | loc_ = nil |
| | 301 | ; |
| | 302 | |
| | 303 | /* |
| | 304 | * Pre-condition: the actor is directly in the given room. This differs |
| | 305 | * from TravelerDirectlyInRoom in that this operates directly on the |
| | 306 | * actor, regardless of whether the actor is in a vehicle. |
| | 307 | */ |
| | 308 | actorDirectlyInRoom: PreCondition |
| | 309 | checkPreCondition(obj, allowImplicit) |
| | 310 | { |
| | 311 | /* ask the actor to do the work */ |
| | 312 | return gActor.checkDirectlyInRoom(obj, allowImplicit); |
| | 313 | } |
| | 314 | ; |
| | 315 | |
| | 316 | /* ------------------------------------------------------------------------ */ |
| | 317 | /* |
| | 318 | * Pre-condition: actor is ready to enter a nested location. This is |
| | 319 | * useful for commands that cause travel within a location, such as "sit |
| | 320 | * on chair": this ensures that the actor is either already in the given |
| | 321 | * nested location, or is in the main location; and that the actor is |
| | 322 | * standing. We simply call the actor to do the work. |
| | 323 | */ |
| | 324 | actorReadyToEnterNestedRoom: PreCondition |
| | 325 | checkPreCondition(obj, allowImplicit) |
| | 326 | { |
| | 327 | /* ask the actor to make the determination */ |
| | 328 | return gActor.checkReadyToEnterNestedRoom(obj, allowImplicit); |
| | 329 | } |
| | 330 | ; |
| | 331 | |
| | 332 | /* ------------------------------------------------------------------------ */ |
| | 333 | /* |
| | 334 | * Pre-condition: the target actor must be able to talk to the object. |
| | 335 | * This is useful for actions that require communications, such as ASK |
| | 336 | * ABOUT, TELL ABOUT, and TALK TO. |
| | 337 | */ |
| | 338 | canTalkToObj: PreCondition |
| | 339 | checkPreCondition(obj, allowImplicit) |
| | 340 | { |
| | 341 | /* |
| | 342 | * if the current actor can't talk to the given object, disallow |
| | 343 | * the command |
| | 344 | */ |
| | 345 | if (obj != nil && !gActor.canTalkTo(obj)) |
| | 346 | { |
| | 347 | reportFailure(&objCannotHearActorMsg, obj); |
| | 348 | exit; |
| | 349 | } |
| | 350 | |
| | 351 | /* we don't perform any implicit commands */ |
| | 352 | return nil; |
| | 353 | } |
| | 354 | ; |
| | 355 | |
| | 356 | /* ------------------------------------------------------------------------ */ |
| | 357 | /* |
| | 358 | * Pre-condition: object must be held. This condition requires that an |
| | 359 | * object of a command must be held by the actor. If it is not, we will |
| | 360 | * attempt a recursive "take" command on the object. |
| | 361 | * |
| | 362 | * This condition is useful for commands where the object is to be |
| | 363 | * manipulated in some way, or used to manipulate some other object. |
| | 364 | * For example, the key in "unlock door with key" would normally have to |
| | 365 | * be held. |
| | 366 | */ |
| | 367 | objHeld: PreCondition |
| | 368 | checkPreCondition(obj, allowImplicit) |
| | 369 | { |
| | 370 | /* if the object is already held, there's nothing we need to do */ |
| | 371 | if (obj == nil || obj.meetsObjHeld(gActor)) |
| | 372 | return nil; |
| | 373 | |
| | 374 | /* the object isn't being held - try an implicit 'take' command */ |
| | 375 | if (allowImplicit && obj.tryHolding()) |
| | 376 | { |
| | 377 | /* |
| | 378 | * we successfully executed the command; check to make sure |
| | 379 | * it worked, and if not, abort the command without further |
| | 380 | * comment (if the command failed, presumably the command |
| | 381 | * showed an explanation as to why) |
| | 382 | */ |
| | 383 | if (!obj.meetsObjHeld(gActor)) |
| | 384 | exit; |
| | 385 | |
| | 386 | /* tell the caller we executed an implicit command */ |
| | 387 | return true; |
| | 388 | } |
| | 389 | |
| | 390 | /* it's not held and we can't take it - fail */ |
| | 391 | reportFailure(&mustBeHoldingMsg, obj); |
| | 392 | |
| | 393 | /* make it the pronoun */ |
| | 394 | gActor.setPronounObj(obj); |
| | 395 | |
| | 396 | /* abort the command */ |
| | 397 | exit; |
| | 398 | } |
| | 399 | |
| | 400 | /* lower the likelihood rating for anything not being held */ |
| | 401 | verifyPreCondition(obj) |
| | 402 | { |
| | 403 | /* if the object isn't being held, reduce its likelihood rating */ |
| | 404 | if (obj != nil && !obj.meetsObjHeld(gActor)) |
| | 405 | logicalRankOrd(80, 'implied take', 150); |
| | 406 | } |
| | 407 | ; |
| | 408 | |
| | 409 | /* ------------------------------------------------------------------------ */ |
| | 410 | /* |
| | 411 | * Pre-condition: a given source object must be able to touch the |
| | 412 | * object. This requires that the source object (given by our property |
| | 413 | * 'sourceObj') has a clear 'touch' path to the target object. |
| | 414 | * |
| | 415 | * This is a base class for arbitrary object-to-object touch conditions. |
| | 416 | * In most cases, you'll want to use the more specific touchObj, which |
| | 417 | * tests that the current actor can touch the current object. |
| | 418 | */ |
| | 419 | class TouchObjCondition: PreCondition |
| | 420 | /* construct with a given source object */ |
| | 421 | construct(src) { sourceObj = src; } |
| | 422 | |
| | 423 | /* |
| | 424 | * the source object - this is the object that is attempting to |
| | 425 | * touch the target object |
| | 426 | */ |
| | 427 | sourceObj = nil |
| | 428 | |
| | 429 | /* check the condition */ |
| | 430 | checkPreCondition(obj, allowImplicit) |
| | 431 | { |
| | 432 | local pastObs; |
| | 433 | |
| | 434 | /* |
| | 435 | * If we can touch the object, we can proceed with no implicit |
| | 436 | * actions. |
| | 437 | */ |
| | 438 | if (sourceObj.canTouch(obj)) |
| | 439 | return nil; |
| | 440 | |
| | 441 | /* we haven't tried removing any obstructors yet */ |
| | 442 | pastObs = new Vector(8); |
| | 443 | |
| | 444 | /* |
| | 445 | * Repeatedly look for and attempt to remove obstructions. |
| | 446 | * There could be multiple things in the way, so try to remove |
| | 447 | * each one we find until either we fail to remove an |
| | 448 | * obstruction or we run out of obstructions. |
| | 449 | */ |
| | 450 | for (;;) |
| | 451 | { |
| | 452 | local stat; |
| | 453 | local path; |
| | 454 | local result; |
| | 455 | local obs; |
| | 456 | |
| | 457 | /* get the path for reaching out and touching the object */ |
| | 458 | path = sourceObj.getTouchPathTo(obj); |
| | 459 | |
| | 460 | /* if we have a path, look for an obstructor */ |
| | 461 | if (path != nil) |
| | 462 | { |
| | 463 | /* traverse the path to find what blocks our touch */ |
| | 464 | stat = sourceObj.traversePath(path, new function(ele, op) |
| | 465 | { |
| | 466 | /* |
| | 467 | * If we can continue the reach via this path element, |
| | 468 | * simply keep going. Otherwise, stop the reach here. |
| | 469 | */ |
| | 470 | result = ele.checkTouchViaPath(sourceObj, obj, op); |
| | 471 | if (result.isSuccess) |
| | 472 | { |
| | 473 | /* no objection here - keep going */ |
| | 474 | return true; |
| | 475 | } |
| | 476 | else |
| | 477 | { |
| | 478 | /* stop here, noting the obstruction */ |
| | 479 | obs = ele; |
| | 480 | return nil; |
| | 481 | } |
| | 482 | }); |
| | 483 | |
| | 484 | /* |
| | 485 | * if we now have a clear path, we're done - simply return |
| | 486 | * true to indicate that we ran one or more implicit |
| | 487 | * commands |
| | 488 | */ |
| | 489 | if (stat) |
| | 490 | return true; |
| | 491 | } |
| | 492 | else |
| | 493 | { |
| | 494 | /* |
| | 495 | * we have no path, so the object must be in an |
| | 496 | * unconnected location; we don't know the obstructor in |
| | 497 | * this case |
| | 498 | */ |
| | 499 | obs = nil; |
| | 500 | } |
| | 501 | |
| | 502 | /* |
| | 503 | * 'result' is a CheckStatus object explaining why we can't |
| | 504 | * reach past 'obs', which is the first object that |
| | 505 | * obstructs our reach. |
| | 506 | * |
| | 507 | * If the obstructor is not visible or we couldn't find one, |
| | 508 | * we can't do anything to try to remove it; simply report |
| | 509 | * that we can't reach the target object and give up. |
| | 510 | */ |
| | 511 | if (obs == nil || !gActor.canSee(obs)) |
| | 512 | { |
| | 513 | reportFailure(&cannotReachObjectMsg, obj); |
| | 514 | exit; |
| | 515 | } |
| | 516 | |
| | 517 | /* |
| | 518 | * Ask the obstructor to get out of the way if possible. |
| | 519 | * |
| | 520 | * If we've already tried to remove this same obstructor on |
| | 521 | * a past iteration, don't try again, as there's no reason |
| | 522 | * to think an implicit command will work any better this |
| | 523 | * time. |
| | 524 | */ |
| | 525 | if (pastObs.indexOf(obs) != nil |
| | 526 | || !allowImplicit |
| | 527 | || !obs.tryImplicitRemoveObstructor(touch, obj)) |
| | 528 | { |
| | 529 | /* |
| | 530 | * We can't remove the obstruction - either we've tried |
| | 531 | * an implicit command on this same obstructor and |
| | 532 | * failed, or we can't try an implicit command at all. |
| | 533 | * In any case, use the explanation of the problem from |
| | 534 | * the CheckStatus result object. |
| | 535 | */ |
| | 536 | reportFailure(result.msgProp, result.msgParams...); |
| | 537 | exit; |
| | 538 | } |
| | 539 | |
| | 540 | /* |
| | 541 | * if the implied command failed, simply give up now - |
| | 542 | * there's no need to go on, since the implied command will |
| | 543 | * have already explained why it failed |
| | 544 | */ |
| | 545 | if (gTranscript.currentActionHasReport({x: x.isFailure})) |
| | 546 | exit; |
| | 547 | |
| | 548 | /* |
| | 549 | * We've tried an implied command to remove this obstructor, |
| | 550 | * but that isn't guaranteed to make the target touchable, |
| | 551 | * as there could be further obstrutions, or the implied |
| | 552 | * command could have failed to actually remove the |
| | 553 | * obstruction. Keep iterating. To avoid looping forever |
| | 554 | * in the event the implicit command we just tried isn't |
| | 555 | * good enough to remove this obstruction, make a note of |
| | 556 | * the obstruction we just tried to remove; if we find it |
| | 557 | * again on a subsequent iteration, we'll know that we've |
| | 558 | * tried before to remove it and failed, and thus we'll know |
| | 559 | * to give up without making the same doomed attempt again. |
| | 560 | */ |
| | 561 | pastObs.append(obs); |
| | 562 | } |
| | 563 | } |
| | 564 | |
| | 565 | verifyPreCondition(obj) |
| | 566 | { |
| | 567 | /* |
| | 568 | * If there's no source object, do nothing at this point. We can |
| | 569 | * have a nil source object when we're resolving nouns for a |
| | 570 | * two-object action, and we have a cross-object condition (for |
| | 571 | * example, we require that the indirect object can touch the |
| | 572 | * direct object). In these cases, when we're resolving the |
| | 573 | * first-resolved noun phrase, the second-resolved noun phrase |
| | 574 | * won't be known yet. The only purpose of verification at times |
| | 575 | * like these is to improve our guess about an ambiguous match, |
| | 576 | * but we have nothing to add at such times, so we can simply |
| | 577 | * return without doing anything. |
| | 578 | */ |
| | 579 | if (sourceObj == nil) |
| | 580 | return; |
| | 581 | |
| | 582 | /* if we can't touch the object, make it less likely */ |
| | 583 | if (!sourceObj.canTouch(obj)) |
| | 584 | { |
| | 585 | /* |
| | 586 | * If we can't see the object, we must be able to sense it |
| | 587 | * by some means other than sight, so it must have a |
| | 588 | * sufficiently distinctive sound or odor to put it in |
| | 589 | * scope. Explain this: "you can hear it but you can't see |
| | 590 | * it", or the like. |
| | 591 | */ |
| | 592 | if (gActor.canSee(obj)) |
| | 593 | { |
| | 594 | local info; |
| | 595 | |
| | 596 | /* |
| | 597 | * It's visible but cannot be reached from here, so it |
| | 598 | * must be too far away, inside a closed but transparent |
| | 599 | * container, or something like that. |
| | 600 | * |
| | 601 | * If it's at a distance, rule it illogical, since |
| | 602 | * there's not usually anything automatic we can do to |
| | 603 | * remove the distance obstruction. |
| | 604 | * |
| | 605 | * If it's not distant, don't rule it illogical, but do |
| | 606 | * reduce the likelihood ranking, so that we'll prefer a |
| | 607 | * different object that can readily be touched. Since |
| | 608 | * we can see where the object is, we might know how to |
| | 609 | * remove the obstruction to reachability. |
| | 610 | */ |
| | 611 | info = gActor.bestVisualInfo(obj); |
| | 612 | if (info != nil && info.trans == distant) |
| | 613 | { |
| | 614 | /* it's distant - assume we can't fix this */ |
| | 615 | inaccessible(&tooDistantMsg, obj); |
| | 616 | } |
| | 617 | else |
| | 618 | { |
| | 619 | /* |
| | 620 | * it's not distant; rank it logical (since we might |
| | 621 | * be able to clear the obstruction with an implied |
| | 622 | * action), but at reduced likelihood (in case |
| | 623 | * there's something that doesn't need any prior |
| | 624 | * implied action to reach) |
| | 625 | */ |
| | 626 | logicalRankOrd(80, 'unreachable but visible', 150); |
| | 627 | } |
| | 628 | } |
| | 629 | else |
| | 630 | { |
| | 631 | /* |
| | 632 | * if it has a sound presence, then "you can hear it but |
| | 633 | * you can't see it"; if it has a smell presence, then |
| | 634 | * "you can smell it but you can't see it"; otherwise, |
| | 635 | * you simply can't see it |
| | 636 | */ |
| | 637 | if (obj.soundPresence && gActor.canHear(obj)) |
| | 638 | { |
| | 639 | /* it can be heard but not seen */ |
| | 640 | inaccessible(&heardButNotSeenMsg, obj); |
| | 641 | } |
| | 642 | else if (obj.smellPresence && gActor.canSmell(obj)) |
| | 643 | { |
| | 644 | /* it can be smelled but not seen */ |
| | 645 | inaccessible(&smelledButNotSeenMsg, obj); |
| | 646 | } |
| | 647 | else if (!gActor.isLocationLit()) |
| | 648 | { |
| | 649 | /* it's too dark to see the object */ |
| | 650 | inaccessible(&tooDarkMsg); |
| | 651 | } |
| | 652 | else |
| | 653 | { |
| | 654 | /* it simply cannot be seen */ |
| | 655 | inaccessible(&mustBeVisibleMsg, obj); |
| | 656 | } |
| | 657 | } |
| | 658 | } |
| | 659 | } |
| | 660 | |
| | 661 | /* |
| | 662 | * This condition tends to be fragile, in the sense that other |
| | 663 | * preconditions for the same action have the potential to undo any |
| | 664 | * implicit action that we perform to make an object touchable. This |
| | 665 | * is most likely to happen when we implicitly move the actor (moving |
| | 666 | * in or out of a nested room, for example) to put the actor within |
| | 667 | * reach of the target object. To reduce the likelihood that this |
| | 668 | * fragility will be visible to a player, try to execute this |
| | 669 | * condition after other conditions. Most other preconditions tend |
| | 670 | * to be "stickier" - less likely to be undone by subsequent |
| | 671 | * preconditions. |
| | 672 | */ |
| | 673 | preCondOrder = 200 |
| | 674 | ; |
| | 675 | |
| | 676 | /* ------------------------------------------------------------------------ */ |
| | 677 | /* |
| | 678 | * Pre-condition: actor must be able to touch the object. This doesn't |
| | 679 | * require that the actor is actually holding the object, but the actor |
| | 680 | * must be able to physically touch the object. This ensures that the |
| | 681 | * actor and object are not, for example, separated by a transparent |
| | 682 | * barrier. |
| | 683 | * |
| | 684 | * If there is a transparent barrier, we will attempt to remove the |
| | 685 | * barrier by calling the barrier object's tryImplicitRemoveObstructor |
| | 686 | * method. Objects that can be opened in an obvious fashion will |
| | 687 | * perform an implicit recursive "open" command, and other types of |
| | 688 | * objects can provide customized behavior as appropriate. |
| | 689 | */ |
| | 690 | touchObj: TouchObjCondition |
| | 691 | /* we want to test reaching from the current actor to the target object */ |
| | 692 | sourceObj = (gActor) |
| | 693 | ; |
| | 694 | |
| | 695 | /* |
| | 696 | * Pre-condition: the indirect object must be able to touch the target |
| | 697 | * object. This can be used for actions where the direct object is going |
| | 698 | * to be manipulated by an "agent" of the action (i.e., the indirect |
| | 699 | * object), rather than directly by the actor: MOVE X WITH Y, for |
| | 700 | * example. |
| | 701 | * |
| | 702 | * Note that the target object of this condition should be the direct |
| | 703 | * object in most cases, so this condition should usually be used like |
| | 704 | * this: |
| | 705 | * |
| | 706 | * dobjFor(MoveWith) { preCond = [iobjTouchObj] } |
| | 707 | * |
| | 708 | * In other words, this is a precondition that we apply in most cases to |
| | 709 | * the *direct* object. |
| | 710 | */ |
| | 711 | iobjTouchObj: TouchObjCondition |
| | 712 | /* the indirect object has to be able to touch the target object */ |
| | 713 | sourceObj = (gIobj) |
| | 714 | ; |
| | 715 | |
| | 716 | /* |
| | 717 | * Pre-condition: the direct object can touch the target object. This |
| | 718 | * is useful for situations where the direct object is being manipulated |
| | 719 | * directly and the indirect object is more of a passive participant in |
| | 720 | * the action, such as PLUG CORD INTO OUTLET. |
| | 721 | */ |
| | 722 | dobjTouchObj: TouchObjCondition |
| | 723 | /* the direct object has to be able to touch the target object */ |
| | 724 | sourceObj = (gDobj) |
| | 725 | ; |
| | 726 | |
| | 727 | /* ------------------------------------------------------------------------ */ |
| | 728 | /* |
| | 729 | * A precondition ensuring that the target object is in the same |
| | 730 | * immediate location as a given object. |
| | 731 | */ |
| | 732 | class SameLocationCondition: PreCondition |
| | 733 | /* |
| | 734 | * construct dynamically, setting the other object whose location we |
| | 735 | * must match |
| | 736 | */ |
| | 737 | construct(obj) { sourceObj = obj; } |
| | 738 | |
| | 739 | /* the object whose location we must match */ |
| | 740 | sourceObj = nil |
| | 741 | |
| | 742 | /* check the condition */ |
| | 743 | checkPreCondition(obj, allowImplicit) |
| | 744 | { |
| | 745 | local moveObj; |
| | 746 | local targetLoc; |
| | 747 | |
| | 748 | /* if we're in the same container, we're fine */ |
| | 749 | if (obj.location == sourceObj.location) |
| | 750 | return nil; |
| | 751 | |
| | 752 | /* |
| | 753 | * Pick an object to move. By default, pick the target object; |
| | 754 | * but if the target object is non-portable, then trying to move |
| | 755 | * it will fail, so pick the source object. |
| | 756 | */ |
| | 757 | if (obj.ofKind(NonPortable)) |
| | 758 | { |
| | 759 | /* 'obj' is unportable, so try moving the source object */ |
| | 760 | moveObj = sourceObj; |
| | 761 | targetLoc = obj.location; |
| | 762 | } |
| | 763 | else |
| | 764 | { |
| | 765 | /* 'obj' is portable, so try moving it by default */ |
| | 766 | moveObj = obj; |
| | 767 | targetLoc = sourceObj.location; |
| | 768 | } |
| | 769 | |
| | 770 | /* try moving the object, and return the result */ |
| | 771 | if (allowImplicit && targetLoc.tryMovingObjInto(moveObj)) |
| | 772 | { |
| | 773 | /* if it didn't work, abort the action */ |
| | 774 | if (obj.location != sourceObj.location) |
| | 775 | exit; |
| | 776 | |
| | 777 | /* tell the caller we executed an implied action */ |
| | 778 | return true; |
| | 779 | } |
| | 780 | |
| | 781 | /* we can't move it - report the failure and abort the action */ |
| | 782 | targetLoc.mustMoveObjInto(moveObj); |
| | 783 | exit; |
| | 784 | } |
| | 785 | ; |
| | 786 | |
| | 787 | /* |
| | 788 | * require that the target object be in the same immediate location as |
| | 789 | * the direct object |
| | 790 | */ |
| | 791 | sameLocationAsDobj: SameLocationCondition |
| | 792 | sourceObj = (gDobj) |
| | 793 | ; |
| | 794 | |
| | 795 | /* |
| | 796 | * require that the target object be in the same immediate location as |
| | 797 | * the indirect object |
| | 798 | */ |
| | 799 | sameLocationAsIobj: SameLocationCondition |
| | 800 | sourceObj = (gIobj) |
| | 801 | ; |
| | 802 | |
| | 803 | /* ------------------------------------------------------------------------ */ |
| | 804 | /* |
| | 805 | * Pre-condition: actor must have room to hold the object directly (such |
| | 806 | * as in the actor's hands). We'll let the actor do the work. |
| | 807 | */ |
| | 808 | roomToHoldObj: PreCondition |
| | 809 | checkPreCondition(obj, allowImplicit) |
| | 810 | { |
| | 811 | /* let the actor check the precondition */ |
| | 812 | return gActor.tryMakingRoomToHold(obj, allowImplicit); |
| | 813 | } |
| | 814 | ; |
| | 815 | |
| | 816 | /* ------------------------------------------------------------------------ */ |
| | 817 | /* |
| | 818 | * Pre-condition: the actor must not be wearing the object. If the |
| | 819 | * actor is currently wearing the object, we'll try asking the actor to |
| | 820 | * doff the object. |
| | 821 | * |
| | 822 | * Note that this pre-condition never needs to be combined with objHeld, |
| | 823 | * because an object being worn is not considered to be held, and |
| | 824 | * Wearable implicitly doffs an article when it must be held. |
| | 825 | */ |
| | 826 | objNotWorn: PreCondition |
| | 827 | checkPreCondition(obj, allowImplicit) |
| | 828 | { |
| | 829 | /* if the object isn't being worn, we have nothing to do */ |
| | 830 | if (obj == nil || !obj.isWornBy(gActor)) |
| | 831 | return nil; |
| | 832 | |
| | 833 | /* try an implicit 'doff' command */ |
| | 834 | if (allowImplicit && tryImplicitAction(Doff, obj)) |
| | 835 | { |
| | 836 | /* |
| | 837 | * we executed the command - make sure it worked, and abort |
| | 838 | * if it didn't |
| | 839 | */ |
| | 840 | if (obj.isWornBy(gActor)) |
| | 841 | exit; |
| | 842 | |
| | 843 | /* tell the caller we executed an implicit command */ |
| | 844 | return true; |
| | 845 | } |
| | 846 | |
| | 847 | /* report the problem and terminate the command */ |
| | 848 | reportFailure(&cannotBeWearingMsg, obj); |
| | 849 | |
| | 850 | /* make it the pronoun */ |
| | 851 | gActor.setPronounObj(obj); |
| | 852 | |
| | 853 | /* abort the command */ |
| | 854 | exit; |
| | 855 | } |
| | 856 | |
| | 857 | /* lower the likelihood rating for anything being worn */ |
| | 858 | verifyPreCondition(obj) |
| | 859 | { |
| | 860 | /* if the object is being worn, reduce its likelihood rating */ |
| | 861 | if (obj != nil && obj.isWornBy(gActor)) |
| | 862 | logicalRankOrd(80, 'implied doff', 150); |
| | 863 | } |
| | 864 | ; |
| | 865 | |
| | 866 | /* ------------------------------------------------------------------------ */ |
| | 867 | /* |
| | 868 | * Pre-condition: the object is open. |
| | 869 | */ |
| | 870 | class ObjOpenCondition: PreCondition |
| | 871 | checkPreCondition(obj, allowImplicit) |
| | 872 | { |
| | 873 | /* if the object is already open, we're already done */ |
| | 874 | if (obj == nil || obj.isOpen) |
| | 875 | return nil; |
| | 876 | |
| | 877 | /* try an implicit 'open' command on the object */ |
| | 878 | if (allowImplicit && tryImplicitAction(Open, obj)) |
| | 879 | { |
| | 880 | /* |
| | 881 | * we executed the command - make sure it worked, and abort |
| | 882 | * if it didn't |
| | 883 | */ |
| | 884 | if (!obj.isOpen) |
| | 885 | exit; |
| | 886 | |
| | 887 | /* tell the caller we executed an implied command */ |
| | 888 | return true; |
| | 889 | } |
| | 890 | |
| | 891 | /* can't open it implicitly - report the failure */ |
| | 892 | conditionFailed(obj); |
| | 893 | exit; |
| | 894 | } |
| | 895 | |
| | 896 | /* |
| | 897 | * The condition failed - report the failure and give up. We |
| | 898 | * separate this to allow subclasses to report failure differently |
| | 899 | * for specialized types of opening. |
| | 900 | */ |
| | 901 | conditionFailed(obj) |
| | 902 | { |
| | 903 | /* can't open it implicitly - report failure and give up */ |
| | 904 | reportFailure(&mustBeOpenMsg, obj); |
| | 905 | |
| | 906 | /* make it the pronoun */ |
| | 907 | gActor.setPronounObj(obj); |
| | 908 | } |
| | 909 | |
| | 910 | /* reduce the likelihood rating for anything that isn't already open */ |
| | 911 | verifyPreCondition(obj) |
| | 912 | { |
| | 913 | /* if the object is closed, reduce its likelihood rating */ |
| | 914 | if (obj != nil && !obj.isOpen) |
| | 915 | logicalRankOrd(80, 'implied open', 150); |
| | 916 | } |
| | 917 | ; |
| | 918 | |
| | 919 | /* |
| | 920 | * The basic object-open condition |
| | 921 | */ |
| | 922 | objOpen: ObjOpenCondition; |
| | 923 | |
| | 924 | /* |
| | 925 | * Pre-condition: a door must be open. This differs from the regular |
| | 926 | * objOpen condition only in that we use a customized version of the |
| | 927 | * failure report. |
| | 928 | */ |
| | 929 | doorOpen: ObjOpenCondition |
| | 930 | conditionFailed(obj) |
| | 931 | { |
| | 932 | /* |
| | 933 | * We can generate implicit open-door commands as a result of |
| | 934 | * travel, which means that the actor issuing the command might |
| | 935 | * never have explicitly referred to the door. (This is not the |
| | 936 | * case for most preconditions, which refer to objects directly |
| | 937 | * used in the command and thus within the actor's awareness, at |
| | 938 | * least initially.) So, if the door isn't visible to the |
| | 939 | * actor, don't tell the actor they have to open the door; |
| | 940 | * instead, just show the standard no-travel message for the |
| | 941 | * door. |
| | 942 | */ |
| | 943 | if (gActor.canSee(obj)) |
| | 944 | { |
| | 945 | /* they can see the door, so tell them they need to open it */ |
| | 946 | reportFailure(&mustOpenDoorMsg, obj); |
| | 947 | |
| | 948 | /* set this as the pronoun antecedent */ |
| | 949 | gActor.setPronounObj(obj); |
| | 950 | } |
| | 951 | else |
| | 952 | { |
| | 953 | /* |
| | 954 | * they can't see the door - call the door's routine to |
| | 955 | * indicate that travel is not possible |
| | 956 | */ |
| | 957 | obj.cannotTravel(); |
| | 958 | } |
| | 959 | } |
| | 960 | ; |
| | 961 | |
| | 962 | /* ------------------------------------------------------------------------ */ |
| | 963 | /* |
| | 964 | * Pre-condition: the object is closed. |
| | 965 | */ |
| | 966 | objClosed: PreCondition |
| | 967 | checkPreCondition(obj, allowImplicit) |
| | 968 | { |
| | 969 | /* if the object is already closed, we're already done */ |
| | 970 | if (obj == nil || !obj.isOpen) |
| | 971 | return nil; |
| | 972 | |
| | 973 | /* try an implicit 'close' command on the object */ |
| | 974 | if (allowImplicit && tryImplicitAction(Close, obj)) |
| | 975 | { |
| | 976 | /* |
| | 977 | * we executed the command - make sure it worked, and abort |
| | 978 | * if it didn't |
| | 979 | */ |
| | 980 | if (obj.isOpen) |
| | 981 | exit; |
| | 982 | |
| | 983 | /* tell the caller we executed an implied command */ |
| | 984 | return true; |
| | 985 | } |
| | 986 | |
| | 987 | /* can't close it implicitly - report failure and give up */ |
| | 988 | reportFailure(&mustBeClosedMsg, obj); |
| | 989 | |
| | 990 | /* make it the pronoun */ |
| | 991 | gActor.setPronounObj(obj); |
| | 992 | |
| | 993 | /* abort the command */ |
| | 994 | exit; |
| | 995 | } |
| | 996 | |
| | 997 | /* reduce the likelihood rating for anything that isn't already closed */ |
| | 998 | verifyPreCondition(obj) |
| | 999 | { |
| | 1000 | /* if the object is closed, reduce its likelihood rating */ |
| | 1001 | if (obj != nil && obj.isOpen) |
| | 1002 | logicalRankOrd(80, 'implied close', 150); |
| | 1003 | } |
| | 1004 | ; |
| | 1005 | |
| | 1006 | /* ------------------------------------------------------------------------ */ |
| | 1007 | /* |
| | 1008 | * Pre-condition: the object is unlocked. |
| | 1009 | */ |
| | 1010 | objUnlocked: PreCondition |
| | 1011 | checkPreCondition(obj, allowImplicit) |
| | 1012 | { |
| | 1013 | /* if the object is already unlocked, we're already done */ |
| | 1014 | if (obj == nil || !obj.isLocked) |
| | 1015 | return nil; |
| | 1016 | |
| | 1017 | /* try an implicit 'unlock' command on the object */ |
| | 1018 | if (allowImplicit && tryImplicitAction(Unlock, obj)) |
| | 1019 | { |
| | 1020 | /* |
| | 1021 | * we executed the command - make sure it worked, and abort |
| | 1022 | * if it didn't |
| | 1023 | */ |
| | 1024 | if (obj.isLocked) |
| | 1025 | exit; |
| | 1026 | |
| | 1027 | /* tell the caller we executed an implied command */ |
| | 1028 | return true; |
| | 1029 | } |
| | 1030 | |
| | 1031 | /* can't unlock it implicitly - report failure and give up */ |
| | 1032 | reportFailure(&mustBeUnlockedMsg, obj); |
| | 1033 | |
| | 1034 | /* make it the pronoun */ |
| | 1035 | gActor.setPronounObj(obj); |
| | 1036 | |
| | 1037 | /* abort the command */ |
| | 1038 | exit; |
| | 1039 | } |
| | 1040 | |
| | 1041 | /* reduce the likelihood rating for anything that's locked */ |
| | 1042 | verifyPreCondition(obj) |
| | 1043 | { |
| | 1044 | /* if the object is locked, reduce its likelihood rating */ |
| | 1045 | if (obj != nil && obj.isLocked) |
| | 1046 | logicalRankOrd(80, 'implied unlock', 150); |
| | 1047 | } |
| | 1048 | ; |
| | 1049 | |
| | 1050 | /* ------------------------------------------------------------------------ */ |
| | 1051 | /* |
| | 1052 | * Pre-condition: destination for "drop" is an outermost room. If the |
| | 1053 | * drop destination is a nested room, we'll try returning the actor to |
| | 1054 | * the outermost room via an implicit command. |
| | 1055 | */ |
| | 1056 | dropDestinationIsOuterRoom: PreCondition |
| | 1057 | checkPreCondition(obj, allowImplicit) |
| | 1058 | { |
| | 1059 | local dest; |
| | 1060 | |
| | 1061 | /* |
| | 1062 | * if the actor's location's drop location is the outermost |
| | 1063 | * room, we don't need to do anything special |
| | 1064 | */ |
| | 1065 | dest = gActor.getDropDestination(obj, nil); |
| | 1066 | if (dest.getOutermostRoom() == dest) |
| | 1067 | return nil; |
| | 1068 | |
| | 1069 | /* |
| | 1070 | * the default drop destination is not an outermost room; try an |
| | 1071 | * implicit command to return the actor to an outermost room |
| | 1072 | */ |
| | 1073 | return actorDirectlyInRoom.checkPreCondition( |
| | 1074 | dest.getOutermostRoom(), allowImplicit); |
| | 1075 | } |
| | 1076 | ; |
| | 1077 | |
| | 1078 | /* ------------------------------------------------------------------------ */ |
| | 1079 | /* |
| | 1080 | * Pre-condition: object is burning. This can be used for matches, |
| | 1081 | * candles, and the like. If the object's isLit is nil, we'll attempt a |
| | 1082 | * "burn" command on the object. |
| | 1083 | */ |
| | 1084 | objBurning: PreCondition |
| | 1085 | checkPreCondition(obj, allowImplicit) |
| | 1086 | { |
| | 1087 | /* if it's already burning, there's nothing to do */ |
| | 1088 | if (obj == nil || obj.isLit) |
| | 1089 | return nil; |
| | 1090 | |
| | 1091 | /* try an implicit 'burn' command */ |
| | 1092 | if (allowImplicit && tryImplicitAction(Burn, obj)) |
| | 1093 | { |
| | 1094 | /* we executed a 'burn' - give up if it didn't work */ |
| | 1095 | if (!obj.isLit) |
| | 1096 | exit; |
| | 1097 | |
| | 1098 | /* tell the caller we executed an implied command */ |
| | 1099 | return true; |
| | 1100 | } |
| | 1101 | |
| | 1102 | /* we can't burn it implicitly - report failure and give up */ |
| | 1103 | reportFailure(&mustBeBurningMsg, obj); |
| | 1104 | |
| | 1105 | /* make it the pronoun */ |
| | 1106 | gActor.setPronounObj(obj); |
| | 1107 | |
| | 1108 | /* abort the command */ |
| | 1109 | exit; |
| | 1110 | } |
| | 1111 | |
| | 1112 | verifyPreCondition(obj) |
| | 1113 | { |
| | 1114 | /* if the object is not already burning, reduce its likelihood */ |
| | 1115 | if (obj != nil && !obj.isLit) |
| | 1116 | logicalRankOrd(80, 'implied burn', 150); |
| | 1117 | } |
| | 1118 | ; |
| | 1119 | |
| | 1120 | |
| | 1121 | /* ------------------------------------------------------------------------ */ |
| | 1122 | /* |
| | 1123 | * Pre-condition: the object is empty. This ensures that the object |
| | 1124 | * does not contain any other objects. |
| | 1125 | * |
| | 1126 | * Note that we unconditionally try to remove all objects. If a |
| | 1127 | * container needs to have some objects that can be removed and others |
| | 1128 | * that can't (such as components within the container), then the |
| | 1129 | * container will have to be implemented as a ComplexContainer - the |
| | 1130 | * non-removable components should be made contents of the enclosing |
| | 1131 | * ComplexContainer, and the secret inner container should be the one |
| | 1132 | * subject to this precondition. |
| | 1133 | */ |
| | 1134 | objEmpty: PreCondition |
| | 1135 | checkPreCondition(obj, allowImplicit) |
| | 1136 | { |
| | 1137 | local chi; |
| | 1138 | |
| | 1139 | /* |
| | 1140 | * if there's no object, or the object already has no contents, |
| | 1141 | * there's nothing to do |
| | 1142 | */ |
| | 1143 | if (obj == nil || obj.contents.length() == 0) |
| | 1144 | return nil; |
| | 1145 | |
| | 1146 | /* |
| | 1147 | * Try an implicit 'take x' on the object's first child. |
| | 1148 | * |
| | 1149 | * Note that we only try this on the first object, because the |
| | 1150 | * precondition mechanism automatically re-applies all |
| | 1151 | * preconditions after any one of them performs an implied |
| | 1152 | * command. If we have multiple objects that must be removed, |
| | 1153 | * that basic loop will ensure that we'll come back here as many |
| | 1154 | * times as necessary. |
| | 1155 | */ |
| | 1156 | chi = obj.contents[1]; |
| | 1157 | if (allowImplicit && tryImplicitAction(TakeFrom, chi, obj)) |
| | 1158 | { |
| | 1159 | /* make sure it worked */ |
| | 1160 | if (chi.isIn(obj)) |
| | 1161 | exit; |
| | 1162 | |
| | 1163 | /* tell the caller we tried an implied command */ |
| | 1164 | return true; |
| | 1165 | } |
| | 1166 | |
| | 1167 | /* we can't remove the objects implicitly, so give up */ |
| | 1168 | reportFailure(&mustBeEmptyMsg, obj); |
| | 1169 | |
| | 1170 | /* make it the pronoun */ |
| | 1171 | gActor.setPronounObj(obj); |
| | 1172 | |
| | 1173 | /* abort the command */ |
| | 1174 | exit; |
| | 1175 | } |
| | 1176 | ; |