X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=actions.c;h=40a1928f62d99299970e9a07fef2775cd59196f1;hp=ec17c07b2145254be33b052c1700b9912df1f8a6;hb=49d9ef1bee7b11055b7a46e2c2b45bd888e1ccef;hpb=a8ac2f477a2564bcf5b40508aea2c192d237c88a diff --git a/actions.c b/actions.c index ec17c07..40a1928 100644 --- a/actions.c +++ b/actions.c @@ -4,13 +4,7 @@ #include "advent.h" #include "dungeon.h" -static int fill(token_t, token_t); - -static void state_change(long obj, long state) -{ - game.prop[obj] = state; - pspeak(obj, change, state, true); -} +static int fill(verb_t, obj_t); static int attack(struct command_t *command) /* Attack. Assume target if unambiguous. "Throw" also links here. @@ -18,11 +12,10 @@ static int attack(struct command_t *command) * dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2 * enemies, or no enemies but 2 others. */ { - vocab_t verb = command->verb; - vocab_t obj = command->obj; + verb_t verb = command->verb; + obj_t obj = command->obj; - if (obj == NO_OBJECT || - obj == INTRANSITIVE) { + if (obj == INTRANSITIVE) { int changes = 0; if (atdwrf(game.loc) > 0) { obj = DWARF; @@ -49,7 +42,7 @@ static int attack(struct command_t *command) ++changes; } /* check for low-priority targets */ - if (obj == NO_OBJECT) { + if (obj == INTRANSITIVE) { /* Can't attack bird or machine by throwing axe. */ if (HERE(BIRD) && verb != THROW) { obj = BIRD; @@ -118,19 +111,22 @@ static int attack(struct command_t *command) } state_change(DRAGON, DRAGON_DEAD); game.prop[RUG] = RUG_FLOOR; - /* FIXME: Arithmetic on location values */ - int k = (objects[DRAGON].plac + objects[DRAGON].fixd) / 2; - move(DRAGON + NOBJECTS, -1); - move(RUG + NOBJECTS, 0); - move(DRAGON, k); - move(RUG, k); - drop(BLOOD, k); - for (obj = 1; obj <= NOBJECTS; obj++) { - if (game.place[obj] == objects[DRAGON].plac || - game.place[obj] == objects[DRAGON].fixd) - move(obj, k); - } - game.loc = k; + /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly. + * The way it was computed before was worse; it depended on the + * two dragon locations being LOC_SECRET4 and LOC_SECRET6 and + * LOC_SECRET5 being right between them. + */ + move(DRAGON + NOBJECTS, IS_FIXED); + move(RUG + NOBJECTS, IS_FREE); + move(DRAGON, LOC_SECRET5); + move(RUG, LOC_SECRET5); + drop(BLOOD, LOC_SECRET5); + 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); + } + game.loc = LOC_SECRET5; return GO_MOVE; } @@ -156,7 +152,7 @@ static int attack(struct command_t *command) } switch (obj) { - case NO_OBJECT: + case INTRANSITIVE: rspeak(NO_TARGET); break; case CLAM: @@ -179,33 +175,27 @@ static int attack(struct command_t *command) rspeak(ROCKY_TROLL); break; default: - rspeak(actions[verb].message); + speak(actions[verb].message); } return GO_CLEAROBJ; } -static int bigwords(token_t foo) +static int bigwords(long id) /* FEE FIE FOE FOO (AND FUM). Advance to next state if given in proper order. * 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). */ { - char word[TOKLEN + 1]; - packed_to_token(foo, word); - int k = (int) get_special_vocab_id(word); - if (game.foobar != 1 - k) { - if (game.foobar != 0 && game.loc == LOC_GIANTROOM) { - rspeak( START_OVER); - } else { - rspeak(NOTHING_HAPPENS); - } - return GO_CLEAROBJ; - } else { - game.foobar = k; - if (k != 4) { + 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)) { + game.foobar = id; + if ((id != FOO) && (id != FUM)) { rspeak(OK_MAN); return GO_CLEAROBJ; } - game.foobar = 0; + game.foobar = WORD_EMPTY; if (game.place[EGGS] == objects[EGGS].plac || (TOTING(EGGS) && game.loc == objects[EGGS].plac)) { rspeak(NOTHING_HAPPENS); @@ -215,55 +205,71 @@ static int bigwords(token_t foo) * crossing. */ if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID) game.prop[TROLL] = TROLL_PAIDONCE; - k = EGGS_DONE; + int k; if (HERE(EGGS)) k = EGGS_VANISHED; - if (game.loc == objects[EGGS].plac) + else if (game.loc == objects[EGGS].plac) k = EGGS_HERE; + else + k = EGGS_DONE; move(EGGS, objects[EGGS].plac); pspeak(EGGS, look, k, true); 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; } } static void blast(void) /* Blast. No effect unless you've got dynamite, which is a neat trick! */ { - if (game.prop[ROD2] < 0 || + if (game.prop[ROD2] == STATE_NOTFOUND || !game.closed) rspeak(REQUIRES_DYNAMITE); else { - game.bonus = VICTORY_MESSAGE; - if (game.loc == LOC_NE) - game.bonus = DEFEAT_MESSAGE; if (HERE(ROD2)) game.bonus = SPLATTER_MESSAGE; + else if (game.loc == LOC_NE) + game.bonus = DEFEAT_MESSAGE; + else + game.bonus = VICTORY_MESSAGE; rspeak(game.bonus); terminate(endgame); } } -static int vbreak(token_t verb, token_t obj) +static int vbreak(verb_t verb, obj_t obj) /* Break. Only works for mirror in repository and, of course, the vase. */ { - if (obj == MIRROR) { + switch (obj) { + case MIRROR: if (game.closed) { rspeak(BREAK_MIRROR); return GO_DWARFWAKE; } else { rspeak(TOO_FAR); - return GO_CLEAROBJ; + break; } + 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; + } + default: + speak(actions[verb].message); } - if (obj == VASE && game.prop[VASE] == VASE_WHOLE) { - if (TOTING(VASE)) - drop(VASE, game.loc); - state_change(VASE, VASE_BROKEN); - game.fixed[VASE] = -1; - return GO_CLEAROBJ; - } - rspeak(actions[verb].message); return (GO_CLEAROBJ); } @@ -276,14 +282,14 @@ static int brief(void) return GO_CLEAROBJ; } -static int vcarry(token_t verb, token_t obj) +static int vcarry(verb_t verb, obj_t obj) /* 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) { /* Carry, no object given yet. OK if only one object present. */ - if (game.atloc[game.loc] == 0 || + if (game.atloc[game.loc] == NO_OBJECT || game.link[game.atloc[game.loc]] != 0 || atdwrf(game.loc) > 0) return GO_UNKNOWN; @@ -291,7 +297,7 @@ static int vcarry(token_t verb, token_t obj) } if (TOTING(obj)) { - rspeak(ALREADY_CARRYING); + speak(actions[verb].message); return GO_CLEAROBJ; } @@ -301,8 +307,9 @@ static int vcarry(token_t verb, token_t obj) return GO_CLEAROBJ; } - if (game.fixed[obj] != 0) { - if (obj == PLANT && game.prop[PLANT] <= 0) { + if (game.fixed[obj] != IS_FREE) { + /* Next guard tests whether plant is tiny or stashed */ + if (obj == PLANT && game.prop[PLANT] <= PLANT_THIRSTY) { rspeak(DEEP_ROOTS); return GO_CLEAROBJ; } @@ -377,24 +384,23 @@ static int vcarry(token_t verb, token_t obj) } game.prop[BIRD] = BIRD_CAGED; } - /* FIXME: Arithmetic on state numbers */ if ((obj == BIRD || obj == CAGE) && (game.prop[BIRD] == BIRD_CAGED || STASHED(BIRD) == BIRD_CAGED)) + /* expression maps BIRD to CAGE and CAGE to BIRD */ carry(BIRD + CAGE - obj, game.loc); carry(obj, game.loc); if (obj == BOTTLE && LIQUID() != NO_OBJECT) game.place[LIQUID()] = CARRIED; - if (GSTONE(obj) && game.prop[obj] != 0) { - game.prop[obj] - = STATE_GROUND; + if (GSTONE(obj) && game.prop[obj] != STATE_FOUND) { + game.prop[obj] = STATE_FOUND; game.prop[CAVITY] = CAVITY_EMPTY; } rspeak(OK_MAN); return GO_CLEAROBJ; } -static int chain(token_t verb) +static int chain(verb_t verb) /* Do something to the bear's chain */ { if (verb != LOCK) { @@ -407,16 +413,21 @@ static int chain(token_t verb) return GO_CLEAROBJ; } game.prop[CHAIN] = CHAIN_HEAP; - game.fixed[CHAIN] = CHAIN_HEAP; + game.fixed[CHAIN] = IS_FREE; if (game.prop[BEAR] != BEAR_DEAD) game.prop[BEAR] = CONTENTED_BEAR; switch (game.prop[BEAR]) { case BEAR_DEAD: - game.fixed[BEAR] = -1; + // LCOV_EXCL_START + /* Can't be reached as long as the only way for the bear to die + * is from a bridge collapse. Leave in in case this changes, but + * exclude from coverage testing. */ + game.fixed[BEAR] = IS_FIXED; break; + // LCOV_EXCL_STOP default: - game.fixed[BEAR] = 0; + game.fixed[BEAR] = IS_FREE; } rspeak(CHAIN_UNLOCKED); return GO_CLEAROBJ; @@ -435,80 +446,76 @@ static int chain(token_t verb) if (TOTING(CHAIN)) drop(CHAIN, game.loc); - game.fixed[CHAIN] = -1; + game.fixed[CHAIN] = IS_FIXED; rspeak(CHAIN_LOCKED); return GO_CLEAROBJ; } -static int discard(token_t verb, token_t obj, bool just_do_it) +static int discard(verb_t verb, obj_t obj) /* 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. */ { - if (!just_do_it) { - if (TOTING(ROD2) && obj == ROD && !TOTING(ROD)) - obj = ROD2; - if (!TOTING(obj)) { - rspeak(actions[verb].message); - return GO_CLEAROBJ; - } - if (obj == BIRD && 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 if ((GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL)) { - rspeak(GEM_FITS); - game.prop[obj] = 1; - game.prop[CAVITY] = CAVITY_FULL; - if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) || - (obj == RUBY && game.prop[RUG] == RUG_HOVER))) { - int spk = RUG_RISES; - if (TOTING(RUG)) - spk = RUG_WIGGLES; - if (obj == RUBY) - spk = RUG_SETTLES; - rspeak(spk); - if (spk != RUG_WIGGLES) { - /* FIXME: Arithmetic on state numbers */ - int k = 2 - game.prop[RUG]; - game.prop[RUG] = k; - if (k == 2) - k = objects[SAPPH].plac; - move(RUG + NOBJECTS, k); - } + if (TOTING(ROD2) && obj == ROD && !TOTING(ROD)) + obj = ROD2; + if (!TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } + if (obj == BIRD && 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 if ((GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL)) { + 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) + rspeak(RUG_SETTLES); + else if (TOTING(RUG)) + rspeak(RUG_WIGGLES); + else + rspeak(RUG_RISES); + if (!TOTING(RUG) || obj == RUBY) { + 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); } - } else if (obj == COINS && HERE(VEND)) { - DESTROY(COINS); - drop(BATTERY, game.loc); - pspeak(BATTERY, look, FRESH_BATTERIES, true); - return GO_CLEAROBJ; - } else if (obj == BIRD && AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) { - rspeak(BIRD_BURNT); - DESTROY(BIRD); - return GO_CLEAROBJ; - } else if (obj == BEAR && AT(TROLL)) { - state_change(TROLL, TROLL_GONE); - move(TROLL, LOC_NOWHERE); - move(TROLL + NOBJECTS, LOC_NOWHERE); - move(TROLL2, objects[TROLL].plac); - move(TROLL2 + NOBJECTS, objects[TROLL].fixd); - juggle(CHASM); - } else if (obj != VASE || - game.loc == objects[PILLOW].plac) { - rspeak(OK_MAN); - } else { - game.prop[VASE] = VASE_BROKEN; - if (AT(PILLOW)) - game.prop[VASE] = VASE_WHOLE; - pspeak(VASE, look, game.prop[VASE] + 1, true); - if (game.prop[VASE] != VASE_WHOLE) - game.fixed[VASE] = -1; } + } else if (obj == COINS && HERE(VEND)) { + DESTROY(COINS); + drop(BATTERY, game.loc); + pspeak(BATTERY, look, FRESH_BATTERIES, true); + return GO_CLEAROBJ; + } else if (obj == BIRD && AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) { + rspeak(BIRD_BURNT); + DESTROY(BIRD); + return GO_CLEAROBJ; + } else if (obj == BEAR && AT(TROLL)) { + state_change(TROLL, TROLL_GONE); + move(TROLL, LOC_NOWHERE); + move(TROLL + NOBJECTS, IS_FREE); + move(TROLL2, objects[TROLL].plac); + move(TROLL2 + NOBJECTS, objects[TROLL].fixd); + juggle(CHASM); + } else if (obj != VASE || + game.loc == objects[PILLOW].plac) { + rspeak(OK_MAN); + } else { + state_change(VASE, AT(PILLOW) + ? VASE_WHOLE + : VASE_DROPPED); + if (game.prop[VASE] != VASE_WHOLE) + game.fixed[VASE] = IS_FIXED; } int k = LIQUID(); if (k == obj) @@ -526,11 +533,11 @@ static int discard(token_t verb, token_t obj, bool just_do_it) return GO_CLEAROBJ; } -static int drink(token_t verb, token_t obj) +static int drink(verb_t verb, obj_t obj) /* 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. */ { - if (obj == NO_OBJECT && LIQLOC(game.loc) != WATER && + if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER && (LIQUID() != WATER || !HERE(BOTTLE))) { return GO_UNKNOWN; } @@ -542,54 +549,50 @@ static int drink(token_t verb, token_t obj) return GO_CLEAROBJ; } - if (obj != NO_OBJECT && obj != WATER) { + if (obj != INTRANSITIVE && obj != WATER) { rspeak(RIDICULOUS_ATTEMPT); return GO_CLEAROBJ; } if (LIQUID() == WATER && HERE(BOTTLE)) { - game.prop[BOTTLE] = EMPTY_BOTTLE; game.place[WATER] = LOC_NOWHERE; - rspeak(BOTTLE_EMPTY); + state_change(BOTTLE, EMPTY_BOTTLE); return GO_CLEAROBJ; } - rspeak(actions[verb].message); + speak(actions[verb].message); return GO_CLEAROBJ; } -static int eat(token_t verb, token_t obj) +static int eat(verb_t verb, obj_t obj) /* Eat. Intransitive: assume food if present, else ask what. Transitive: food * ok, some things lose appetite, rest are ridiculous. */ { - if (obj == INTRANSITIVE) { + switch (obj) { + case INTRANSITIVE: if (!HERE(FOOD)) return GO_UNKNOWN; + case FOOD: DESTROY(FOOD); rspeak(THANKS_DELICIOUS); - return GO_CLEAROBJ; - } - if (obj == FOOD) { - DESTROY(FOOD); - rspeak(THANKS_DELICIOUS); - return GO_CLEAROBJ; - } - if (obj == BIRD || - obj == SNAKE || - obj == CLAM || - obj == OYSTER || - obj == DWARF || - obj == DRAGON || - obj == TROLL || - obj == BEAR || - obj == OGRE) { + break; + case BIRD: + case SNAKE: + case CLAM: + case OYSTER: + case DWARF: + case DRAGON: + case TROLL: + case BEAR: + case OGRE: rspeak(LOST_APPETITE); - return GO_CLEAROBJ; + break; + default: + speak(actions[verb].message); } - rspeak(actions[verb].message); return GO_CLEAROBJ; } -static int extinguish(token_t verb, int obj) +static int extinguish(verb_t verb, obj_t obj) /* Extinguish. Lamp, urn, dragon/volcano (nice try). */ { if (obj == INTRANSITIVE) { @@ -601,81 +604,91 @@ static int extinguish(token_t verb, int obj) return GO_UNKNOWN; } - if (obj == URN) { + switch (obj) { + case URN: if (game.prop[URN] != URN_EMPTY) { state_change(URN, URN_DARK); } else { pspeak(URN, change, URN_DARK, true); } - return GO_CLEAROBJ; - } - - if (obj == LAMP) { + break; + case LAMP: state_change(LAMP, LAMP_DARK); rspeak(DARK(game.loc) ? PITCH_DARK : NO_MESSAGE); - return GO_CLEAROBJ; - } - - if (obj == DRAGON || - obj == VOLCANO) { + break; + case DRAGON: + case VOLCANO: rspeak(BEYOND_POWER); - return GO_CLEAROBJ; + break; + default: + speak(actions[verb].message); } - - rspeak(actions[verb].message); return GO_CLEAROBJ; } -static int feed(token_t verb, token_t obj) +static int feed(verb_t verb, obj_t obj) /* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf, make him * mad. Bear, special. */ { - int spk = actions[verb].message; - if (obj == BIRD) { + switch (obj) { + case BIRD: rspeak(BIRD_PINING); - return GO_CLEAROBJ; - } else if (obj == SNAKE || - obj == DRAGON || - obj == TROLL) { - spk = NOTHING_EDIBLE; - if (obj == DRAGON && game.prop[DRAGON] != DRAGON_BARS) - spk = RIDICULOUS_ATTEMPT; - if (obj == TROLL) - spk = TROLL_VICES; - if (obj == SNAKE && !game.closed && HERE(BIRD)) { + break; + case DRAGON: + if (game.prop[DRAGON] != DRAGON_BARS) + rspeak(RIDICULOUS_ATTEMPT); + else + rspeak(NOTHING_EDIBLE); + break; + case SNAKE: + if (!game.closed && HERE(BIRD)) { DESTROY(BIRD); - spk = BIRD_DEVOURED; - } - } else if (obj == DWARF) { + rspeak(BIRD_DEVOURED); + } else + rspeak(NOTHING_EDIBLE); + break; + case TROLL: + rspeak(TROLL_VICES); + break; + case DWARF: if (HERE(FOOD)) { game.dflag += 2; - spk = REALLY_MAD; + rspeak(REALLY_MAD); + } else + speak(actions[verb].message); + break; + case BEAR: + if (game.prop[BEAR] == BEAR_DEAD) { + rspeak(RIDICULOUS_ATTEMPT); + break; } - } else if (obj == BEAR) { - if (game.prop[BEAR] == UNTAMED_BEAR) - spk = NOTHING_EDIBLE; - if (game.prop[BEAR] == BEAR_DEAD) - spk = RIDICULOUS_ATTEMPT; - if (HERE(FOOD)) { - DESTROY(FOOD); - game.prop[BEAR] = SITTING_BEAR; - game.fixed[AXE] = 0; - game.prop[AXE] = 0; - spk = BEAR_TAMED; + 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); + break; } - } else if (obj == OGRE) { + speak(actions[verb].message); + break; + case OGRE: if (HERE(FOOD)) - spk = OGRE_FULL; - } else { - spk = AM_GAME; + rspeak(OGRE_FULL); + else + speak(actions[verb].message); + break; + default: + rspeak(AM_GAME); } - rspeak(spk); return GO_CLEAROBJ; } -int fill(token_t verb, token_t obj) +int fill(verb_t verb, obj_t obj) /* Fill. Bottle or urn must be empty, and liquid available. (Vase * is nasty.) */ { @@ -690,8 +703,9 @@ int fill(token_t verb, token_t obj) } rspeak(SHATTER_VASE); game.prop[VASE] = VASE_BROKEN; - game.fixed[VASE] = -1; - return (discard(verb, VASE, true)); + game.fixed[VASE] = IS_FIXED; + drop(VASE, game.loc); + return GO_CLEAROBJ; } if (obj == URN) { @@ -722,11 +736,11 @@ int fill(token_t verb, token_t obj) game.place[k] = LOC_NOWHERE; return GO_CLEAROBJ; } - if (obj != NO_OBJECT && obj != BOTTLE) { - rspeak(actions[verb].message); + if (obj != INTRANSITIVE && obj != BOTTLE) { + speak(actions[verb].message); return GO_CLEAROBJ; } - if (obj == NO_OBJECT && !HERE(BOTTLE)) + if (obj == INTRANSITIVE && !HERE(BOTTLE)) return GO_UNKNOWN; if (HERE(URN) && game.prop[URN] != URN_EMPTY) { @@ -742,17 +756,15 @@ int fill(token_t verb, token_t obj) return GO_CLEAROBJ; } - game.prop[BOTTLE] = (LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE; + state_change(BOTTLE, (LIQLOC(game.loc) == OIL) + ? OIL_BOTTLE + : WATER_BOTTLE); if (TOTING(BOTTLE)) game.place[LIQUID()] = CARRIED; - if (LIQUID() == OIL) - rspeak(BOTTLED_OIL); - else - rspeak(BOTTLED_WATER); return GO_CLEAROBJ; } -static int find(token_t verb, token_t obj) +static int find(verb_t verb, obj_t obj) /* Find. Might be carrying it, or it might be here. Else give caveat. */ { if (TOTING(obj)) { @@ -774,11 +786,11 @@ static int find(token_t verb, token_t obj) } - rspeak(actions[verb].message); + speak(actions[verb].message); return GO_CLEAROBJ; } -static int fly(token_t verb, token_t obj) +static int fly(verb_t verb, obj_t obj) /* Fly. Snide remarks unless hovering rug is here. */ { if (obj == INTRANSITIVE) { @@ -794,7 +806,7 @@ static int fly(token_t verb, token_t obj) } if (obj != RUG) { - rspeak(actions[verb].message); + speak(actions[verb].message); return GO_CLEAROBJ; } if (game.prop[RUG] != RUG_HOVER) { @@ -803,13 +815,13 @@ static int fly(token_t verb, token_t obj) } game.oldlc2 = game.oldloc; game.oldloc = game.loc; - /* FIXME: Arithmetic on location values */ - game.newloc = game.place[RUG] + game.fixed[RUG] - game.loc; - if (game.prop[SAPPH] >= 0) { - rspeak(RUG_RETURNS); - } else { + if (game.prop[SAPPH] == STATE_NOTFOUND) { + game.newloc = game.place[SAPPH]; rspeak(RUG_GOES); + } else { + game.newloc = LOC_CLIFF; + rspeak(RUG_RETURNS); } return GO_TERMINATE; } @@ -818,7 +830,7 @@ static int inven(void) /* Inventory. If object, treat same as find. Else report on current burden. */ { bool empty = true; - for (int i = 1; i <= NOBJECTS; i++) { + for (obj_t i = 1; i <= NOBJECTS; i++) { if (i == BEAR || !TOTING(i)) continue; @@ -835,44 +847,46 @@ static int inven(void) return GO_CLEAROBJ; } -static int light(token_t verb, token_t obj) +static int light(verb_t verb, obj_t obj) /* Light. Applicable only to lamp and urn. */ { if (obj == INTRANSITIVE) { - if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0) + int selects = 0; + if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0) { obj = LAMP; - if (HERE(URN) && game.prop[URN] == URN_DARK) + selects++; + } + if (HERE(URN) && game.prop[URN] == URN_DARK) { obj = URN; - if (obj == INTRANSITIVE || - (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0 && - HERE(URN) && game.prop[URN] == URN_DARK)) + selects++; + } + if (selects != 1) return GO_UNKNOWN; } - if (obj == URN) { + switch (obj) { + case URN: state_change(URN, game.prop[URN] == URN_EMPTY ? URN_EMPTY : URN_LIT); - return GO_CLEAROBJ; - } else { - if (obj != LAMP) { - rspeak(actions[verb].message); - return GO_CLEAROBJ; - } + break; + case LAMP: if (game.limit < 0) { rspeak(LAMP_OUT); - return GO_CLEAROBJ; + break; } state_change(LAMP, LAMP_BRIGHT); if (game.wzdark) return GO_TOP; - else - return GO_CLEAROBJ; + break; + default: + speak(actions[verb].message); } + return GO_CLEAROBJ; } static int listen(void) -/* Listen. Intransitive only. Print stuff based on objsnd/locsnd. */ +/* Listen. Intransitive only. Print stuff based on object sound proprties. */ { long sound = locations[game.loc].sound; if (sound != SILENT) { @@ -881,19 +895,23 @@ static int listen(void) rspeak(NO_MESSAGE); return GO_CLEAROBJ; } - for (int i = 1; i <= NOBJECTS; i++) { + for (obj_t i = 1; i <= NOBJECTS; i++) { if (!HERE(i) || objects[i].sounds[0] == NULL || game.prop[i] < 0) continue; int mi = game.prop[i]; + /* (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. */ if (i == BIRD) mi += 3 * game.blooded; long packed_zzword = token_to_packed(game.zzword); pspeak(i, hear, mi, true, packed_zzword); rspeak(NO_MESSAGE); - /* FIXME: Magic number, sensitive to bird state logic */ - if (i == BIRD && game.prop[i] == 5) + if (i == BIRD && mi == BIRD_ENDSTATE) DESTROY(BIRD); return GO_CLEAROBJ; } @@ -901,7 +919,7 @@ static int listen(void) return GO_CLEAROBJ; } -static int lock(token_t verb, token_t obj) +static int lock(verb_t verb, obj_t obj) /* Lock, unlock, no object given. Assume various things if present. */ { if (obj == INTRANSITIVE) { @@ -923,29 +941,29 @@ static int lock(token_t verb, token_t obj) /* Lock, unlock object. Special stuff for opening clam/oyster * and for chain. */ - if (obj == GRATE || - obj == CHAIN) { + + switch (obj) { + case CHAIN: + if (HERE(KEYS)) { + return chain(verb); + } else + rspeak(NO_KEYS); + break; + case GRATE: if (HERE(KEYS)) { - if (obj == CHAIN) - return chain(verb); if (game.closng) { rspeak(EXIT_CLOSED); if (!game.panic) game.clock2 = PANICTIME; game.panic = true; - return GO_CLEAROBJ ; } else { state_change(GRATE, (verb == LOCK) ? GRATE_CLOSED : GRATE_OPEN); - return GO_CLEAROBJ; } - } - rspeak(NO_KEYS); - return GO_CLEAROBJ; - } - - switch (obj) { + } else + rspeak(NO_KEYS); + break; case CLAM: if (verb == LOCK) rspeak(HUH_MAN); @@ -957,14 +975,13 @@ static int lock(token_t verb, token_t obj) drop(PEARL, LOC_CULDESAC); rspeak(PEARL_FALLS); } - return GO_CLEAROBJ; + break; case OYSTER: if (verb == LOCK) rspeak(HUH_MAN); else rspeak(OYSTER_OPENER); - - return GO_CLEAROBJ; + break; case DOOR: rspeak((game.prop[DOOR] == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR); break; @@ -975,23 +992,23 @@ static int lock(token_t verb, token_t obj) rspeak(CANNOT_UNLOCK); break; default: - rspeak(actions[verb].message); + speak(actions[verb].message); } return GO_CLEAROBJ; } -static int pour(token_t verb, token_t obj) +static int pour(verb_t verb, obj_t obj) /* Pour. If no object, or object is bottle, assume contents of bottle. * special tests for pouring water or oil on plant or rusty door. */ { if (obj == BOTTLE || - obj == NO_OBJECT) + obj == INTRANSITIVE) obj = LIQUID(); if (obj == NO_OBJECT) return GO_UNKNOWN; if (!TOTING(obj)) { - rspeak(actions[verb].message); + speak(actions[verb].message); return GO_CLEAROBJ; } @@ -1038,13 +1055,13 @@ static int read(struct command_t command) /* Read. Print stuff based on objtxt. Oyster (?) is special case. */ { if (command.obj == INTRANSITIVE) { - command.obj = 0; + command.obj = NO_OBJECT; for (int i = 1; i <= NOBJECTS; i++) { if (HERE(i) && objects[i].texts[0] != NULL && game.prop[i] >= 0) command.obj = command.obj * NOBJECTS + i; } if (command.obj > NOBJECTS || - command.obj == 0 || + command.obj == NO_OBJECT || DARK(game.loc)) return GO_UNKNOWN; } @@ -1054,8 +1071,8 @@ static int read(struct command_t command) } else if (command.obj == OYSTER && !game.clshnt && game.closed) { game.clshnt = yes(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]); } else if (objects[command.obj].texts[0] == NULL || - game.prop[command.obj] < 0) { - rspeak(actions[command.verb].message); + game.prop[command.obj] == STATE_NOTFOUND) { + speak(actions[command.verb].message); } else pspeak(command.obj, study, game.prop[command.obj], true); return GO_CLEAROBJ; @@ -1064,8 +1081,7 @@ static int read(struct command_t command) static int reservoir(void) /* Z'ZZZ (word gets recomputed at startup; different each game). */ { - /* FIXME: Arithmetic on state numbers */ - if (!AT(RESER) && game.loc != game.fixed[RESER] - 1) { + if (!AT(RESER) && game.loc != LOC_RESBOTTOM) { rspeak(NOTHING_HAPPENS); return GO_CLEAROBJ; } else { @@ -1082,7 +1098,7 @@ static int reservoir(void) } } -static int rub(token_t verb, token_t obj) +static int rub(verb_t verb, obj_t obj) /* Rub. Yields various snide remarks except for lit urn. */ { if (obj == URN && game.prop[URN] == URN_LIT) { @@ -1095,7 +1111,7 @@ static int rub(token_t verb, token_t obj) } else if (obj != LAMP) { rspeak(PECULIAR_NOTHING); } else { - rspeak(actions[verb].message); + speak(actions[verb].message); } return GO_CLEAROBJ; } @@ -1105,17 +1121,22 @@ static int say(struct command_t *command) { if (command->wd2 > 0) { command->wd1 = command->wd2; - strcpy(command->raw1, command->raw2); + strncpy(command->raw1, command->raw2, LINESIZE - 1); } char word1[TOKLEN + 1]; packed_to_token(command->wd1, word1); - int wd = (int) get_vocab_id(word1); - /* FIXME: magic numbers */ - if (wd == MOTION_WORD(XYZZY) || - wd == MOTION_WORD(PLUGH) || - wd == MOTION_WORD(PLOVER) || - wd == ACTION_WORD(GIANTWORDS) || - wd == ACTION_WORD(PART)) { + long wd; + enum wordtype type; + get_vocab_metadata(word1, &wd, &type); + if (wd == XYZZY || + wd == PLUGH || + wd == PLOVER || + wd == FEE || + wd == FIE || + wd == FOE || + wd == FOO || + wd == FUM || + wd == PART) { /* FIXME: scribbles on the interpreter's command block */ wordclear(&command->wd2); return GO_LOOKUP; @@ -1137,17 +1158,15 @@ static int throw (struct command_t *command) * (Only way to do so!) Axe also special for dragon, bear, and * troll. Treasures special for troll. */ { - if (TOTING(ROD2) && command->obj == ROD && !TOTING(ROD)) - command->obj = ROD2; if (!TOTING(command->obj)) { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } if (objects[command->obj].is_treasure && AT(TROLL)) { /* Snarf a treasure for the troll. */ drop(command->obj, LOC_NOWHERE); move(TROLL, LOC_NOWHERE); - move(TROLL + NOBJECTS, LOC_NOWHERE); + move(TROLL + NOBJECTS, IS_FREE); drop(TROLL2, objects[TROLL].plac); drop(TROLL2 + NOBJECTS, objects[TROLL].fixd); juggle(CHASM); @@ -1160,24 +1179,24 @@ static int throw (struct command_t *command) return (feed(command->verb, command->obj)); } if (command->obj != AXE) - return (discard(command->verb, command->obj, false)); + return (discard(command->verb, command->obj)); else { if (atdwrf(game.loc) <= 0) { if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) return throw_support(DRAGON_SCALES); if (AT(TROLL)) return throw_support(TROLL_RETURNS); - else if (AT(OGRE)) + if (AT(OGRE)) return throw_support(OGRE_DODGE); - else if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) { + if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) { /* This'll teach him to throw the axe at the bear! */ drop(AXE, game.loc); - game.fixed[AXE] = -1; + game.fixed[AXE] = IS_FIXED; juggle(BEAR); state_change(AXE, AXE_LOST); return GO_CLEAROBJ; } - command->obj = NO_OBJECT; + command->obj = INTRANSITIVE; return (attack(command)); } @@ -1194,12 +1213,12 @@ static int throw (struct command_t *command) } } -static int wake(token_t verb, token_t obj) +static int wake(verb_t verb, obj_t obj) /* Wake. Only use is to disturb the dwarves. */ { if (obj != DWARF || !game.closed) { - rspeak(actions[verb].message); + speak(actions[verb].message); return GO_CLEAROBJ; } else { rspeak(PROD_DWARF); @@ -1207,7 +1226,7 @@ static int wake(token_t verb, token_t obj) } } -static int wave(token_t verb, token_t obj) +static int wave(verb_t verb, obj_t obj) /* Wave. No effect unless waving rod at fissure or at bird. */ { if (obj != ROD || @@ -1215,16 +1234,16 @@ static int wave(token_t verb, token_t obj) (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) { - rspeak(((!TOTING(obj)) && (obj != ROD || - !TOTING(ROD2))) ? - ARENT_CARRYING : - actions[verb].message); + speak(((!TOTING(obj)) && (obj != ROD || + !TOTING(ROD2))) ? + arbitrary_messages[ARENT_CARRYING] : + actions[verb].message); return GO_CLEAROBJ; } - if (game.prop[BIRD] == BIRD_UNCAGED && game.loc == game.place[STEPS] && game.prop[JADE] < 0) { + if (game.prop[BIRD] == BIRD_UNCAGED && game.loc == game.place[STEPS] && game.prop[JADE] == STATE_NOTFOUND) { drop(JADE, game.loc); - game.prop[JADE] = 0; + game.prop[JADE] = STATE_FOUND; --game.tally; rspeak(NECKLACE_FLY); return GO_CLEAROBJ; @@ -1286,10 +1305,10 @@ int action(struct command_t *command) else if ((LIQUID() == command->obj && HERE(BOTTLE)) || command->obj == LIQLOC(game.loc)) /* FALL THROUGH */; - else if (command->obj == OIL && HERE(URN) && game.prop[URN] != 0) { + else if (command->obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) { command->obj = URN; /* FALL THROUGH */; - } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) { + } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) { command->obj = PLANT2; /* FALL THROUGH */; } else if (command->obj == KNIFE && game.knfloc == game.loc) { @@ -1346,17 +1365,18 @@ int action(struct command_t *command) case TAME: return GO_UNKNOWN; case GO: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case ATTACK: + command->obj = INTRANSITIVE; return attack(command); case POUR: - return pour(command->verb, command->obj); + return pour(command->verb, INTRANSITIVE); case EAT: return eat(command->verb, INTRANSITIVE); case DRINK: - return drink(command->verb, command->obj); + return drink(command->verb, INTRANSITIVE); case RUB: return GO_UNKNOWN; case THROW: @@ -1370,15 +1390,19 @@ int action(struct command_t *command) case FEED: return GO_UNKNOWN; case FILL: - return fill(command->verb, command->obj); + return fill(command->verb, INTRANSITIVE); case BLAST: blast(); return GO_CLEAROBJ; case SCORE: score(scoregame); return GO_CLEAROBJ; - case GIANTWORDS: - return bigwords(command->wd1); + case FEE: + case FIE: + case FOE: + case FOO: + case FUM: + return bigwords(command->id1); case BRIEF: return brief(); case READ: @@ -1409,7 +1433,7 @@ int action(struct command_t *command) case CARRY: return vcarry(command->verb, command->obj); case DROP: - return discard(command->verb, command->obj, false); + return discard(command->verb, command->obj); case SAY: return say(command); case UNLOCK: @@ -1427,11 +1451,11 @@ int action(struct command_t *command) case WAVE: return wave(command->verb, command->obj); case TAME: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case GO: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case ATTACK: @@ -1447,7 +1471,7 @@ int action(struct command_t *command) case THROW: return throw (command); case QUIT: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case FIND: @@ -1462,15 +1486,19 @@ int action(struct command_t *command) blast(); return GO_CLEAROBJ; case SCORE: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } - case GIANTWORDS: { - rspeak(actions[command->verb].message); + case FEE: + case FIE: + case FOE: + case FOO: + case FUM: { + speak(actions[command->verb].message); return GO_CLEAROBJ; } case BRIEF: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case READ: @@ -1480,17 +1508,17 @@ int action(struct command_t *command) case WAKE: return wake(command->verb, command->obj); case SAVE: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case RESUME: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case FLY: return fly(command->verb, command->obj); case LISTEN: { - rspeak(actions[command->verb].message); + speak(actions[command->verb].message); return GO_CLEAROBJ; } case PART: