WD* globals abolished. They're now members of the command block.

This commit is contained in:
Eric S. Raymond 2017-06-20 20:06:32 -04:00
parent 9714c9fc2a
commit 9437ccca36
4 changed files with 124 additions and 126 deletions

4
TODO
View file

@ -3,10 +3,6 @@
The FORTRANish mess that once was is now mostly idiomatic C. Some issues
remain to be cleaned up:
* The remaining shared globals: WD1, WD1X, WD2, WD2X. These are used
rather promiscuously to pass around information that ought to be function
arguments in a modern language.
* Remaining unstructured gotos in do_command().
* The program is still pretty much typeless - full of magic numbers being

203
actions.c
View file

@ -4,17 +4,16 @@
#include "database.h"
#include "newdb.h"
/* Limit visibility of ugly globals. Eventually these should go away. */
extern token_t WD1, WD1X, WD2, WD2X;
static int fill(token_t, token_t);
static int attack(FILE *input, token_t verb, token_t obj)
static int attack(FILE *input, struct command_t *command)
/* 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. */
{
vocab_t verb = command->verb;
vocab_t obj = command->obj;
int spk = ACTSPK[verb];
if (obj == 0 || obj == INTRANSITIVE) {
if (ATDWRF(game.loc) > 0)
@ -81,8 +80,8 @@ static int attack(FILE *input, token_t verb, token_t obj)
* fixed), move rug there (not fixed), and move him there,
* too. Then do a null motion to get new description. */
RSPEAK(BARE_HANDS_QUERY);
GETIN(input, &WD1, &WD1X, &WD2, &WD2X);
if (WD1 != MAKEWD(WORD_YINIT) && WD1 != MAKEWD(WORD_YES))
GETIN(input, &command->wd1, &command->wd1x, &command->wd2, &command->wd2x);
if (command->wd1 != MAKEWD(WORD_YINIT) && command->wd1 != MAKEWD(WORD_YES))
return GO_CHECKFOO;
PSPEAK(DRAGON, 3);
game.prop[DRAGON] = 1;
@ -107,7 +106,7 @@ static int attack(FILE *input, token_t verb, token_t obj)
static int bigwords(token_t foo)
/* FEE FIE FOE FOO (AND FUM). Advance to next state if given in proper order.
* Look up WD1 in section 3 of vocab to determine which word we've got. Last
* Look up foo in section 3 of vocab to determine which word we've got. Last
* word zips the eggs back to the giant room (unless already there). */
{
int k = VOCAB(foo, 3);
@ -792,32 +791,32 @@ static int quit(void)
return GO_CLEAROBJ;
}
static int read(token_t verb, token_t obj)
static int read(struct command_t *command)
/* Read. Print stuff based on objtxt. Oyster (?) is special case. */
{
if (obj == INTRANSITIVE) {
obj = 0;
if (command->obj == INTRANSITIVE) {
command->obj = 0;
for (int i = 1; i <= NOBJECTS; i++) {
if (HERE(i) && OBJTXT[i] != 0 && game.prop[i] >= 0)
obj = obj * NOBJECTS + i;
command->obj = command->obj * NOBJECTS + i;
}
if (obj > NOBJECTS || obj == 0 || DARK(game.loc)) return GO_UNKNOWN;
if (command->obj > NOBJECTS || command->obj == 0 || DARK(game.loc)) return GO_UNKNOWN;
}
if (DARK(game.loc)) {
SETPRM(1, WD1, WD1X);
SETPRM(1, command->wd1, command->wd1x);
RSPEAK(NO_SEE);
return GO_CLEAROBJ;
}
if (OBJTXT[obj] == 0 || game.prop[obj] < 0) {
RSPEAK(ACTSPK[verb]);
if (OBJTXT[command->obj] == 0 || game.prop[command->obj] < 0) {
RSPEAK(ACTSPK[command->verb]);
return GO_CLEAROBJ;
}
if (obj == OYSTER && !game.clshnt) {
if (command->obj == OYSTER && !game.clshnt) {
game.clshnt = YES(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
return GO_CLEAROBJ;
}
PSPEAK(obj, OBJTXT[obj] + game.prop[obj]);
PSPEAK(command->obj, OBJTXT[command->obj] + game.prop[command->obj]);
return GO_CLEAROBJ;
}
@ -859,19 +858,19 @@ static int rub(token_t verb, token_t obj)
return GO_CLEAROBJ;
}
static int say(void)
static int say(struct command_t *command)
/* Say. Echo WD2 (or WD1 if no WD2 (SAY WHAT?, etc.).) Magic words override. */
{
/* FIXME: ugly use of globals */
SETPRM(1, WD2, WD2X);
if (WD2 <= 0)
SETPRM(1, WD1, WD1X);
if (WD2 > 0)
WD1 = WD2;
int wd = VOCAB(WD1, -1);
SETPRM(1, command->wd2, command->wd2x);
if (command->wd2 <= 0)
SETPRM(1, command->wd1, command->wd1x);
if (command->wd2 > 0)
command->wd1 = command->wd2;
int wd = VOCAB(command->wd1, -1);
/* FIXME: Magic numbers */
if (wd == 62 || wd == 65 || wd == 71 || wd == 2025 || wd == 2034) {
WD2 = 0;
command->wd2 = 0;
return GO_LOOKUP;
}
RSPEAK(OKEY_DOKEY);
@ -885,22 +884,22 @@ static int throw_support(long spk)
return GO_MOVE;
}
static int throw (FILE *cmdin, long verb, token_t obj)
static int throw (FILE *cmdin, struct command_t *command)
/* Throw. Same as discard unless axe. Then same as attack except
* ignore bird, and if dwarf is present then one might be killed.
* (Only way to do so!) Axe also special for dragon, bear, and
* troll. Treasures special for troll. */
{
int spk = ACTSPK[verb];
if (TOTING(ROD2) && obj == ROD && !TOTING(ROD))obj = ROD2;
if (!TOTING(obj)) {
int spk = ACTSPK[command->verb];
if (TOTING(ROD2) && command->obj == ROD && !TOTING(ROD))command->obj = ROD2;
if (!TOTING(command->obj)) {
RSPEAK(spk);
return GO_CLEAROBJ;
}
if (obj >= MINTRS && obj <= MAXTRS && AT(TROLL)) {
if (command->obj >= MINTRS && command->obj <= MAXTRS && AT(TROLL)) {
spk = TROLL_SATISFIED;
/* Snarf a treasure for the troll. */
DROP(obj, 0);
DROP(command->obj, 0);
MOVE(TROLL, 0);
MOVE(TROLL + NOBJECTS, 0);
DROP(TROLL2, PLAC[TROLL]);
@ -909,13 +908,13 @@ static int throw (FILE *cmdin, long verb, token_t obj)
RSPEAK(spk);
return GO_CLEAROBJ;
}
if (obj == FOOD && HERE(BEAR)) {
if (command->obj == FOOD && HERE(BEAR)) {
/* But throwing food is another story. */
obj = BEAR;
return (feed(verb, obj));
command->obj = BEAR;
return (feed(command->verb, command->obj));
}
if (obj != AXE)
return (discard(verb, obj, false));
if (command->obj != AXE)
return (discard(command->verb, command->obj, false));
else {
int i = ATDWRF(game.loc);
if (i <= 0) {
@ -934,7 +933,8 @@ static int throw (FILE *cmdin, long verb, token_t obj)
RSPEAK(AXE_LOST);
return GO_CLEAROBJ;
}
return (attack(cmdin, verb, 0));
command->obj = 0;
return (attack(cmdin, command));
}
if (randrange(NDWARVES + 1) < game.dflag) {
@ -995,14 +995,14 @@ static int wave(token_t verb, token_t obj)
}
}
int action(FILE *input, struct command_t command)
int action(FILE *input, struct command_t *command)
/* Analyse a verb. Remember what it was, go back for object if second word
* unless verb is "say", which snarfs arbitrary second word.
*/
{
token_t spk = ACTSPK[command.verb];
token_t spk = ACTSPK[command->verb];
if (command.part == unknown) {
if (command->part == unknown) {
/* Analyse an object word. See if the thing is here, whether
* we've got a verb yet, and so on. Object must be here
* unless verb is "find" or "invent(ory)" (and no new verb
@ -1010,74 +1010,74 @@ int action(FILE *input, struct command_t command)
* they are never actually dropped at any location, but might
* be here inside the bottle or urn or as a feature of the
* location. */
if (HERE(command.obj))
if (HERE(command->obj))
/* FALL THROUGH */;
else if (command.obj == GRATE) {
else if (command->obj == GRATE) {
if (game.loc == LOC_START || game.loc == LOC_VALLEY || game.loc == LOC_SLIT)
command.obj = DPRSSN;
command->obj = DPRSSN;
if (game.loc == LOC_COBBLE || game.loc == LOC_DEBRIS || game.loc == LOC_AWKWARD ||
game.loc == LOC_BIRD || game.loc == LOC_PITTOP)
command.obj = ENTRNC;
if (command.obj != GRATE)
command->obj = ENTRNC;
if (command->obj != GRATE)
return GO_MOVE;
} else if (command.obj == DWARF && ATDWRF(game.loc) > 0)
} else if (command->obj == DWARF && ATDWRF(game.loc) > 0)
/* FALL THROUGH */;
else if ((LIQUID() == command.obj && HERE(BOTTLE)) || command.obj == LIQLOC(game.loc))
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) {
command.obj = URN;
else if (command->obj == OIL && HERE(URN) && game.prop[URN] != 0) {
command->obj = URN;
/* FALL THROUGH */;
} else if (command.obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
command.obj = PLANT2;
} else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
command->obj = PLANT2;
/* FALL THROUGH */;
} else if (command.obj == KNIFE && game.knfloc == game.loc) {
} else if (command->obj == KNIFE && game.knfloc == game.loc) {
game.knfloc = -1;
spk = KNIVES_VANISH;
RSPEAK(spk);
return GO_CLEAROBJ;
} else if (command.obj == ROD && HERE(ROD2)) {
command.obj = ROD2;
} else if (command->obj == ROD && HERE(ROD2)) {
command->obj = ROD2;
/* FALL THROUGH */;
} else if ((command.verb == FIND || command.verb == INVENT) && WD2 <= 0)
} else if ((command->verb == FIND || command->verb == INVENT) && command->wd2 <= 0)
/* FALL THROUGH */;
else {
SETPRM(1, WD1, WD1X);
SETPRM(1, command->wd1, command->wd1x);
RSPEAK(NO_SEE);
return GO_CLEAROBJ;
}
if (WD2 > 0)
if (command->wd2 > 0)
return GO_WORD2;
if (command.verb != 0)
command.part = transitive;
if (command->verb != 0)
command->part = transitive;
}
switch (command.part) {
switch (command->part) {
case intransitive:
if (WD2 > 0 && command.verb != SAY)
if (command->wd2 > 0 && command->verb != SAY)
return GO_WORD2;
if (command.verb == SAY)command.obj = WD2;
if (command.obj == 0 || command.obj == INTRANSITIVE) {
if (command->verb == SAY)command->obj = command->wd2;
if (command->obj == 0 || command->obj == INTRANSITIVE) {
/* Analyse an intransitive verb (ie, no object given yet). */
switch (command.verb - 1) {
switch (command->verb - 1) {
case 0: /* CARRY */
return carry(command.verb, INTRANSITIVE);
return carry(command->verb, INTRANSITIVE);
case 1: /* DROP */
return GO_UNKNOWN;
case 2: /* SAY */
return GO_UNKNOWN;
case 3: /* UNLOC */
return lock(command.verb, INTRANSITIVE);
return lock(command->verb, INTRANSITIVE);
case 4: { /* NOTHI */
RSPEAK(OK_MAN);
return (GO_CLEAROBJ);
}
case 5: /* LOCK */
return lock(command.verb, INTRANSITIVE);
return lock(command->verb, INTRANSITIVE);
case 6: /* LIGHT */
return light(command.verb, INTRANSITIVE);
return light(command->verb, INTRANSITIVE);
case 7: /* EXTIN */
return extinguish(command.verb, INTRANSITIVE);
return extinguish(command->verb, INTRANSITIVE);
case 8: /* WAVE */
return GO_UNKNOWN;
case 9: /* CALM */
@ -1087,13 +1087,13 @@ int action(FILE *input, struct command_t command)
return GO_CLEAROBJ;
}
case 11: /* ATTAC */
return attack(input, command.verb, command.obj);
return attack(input, command);
case 12: /* POUR */
return pour(command.verb, command.obj);
return pour(command->verb, command->obj);
case 13: /* EAT */
return eat(command.verb, INTRANSITIVE);
return eat(command->verb, INTRANSITIVE);
case 14: /* DRINK */
return drink(command.verb, command.obj);
return drink(command->verb, command->obj);
case 15: /* RUB */
return GO_UNKNOWN;
case 16: /* TOSS */
@ -1107,7 +1107,7 @@ int action(FILE *input, struct command_t command)
case 20: /* FEED */
return GO_UNKNOWN;
case 21: /* FILL */
return fill(command.verb, command.obj);
return fill(command->verb, command->obj);
case 22: /* BLAST */
blast();
return GO_CLEAROBJ;
@ -1115,11 +1115,12 @@ int action(FILE *input, struct command_t command)
score(scoregame);
return GO_CLEAROBJ;
case 24: /* FOO */
return bigwords(WD1);
return bigwords(command->wd1);
case 25: /* BRIEF */
return brief();
case 26: /* READ */
return read(command.verb, INTRANSITIVE);
command->obj = INTRANSITIVE;
return read(command);
case 27: /* BREAK */
return GO_UNKNOWN;
case 28: /* WAKE */
@ -1129,7 +1130,7 @@ int action(FILE *input, struct command_t command)
case 30: /* RESU */
return resume();
case 31: /* FLY */
return fly(command.verb, INTRANSITIVE);
return fly(command->verb, INTRANSITIVE);
case 32: /* LISTE */
return listen();
case 33: /* ZZZZ */
@ -1140,27 +1141,27 @@ int action(FILE *input, struct command_t command)
/* FALLTHRU */
case transitive:
/* Analyse a transitive verb. */
switch (command.verb - 1) {
switch (command->verb - 1) {
case 0: /* CARRY */
return carry(command.verb, command.obj);
return carry(command->verb, command->obj);
case 1: /* DROP */
return discard(command.verb, command.obj, false);
return discard(command->verb, command->obj, false);
case 2: /* SAY */
return say();
return say(command);
case 3: /* UNLOC */
return lock(command.verb, command.obj);
return lock(command->verb, command->obj);
case 4: { /* NOTHI */
RSPEAK(OK_MAN);
return (GO_CLEAROBJ);
}
case 5: /* LOCK */
return lock(command.verb, command.obj);
return lock(command->verb, command->obj);
case 6: /* LIGHT */
return light(command.verb, command.obj);
return light(command->verb, command->obj);
case 7: /* EXTI */
return extinguish(command.verb, command.obj);
return extinguish(command->verb, command->obj);
case 8: /* WAVE */
return wave(command.verb, command.obj);
return wave(command->verb, command->obj);
case 9: { /* CALM */
RSPEAK(spk);
return GO_CLEAROBJ;
@ -1170,29 +1171,29 @@ int action(FILE *input, struct command_t command)
return GO_CLEAROBJ;
}
case 11: /* ATTAC */
return attack(input, command.verb, command.obj);
return attack(input, command);
case 12: /* POUR */
return pour(command.verb, command.obj);
return pour(command->verb, command->obj);
case 13: /* EAT */
return eat(command.verb, command.obj);
return eat(command->verb, command->obj);
case 14: /* DRINK */
return drink(command.verb, command.obj);
return drink(command->verb, command->obj);
case 15: /* RUB */
return rub(command.verb, command.obj);
return rub(command->verb, command->obj);
case 16: /* TOSS */
return throw (input, command.verb, command.obj);
return throw(input, command);
case 17: { /* QUIT */
RSPEAK(spk);
return GO_CLEAROBJ;
}
case 18: /* FIND */
return find(command.verb, command.obj);
return find(command->verb, command->obj);
case 19: /* INVEN */
return find(command.verb, command.obj);
return find(command->verb, command->obj);
case 20: /* FEED */
return feed(command.verb, command.obj);
return feed(command->verb, command->obj);
case 21: /* FILL */
return fill(command.verb, command.obj);
return fill(command->verb, command->obj);
case 22: /* BLAST */
blast();
return GO_CLEAROBJ;
@ -1209,11 +1210,11 @@ int action(FILE *input, struct command_t command)
return GO_CLEAROBJ;
}
case 26: /* READ */
return read(command.verb, command.obj);
return read(command);
case 27: /* BREAK */
return vbreak(command.verb, command.obj);
return vbreak(command->verb, command->obj);
case 28: /* WAKE */
return wake(command.verb, command.obj);
return wake(command->verb, command->obj);
case 29: { /* SUSP */
RSPEAK(spk);
return GO_CLEAROBJ;
@ -1223,7 +1224,7 @@ int action(FILE *input, struct command_t command)
return GO_CLEAROBJ;
}
case 31: /* FLY */
return fly(command.verb, command.obj);
return fly(command->verb, command->obj);
case 32: { /* LISTE */
RSPEAK(spk);
return GO_CLEAROBJ;
@ -1234,7 +1235,7 @@ int action(FILE *input, struct command_t command)
BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST);
case unknown:
/* Unknown verb, couldn't deduce object - might need hint */
SETPRM(1, WD1, WD1X);
SETPRM(1, command->wd1, command->wd1x);
RSPEAK(WHAT_DO);
return GO_CHECKHINT;
default:

View file

@ -185,10 +185,12 @@ struct command_t {
enum speechpart part;
vocab_t verb;
vocab_t obj;
token_t wd1, wd1x;
token_t wd2, wd2x;
};
void initialise(void);
int action(FILE *input, struct command_t command);
int action(FILE *input, struct command_t *command);
/* Phase codes for action returns.
* These were at one time FORTRAN line numbers.

39
main.c
View file

@ -41,7 +41,6 @@ long AMBER, AXE, BACK, BATTERY, BEAR, BIRD, BLOOD,
RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
STEPS, STREAM, THROW, TRIDENT, TROLL, TROLL2,
URN, VASE, VEND, VOLCANO, WATER;
token_t WD1, WD1X, WD2, WD2X;
FILE *logfp = NULL, *rfp = NULL;
bool oldstyle = false;
@ -1015,7 +1014,7 @@ L2600:
game.knfloc = 0;
/* This is where we get a new command from the user */
if (!GETIN(cmdin, &WD1, &WD1X, &WD2, &WD2X))
if (!GETIN(cmdin, &command.wd1, &command.wd1x, &command.wd2, &command.wd2x))
return false;
/* Every input, check "game.foobar" flag. If zero, nothing's
@ -1036,7 +1035,7 @@ L2607:
}
}
if (command.verb == SAY && WD2 > 0)
if (command.verb == SAY && command.wd2 > 0)
command.verb = 0;
if (command.verb == SAY) {
command.part = transitive;
@ -1048,8 +1047,8 @@ L2607:
} else
lampcheck();
V1 = VOCAB(WD1, -1);
V2 = VOCAB(WD2, -1);
V1 = VOCAB(command.wd1, -1);
V2 = VOCAB(command.wd2, -1);
if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
if (LIQLOC(game.loc) == WATER) {
RSPEAK(FEET_WET);
@ -1058,37 +1057,37 @@ L2607:
}
goto L2012;
}
if (V1 == ENTER && WD2 > 0) {
WD1 = WD2;
WD1X = WD2X;
wordclear(&WD2);
if (V1 == ENTER && command.wd2 > 0) {
command.wd1 = command.wd2;
command.wd1x = command.wd2x;
wordclear(&command.wd2);
} else {
/* FIXME: Magic numbers */
if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
(V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
if (AT(V2 - 1000))
WD2 = MAKEWD(WORD_POUR);
command.wd2 = MAKEWD(WORD_POUR);
}
if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
WD1 = MAKEWD(WORD_CATCH);
command.wd1 = MAKEWD(WORD_CATCH);
}
L2620:
if (wordeq(WD1, MAKEWD(WORD_WEST))) {
if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
++game.iwest;
if (game.iwest == 10)
RSPEAK(W_IS_WEST);
}
if (wordeq(WD1, MAKEWD(WORD_GO)) && !wordempty(WD2)) {
if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
if (++igo == 10)
RSPEAK(GO_UNNEEDED);
}
Lookup:
defn = VOCAB(WD1, -1);
defn = VOCAB(command.wd1, -1);
if (defn == -1) {
/* Gee, I don't understand. */
if (fallback_handler(rawbuf))
continue;
SETPRM(1, WD1, WD1X);
SETPRM(1, command.wd1, command.wd1x);
RSPEAK(DONT_KNOW);
goto L2600;
}
@ -1115,7 +1114,7 @@ Lookup:
}
Laction:
switch (action(cmdin, command)) {
switch (action(cmdin, &command)) {
case GO_TERMINATE:
return true;
case GO_MOVE:
@ -1133,14 +1132,14 @@ Laction:
goto Lookup;
case GO_WORD2:
/* Get second word for analysis. */
WD1 = WD2;
WD1X = WD2X;
wordclear(&WD2);
command.wd1 = command.wd2;
command.wd1x = command.wd2x;
wordclear(&command.wd2);
goto L2620;
case GO_UNKNOWN:
/* Random intransitive verbs come here. Clear obj just in case
* (see attack()). */
SETPRM(1, WD1, WD1X);
SETPRM(1, command.wd1, command.wd1x);
RSPEAK(DO_WHAT);
command.obj = 0;
goto L2600;