X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=actions.c;h=40a1928f62d99299970e9a07fef2775cd59196f1;hp=a384e61431e96f1531397f159e2e2e723bcd27d5;hb=49d9ef1bee7b11055b7a46e2c2b45bd888e1ccef;hpb=da6a090b833070da1726a92d5db3b476c2313050 diff --git a/actions.c b/actions.c index a384e61..40a1928 100644 --- a/actions.c +++ b/actions.c @@ -4,7 +4,7 @@ #include "advent.h" #include "dungeon.h" -static int fill(token_t, token_t); +static int fill(verb_t, obj_t); static int attack(struct command_t *command) /* Attack. Assume target if unambiguous. "Throw" also links here. @@ -12,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; + 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; @@ -43,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; @@ -113,19 +112,19 @@ static int attack(struct command_t *command) state_change(DRAGON, DRAGON_DEAD); game.prop[RUG] = RUG_FLOOR; /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly. - * The way it was computed before was wirse; it depended on the + * 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, -1); - move(RUG + NOBJECTS, LOC_NOWHERE); + move(DRAGON + NOBJECTS, IS_FIXED); + move(RUG + NOBJECTS, IS_FREE); move(DRAGON, LOC_SECRET5); move(RUG, LOC_SECRET5); drop(BLOOD, LOC_SECRET5); - for (obj = 1; obj <= NOBJECTS; obj++) { - if (game.place[obj] == objects[DRAGON].plac || - game.place[obj] == objects[DRAGON].fixd) - move(obj, 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; @@ -153,7 +152,7 @@ static int attack(struct command_t *command) } switch (obj) { - case NO_OBJECT: + case INTRANSITIVE: rspeak(NO_TARGET); break; case CLAM: @@ -186,13 +185,13 @@ static int bigwords(long id) * 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). */ { - if ((game.foobar == WORD_EMPTY && id == ACTION_WORD(FEE)) || - (game.foobar == ACTION_WORD(FEE) && id == ACTION_WORD(FIE)) || - (game.foobar == ACTION_WORD(FIE) && id == ACTION_WORD(FOE)) || - (game.foobar == ACTION_WORD(FOE) && id == ACTION_WORD(FOO)) || - (game.foobar == ACTION_WORD(FOE) && id == ACTION_WORD(FUM))) { + 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 != ACTION_WORD(FOO)) && (id != ACTION_WORD(FUM))) { + if ((id != FOO) && (id != FUM)) { rspeak(OK_MAN); return GO_CLEAROBJ; } @@ -206,11 +205,13 @@ static int bigwords(long id) * crossing. */ if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID) game.prop[TROLL] = TROLL_PAIDONCE; - int 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; @@ -231,40 +232,44 @@ static int bigwords(long id) 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(vocab_t verb, obj_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] = IS_FIXED; - return GO_CLEAROBJ; - } - speak(actions[verb].message); return (GO_CLEAROBJ); } @@ -277,14 +282,14 @@ static int brief(void) return GO_CLEAROBJ; } -static int vcarry(token_t verb, obj_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; @@ -292,7 +297,7 @@ static int vcarry(token_t verb, obj_t obj) } if (TOTING(obj)) { - rspeak(ALREADY_CARRYING); + speak(actions[verb].message); return GO_CLEAROBJ; } @@ -395,7 +400,7 @@ static int vcarry(token_t verb, obj_t obj) 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) { @@ -414,8 +419,13 @@ static int chain(token_t verb) switch (game.prop[BEAR]) { case BEAR_DEAD: + // 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] = IS_FREE; } @@ -442,7 +452,7 @@ static int chain(token_t verb) return GO_CLEAROBJ; } -static int discard(token_t verb, obj_t obj) +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. */ @@ -467,13 +477,13 @@ static int discard(token_t verb, obj_t obj) 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) { + 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) @@ -493,7 +503,7 @@ static int discard(token_t verb, obj_t obj) } else if (obj == BEAR && AT(TROLL)) { state_change(TROLL, TROLL_GONE); move(TROLL, LOC_NOWHERE); - move(TROLL + NOBJECTS, LOC_NOWHERE); + move(TROLL + NOBJECTS, IS_FREE); move(TROLL2, objects[TROLL].plac); move(TROLL2 + NOBJECTS, objects[TROLL].fixd); juggle(CHASM); @@ -523,11 +533,11 @@ static int discard(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int drink(token_t verb, obj_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; } @@ -539,7 +549,7 @@ static int drink(token_t verb, obj_t obj) return GO_CLEAROBJ; } - if (obj != NO_OBJECT && obj != WATER) { + if (obj != INTRANSITIVE && obj != WATER) { rspeak(RIDICULOUS_ATTEMPT); return GO_CLEAROBJ; } @@ -553,7 +563,7 @@ static int drink(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int eat(token_t verb, obj_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. */ { @@ -582,7 +592,7 @@ static int eat(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int extinguish(token_t verb, obj_t obj) +static int extinguish(verb_t verb, obj_t obj) /* Extinguish. Lamp, urn, dragon/volcano (nice try). */ { if (obj == INTRANSITIVE) { @@ -618,7 +628,7 @@ static int extinguish(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int feed(token_t verb, obj_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. */ { @@ -678,7 +688,7 @@ static int feed(token_t verb, obj_t obj) return GO_CLEAROBJ; } -int fill(token_t verb, obj_t obj) +int fill(verb_t verb, obj_t obj) /* Fill. Bottle or urn must be empty, and liquid available. (Vase * is nasty.) */ { @@ -726,11 +736,11 @@ int fill(token_t verb, obj_t obj) game.place[k] = LOC_NOWHERE; return GO_CLEAROBJ; } - if (obj != NO_OBJECT && obj != BOTTLE) { + 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) { @@ -754,7 +764,7 @@ int fill(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int find(token_t verb, obj_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)) { @@ -780,7 +790,7 @@ static int find(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int fly(token_t verb, obj_t obj) +static int fly(verb_t verb, obj_t obj) /* Fly. Snide remarks unless hovering rug is here. */ { if (obj == INTRANSITIVE) { @@ -837,7 +847,7 @@ static int inven(void) return GO_CLEAROBJ; } -static int light(token_t verb, obj_t obj) +static int light(verb_t verb, obj_t obj) /* Light. Applicable only to lamp and urn. */ { if (obj == INTRANSITIVE) { @@ -891,7 +901,11 @@ static int listen(void) game.prop[i] < 0) continue; int mi = game.prop[i]; - /* FIXME: Weird magic on object states */ + /* (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); @@ -905,7 +919,7 @@ static int listen(void) return GO_CLEAROBJ; } -static int lock(token_t verb, obj_t obj) +static int lock(verb_t verb, obj_t obj) /* Lock, unlock, no object given. Assume various things if present. */ { if (obj == INTRANSITIVE) { @@ -984,12 +998,12 @@ static int lock(token_t verb, obj_t obj) return GO_CLEAROBJ; } -static int pour(token_t verb, obj_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; @@ -1084,7 +1098,7 @@ static int reservoir(void) } } -static int rub(token_t verb, obj_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) { @@ -1107,20 +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); - if (wd == MOTION_WORD(XYZZY) || - wd == MOTION_WORD(PLUGH) || - wd == MOTION_WORD(PLOVER) || - wd == ACTION_WORD(FEE) || - wd == ACTION_WORD(FIE) || - wd == ACTION_WORD(FOE) || - wd == ACTION_WORD(FOO) || - wd == ACTION_WORD(FUM) || - 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; @@ -1150,7 +1166,7 @@ static int throw (struct command_t *command) /* 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); @@ -1180,7 +1196,7 @@ static int throw (struct command_t *command) state_change(AXE, AXE_LOST); return GO_CLEAROBJ; } - command->obj = NO_OBJECT; + command->obj = INTRANSITIVE; return (attack(command)); } @@ -1197,7 +1213,7 @@ static int throw (struct command_t *command) } } -static int wake(token_t verb, obj_t obj) +static int wake(verb_t verb, obj_t obj) /* Wake. Only use is to disturb the dwarves. */ { if (obj != DWARF || @@ -1210,7 +1226,7 @@ static int wake(token_t verb, obj_t obj) } } -static int wave(token_t verb, obj_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 || @@ -1353,13 +1369,14 @@ int action(struct command_t *command) 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: @@ -1373,7 +1390,7 @@ 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;