actions.c 45.9 KB
Newer Older
Eric S. Raymond's avatar
Eric S. Raymond committed
1 2 3 4 5 6 7 8
/*
 * Actions for the duneon-running code.
 *
 * Copyright (c) 1977, 2005 by Will Crowther and Don Woods
 * Copyright (c) 2017 by Eric S. Raymond
 * SPDX-License-Identifier: BSD-2-clause
 */

9
#include <stdlib.h>
10
#include <stdbool.h>
11
#include <string.h>
12
#include "advent.h"
13
#include "dungeon.h"
NHOrus's avatar
NHOrus committed
14
#include <inttypes.h>
15

16
static phase_codes_t fill(verb_t, obj_t);
17

18
static phase_codes_t attack(command_t command)
19 20 21 22 23
/*  Attack.  Assume target if unambiguous.  "Throw" also links here.
 *  Attackable objects fall into two categories: enemies (snake,
 *  dwarf, etc.)  and others (bird, clam, machine).  Ambiguous if 2
 *  enemies, or no enemies but 2 others. */
{
24 25
    verb_t verb = command.verb;
    obj_t obj = command.obj;
NHOrus's avatar
NHOrus committed
26

NHOrus's avatar
NHOrus committed
27
    if (obj == INTRANSITIVE) {
NHOrus's avatar
NHOrus committed
28 29
        int changes = 0;
        if (atdwrf(game.loc) > 0) {
30
            obj = DWARF;
NHOrus's avatar
NHOrus committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
            ++changes;
        }
        if (HERE(SNAKE)) {
            obj = SNAKE;
            ++changes;
        }
        if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
            obj = DRAGON;
            ++changes;
        }
        if (AT(TROLL)) {
            obj = TROLL;
            ++changes;
        }
        if (AT(OGRE)) {
            obj = OGRE;
            ++changes;
        }
        if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) {
            obj = BEAR;
            ++changes;
        }
        /* check for low-priority targets */
NHOrus's avatar
NHOrus committed
54
        if (obj == INTRANSITIVE) {
55
            /* Can't attack bird or machine by throwing axe. */
NHOrus's avatar
NHOrus committed
56
            if (HERE(BIRD) && verb != THROW) {
NHOrus's avatar
NHOrus committed
57
                obj = BIRD;
NHOrus's avatar
NHOrus committed
58 59 60 61 62 63
                ++changes;
            }
            if (HERE(VEND) && verb != THROW) {
                obj = VEND;
                ++changes;
            }
64 65
            /* Clam and oyster both treated as clam for intransitive case;
             * no harm done. */
NHOrus's avatar
NHOrus committed
66 67 68 69
            if (HERE(CLAM) || HERE(OYSTER)) {
                obj = CLAM;
                ++changes;
            }
70
        }
NHOrus's avatar
NHOrus committed
71 72
        if (changes >= 2)
            return GO_UNKNOWN;
73
    }
74

75
    if (obj == BIRD) {
76
        if (game.closed) {
NHOrus's avatar
NHOrus committed
77
            rspeak(UNHAPPY_BIRD);
78 79 80
        } else {
            DESTROY(BIRD);
            rspeak(BIRD_DEAD);
81
        }
82 83 84
        return GO_CLEAROBJ;
    }
    if (obj == VEND) {
NHOrus's avatar
NHOrus committed
85 86
        state_change(VEND,
                     game.prop[VEND] == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
87

88
        return GO_CLEAROBJ;
89 90
    }

91
    if (obj == BEAR) {
92 93
        switch (game.prop[BEAR]) {
        case UNTAMED_BEAR:
94
            rspeak(BEAR_HANDS);
95 96
            break;
        case SITTING_BEAR:
97
            rspeak(BEAR_CONFUSED);
98 99
            break;
        case CONTENTED_BEAR:
100
            rspeak(BEAR_CONFUSED);
101 102
            break;
        case BEAR_DEAD:
103
            rspeak(ALREADY_DEAD);
104 105
            break;
        }
106 107 108
        return GO_CLEAROBJ;
    }
    if (obj == DRAGON && game.prop[DRAGON] == DRAGON_BARS) {
109 110 111 112
        /*  Fun stuff for dragon.  If he insists on attacking it, win!
         *  Set game.prop to dead, move dragon to central loc (still
         *  fixed), move rug there (not fixed), and move him there,
         *  too.  Then do a null motion to get new description. */
113
        rspeak(BARE_HANDS_QUERY);
114 115 116 117
        if (!silent_yes()) {
            speak(arbitrary_messages[NASTY_DRAGON]);
            return GO_MOVE;
        }
118 119
        state_change(DRAGON, DRAGON_DEAD);
        game.prop[RUG] = RUG_FLOOR;
120
        /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly.
121
         * The way it was computed before was worse; it depended on the
122 123 124
         * two dragon locations being LOC_SECRET4 and LOC_SECRET6 and
         * LOC_SECRET5 being right between them.
         */
125 126
        move(DRAGON + NOBJECTS, IS_FIXED);
        move(RUG + NOBJECTS, IS_FREE);
127 128 129
        move(DRAGON, LOC_SECRET5);
        move(RUG, LOC_SECRET5);
        drop(BLOOD, LOC_SECRET5);
130 131 132 133
        for (obj_t i = 1; i <= NOBJECTS; i++) {
            if (game.place[i] == objects[DRAGON].plac ||
                game.place[i] == objects[DRAGON].fixd)
                move(i, LOC_SECRET5);
134
        }
135
        game.loc = LOC_SECRET5;
136
        return GO_MOVE;
137
    }
Eric S. Raymond's avatar
Eric S. Raymond committed
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    if (obj == OGRE) {
        rspeak(OGRE_DODGE);
        if (atdwrf(game.loc) == 0)
            return GO_CLEAROBJ;

        rspeak(KNIFE_THROWN);
        DESTROY(OGRE);
        int dwarves = 0;
        for (int i = 1; i < PIRATE; i++) {
            if (game.dloc[i] == game.loc) {
                ++dwarves;
                game.dloc[i] = LOC_LONGWEST;
                game.dseen[i] = false;
            }
        }
        rspeak((dwarves > 1) ?
               OGRE_PANIC1 :
               OGRE_PANIC2);
        return GO_CLEAROBJ;
    }

    switch (obj) {
NHOrus's avatar
NHOrus committed
161
    case INTRANSITIVE:
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
        rspeak(NO_TARGET);
        break;
    case CLAM:
    case OYSTER:
        rspeak(SHELL_IMPERVIOUS);
        break;
    case SNAKE:
        rspeak(SNAKE_WARNING);
        break;
    case DWARF:
        if (game.closed) {
            return GO_DWARFWAKE;
        }
        rspeak(BARE_HANDS_QUERY);
        break;
    case DRAGON:
        rspeak(ALREADY_DEAD);
        break;
    case TROLL:
        rspeak(ROCKY_TROLL);
        break;
    default:
184
        speak(actions[verb].message);
185
    }
Eric S. Raymond's avatar
Eric S. Raymond committed
186
    return GO_CLEAROBJ;
187 188
}

189
static phase_codes_t bigwords(vocab_t id)
Eric S. Raymond's avatar
Eric S. Raymond committed
190
/*  FEE FIE FOE FOO (AND FUM).  Advance to next state if given in proper order.
191 192
 *  Look up foo in special section of vocab to determine which word we've got.
 *  Last word zips the eggs back to the giant room (unless already there). */
Eric S. Raymond's avatar
Eric S. Raymond committed
193
{
194 195 196 197 198
    if ((game.foobar == WORD_EMPTY && id == FEE) ||
        (game.foobar == FEE && id == FIE) ||
        (game.foobar == FIE && id == FOE) ||
        (game.foobar == FOE && id == FOO) ||
        (game.foobar == FOE && id == FUM)) {
199
        game.foobar = id;
200
        if ((id != FOO) && (id != FUM)) {
201 202 203 204 205 206 207 208 209 210 211 212 213 214
            rspeak(OK_MAN);
            return GO_CLEAROBJ;
        }
        game.foobar = WORD_EMPTY;
        if (game.place[EGGS] == objects[EGGS].plac ||
            (TOTING(EGGS) && game.loc == objects[EGGS].plac)) {
            rspeak(NOTHING_HAPPENS);
            return GO_CLEAROBJ;
        } else {
            /*  Bring back troll if we steal the eggs back from him before
             *  crossing. */
            if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID)
                game.prop[TROLL] = TROLL_PAIDONCE;
            if (HERE(EGGS))
215
                pspeak(EGGS, look, true, EGGS_VANISHED);
216
            else if (game.loc == objects[EGGS].plac)
217
                pspeak(EGGS, look, true, EGGS_HERE);
218
            else
219
                pspeak(EGGS, look, true, EGGS_DONE);
220
            move(EGGS, objects[EGGS].plac);
221

222 223 224 225 226 227 228 229 230 231 232 233
            return GO_CLEAROBJ;
        }
    } else {
        if (game.loc == LOC_GIANTROOM) {
            rspeak(START_OVER);
        } else {
            /* This is new begavior in Open Adventure - sounds better when
             * player isn't in the Giant Room. */
            rspeak(WELL_POINTLESS);
        }
        game.foobar = WORD_EMPTY;
        return GO_CLEAROBJ;
Eric S. Raymond's avatar
Eric S. Raymond committed
234 235 236
    }
}

Eric S. Raymond's avatar
Eric S. Raymond committed
237
static void blast(void)
238 239
/*  Blast.  No effect unless you've got dynamite, which is a neat trick! */
{
240
    if (game.prop[ROD2] == STATE_NOTFOUND ||
241
        !game.closed)
242
        rspeak(REQUIRES_DYNAMITE);
Eric S. Raymond's avatar
Eric S. Raymond committed
243
    else {
244 245 246 247 248 249 250 251 252 253
        if (HERE(ROD2)) {
            game.bonus = splatter;
            rspeak(SPLATTER_MESSAGE);
        } else if (game.loc == LOC_NE) {
            game.bonus = defeat;
            rspeak(DEFEAT_MESSAGE);
        } else {
            game.bonus = victory;
            rspeak(VICTORY_MESSAGE);
        }
254
        terminate(endgame);
255
    }
256 257
}

258
static phase_codes_t vbreak(verb_t verb, obj_t obj)
259 260
/*  Break.  Only works for mirror in repository and, of course, the vase. */
{
NHOrus's avatar
NHOrus committed
261 262
    switch (obj) {
    case MIRROR:
NHOrus's avatar
NHOrus committed
263
        if (game.closed) {
264
            state_change(MIRROR, MIRROR_BROKEN);
265
            return GO_DWARFWAKE;
NHOrus's avatar
NHOrus committed
266 267
        } else {
            rspeak(TOO_FAR);
NHOrus's avatar
NHOrus committed
268
            break;
269
        }
NHOrus's avatar
NHOrus committed
270 271 272 273 274 275 276 277
    case VASE:
        if (game.prop[VASE] == VASE_WHOLE) {
            if (TOTING(VASE))
                drop(VASE, game.loc);
            state_change(VASE, VASE_BROKEN);
            game.fixed[VASE] = IS_FIXED;
            break;
        }
NHOrus's avatar
NHOrus committed
278
    /* FALLTHRU */
NHOrus's avatar
NHOrus committed
279 280
    default:
        speak(actions[verb].message);
281
    }
NHOrus's avatar
NHOrus committed
282
    return (GO_CLEAROBJ);
283 284
}

285
static phase_codes_t brief(void)
286
/*  Brief.  Intransitive only.  Suppress full descriptions after first time. */
287
{
288 289
    game.abbnum = 10000;
    game.detail = 3;
290
    rspeak(BRIEF_CONFIRM);
291
    return GO_CLEAROBJ;
292 293
}

294
static phase_codes_t vcarry(verb_t verb, obj_t obj)
295 296 297 298 299
/*  Carry an object.  Special cases for bird and cage (if bird in cage, can't
 *  take one without the other).  Liquids also special, since they depend on
 *  status of bottle.  Also various side effects, etc. */
{
    if (obj == INTRANSITIVE) {
300
        /*  Carry, no object given yet.  OK if only one object present. */
NHOrus's avatar
NHOrus committed
301
        if (game.atloc[game.loc] == NO_OBJECT ||
302
            game.link[game.atloc[game.loc]] != 0 ||
303
            atdwrf(game.loc) > 0)
304 305
            return GO_UNKNOWN;
        obj = game.atloc[game.loc];
306 307
    }

308
    if (TOTING(obj)) {
NHOrus's avatar
NHOrus committed
309
        speak(actions[verb].message);
310 311
        return GO_CLEAROBJ;
    }
312

313
    if (obj == MESSAG) {
314
        rspeak(REMOVE_MESSAGE);
315 316
        DESTROY(MESSAG);
        return GO_CLEAROBJ;
317
    }
318

NHOrus's avatar
NHOrus committed
319
    if (game.fixed[obj] != IS_FREE) {
NHOrus's avatar
NHOrus committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
        switch (obj) {
        case PLANT:
            /* Next guard tests whether plant is tiny or stashed */
            rspeak(game.prop[PLANT] <= PLANT_THIRSTY ? DEEP_ROOTS : YOU_JOKING);
            break;
        case BEAR:
            rspeak( game.prop[BEAR] == SITTING_BEAR ? BEAR_CHAINED : YOU_JOKING);
            break;
        case CHAIN:
            rspeak( game.prop[BEAR] != UNTAMED_BEAR ? STILL_LOCKED : YOU_JOKING);
            break;
        case RUG:
            rspeak(game.prop[RUG] == RUG_HOVER ? RUG_HOVERS : YOU_JOKING);
            break;
        case URN:
335
            rspeak(URN_NOBUDGE);
NHOrus's avatar
NHOrus committed
336 337
            break;
        case CAVITY:
338
            rspeak(DOUGHNUT_HOLES);
NHOrus's avatar
NHOrus committed
339 340
            break;
        case BLOOD:
341
            rspeak(FEW_DROPS);
NHOrus's avatar
NHOrus committed
342 343
            break;
        case SIGN:
344
            rspeak(HAND_PASSTHROUGH);
NHOrus's avatar
NHOrus committed
345 346 347
            break;
        default:
            rspeak(YOU_JOKING);
348
        }
349
        return GO_CLEAROBJ;
350
    }
NHOrus's avatar
NHOrus committed
351

352 353 354 355
    if (obj == WATER ||
        obj == OIL) {
        if (!HERE(BOTTLE) ||
            LIQUID() != obj) {
NHOrus's avatar
NHOrus committed
356 357
            if (!TOTING(BOTTLE)) {
                rspeak(NO_CONTAINER);
358 359
                return GO_CLEAROBJ;
            }
NHOrus's avatar
NHOrus committed
360 361 362 363
            if (game.prop[BOTTLE] == EMPTY_BOTTLE) {
                return (fill(verb, BOTTLE));
            } else
                rspeak(BOTTLE_FULL);
364
            return GO_CLEAROBJ;
365 366
        }
        obj = BOTTLE;
367 368
    }

369
    if (game.holdng >= INVLIMIT) {
NHOrus's avatar
NHOrus committed
370
        rspeak(CARRY_LIMIT);
371
        return GO_CLEAROBJ;
NHOrus's avatar
NHOrus committed
372 373 374

    }

375
    if (obj == BIRD && game.prop[BIRD] != BIRD_CAGED && STASHED(BIRD) != BIRD_CAGED) {
376
        if (game.prop[BIRD] == BIRD_FOREST_UNCAGED) {
377
            DESTROY(BIRD);
378
            rspeak(BIRD_CRAP);
379 380
            return GO_CLEAROBJ;
        }
NHOrus's avatar
NHOrus committed
381 382 383 384 385 386
        if (!TOTING(CAGE)) {
            rspeak(CANNOT_CARRY);
            return GO_CLEAROBJ;
        }
        if (TOTING(ROD)) {
            rspeak(BIRD_EVADES);
387 388
            return GO_CLEAROBJ;
        }
389
        game.prop[BIRD] = BIRD_CAGED;
390
    }
391 392
    if ((obj == BIRD ||
         obj == CAGE) &&
NHOrus's avatar
NHOrus committed
393
        (game.prop[BIRD] == BIRD_CAGED || STASHED(BIRD) == BIRD_CAGED)) {
NHOrus's avatar
NHOrus committed
394
        /* expression maps BIRD to CAGE and CAGE to BIRD */
395
        carry(BIRD + CAGE - obj, game.loc);
NHOrus's avatar
NHOrus committed
396 397
    }

398
    carry(obj, game.loc);
NHOrus's avatar
NHOrus committed
399

400
    if (obj == BOTTLE && LIQUID() != NO_OBJECT)
401
        game.place[LIQUID()] = CARRIED;
NHOrus's avatar
NHOrus committed
402

403 404
    if (GSTONE(obj) && game.prop[obj] != STATE_FOUND) {
        game.prop[obj] = STATE_FOUND;
405
        game.prop[CAVITY] = CAVITY_EMPTY;
Eric S. Raymond's avatar
Eric S. Raymond committed
406
    }
407
    rspeak(OK_MAN);
408
    return GO_CLEAROBJ;
409 410
}

NHOrus's avatar
NHOrus committed
411
static int chain(verb_t verb)
412
/* Do something to the bear's chain */
413
{
414
    if (verb != LOCK) {
NHOrus's avatar
NHOrus committed
415 416 417 418 419 420
        if (game.prop[BEAR] == UNTAMED_BEAR) {
            rspeak(BEAR_BLOCKS);
            return GO_CLEAROBJ;
        }
        if (game.prop[CHAIN] == CHAIN_HEAP) {
            rspeak(ALREADY_UNLOCKED);
421 422
            return GO_CLEAROBJ;
        }
423
        game.prop[CHAIN] = CHAIN_HEAP;
NHOrus's avatar
NHOrus committed
424
        game.fixed[CHAIN] = IS_FREE;
425
        if (game.prop[BEAR] != BEAR_DEAD)
NHOrus's avatar
NHOrus committed
426
            game.prop[BEAR] = CONTENTED_BEAR;
NHOrus's avatar
NHOrus committed
427 428

        switch (game.prop[BEAR]) {
429
        // LCOV_EXCL_START
NHOrus's avatar
NHOrus committed
430
        case BEAR_DEAD:
431 432
            /* Can't be reached until the bear can die in some way other
             * than a bridge collapse. Leave in in case this changes, but
Eric S. Raymond's avatar
Eric S. Raymond committed
433
             * exclude from coverage testing. */
434
            game.fixed[BEAR] = IS_FIXED;
NHOrus's avatar
NHOrus committed
435
            break;
Eric S. Raymond's avatar
Eric S. Raymond committed
436
        // LCOV_EXCL_STOP
NHOrus's avatar
NHOrus committed
437
        default:
NHOrus's avatar
NHOrus committed
438
            game.fixed[BEAR] = IS_FREE;
439
        }
NHOrus's avatar
NHOrus committed
440 441
        rspeak(CHAIN_UNLOCKED);
        return GO_CLEAROBJ;
442
    }
NHOrus's avatar
NHOrus committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456

    if (game.prop[CHAIN] != CHAIN_HEAP) {
        rspeak(ALREADY_LOCKED);
        return GO_CLEAROBJ;
    }
    if (game.loc != objects[CHAIN].plac) {
        rspeak(NO_LOCKSITE);
        return GO_CLEAROBJ;
    }

    game.prop[CHAIN] = CHAIN_FIXED;

    if (TOTING(CHAIN))
        drop(CHAIN, game.loc);
NHOrus's avatar
NHOrus committed
457
    game.fixed[CHAIN] = IS_FIXED;
NHOrus's avatar
NHOrus committed
458 459

    rspeak(CHAIN_LOCKED);
460
    return GO_CLEAROBJ;
461 462
}

463
static phase_codes_t discard(verb_t verb, obj_t obj)
464 465 466 467
/*  Discard object.  "Throw" also comes here for most objects.  Special cases for
 *  bird (might attack snake or dragon) and cage (might contain bird) and vase.
 *  Drop coins at vending machine for extra batteries. */
{
468
    if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
469
        obj = ROD2;
470 471
    }

472 473 474 475 476
    if (!TOTING(obj)) {
        speak(actions[verb].message);
        return GO_CLEAROBJ;
    }

477
    if (GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL) {
478 479 480 481 482 483
        rspeak(GEM_FITS);
        game.prop[obj] = STATE_IN_CAVITY;
        game.prop[CAVITY] = CAVITY_FULL;
        if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) ||
                          (obj == RUBY && game.prop[RUG] == RUG_HOVER))) {
            if (obj == RUBY)
NHOrus's avatar
NHOrus committed
484
                rspeak(RUG_SETTLES);
485
            else if (TOTING(RUG))
NHOrus's avatar
NHOrus committed
486
                rspeak(RUG_WIGGLES);
487
            else
NHOrus's avatar
NHOrus committed
488 489
                rspeak(RUG_RISES);
            if (!TOTING(RUG) || obj == RUBY) {
490 491 492 493 494
                int k = (game.prop[RUG] == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER;
                game.prop[RUG] = k;
                if (k == RUG_HOVER)
                    k = objects[SAPPH].plac;
                move(RUG + NOBJECTS, k);
495 496
            }
        }
497 498 499 500 501
        drop(obj, game.loc);
        return GO_CLEAROBJ;
    }

    if (obj == COINS && HERE(VEND)) {
502 503
        DESTROY(COINS);
        drop(BATTERY, game.loc);
504
        pspeak(BATTERY, look, true, FRESH_BATTERIES);
505
        return GO_CLEAROBJ;
506 507 508 509 510 511 512 513 514
    }

    if (LIQUID() == obj)
        obj = BOTTLE;
    if (obj == BOTTLE && LIQUID() != NO_OBJECT) {
        game.place[LIQUID()] = LOC_NOWHERE;
    }

    if (obj == BEAR && AT(TROLL)) {
515 516
        state_change(TROLL, TROLL_GONE);
        move(TROLL, LOC_NOWHERE);
517
        move(TROLL + NOBJECTS, IS_FREE);
518 519 520
        move(TROLL2, objects[TROLL].plac);
        move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
        juggle(CHASM);
521 522
        drop(obj, game.loc);
        return GO_CLEAROBJ;
523
    }
524 525 526 527 528 529 530 531 532 533 534 535 536 537

    if (obj == VASE) {
        if (game.loc != objects[PILLOW].plac) {
            state_change(VASE, AT(PILLOW)
                         ? VASE_WHOLE
                         : VASE_DROPPED);
            if (game.prop[VASE] != VASE_WHOLE)
                game.fixed[VASE] = IS_FIXED;
            drop(obj, game.loc);
            return GO_CLEAROBJ;
        }
    }

    if (obj == CAGE && game.prop[BIRD] == BIRD_CAGED) {
NHOrus's avatar
NHOrus committed
538
        drop(BIRD, game.loc);
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
    }

    if (obj == BIRD) {
        if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
            rspeak(BIRD_BURNT);
            DESTROY(BIRD);
            return GO_CLEAROBJ;
        }
        if (HERE(SNAKE)) {
            rspeak(BIRD_ATTACKS);
            if (game.closed)
                return GO_DWARFWAKE;
            DESTROY(SNAKE);
            /* Set game.prop for use by travel options */
            game.prop[SNAKE] = SNAKE_CHASED;
        } else
            rspeak(OK_MAN);

557
        game.prop[BIRD] = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
558
        drop(obj, game.loc);
NHOrus's avatar
NHOrus committed
559
        return GO_CLEAROBJ;
560 561 562 563
    }

    rspeak(OK_MAN);
    drop(obj, game.loc);
564
    return GO_CLEAROBJ;
565 566
}

567
static phase_codes_t drink(verb_t verb, obj_t obj)
568 569 570
/*  Drink.  If no object, assume water and look for it here.  If water is in
 *  the bottle, drink that, else must be at a water loc, so drink stream. */
{
NHOrus's avatar
NHOrus committed
571
    if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
NHOrus's avatar
NHOrus committed
572
        (LIQUID() != WATER || !HERE(BOTTLE))) {
573
        return GO_UNKNOWN;
NHOrus's avatar
NHOrus committed
574 575 576
    }

    if (obj == BLOOD) {
577
        DESTROY(BLOOD);
578
        state_change(DRAGON, DRAGON_BLOODLESS);
579
        game.blooded = true;
NHOrus's avatar
NHOrus committed
580
        return GO_CLEAROBJ;
581
    }
NHOrus's avatar
NHOrus committed
582

NHOrus's avatar
NHOrus committed
583
    if (obj != INTRANSITIVE && obj != WATER) {
NHOrus's avatar
NHOrus committed
584 585 586 587 588
        rspeak(RIDICULOUS_ATTEMPT);
        return GO_CLEAROBJ;
    }
    if (LIQUID() == WATER && HERE(BOTTLE)) {
        game.place[WATER] = LOC_NOWHERE;
589
        state_change(BOTTLE, EMPTY_BOTTLE);
NHOrus's avatar
NHOrus committed
590 591 592
        return GO_CLEAROBJ;
    }

593
    speak(actions[verb].message);
594
    return GO_CLEAROBJ;
595 596
}

597
static phase_codes_t eat(verb_t verb, obj_t obj)
598 599 600
/*  Eat.  Intransitive: assume food if present, else ask what.  Transitive: food
 *  ok, some things lose appetite, rest are ridiculous. */
{
NHOrus's avatar
NHOrus committed
601 602
    switch (obj) {
    case INTRANSITIVE:
603 604
        if (!HERE(FOOD))
            return GO_UNKNOWN;
NHOrus's avatar
NHOrus committed
605
    /* FALLTHRU */
NHOrus's avatar
NHOrus committed
606
    case FOOD:
607
        DESTROY(FOOD);
608
        rspeak(THANKS_DELICIOUS);
NHOrus's avatar
NHOrus committed
609 610 611 612 613 614 615 616 617 618
        break;
    case BIRD:
    case SNAKE:
    case CLAM:
    case OYSTER:
    case DWARF:
    case DRAGON:
    case TROLL:
    case BEAR:
    case OGRE:
619
        rspeak(LOST_APPETITE);
NHOrus's avatar
NHOrus committed
620 621 622
        break;
    default:
        speak(actions[verb].message);
623
    }
624
    return GO_CLEAROBJ;
625 626
}

627
static phase_codes_t extinguish(verb_t verb, obj_t obj)
628
/* Extinguish.  Lamp, urn, dragon/volcano (nice try). */
629
{
Eric S. Raymond's avatar
Eric S. Raymond committed
630
    if (obj == INTRANSITIVE) {
631
        if (HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT)
NHOrus's avatar
NHOrus committed
632
            obj = LAMP;
NHOrus's avatar
NHOrus committed
633
        if (HERE(URN) && game.prop[URN] == URN_LIT)
634
            obj = URN;
NHOrus's avatar
NHOrus committed
635
        if (obj == INTRANSITIVE)
NHOrus's avatar
NHOrus committed
636
            return GO_UNKNOWN;
Eric S. Raymond's avatar
Eric S. Raymond committed
637 638
    }

NHOrus's avatar
NHOrus committed
639 640
    switch (obj) {
    case URN:
NHOrus's avatar
NHOrus committed
641 642 643
        if (game.prop[URN] != URN_EMPTY) {
            state_change(URN, URN_DARK);
        } else {
644
            pspeak(URN, change, true, URN_DARK);
NHOrus's avatar
NHOrus committed
645
        }
NHOrus's avatar
NHOrus committed
646 647
        break;
    case LAMP:
NHOrus's avatar
NHOrus committed
648
        state_change(LAMP, LAMP_DARK);
649 650 651
        rspeak(DARK(game.loc) ?
               PITCH_DARK :
               NO_MESSAGE);
NHOrus's avatar
NHOrus committed
652 653 654
        break;
    case DRAGON:
    case VOLCANO:
655
        rspeak(BEYOND_POWER);
NHOrus's avatar
NHOrus committed
656 657 658
        break;
    default:
        speak(actions[verb].message);
659
    }
660
    return GO_CLEAROBJ;
661 662
}

663
static phase_codes_t feed(verb_t verb, obj_t obj)
664 665 666
/*  Feed.  If bird, no seed.  Snake, dragon, troll: quip.  If dwarf, make him
 *  mad.  Bear, special. */
{
667 668
    switch (obj) {
    case BIRD:
669
        rspeak(BIRD_PINING);
670 671 672 673 674 675 676 677 678
        break;
    case DRAGON:
        if (game.prop[DRAGON] != DRAGON_BARS)
            rspeak(RIDICULOUS_ATTEMPT);
        else
            rspeak(NOTHING_EDIBLE);
        break;
    case SNAKE:
        if (!game.closed && HERE(BIRD)) {
679
            DESTROY(BIRD);
680 681 682 683 684 685 686 687
            rspeak(BIRD_DEVOURED);
        } else
            rspeak(NOTHING_EDIBLE);
        break;
    case TROLL:
        rspeak(TROLL_VICES);
        break;
    case DWARF:
688 689
        if (HERE(FOOD)) {
            game.dflag += 2;
690
            rspeak(REALLY_MAD);
691
        } else
692
            speak(actions[verb].message);
693 694 695 696 697
        break;
    case BEAR:
        if (game.prop[BEAR] == BEAR_DEAD) {
            rspeak(RIDICULOUS_ATTEMPT);
            break;
698
        }
699 700 701 702 703 704 705 706
        if (game.prop[BEAR] == UNTAMED_BEAR) {
            if (HERE(FOOD)) {
                DESTROY(FOOD);
                game.fixed[AXE] = IS_FREE;
                game.prop[AXE] = AXE_HERE;
                state_change(BEAR, SITTING_BEAR);
            } else
                rspeak(NOTHING_EDIBLE);
707
            break;
708
        }
709
        speak(actions[verb].message);
710 711
        break;
    case OGRE:
712
        if (HERE(FOOD))
713
            rspeak(OGRE_FULL);
714
        else
715
            speak(actions[verb].message);
716 717 718
        break;
    default:
        rspeak(AM_GAME);
719
    }
720
    return GO_CLEAROBJ;
721 722
}

723
phase_codes_t fill(verb_t verb, obj_t obj)
724 725 726 727
/*  Fill.  Bottle or urn must be empty, and liquid available.  (Vase
 *  is nasty.) */
{
    if (obj == VASE) {
NHOrus's avatar
NHOrus committed
728 729 730 731 732 733
        if (LIQLOC(game.loc) == NO_OBJECT) {
            rspeak(FILL_INVALID);
            return GO_CLEAROBJ;
        }
        if (!TOTING(VASE)) {
            rspeak(ARENT_CARRYING);
734 735
            return GO_CLEAROBJ;
        }
736
        rspeak(SHATTER_VASE);
737
        game.prop[VASE] = VASE_BROKEN;
NHOrus's avatar
NHOrus committed
738
        game.fixed[VASE] = IS_FIXED;
739 740
        drop(VASE, game.loc);
        return GO_CLEAROBJ;
NHOrus's avatar
NHOrus committed
741 742 743
    }

    if (obj == URN) {
744
        if (game.prop[URN] != URN_EMPTY) {
NHOrus's avatar
NHOrus committed
745
            rspeak(FULL_URN);
746 747
            return GO_CLEAROBJ;
        }
NHOrus's avatar
NHOrus committed
748 749
        if (!HERE(BOTTLE)) {
            rspeak(FILL_INVALID);
750 751
            return GO_CLEAROBJ;
        }
NHOrus's avatar
NHOrus committed
752 753 754 755 756 757 758
        int k = LIQUID();
        switch (k) {
        case WATER:
            game.prop[BOTTLE] = EMPTY_BOTTLE;
            rspeak(WATER_URN);
            break;
        case OIL:
759
            game.prop[URN] = URN_DARK;
NHOrus's avatar
NHOrus committed
760 761 762 763 764 765 766 767 768
            game.prop[BOTTLE] = EMPTY_BOTTLE;
            rspeak(OIL_URN);
            break;
        case NO_OBJECT:
        default:
            rspeak(FILL_INVALID);
            return GO_CLEAROBJ;
        }
        game.place[k] = LOC_NOWHERE;
769
        return GO_CLEAROBJ;
NHOrus's avatar
NHOrus committed
770
    }
NHOrus's avatar
NHOrus committed
771
    if (obj != INTRANSITIVE && obj != BOTTLE) {
772
        speak(actions[verb].message);
773
        return GO_CLEAROBJ;
NHOrus's avatar
NHOrus committed
774
    }
NHOrus's avatar
NHOrus committed
775
    if (obj == INTRANSITIVE && !HERE(BOTTLE))
776
        return GO_UNKNOWN;
NHOrus's avatar
NHOrus committed
777 778 779 780

    if (HERE(URN) && game.prop[URN] != URN_EMPTY) {
        rspeak(URN_NOPOUR);
        return GO_CLEAROBJ;
781
    }
NHOrus's avatar
NHOrus committed
782 783 784 785 786 787 788 789 790
    if (LIQUID() != NO_OBJECT) {
        rspeak(BOTTLE_FULL);
        return GO_CLEAROBJ;
    }
    if (LIQLOC(game.loc) == NO_OBJECT) {
        rspeak(NO_LIQUID);
        return GO_CLEAROBJ;
    }

791
    state_change(BOTTLE, (LIQLOC(game.loc) == OIL)
792 793
                 ? OIL_BOTTLE
                 : WATER_BOTTLE);
NHOrus's avatar
NHOrus committed
794 795
    if (TOTING(BOTTLE))
        game.place[LIQUID()] = CARRIED;
796
    return GO_CLEAROBJ;
797 798
}

799
static phase_codes_t find(verb_t verb, obj_t obj)
800
/* Find.  Might be carrying it, or it might be here.  Else give caveat. */
801
{
NHOrus's avatar
NHOrus committed
802 803 804 805 806 807 808 809 810 811
    if (TOTING(obj)) {
        rspeak(ALREADY_CARRYING);
        return GO_CLEAROBJ;
    }

    if (game.closed) {
        rspeak(NEEDED_NEARBY);
        return GO_CLEAROBJ;
    }

812
    if (AT(obj) ||
813 814
        (LIQUID() == obj && AT(BOTTLE)) ||
        obj == LIQLOC(game.loc) ||
NHOrus's avatar
NHOrus committed
815 816 817 818 819 820
        (obj == DWARF && atdwrf(game.loc) > 0)) {
        rspeak(YOU_HAVEIT);
        return GO_CLEAROBJ;
    }


821
    speak(actions[verb].message);
822
    return GO_CLEAROBJ;
823 824
}

825
static phase_codes_t fly(verb_t verb, obj_t obj)
826 827 828
/* Fly.  Snide remarks unless hovering rug is here. */
{
    if (obj == INTRANSITIVE) {
NHOrus's avatar
NHOrus committed
829 830 831 832 833 834
        if (!HERE(RUG)) {
            rspeak(FLAP_ARMS);
            return GO_CLEAROBJ;
        }
        if (game.prop[RUG] != RUG_HOVER) {
            rspeak(RUG_NOTHING2);
835 836 837
            return GO_CLEAROBJ;
        }
        obj = RUG;
838 839
    }

840
    if (obj != RUG) {
841
        speak(actions[verb].message);
842 843
        return GO_CLEAROBJ;
    }
844
    if (game.prop[RUG] != RUG_HOVER) {
NHOrus's avatar
NHOrus committed
845
        rspeak(RUG_NOTHING1);
846
        return GO_CLEAROBJ;
847
    }
848 849
    game.oldlc2 = game.oldloc;
    game.oldloc = game.loc;
NHOrus's avatar
NHOrus committed
850

Eric S. Raymond's avatar
Eric S. Raymond committed
851
    if (game.prop[SAPPH] == STATE_NOTFOUND) {
852
        game.newloc = game.place[SAPPH];
NHOrus's avatar
NHOrus committed
853
        rspeak(RUG_GOES);
Eric S. Raymond's avatar
Eric S. Raymond committed
854
    } else {
855
        game.newloc = LOC_CLIFF;
Eric S. Raymond's avatar
Eric S. Raymond committed
856
        rspeak(RUG_RETURNS);
NHOrus's avatar
NHOrus committed
857
    }
858
    return GO_TERMINATE;
859
}
NHOrus's avatar
NHOrus committed
860

861
static phase_codes_t inven(void)
862 863
/* Inventory. If object, treat same as find.  Else report on current burden. */
{
NHOrus's avatar
NHOrus committed
864
    bool empty = true;
865
    for (obj_t i = 1; i <= NOBJECTS; i++) {
866 867
        if (i == BEAR ||
            !TOTING(i))
868
            continue;
NHOrus's avatar
NHOrus committed
869
        if (empty) {
870
            rspeak(NOW_HOLDING);
NHOrus's avatar
NHOrus committed
871 872
            empty = false;
        }
873
        pspeak(i, touch, false, -1);
874
    }
875
    if (TOTING(BEAR))
NHOrus's avatar
NHOrus committed
876 877 878
        rspeak(TAME_BEAR);
    if (empty)
        rspeak(NO_CARRY);
879
    return GO_CLEAROBJ;
880 881
}

882
static phase_codes_t light(verb_t verb, obj_t obj)
883 884 885
/*  Light.  Applicable only to lamp and urn. */
{
    if (obj == INTRANSITIVE) {
NHOrus's avatar
NHOrus committed
886 887
        int selects = 0;
        if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0) {
NHOrus's avatar
NHOrus committed
888
            obj = LAMP;
NHOrus's avatar
NHOrus committed
889 890 891
            selects++;
        }
        if (HERE(URN) && game.prop[URN] == URN_DARK) {
NHOrus's avatar
NHOrus committed
892
            obj =  URN;
NHOrus's avatar
NHOrus committed
893 894 895
            selects++;
        }
        if (selects != 1)
NHOrus's avatar
NHOrus committed
896
            return GO_UNKNOWN;
897 898
    }

NHOrus's avatar
NHOrus committed
899 900
    switch (obj) {
    case URN:
901 902 903
        state_change(URN, game.prop[URN] == URN_EMPTY ?
                     URN_EMPTY :
                     URN_LIT);
NHOrus's avatar
NHOrus committed
904 905
        break;
    case LAMP:
906
        if (game.limit < 0) {
NHOrus's avatar
NHOrus committed
907
            rspeak(LAMP_OUT);
NHOrus's avatar
NHOrus committed
908
            break;
909
        }
NHOrus's avatar
NHOrus committed
910
        state_change(LAMP, LAMP_BRIGHT);
911 912
        if (game.wzdark)
            return GO_TOP;
NHOrus's avatar
NHOrus committed
913 914 915
        break;
    default:
        speak(actions[verb].message);
NHOrus's avatar
NHOrus committed
916
    }
NHOrus's avatar
NHOrus committed
917
    return GO_CLEAROBJ;
918 919
}

920
static phase_codes_t listen(void)
Eric S. Raymond's avatar
Eric S. Raymond committed
921
/*  Listen.  Intransitive only.  Print stuff based on object sound proprties. */
922
{
923
    vocab_t sound = locations[game.loc].sound;
924 925 926 927 928
    if (sound != SILENT) {
        rspeak(sound);
        if (!locations[game.loc].loud)
            rspeak(NO_MESSAGE);
        return GO_CLEAROBJ;
929
    }
930
    for (obj_t i = 1; i <= NOBJECTS; i++) {
931 932 933
        if (!HERE(i) ||
            objects[i].sounds[0] == NULL ||
            game.prop[i] < 0)
934
            continue;
NHOrus's avatar
NHOrus committed
935
        int mi =  game.prop[i];
Eric S. Raymond's avatar
Eric S. Raymond committed
936 937 938 939 940
        /* (ESR) Some unpleasant magic on object states here. Ideally
         * we'd have liked the bird to be a normal object that we can
         * use state_change() on; can't do it, because there are
         * actually two different series of per-state birdsounds
         * depending on whether player has drunk dragon's blood. */
NHOrus's avatar
NHOrus committed
941 942
        if (i == BIRD)
            mi += 3 * game.blooded;
943
        pspeak(i, hear, true, mi, game.zzword);
944
        rspeak(NO_MESSAGE);
945
        if (i == BIRD && mi == BIRD_ENDSTATE)
946
            DESTROY(BIRD);
947
        return GO_CLEAROBJ;
948
    }
949
    rspeak(ALL_SILENT);
950
    return GO_CLEAROBJ;
951 952
}

953
static phase_codes_t lock(verb_t verb, obj_t obj)
Eric S. Raymond's avatar
Eric S. Raymond committed
954 955 956
/* Lock, unlock, no object given.  Assume various things if present. */
{
    if (obj == INTRANSITIVE) {
NHOrus's avatar
NHOrus committed
957 958 959 960 961 962 963 964 965 966
        if (HERE(CLAM))
            obj = CLAM;
        if (HERE(OYSTER))
            obj = OYSTER;
        if (AT(DOOR))
            obj = DOOR;
        if (AT(GRATE))
            obj = GRATE;
        if (HERE(CHAIN))
            obj = CHAIN;
NHOrus's avatar
NHOrus committed
967
        if (obj == INTRANSITIVE) {
968
            rspeak(NOTHING_LOCKED);
969 970
            return GO_CLEAROBJ;
        }
Eric S. Raymond's avatar
Eric S. Raymond committed
971
    }
972

Eric S. Raymond's avatar
Eric S. Raymond committed
973 974
    /*  Lock, unlock object.  Special stuff for opening clam/oyster
     *  and for chain. */
NHOrus's avatar
NHOrus committed
975 976 977 978 979 980 981 982 983

    switch (obj) {
    case CHAIN:
        if (HERE(KEYS)) {
            return chain(verb);
        } else
            rspeak(NO_KEYS);
        break;
    case GRATE:
984 985
        if (HERE(KEYS)) {
            if (game.closng) {
NHOrus's avatar
NHOrus committed
986
                rspeak(EXIT_CLOSED);
NHOrus's avatar
NHOrus committed
987 988
                if (!game.panic)
                    game.clock2 = PANICTIME;
989 990
                game.panic = true;
            } else {
991 992 993
                state_change(GRATE, (verb == LOCK) ?
                             GRATE_CLOSED :
                             GRATE_OPEN);
994
            }
NHOrus's avatar
NHOrus committed
995 996 997
        } else
            rspeak(NO_KEYS);
        break;
NHOrus's avatar
NHOrus committed
998
    case CLAM:
999 1000 1001
        if (verb == LOCK)
            rspeak(HUH_MAN);
        else if (!TOTING(TRIDENT))
1002
            rspeak(CLAM_OPENER);
1003 1004 1005 1006 1007 1008
        else {
            DESTROY(CLAM);
            drop(OYSTER, game.loc);
            drop(PEARL, LOC_CULDESAC);
            rspeak(PEARL_FALLS);
        }
NHOrus's avatar
NHOrus committed
1009
        break;
NHOrus's avatar
NHOrus committed
1010
    case OYSTER:
1011 1012
        if (verb == LOCK)
            rspeak(HUH_MAN);
1013 1014
        else if (TOTING(OYSTER))
            rspeak(DROP_OYSTER);
1015
        else if (!TOTING(TRIDENT))
1016
            rspeak(OYSTER_OPENER);
1017 1018
        else
            rspeak(OYSTER_OPENS);
NHOrus's avatar
NHOrus committed
1019
        break;
NHOrus's avatar
NHOrus committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
    case DOOR:
        rspeak((game.prop[DOOR] == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR);
        break;
    case CAGE:
        rspeak( NO_LOCK);
        break;
    case KEYS:
        rspeak(CANNOT_UNLOCK);
        break;
    default:
1030
        speak(actions[verb].message);
NHOrus's avatar
NHOrus committed
1031 1032
    }

1033
    return GO_CLEAROBJ;
Eric S. Raymond's avatar
Eric S. Raymond committed
1034 1035
}

1036
static phase_codes_t pour(verb_t verb, obj_t obj)
Eric S. Raymond's avatar
Eric S. Raymond committed
1037 1038 1039
/*  Pour.  If no object, or object is bottle, assume contents of bottle.
 *  special tests for pouring water or oil on plant or rusty door. */
{
1040
    if (obj == BOTTLE ||
NHOrus's avatar
NHOrus committed
1041
        obj == INTRANSITIVE)
NHOrus's avatar
NHOrus committed
1042
        obj = LIQUID();
NHOrus's avatar
NHOrus committed
1043
    if (obj == NO_OBJECT)
NHOrus's avatar
NHOrus committed
1044
        return GO_UNKNOWN;
1045
    if (!TOTING(obj)) {
1046
        speak(actions[verb].message);
1047 1048
        return GO_CLEAROBJ;
    }
NHOrus's avatar
NHOrus committed
1049

1050
    if (obj != OIL && obj != WATER) {
NHOrus's avatar
NHOrus committed
1051
        rspeak(CANT_POUR);
1052 1053
        return GO_CLEAROBJ;
    }
1054
    if (HERE(URN) && game.prop[URN] == URN_EMPTY)
1055
        return fill(verb, URN);
1056
    game.prop[BOTTLE] = EMPTY_BOTTLE;
NHOrus's avatar
NHOrus committed
1057
    game.place[obj] = LOC_NOWHERE;
1058 1059
    if (!(AT(PLANT) ||
          AT(DOOR))) {
NHOrus's avatar
NHOrus committed
1060
        rspeak(GROUND_WET);
1061 1062
        return GO_CLEAROBJ;
    }
1063
    if (!AT(DOOR)) {
1064
        if (obj == WATER) {
1065 1066 1067 1068
            /* cycle through the three plant states */
            state_change(PLANT, MOD(game.prop[PLANT] + 1, 3));
            game.prop[PLANT2] = game.prop[PLANT];
            return GO_MOVE;
1069
        } else {
NHOrus's avatar
NHOrus committed
1070
            rspeak(SHAKING_LEAVES);
1071
            return GO_CLEAROBJ;
1072
        }
Eric S. Raymond's avatar
Eric S. Raymond committed
1073
    } else {
1074 1075 1076
        state_change(DOOR, (obj == OIL) ?
                     DOOR_UNRUSTED :
                     DOOR_RUSTED);
1077
        return GO_CLEAROBJ;
Eric S. Raymond's avatar
Eric S. Raymond committed
1078 1079 1080
    }
}

1081
static phase_codes_t quit(void)
1082 1083
/*  Quit.  Intransitive only.  Verify intent and exit if that's what he wants. */
{
1084
    if (yes(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
1085
        terminate(quitgame);
1086
    return GO_CLEAROBJ;
1087 1088
}

1089
static phase_codes_t read(command_t command)
1090 1091
/*  Read.  Print stuff based on objtxt.  Oyster (?) is special case. */
{
1092
    if (command.obj == INTRANSITIVE) {
1093
        command.obj = NO_OBJECT;
1094
        for (int i = 1; i <= NOBJECTS; i++) {
1095
            if (HERE(i) && objects[i].texts[0] != NULL && game.prop[i] >= 0)
1096
                command.obj = command.obj * NOBJECTS + i;
1097
        }
1098
        if (command.obj > NOBJECTS ||
1099
            command.obj == NO_OBJECT ||
1100
            DARK(game.loc))
1101
            return GO_UNKNOWN;
1102
    }
1103

1104
    if (DARK(game.loc)) {
1105
        sspeak(NO_SEE, command.word[0].raw);
1106
    } else if (command.obj == OYSTER && !game.clshnt && game.closed) {
1107
        game.clshnt = yes(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
1108
    } else if (objects[command.obj].texts[0] == NULL ||
1109
               game.prop[command.obj] == STATE_NOTFOUND) {
1110
        speak(actions[command.verb].message);
1111
    } else
1112
        pspeak(command.obj, study, true, game.prop[command.obj]);
1113
    return GO_CLEAROBJ;
1114 1115
}

1116
static phase_codes_t reservoir(void)
1117 1118
/*  Z'ZZZ (word gets recomputed at startup; different each game). */
{
1119
    if (!AT(RESER) && game.loc != LOC_RESBOTTOM) {
1120
        rspeak(NOTHING_HAPPENS);
1121
        return GO_CLEAROBJ;
1122
    } else {
1123 1124
        state_change(RESER,
                     game.prop[RESER] == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED);