diff --git a/.gitignore b/.gitignore
index fcfc9f2..7aa1928 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
advent
*.gcda
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b8f4b91..6b6bd6d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
stages:
- ci-build
diff --git a/.shipper b/.shipper
index 5e5904c..d3ed58d 100644
--- a/.shipper
+++ b/.shipper
@@ -1,4 +1,4 @@
-#SPDX-FileCopyrightText: (C) Eric S. Raymond
+#SPDX-FileCopyrightText: Eric S. Raymond
#SPDX-License-Identifier: BSD-2-Clause
extralines="""
There is a code coverage analysis and a symbol coverage analysis
diff --git a/Dockerfile.ci b/Dockerfile.ci
index b8eeef5..c8f29a1 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -1,6 +1,6 @@
# This image is built by the Gitlab CI pipeline to be used in subsequent
# pipeline steps.
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
FROM ubuntu
@@ -9,4 +9,4 @@ FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
-RUN apt-get install --yes --no-install-recommends make gcc libedit-dev libasan6 libubsan1 python3 python3-yaml lcov asciidoctor libxslt1.1 pkg-config docbook-xml xsltproc
+RUN apt-get install --yes --no-install-recommends make gcc-10 libedit-dev libasan6 libubsan1 python3 python3-yaml lcov asciidoc libxslt1.1 pkg-config docbook-xml xsltproc
diff --git a/INSTALL.adoc b/INSTALL.adoc
index fb93b7d..b6f5b4b 100644
--- a/INSTALL.adoc
+++ b/INSTALL.adoc
@@ -1,5 +1,5 @@
= Installing Open Adventure =
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
Installation now requires Python3 due to a security issue
@@ -21,7 +21,3 @@ You can also use pip to install PyYAML: `pip3 install PyYAML`.
4. Optionally run a regression test on the code with `make check`.
5. Run `./advent` to play.
-
-6. If you want to buld the documentation you will need asciidoctor.
-
-7. Running the regression tests requires batchspell
diff --git a/Makefile b/Makefile
index e4e31cb..dfd3ff2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Makefile for the open-source release of adventure 2.5
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
# To build with save/resume disabled, pass CFLAGS="-DADVENT_NOSAVE"
@@ -12,7 +12,7 @@ VERS=$(shell sed -n
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
-1.20: 2024-09-23::
- Make oldstyle correctly suppress line editing.
-
-1.19: 2024-06-27::
- Ensore that the KNIVES_VANISH message can't issue twice.
-
-1.18: 2024-02-15::
- Bring the manual page fully up to date.
-
1.17: 2024-01-02::
Saying Z'ZZZ at reservoir no longer causes the waters to part and crash.
diff --git a/README.adoc b/README.adoc
index 6b25f7f..ba67510 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,5 +1,5 @@
= README for Open Adventure =
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
If you are reading this anywhere but at http://www.catb.org/~esr/open-adventure
diff --git a/actions.c b/actions.c
index 469e92f..4f4ced1 100644
--- a/actions.c
+++ b/actions.c
@@ -1,1659 +1,1596 @@
/*
* Actions for the dungeon-running code.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include
-#include
#include
+#include
#include
-
#include "advent.h"
+#include "dungeon.h"
+#include
static phase_codes_t fill(verb_t, obj_t);
-static phase_codes_t attack(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. */
- verb_t verb = command.verb;
- obj_t obj = command.obj;
+static phase_codes_t attack(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. */
+{
+ verb_t verb = command.verb;
+ obj_t obj = command.obj;
- if (obj == INTRANSITIVE) {
- int changes = 0;
- if (atdwrf(game.loc) > 0) {
- obj = DWARF;
- ++changes;
- }
- if (HERE(SNAKE)) {
- obj = SNAKE;
- ++changes;
- }
- if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS) {
- obj = DRAGON;
- ++changes;
- }
- if (AT(TROLL)) {
- obj = TROLL;
- ++changes;
- }
- if (AT(OGRE)) {
- obj = OGRE;
- ++changes;
- }
- if (HERE(BEAR) && game.objects[BEAR].prop == UNTAMED_BEAR) {
- obj = BEAR;
- ++changes;
- }
- /* check for low-priority targets */
- if (obj == INTRANSITIVE) {
- /* Can't attack bird or machine by throwing axe. */
- if (HERE(BIRD) && verb != THROW) {
- obj = BIRD;
- ++changes;
- }
- if (HERE(VEND) && verb != THROW) {
- obj = VEND;
- ++changes;
- }
- /* Clam and oyster both treated as clam for intransitive
- * case; no harm done. */
- if (HERE(CLAM) || HERE(OYSTER)) {
- obj = CLAM;
- ++changes;
- }
- }
- if (changes >= 2) {
- return GO_UNKNOWN;
- }
- }
+ if (obj == INTRANSITIVE) {
+ int changes = 0;
+ if (atdwrf(game.loc) > 0) {
+ obj = DWARF;
+ ++changes;
+ }
+ if (HERE(SNAKE)) {
+ obj = SNAKE;
+ ++changes;
+ }
+ if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS) {
+ obj = DRAGON;
+ ++changes;
+ }
+ if (AT(TROLL)) {
+ obj = TROLL;
+ ++changes;
+ }
+ if (AT(OGRE)) {
+ obj = OGRE;
+ ++changes;
+ }
+ if (HERE(BEAR) && game.objects[BEAR].prop == UNTAMED_BEAR) {
+ obj = BEAR;
+ ++changes;
+ }
+ /* check for low-priority targets */
+ if (obj == INTRANSITIVE) {
+ /* Can't attack bird or machine by throwing axe. */
+ if (HERE(BIRD) && verb != THROW) {
+ obj = BIRD;
+ ++changes;
+ }
+ if (HERE(VEND) && verb != THROW) {
+ obj = VEND;
+ ++changes;
+ }
+ /* Clam and oyster both treated as clam for intransitive case;
+ * no harm done. */
+ if (HERE(CLAM) || HERE(OYSTER)) {
+ obj = CLAM;
+ ++changes;
+ }
+ }
+ if (changes >= 2)
+ return GO_UNKNOWN;
+ }
- if (obj == BIRD) {
- if (game.closed) {
- rspeak(UNHAPPY_BIRD);
- } else {
- DESTROY(BIRD);
- rspeak(BIRD_DEAD);
- }
- return GO_CLEAROBJ;
- }
- if (obj == VEND) {
- state_change(VEND, game.objects[VEND].prop == VEND_BLOCKS
- ? VEND_UNBLOCKS
- : VEND_BLOCKS);
+ if (obj == BIRD) {
+ if (game.closed) {
+ rspeak(UNHAPPY_BIRD);
+ } else {
+ DESTROY(BIRD);
+ rspeak(BIRD_DEAD);
+ }
+ return GO_CLEAROBJ;
+ }
+ if (obj == VEND) {
+ state_change(VEND,
+ game.objects[VEND].prop == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
- return GO_CLEAROBJ;
- }
+ return GO_CLEAROBJ;
+ }
- if (obj == BEAR) {
- switch (game.objects[BEAR].prop) {
- case UNTAMED_BEAR:
- rspeak(BEAR_HANDS);
- break;
- case SITTING_BEAR:
- rspeak(BEAR_CONFUSED);
- break;
- case CONTENTED_BEAR:
- rspeak(BEAR_CONFUSED);
- break;
- case BEAR_DEAD:
- rspeak(ALREADY_DEAD);
- break;
- }
- return GO_CLEAROBJ;
- }
- if (obj == DRAGON && game.objects[DRAGON].prop == DRAGON_BARS) {
- /* 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. */
- rspeak(BARE_HANDS_QUERY);
- if (!silent_yes_or_no()) {
- speak(arbitrary_messages[NASTY_DRAGON]);
- return GO_MOVE;
- }
- state_change(DRAGON, DRAGON_DEAD);
- game.objects[RUG].prop = RUG_FLOOR;
- /* 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.objects[i].place == objects[DRAGON].plac ||
- game.objects[i].place == objects[DRAGON].fixd) {
- move(i, LOC_SECRET5);
- }
- }
- game.loc = LOC_SECRET5;
- return GO_MOVE;
- }
+ if (obj == BEAR) {
+ switch (game.objects[BEAR].prop) {
+ case UNTAMED_BEAR:
+ rspeak(BEAR_HANDS);
+ break;
+ case SITTING_BEAR:
+ rspeak(BEAR_CONFUSED);
+ break;
+ case CONTENTED_BEAR:
+ rspeak(BEAR_CONFUSED);
+ break;
+ case BEAR_DEAD:
+ rspeak(ALREADY_DEAD);
+ break;
+ }
+ return GO_CLEAROBJ;
+ }
+ if (obj == DRAGON && game.objects[DRAGON].prop == DRAGON_BARS) {
+ /* 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. */
+ rspeak(BARE_HANDS_QUERY);
+ if (!silent_yes_or_no()) {
+ speak(arbitrary_messages[NASTY_DRAGON]);
+ return GO_MOVE;
+ }
+ state_change(DRAGON, DRAGON_DEAD);
+ game.objects[RUG].prop = RUG_FLOOR;
+ /* 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.objects[i].place == objects[DRAGON].plac ||
+ game.objects[i].place == objects[DRAGON].fixd)
+ move(i, LOC_SECRET5);
+ }
+ game.loc = LOC_SECRET5;
+ return GO_MOVE;
+ }
- 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.dwarves[i].loc == game.loc) {
- ++dwarves;
- game.dwarves[i].loc = LOC_LONGWEST;
- game.dwarves[i].seen = false;
- }
- }
- rspeak((dwarves > 1) ? OGRE_PANIC1 : OGRE_PANIC2);
- return GO_CLEAROBJ;
- }
+ if (obj == OGRE) {
+ rspeak(OGRE_DODGE);
+ if (atdwrf(game.loc) == 0)
+ return GO_CLEAROBJ;
- switch (obj) {
- case INTRANSITIVE:
- 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:
- speak(actions[verb].message);
- }
+ rspeak(KNIFE_THROWN);
+ DESTROY(OGRE);
+ int dwarves = 0;
+ for (int i = 1; i < PIRATE; i++) {
+ if (game.dwarves[i].loc == game.loc) {
+ ++dwarves;
+ game.dwarves[i].loc = LOC_LONGWEST;
+ game.dwarves[i].seen = false;
+ }
+ }
+ rspeak((dwarves > 1) ?
+ OGRE_PANIC1 :
+ OGRE_PANIC2);
+ return GO_CLEAROBJ;
+ }
+
+ switch (obj) {
+ case INTRANSITIVE:
+ 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:
+ speak(actions[verb].message);
+ }
+ return GO_CLEAROBJ;
+}
+
+static phase_codes_t bigwords(vocab_t id)
+/* Only called on 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). */
+{
+ int foobar = abs(game.foobar);
+
+ /* Only FEE can start a magic-word sequence. */
+ if ((foobar == WORD_EMPTY) && (id == FIE || id == FOE || id == FOO || id == FUM)) {
+ rspeak(NOTHING_HAPPENS);
return GO_CLEAROBJ;
+ }
+
+ if ((foobar == WORD_EMPTY && id == FEE) ||
+ (foobar == FEE && id == FIE) ||
+ (foobar == FIE && id == FOE) ||
+ (foobar == FOE && id == FOO)) {
+ game.foobar = id;
+ if (id != FOO) {
+ rspeak(OK_MAN);
+ return GO_CLEAROBJ;
+ }
+ game.foobar = WORD_EMPTY;
+ if (game.objects[EGGS].place == 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.objects[EGGS].place == LOC_NOWHERE && game.objects[TROLL].place == LOC_NOWHERE
+ && game.objects[TROLL].prop == TROLL_UNPAID)
+ game.objects[TROLL].prop = TROLL_PAIDONCE;
+ if (HERE(EGGS))
+ pspeak(EGGS, look, true, EGGS_VANISHED);
+ else if (game.loc == objects[EGGS].plac)
+ pspeak(EGGS, look, true, EGGS_HERE);
+ else
+ pspeak(EGGS, look, true, EGGS_DONE);
+ move(EGGS, objects[EGGS].plac);
+
+ return GO_CLEAROBJ;
+ }
+ } else {
+ /* Magic-word sequence was started but is incorrect */
+ if (settings.oldstyle || game.seenbigwords)
+ rspeak(START_OVER);
+ else
+ rspeak(WELL_POINTLESS);
+ game.foobar = WORD_EMPTY;
+ return GO_CLEAROBJ;
+ }
}
-static phase_codes_t bigwords(vocab_t id) {
- /* Only called on 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). */
- int foobar = abs(game.foobar);
-
- /* Only FEE can start a magic-word sequence. */
- if ((foobar == WORD_EMPTY) &&
- (id == FIE || id == FOE || id == FOO || id == FUM)) {
- rspeak(NOTHING_HAPPENS);
- return GO_CLEAROBJ;
- }
-
- if ((foobar == WORD_EMPTY && id == FEE) ||
- (foobar == FEE && id == FIE) || (foobar == FIE && id == FOE) ||
- (foobar == FOE && id == FOO)) {
- game.foobar = id;
- if (id != FOO) {
- rspeak(OK_MAN);
- return GO_CLEAROBJ;
- }
- game.foobar = WORD_EMPTY;
- if (game.objects[EGGS].place == 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.objects[EGGS].place == LOC_NOWHERE &&
- game.objects[TROLL].place == LOC_NOWHERE &&
- game.objects[TROLL].prop == TROLL_UNPAID) {
- game.objects[TROLL].prop = TROLL_PAIDONCE;
- }
- if (HERE(EGGS)) {
- pspeak(EGGS, look, true, EGGS_VANISHED);
- } else if (game.loc == objects[EGGS].plac) {
- pspeak(EGGS, look, true, EGGS_HERE);
- } else {
- pspeak(EGGS, look, true, EGGS_DONE);
- }
- move(EGGS, objects[EGGS].plac);
-
- return GO_CLEAROBJ;
- }
- } else {
- /* Magic-word sequence was started but is incorrect */
- if (settings.oldstyle || game.seenbigwords) {
- rspeak(START_OVER);
- } else {
- 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 (PROP_IS_NOTFOUND(ROD2) || !game.closed)
+ rspeak(REQUIRES_DYNAMITE);
+ else {
+ 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);
+ }
+ terminate(endgame);
+ }
}
-static void blast(void) {
- /* Blast. No effect unless you've got dynamite, which is a neat trick!
- */
- if (OBJECT_IS_NOTFOUND(ROD2) || !game.closed) {
- rspeak(REQUIRES_DYNAMITE);
- } else {
- 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);
- }
- terminate(endgame);
- }
+static phase_codes_t vbreak(verb_t verb, obj_t obj)
+/* Break. Only works for mirror in repository and, of course, the vase. */
+{
+ switch (obj) {
+ case MIRROR:
+ if (game.closed) {
+ state_change(MIRROR, MIRROR_BROKEN);
+ return GO_DWARFWAKE;
+ } else {
+ rspeak(TOO_FAR);
+ break;
+ }
+ case VASE:
+ if (game.objects[VASE].prop == VASE_WHOLE) {
+ if (TOTING(VASE))
+ drop(VASE, game.loc);
+ state_change(VASE, VASE_BROKEN);
+ game.objects[VASE].fixed = IS_FIXED;
+ break;
+ }
+ /* FALLTHRU */
+ default:
+ speak(actions[verb].message);
+ }
+ return (GO_CLEAROBJ);
}
-static phase_codes_t vbreak(verb_t verb, obj_t obj) {
- /* Break. Only works for mirror in repository and, of course, the
- * vase. */
- switch (obj) {
- case MIRROR:
- if (game.closed) {
- state_change(MIRROR, MIRROR_BROKEN);
- return GO_DWARFWAKE;
- } else {
- rspeak(TOO_FAR);
- break;
- }
- case VASE:
- if (game.objects[VASE].prop == VASE_WHOLE) {
- if (TOTING(VASE)) {
- drop(VASE, game.loc);
- }
- state_change(VASE, VASE_BROKEN);
- game.objects[VASE].fixed = IS_FIXED;
- break;
- }
- /* FALLTHRU */
- default:
- speak(actions[verb].message);
- }
- return (GO_CLEAROBJ);
+static phase_codes_t brief(void)
+/* Brief. Intransitive only. Suppress full descriptions after first time. */
+{
+ game.abbnum = 10000;
+ game.detail = 3;
+ rspeak(BRIEF_CONFIRM);
+ return GO_CLEAROBJ;
}
-static phase_codes_t brief(void) {
- /* Brief. Intransitive only. Suppress full descriptions after first
- * time. */
- game.abbnum = 10000;
- game.detail = 3;
- rspeak(BRIEF_CONFIRM);
- return GO_CLEAROBJ;
+static phase_codes_t 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.locs[game.loc].atloc == NO_OBJECT ||
+ game.link[game.locs[game.loc].atloc] != 0 ||
+ atdwrf(game.loc) > 0)
+ return GO_UNKNOWN;
+ obj = game.locs[game.loc].atloc;
+ }
+
+ if (TOTING(obj)) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ }
+
+ if (obj == MESSAG) {
+ rspeak(REMOVE_MESSAGE);
+ DESTROY(MESSAG);
+ return GO_CLEAROBJ;
+ }
+
+ if (game.objects[obj].fixed != IS_FREE) {
+ switch (obj) {
+ case PLANT:
+ /* Next guard tests whether plant is tiny or stashed */
+ rspeak(game.objects[PLANT].prop <= PLANT_THIRSTY ? DEEP_ROOTS : YOU_JOKING);
+ break;
+ case BEAR:
+ rspeak( game.objects[BEAR].prop == SITTING_BEAR ? BEAR_CHAINED : YOU_JOKING);
+ break;
+ case CHAIN:
+ rspeak( game.objects[BEAR].prop != UNTAMED_BEAR ? STILL_LOCKED : YOU_JOKING);
+ break;
+ case RUG:
+ rspeak(game.objects[RUG].prop == RUG_HOVER ? RUG_HOVERS : YOU_JOKING);
+ break;
+ case URN:
+ rspeak(URN_NOBUDGE);
+ break;
+ case CAVITY:
+ rspeak(DOUGHNUT_HOLES);
+ break;
+ case BLOOD:
+ rspeak(FEW_DROPS);
+ break;
+ case SIGN:
+ rspeak(HAND_PASSTHROUGH);
+ break;
+ default:
+ rspeak(YOU_JOKING);
+ }
+ return GO_CLEAROBJ;
+ }
+
+ if (obj == WATER || obj == OIL) {
+ if (!HERE(BOTTLE) || LIQUID() != obj) {
+ if (!TOTING(BOTTLE)) {
+ rspeak(NO_CONTAINER);
+ return GO_CLEAROBJ;
+ }
+ if (game.objects[BOTTLE].prop == EMPTY_BOTTLE) {
+ return (fill(verb, BOTTLE));
+ } else
+ rspeak(BOTTLE_FULL);
+ return GO_CLEAROBJ;
+ }
+ obj = BOTTLE;
+ }
+
+ if (game.holdng >= INVLIMIT) {
+ rspeak(CARRY_LIMIT);
+ return GO_CLEAROBJ;
+
+ }
+
+ if (obj == BIRD && game.objects[BIRD].prop != BIRD_CAGED && !PROP_IS_STASHED(BIRD)) {
+ if (game.objects[BIRD].prop == BIRD_FOREST_UNCAGED) {
+ DESTROY(BIRD);
+ rspeak(BIRD_CRAP);
+ return GO_CLEAROBJ;
+ }
+ if (!TOTING(CAGE)) {
+ rspeak(CANNOT_CARRY);
+ return GO_CLEAROBJ;
+ }
+ if (TOTING(ROD)) {
+ rspeak(BIRD_EVADES);
+ return GO_CLEAROBJ;
+ }
+ game.objects[BIRD].prop = BIRD_CAGED;
+ }
+ if ((obj == BIRD || obj == CAGE) &&
+ (game.objects[BIRD].prop == BIRD_CAGED || PROP_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.objects[LIQUID()].place = CARRIED;
+
+ if (GSTONE(obj) && !PROP_IS_FOUND(obj)) {
+ PROP_SET_FOUND(obj);
+ game.objects[CAVITY].prop = CAVITY_EMPTY;
+ }
+ rspeak(OK_MAN);
+ return GO_CLEAROBJ;
}
-static phase_codes_t 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.locs[game.loc].atloc == NO_OBJECT ||
- game.link[game.locs[game.loc].atloc] != 0 ||
- atdwrf(game.loc) > 0) {
- return GO_UNKNOWN;
- }
- obj = game.locs[game.loc].atloc;
- }
+static int chain(verb_t verb)
+/* Do something to the bear's chain */
+{
+ if (verb != LOCK) {
+ if (game.objects[BEAR].prop == UNTAMED_BEAR) {
+ rspeak(BEAR_BLOCKS);
+ return GO_CLEAROBJ;
+ }
+ if (game.objects[CHAIN].prop == CHAIN_HEAP) {
+ rspeak(ALREADY_UNLOCKED);
+ return GO_CLEAROBJ;
+ }
+ game.objects[CHAIN].prop = CHAIN_HEAP;
+ game.objects[CHAIN].fixed = IS_FREE;
+ if (game.objects[BEAR].prop != BEAR_DEAD)
+ game.objects[BEAR].prop = CONTENTED_BEAR;
- if (TOTING(obj)) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- }
+ switch (game.objects[BEAR].prop) {
+ // LCOV_EXCL_START
+ case BEAR_DEAD:
+ /* Can't be reached until the bear can die in some way other
+ * than a bridge collapse. Leave in in case this changes, but
+ * exclude from coverage testing. */
+ game.objects[BEAR].fixed = IS_FIXED;
+ break;
+ // LCOV_EXCL_STOP
+ default:
+ game.objects[BEAR].fixed = IS_FREE;
+ }
+ rspeak(CHAIN_UNLOCKED);
+ return GO_CLEAROBJ;
+ }
- if (obj == MESSAG) {
- rspeak(REMOVE_MESSAGE);
- DESTROY(MESSAG);
- return GO_CLEAROBJ;
- }
+ if (game.objects[CHAIN].prop != CHAIN_HEAP) {
+ rspeak(ALREADY_LOCKED);
+ return GO_CLEAROBJ;
+ }
+ if (game.loc != objects[CHAIN].plac) {
+ rspeak(NO_LOCKSITE);
+ return GO_CLEAROBJ;
+ }
- if (game.objects[obj].fixed != IS_FREE) {
- switch (obj) {
- case PLANT:
- rspeak((game.objects[PLANT].prop == PLANT_THIRSTY ||
- OBJECT_IS_STASHED(PLANT))
- ? DEEP_ROOTS
- : YOU_JOKING);
- break;
- case BEAR:
- rspeak(game.objects[BEAR].prop == SITTING_BEAR
- ? BEAR_CHAINED
- : YOU_JOKING);
- break;
- case CHAIN:
- rspeak(game.objects[BEAR].prop != UNTAMED_BEAR
- ? STILL_LOCKED
- : YOU_JOKING);
- break;
- case RUG:
- rspeak(game.objects[RUG].prop == RUG_HOVER
- ? RUG_HOVERS
- : YOU_JOKING);
- break;
- case URN:
- rspeak(URN_NOBUDGE);
- break;
- case CAVITY:
- rspeak(DOUGHNUT_HOLES);
- break;
- case BLOOD:
- rspeak(FEW_DROPS);
- break;
- case SIGN:
- rspeak(HAND_PASSTHROUGH);
- break;
- default:
- rspeak(YOU_JOKING);
- }
- return GO_CLEAROBJ;
- }
+ game.objects[CHAIN].prop = CHAIN_FIXED;
- if (obj == WATER || obj == OIL) {
- if (!HERE(BOTTLE) || LIQUID() != obj) {
- if (!TOTING(BOTTLE)) {
- rspeak(NO_CONTAINER);
- return GO_CLEAROBJ;
- }
- if (game.objects[BOTTLE].prop == EMPTY_BOTTLE) {
- return (fill(verb, BOTTLE));
- } else {
- rspeak(BOTTLE_FULL);
- }
- return GO_CLEAROBJ;
- }
- obj = BOTTLE;
- }
+ if (TOTING(CHAIN))
+ drop(CHAIN, game.loc);
+ game.objects[CHAIN].fixed = IS_FIXED;
- if (game.holdng >= INVLIMIT) {
- rspeak(CARRY_LIMIT);
- return GO_CLEAROBJ;
- }
-
- if (obj == BIRD && game.objects[BIRD].prop != BIRD_CAGED &&
- !OBJECT_IS_STASHED(BIRD)) {
- if (game.objects[BIRD].prop == BIRD_FOREST_UNCAGED) {
- DESTROY(BIRD);
- rspeak(BIRD_CRAP);
- return GO_CLEAROBJ;
- }
- if (!TOTING(CAGE)) {
- rspeak(CANNOT_CARRY);
- return GO_CLEAROBJ;
- }
- if (TOTING(ROD)) {
- rspeak(BIRD_EVADES);
- return GO_CLEAROBJ;
- }
- game.objects[BIRD].prop = BIRD_CAGED;
- }
- if ((obj == BIRD || obj == CAGE) &&
- OBJECT_STATE_EQUALS(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.objects[LIQUID()].place = CARRIED;
- }
-
- if (GSTONE(obj) && !OBJECT_IS_FOUND(obj)) {
- OBJECT_SET_FOUND(obj);
- game.objects[CAVITY].prop = CAVITY_EMPTY;
- }
- rspeak(OK_MAN);
- return GO_CLEAROBJ;
+ rspeak(CHAIN_LOCKED);
+ return GO_CLEAROBJ;
}
-static int chain(verb_t verb) {
- /* Do something to the bear's chain */
- if (verb != LOCK) {
- if (game.objects[BEAR].prop == UNTAMED_BEAR) {
- rspeak(BEAR_BLOCKS);
- return GO_CLEAROBJ;
- }
- if (game.objects[CHAIN].prop == CHAIN_HEAP) {
- rspeak(ALREADY_UNLOCKED);
- return GO_CLEAROBJ;
- }
- game.objects[CHAIN].prop = CHAIN_HEAP;
- game.objects[CHAIN].fixed = IS_FREE;
- if (game.objects[BEAR].prop != BEAR_DEAD) {
- game.objects[BEAR].prop = CONTENTED_BEAR;
- }
+static phase_codes_t 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 (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
+ obj = ROD2;
+ }
- switch (game.objects[BEAR].prop) {
- // LCOV_EXCL_START
- case BEAR_DEAD:
- /* Can't be reached until the bear can die in some way
- * other than a bridge collapse. Leave in in case this
- * changes, but exclude from coverage testing. */
- game.objects[BEAR].fixed = IS_FIXED;
- break;
- // LCOV_EXCL_STOP
- default:
- game.objects[BEAR].fixed = IS_FREE;
- }
- rspeak(CHAIN_UNLOCKED);
- return GO_CLEAROBJ;
- }
+ if (!TOTING(obj)) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ }
- if (game.objects[CHAIN].prop != CHAIN_HEAP) {
- rspeak(ALREADY_LOCKED);
- return GO_CLEAROBJ;
- }
- if (game.loc != objects[CHAIN].plac) {
- rspeak(NO_LOCKSITE);
- return GO_CLEAROBJ;
- }
+ if (GSTONE(obj) && AT(CAVITY) && game.objects[CAVITY].prop != CAVITY_FULL) {
+ rspeak(GEM_FITS);
+ game.objects[obj].prop = STATE_IN_CAVITY;
+ game.objects[CAVITY].prop = CAVITY_FULL;
+ if (HERE(RUG) && ((obj == EMERALD && game.objects[RUG].prop != RUG_HOVER) ||
+ (obj == RUBY && game.objects[RUG].prop == 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.objects[RUG].prop == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER;
+ game.objects[RUG].prop = k;
+ if (k == RUG_HOVER)
+ k = objects[SAPPH].plac;
+ move(RUG + NOBJECTS, k);
+ }
+ }
+ drop(obj, game.loc);
+ return GO_CLEAROBJ;
+ }
- game.objects[CHAIN].prop = CHAIN_FIXED;
+ if (obj == COINS && HERE(VEND)) {
+ DESTROY(COINS);
+ drop(BATTERY, game.loc);
+ pspeak(BATTERY, look, true, FRESH_BATTERIES);
+ return GO_CLEAROBJ;
+ }
- if (TOTING(CHAIN)) {
- drop(CHAIN, game.loc);
- }
- game.objects[CHAIN].fixed = IS_FIXED;
+ if (LIQUID() == obj)
+ obj = BOTTLE;
+ if (obj == BOTTLE && LIQUID() != NO_OBJECT) {
+ game.objects[LIQUID()].place = LOC_NOWHERE;
+ }
- rspeak(CHAIN_LOCKED);
- return GO_CLEAROBJ;
+ 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);
+ drop(obj, game.loc);
+ return GO_CLEAROBJ;
+ }
+
+ if (obj == VASE) {
+ if (game.loc != objects[PILLOW].plac) {
+ state_change(VASE, AT(PILLOW)
+ ? VASE_WHOLE
+ : VASE_DROPPED);
+ if (game.objects[VASE].prop != VASE_WHOLE)
+ game.objects[VASE].fixed = IS_FIXED;
+ drop(obj, game.loc);
+ return GO_CLEAROBJ;
+ }
+ }
+
+ if (obj == CAGE && game.objects[BIRD].prop == BIRD_CAGED) {
+ drop(BIRD, game.loc);
+ }
+
+ if (obj == BIRD) {
+ if (AT(DRAGON) && game.objects[DRAGON].prop == 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.objects[SNAKE].prop = SNAKE_CHASED;
+ } else
+ rspeak(OK_MAN);
+
+ game.objects[BIRD].prop = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
+ drop(obj, game.loc);
+ return GO_CLEAROBJ;
+ }
+
+ rspeak(OK_MAN);
+ drop(obj, game.loc);
+ return GO_CLEAROBJ;
}
-static phase_codes_t 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 (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
- obj = ROD2;
- }
+static phase_codes_t 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 == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
+ (LIQUID() != WATER || !HERE(BOTTLE))) {
+ return GO_UNKNOWN;
+ }
- if (!TOTING(obj)) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- }
+ if (obj == BLOOD) {
+ DESTROY(BLOOD);
+ state_change(DRAGON, DRAGON_BLOODLESS);
+ game.blooded = true;
+ return GO_CLEAROBJ;
+ }
- if (GSTONE(obj) && AT(CAVITY) &&
- game.objects[CAVITY].prop != CAVITY_FULL) {
- rspeak(GEM_FITS);
- game.objects[obj].prop = STATE_IN_CAVITY;
- game.objects[CAVITY].prop = CAVITY_FULL;
- if (HERE(RUG) &&
- ((obj == EMERALD && game.objects[RUG].prop != RUG_HOVER) ||
- (obj == RUBY && game.objects[RUG].prop == 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.objects[RUG].prop == RUG_HOVER)
- ? RUG_FLOOR
- : RUG_HOVER;
- game.objects[RUG].prop = k;
- if (k == RUG_HOVER) {
- k = objects[SAPPH].plac;
- }
- move(RUG + NOBJECTS, k);
- }
- }
- drop(obj, game.loc);
- return GO_CLEAROBJ;
- }
+ if (obj != INTRANSITIVE && obj != WATER) {
+ rspeak(RIDICULOUS_ATTEMPT);
+ return GO_CLEAROBJ;
+ }
+ if (LIQUID() == WATER && HERE(BOTTLE)) {
+ game.objects[WATER].place = LOC_NOWHERE;
+ state_change(BOTTLE, EMPTY_BOTTLE);
+ return GO_CLEAROBJ;
+ }
- if (obj == COINS && HERE(VEND)) {
- DESTROY(COINS);
- drop(BATTERY, game.loc);
- pspeak(BATTERY, look, true, FRESH_BATTERIES);
- return GO_CLEAROBJ;
- }
-
- if (LIQUID() == obj) {
- obj = BOTTLE;
- }
- if (obj == BOTTLE && LIQUID() != NO_OBJECT) {
- game.objects[LIQUID()].place = LOC_NOWHERE;
- }
-
- 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);
- drop(obj, game.loc);
- return GO_CLEAROBJ;
- }
-
- if (obj == VASE) {
- if (game.loc != objects[PILLOW].plac) {
- state_change(VASE,
- AT(PILLOW) ? VASE_WHOLE : VASE_DROPPED);
- if (game.objects[VASE].prop != VASE_WHOLE) {
- game.objects[VASE].fixed = IS_FIXED;
- }
- drop(obj, game.loc);
- return GO_CLEAROBJ;
- }
- }
-
- if (obj == CAGE && game.objects[BIRD].prop == BIRD_CAGED) {
- drop(BIRD, game.loc);
- }
-
- if (obj == BIRD) {
- if (AT(DRAGON) && game.objects[DRAGON].prop == 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.objects[SNAKE].prop = SNAKE_CHASED;
- } else {
- rspeak(OK_MAN);
- }
-
- game.objects[BIRD].prop =
- FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
- drop(obj, game.loc);
- return GO_CLEAROBJ;
- }
-
- rspeak(OK_MAN);
- drop(obj, game.loc);
- return GO_CLEAROBJ;
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
}
-static phase_codes_t 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 == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
- (LIQUID() != WATER || !HERE(BOTTLE))) {
- return GO_UNKNOWN;
- }
-
- if (obj == BLOOD) {
- DESTROY(BLOOD);
- state_change(DRAGON, DRAGON_BLOODLESS);
- game.blooded = true;
- return GO_CLEAROBJ;
- }
-
- if (obj != INTRANSITIVE && obj != WATER) {
- rspeak(RIDICULOUS_ATTEMPT);
- return GO_CLEAROBJ;
- }
- if (LIQUID() == WATER && HERE(BOTTLE)) {
- game.objects[WATER].place = LOC_NOWHERE;
- state_change(BOTTLE, EMPTY_BOTTLE);
- return GO_CLEAROBJ;
- }
-
- speak(actions[verb].message);
- return GO_CLEAROBJ;
+static phase_codes_t 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. */
+{
+ switch (obj) {
+ case INTRANSITIVE:
+ if (!HERE(FOOD))
+ return GO_UNKNOWN;
+ /* FALLTHRU */
+ case FOOD:
+ DESTROY(FOOD);
+ rspeak(THANKS_DELICIOUS);
+ break;
+ case BIRD:
+ case SNAKE:
+ case CLAM:
+ case OYSTER:
+ case DWARF:
+ case DRAGON:
+ case TROLL:
+ case BEAR:
+ case OGRE:
+ rspeak(LOST_APPETITE);
+ break;
+ default:
+ speak(actions[verb].message);
+ }
+ return GO_CLEAROBJ;
}
-static phase_codes_t 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.
- */
- switch (obj) {
- case INTRANSITIVE:
- if (!HERE(FOOD)) {
- return GO_UNKNOWN;
- }
- /* FALLTHRU */
- case FOOD:
- DESTROY(FOOD);
- rspeak(THANKS_DELICIOUS);
- break;
- case BIRD:
- case SNAKE:
- case CLAM:
- case OYSTER:
- case DWARF:
- case DRAGON:
- case TROLL:
- case BEAR:
- case OGRE:
- rspeak(LOST_APPETITE);
- break;
- default:
- speak(actions[verb].message);
- }
- return GO_CLEAROBJ;
+static phase_codes_t extinguish(verb_t verb, obj_t obj)
+/* Extinguish. Lamp, urn, dragon/volcano (nice try). */
+{
+ if (obj == INTRANSITIVE) {
+ if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_BRIGHT)
+ obj = LAMP;
+ if (HERE(URN) && game.objects[URN].prop == URN_LIT)
+ obj = URN;
+ if (obj == INTRANSITIVE)
+ return GO_UNKNOWN;
+ }
+
+ switch (obj) {
+ case URN:
+ if (game.objects[URN].prop != URN_EMPTY) {
+ state_change(URN, URN_DARK);
+ } else {
+ pspeak(URN, change, true, URN_DARK);
+ }
+ break;
+ case LAMP:
+ state_change(LAMP, LAMP_DARK);
+ rspeak(DARK(game.loc) ?
+ PITCH_DARK :
+ NO_MESSAGE);
+ break;
+ case DRAGON:
+ case VOLCANO:
+ rspeak(BEYOND_POWER);
+ break;
+ default:
+ speak(actions[verb].message);
+ }
+ return GO_CLEAROBJ;
}
-static phase_codes_t extinguish(verb_t verb, obj_t obj) {
- /* Extinguish. Lamp, urn, dragon/volcano (nice try). */
- if (obj == INTRANSITIVE) {
- if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_BRIGHT) {
- obj = LAMP;
- }
- if (HERE(URN) && game.objects[URN].prop == URN_LIT) {
- obj = URN;
- }
- if (obj == INTRANSITIVE) {
- return GO_UNKNOWN;
- }
- }
-
- switch (obj) {
- case URN:
- if (game.objects[URN].prop != URN_EMPTY) {
- state_change(URN, URN_DARK);
- } else {
- pspeak(URN, change, true, URN_DARK);
- }
- break;
- case LAMP:
- state_change(LAMP, LAMP_DARK);
- rspeak(IS_DARK_HERE() ? PITCH_DARK : NO_MESSAGE);
- break;
- case DRAGON:
- case VOLCANO:
- rspeak(BEYOND_POWER);
- break;
- default:
- speak(actions[verb].message);
- }
- return GO_CLEAROBJ;
+static phase_codes_t feed(verb_t verb, obj_t obj)
+/* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf, make him
+ * mad. Bear, special. */
+{
+ switch (obj) {
+ case BIRD:
+ rspeak(BIRD_PINING);
+ break;
+ case DRAGON:
+ if (game.objects[DRAGON].prop != DRAGON_BARS)
+ rspeak(RIDICULOUS_ATTEMPT);
+ else
+ rspeak(NOTHING_EDIBLE);
+ break;
+ case SNAKE:
+ if (!game.closed && HERE(BIRD)) {
+ DESTROY(BIRD);
+ rspeak(BIRD_DEVOURED);
+ } else
+ rspeak(NOTHING_EDIBLE);
+ break;
+ case TROLL:
+ rspeak(TROLL_VICES);
+ break;
+ case DWARF:
+ if (HERE(FOOD)) {
+ game.dflag += 2;
+ rspeak(REALLY_MAD);
+ } else
+ speak(actions[verb].message);
+ break;
+ case BEAR:
+ if (game.objects[BEAR].prop == BEAR_DEAD) {
+ rspeak(RIDICULOUS_ATTEMPT);
+ break;
+ }
+ if (game.objects[BEAR].prop == UNTAMED_BEAR) {
+ if (HERE(FOOD)) {
+ DESTROY(FOOD);
+ game.objects[AXE].fixed = IS_FREE;
+ game.objects[AXE].prop = AXE_HERE;
+ state_change(BEAR, SITTING_BEAR);
+ } else
+ rspeak(NOTHING_EDIBLE);
+ break;
+ }
+ speak(actions[verb].message);
+ break;
+ case OGRE:
+ if (HERE(FOOD))
+ rspeak(OGRE_FULL);
+ else
+ speak(actions[verb].message);
+ break;
+ default:
+ rspeak(AM_GAME);
+ }
+ return GO_CLEAROBJ;
}
-static phase_codes_t feed(verb_t verb, obj_t obj) {
- /* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf,
- * make him mad. Bear, special. */
- switch (obj) {
- case BIRD:
- rspeak(BIRD_PINING);
- break;
- case DRAGON:
- if (game.objects[DRAGON].prop != DRAGON_BARS) {
- rspeak(RIDICULOUS_ATTEMPT);
- } else {
- rspeak(NOTHING_EDIBLE);
- }
- break;
- case SNAKE:
- if (!game.closed && HERE(BIRD)) {
- DESTROY(BIRD);
- rspeak(BIRD_DEVOURED);
- } else {
- rspeak(NOTHING_EDIBLE);
- }
- break;
- case TROLL:
- rspeak(TROLL_VICES);
- break;
- case DWARF:
- if (HERE(FOOD)) {
- game.dflag += 2;
- rspeak(REALLY_MAD);
- } else {
- speak(actions[verb].message);
- }
- break;
- case BEAR:
- if (game.objects[BEAR].prop == BEAR_DEAD) {
- rspeak(RIDICULOUS_ATTEMPT);
- break;
- }
- if (game.objects[BEAR].prop == UNTAMED_BEAR) {
- if (HERE(FOOD)) {
- DESTROY(FOOD);
- game.objects[AXE].fixed = IS_FREE;
- game.objects[AXE].prop = AXE_HERE;
- state_change(BEAR, SITTING_BEAR);
- } else {
- rspeak(NOTHING_EDIBLE);
- }
- break;
- }
- speak(actions[verb].message);
- break;
- case OGRE:
- if (HERE(FOOD)) {
- rspeak(OGRE_FULL);
- } else {
- speak(actions[verb].message);
- }
- break;
- default:
- rspeak(AM_GAME);
- }
- return GO_CLEAROBJ;
+phase_codes_t fill(verb_t verb, obj_t obj)
+/* Fill. Bottle or urn must be empty, and liquid available. (Vase
+ * is nasty.) */
+{
+ if (obj == VASE) {
+ if (LIQLOC(game.loc) == NO_OBJECT) {
+ rspeak(FILL_INVALID);
+ return GO_CLEAROBJ;
+ }
+ if (!TOTING(VASE)) {
+ rspeak(ARENT_CARRYING);
+ return GO_CLEAROBJ;
+ }
+ rspeak(SHATTER_VASE);
+ game.objects[VASE].prop = VASE_BROKEN;
+ game.objects[VASE].fixed = IS_FIXED;
+ drop(VASE, game.loc);
+ return GO_CLEAROBJ;
+ }
+
+ if (obj == URN) {
+ if (game.objects[URN].prop != URN_EMPTY) {
+ rspeak(FULL_URN);
+ return GO_CLEAROBJ;
+ }
+ if (!HERE(BOTTLE)) {
+ rspeak(FILL_INVALID);
+ return GO_CLEAROBJ;
+ }
+ int k = LIQUID();
+ switch (k) {
+ case WATER:
+ game.objects[BOTTLE].prop = EMPTY_BOTTLE;
+ rspeak(WATER_URN);
+ break;
+ case OIL:
+ game.objects[URN].prop = URN_DARK;
+ game.objects[BOTTLE].prop = EMPTY_BOTTLE;
+ rspeak(OIL_URN);
+ break;
+ case NO_OBJECT:
+ default:
+ rspeak(FILL_INVALID);
+ return GO_CLEAROBJ;
+ }
+ game.objects[k].place = LOC_NOWHERE;
+ return GO_CLEAROBJ;
+ }
+ if (obj != INTRANSITIVE && obj != BOTTLE) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ }
+ if (obj == INTRANSITIVE && !HERE(BOTTLE))
+ return GO_UNKNOWN;
+
+ if (HERE(URN) && game.objects[URN].prop != URN_EMPTY) {
+ rspeak(URN_NOPOUR);
+ return GO_CLEAROBJ;
+ }
+ if (LIQUID() != NO_OBJECT) {
+ rspeak(BOTTLE_FULL);
+ return GO_CLEAROBJ;
+ }
+ if (LIQLOC(game.loc) == NO_OBJECT) {
+ rspeak(NO_LIQUID);
+ return GO_CLEAROBJ;
+ }
+
+ state_change(BOTTLE, (LIQLOC(game.loc) == OIL)
+ ? OIL_BOTTLE
+ : WATER_BOTTLE);
+ if (TOTING(BOTTLE))
+ game.objects[LIQUID()].place = CARRIED;
+ return GO_CLEAROBJ;
}
-phase_codes_t fill(verb_t verb, obj_t obj) {
- /* Fill. Bottle or urn must be empty, and liquid available. (Vase
- * is nasty.) */
- if (obj == VASE) {
- if (LIQLOC(game.loc) == NO_OBJECT) {
- rspeak(FILL_INVALID);
- return GO_CLEAROBJ;
- }
- if (!TOTING(VASE)) {
- rspeak(ARENT_CARRYING);
- return GO_CLEAROBJ;
- }
- rspeak(SHATTER_VASE);
- game.objects[VASE].prop = VASE_BROKEN;
- game.objects[VASE].fixed = IS_FIXED;
- drop(VASE, game.loc);
- return GO_CLEAROBJ;
- }
+static phase_codes_t find(verb_t verb, obj_t obj)
+/* Find. Might be carrying it, or it might be here. Else give caveat. */
+{
+ if (TOTING(obj)) {
+ rspeak(ALREADY_CARRYING);
+ return GO_CLEAROBJ;
+ }
- if (obj == URN) {
- if (game.objects[URN].prop != URN_EMPTY) {
- rspeak(FULL_URN);
- return GO_CLEAROBJ;
- }
- if (!HERE(BOTTLE)) {
- rspeak(FILL_INVALID);
- return GO_CLEAROBJ;
- }
- int k = LIQUID();
- switch (k) {
- case WATER:
- game.objects[BOTTLE].prop = EMPTY_BOTTLE;
- rspeak(WATER_URN);
- break;
- case OIL:
- game.objects[URN].prop = URN_DARK;
- game.objects[BOTTLE].prop = EMPTY_BOTTLE;
- rspeak(OIL_URN);
- break;
- case NO_OBJECT:
- default:
- rspeak(FILL_INVALID);
- return GO_CLEAROBJ;
- }
- game.objects[k].place = LOC_NOWHERE;
- return GO_CLEAROBJ;
- }
- if (obj != INTRANSITIVE && obj != BOTTLE) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- }
- if (obj == INTRANSITIVE && !HERE(BOTTLE)) {
- return GO_UNKNOWN;
- }
+ if (game.closed) {
+ rspeak(NEEDED_NEARBY);
+ return GO_CLEAROBJ;
+ }
- if (HERE(URN) && game.objects[URN].prop != URN_EMPTY) {
- rspeak(URN_NOPOUR);
- return GO_CLEAROBJ;
- }
- if (LIQUID() != NO_OBJECT) {
- rspeak(BOTTLE_FULL);
- return GO_CLEAROBJ;
- }
- if (LIQLOC(game.loc) == NO_OBJECT) {
- rspeak(NO_LIQUID);
- return GO_CLEAROBJ;
- }
+ if (AT(obj) || (LIQUID() == obj && AT(BOTTLE)) ||
+ obj == LIQLOC(game.loc) || (obj == DWARF && atdwrf(game.loc) > 0)) {
+ rspeak(YOU_HAVEIT);
+ return GO_CLEAROBJ;
+ }
- state_change(BOTTLE,
- (LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE);
- if (TOTING(BOTTLE)) {
- game.objects[LIQUID()].place = CARRIED;
- }
- return GO_CLEAROBJ;
+
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
}
-static phase_codes_t find(verb_t verb, obj_t obj) {
- /* Find. Might be carrying it, or it might be here. Else give caveat.
- */
- if (TOTING(obj)) {
- rspeak(ALREADY_CARRYING);
- return GO_CLEAROBJ;
- }
+static phase_codes_t fly(verb_t verb, obj_t obj)
+/* Fly. Snide remarks unless hovering rug is here. */
+{
+ if (obj == INTRANSITIVE) {
+ if (!HERE(RUG)) {
+ rspeak(FLAP_ARMS);
+ return GO_CLEAROBJ;
+ }
+ if (game.objects[RUG].prop != RUG_HOVER) {
+ rspeak(RUG_NOTHING2);
+ return GO_CLEAROBJ;
+ }
+ obj = RUG;
+ }
- if (game.closed) {
- rspeak(NEEDED_NEARBY);
- return GO_CLEAROBJ;
- }
+ if (obj != RUG) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ }
+ if (game.objects[RUG].prop != RUG_HOVER) {
+ rspeak(RUG_NOTHING1);
+ return GO_CLEAROBJ;
+ }
- if (AT(obj) || (LIQUID() == obj && AT(BOTTLE)) ||
- obj == LIQLOC(game.loc) || (obj == DWARF && atdwrf(game.loc) > 0)) {
- rspeak(YOU_HAVEIT);
- return GO_CLEAROBJ;
- }
-
- speak(actions[verb].message);
- return GO_CLEAROBJ;
+ if (game.loc == LOC_CLIFF) {
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ game.newloc = LOC_LEDGE;
+ rspeak(RUG_GOES);
+ } else if (game.loc == LOC_LEDGE) {
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ game.newloc = LOC_CLIFF;
+ rspeak(RUG_RETURNS);
+ } else {
+// LCOV_EXCL_START
+ /* should never happen */
+ rspeak(NOTHING_HAPPENS);
+// LCOV_EXCL_STOP
+ }
+ return GO_TERMINATE;
}
-static phase_codes_t fly(verb_t verb, obj_t obj) {
- /* Fly. Snide remarks unless hovering rug is here. */
- if (obj == INTRANSITIVE) {
- if (!HERE(RUG)) {
- rspeak(FLAP_ARMS);
- return GO_CLEAROBJ;
- }
- if (game.objects[RUG].prop != RUG_HOVER) {
- rspeak(RUG_NOTHING2);
- return GO_CLEAROBJ;
- }
- obj = RUG;
- }
-
- if (obj != RUG) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- }
- if (game.objects[RUG].prop != RUG_HOVER) {
- rspeak(RUG_NOTHING1);
- return GO_CLEAROBJ;
- }
-
- if (game.loc == LOC_CLIFF) {
- game.oldlc2 = game.oldloc;
- game.oldloc = game.loc;
- game.newloc = LOC_LEDGE;
- rspeak(RUG_GOES);
- } else if (game.loc == LOC_LEDGE) {
- game.oldlc2 = game.oldloc;
- game.oldloc = game.loc;
- game.newloc = LOC_CLIFF;
- rspeak(RUG_RETURNS);
- } else {
- // LCOV_EXCL_START
- /* should never happen */
- rspeak(NOTHING_HAPPENS);
- // LCOV_EXCL_STOP
- }
- return GO_TERMINATE;
+static phase_codes_t inven(void)
+/* Inventory. If object, treat same as find. Else report on current burden. */
+{
+ bool empty = true;
+ for (obj_t i = 1; i <= NOBJECTS; i++) {
+ if (i == BEAR || !TOTING(i))
+ continue;
+ if (empty) {
+ rspeak(NOW_HOLDING);
+ empty = false;
+ }
+ pspeak(i, touch, false, -1);
+ }
+ if (TOTING(BEAR))
+ rspeak(TAME_BEAR);
+ if (empty)
+ rspeak(NO_CARRY);
+ return GO_CLEAROBJ;
}
-static phase_codes_t inven(void) {
- /* Inventory. If object, treat same as find. Else report on current
- * burden. */
- bool empty = true;
- for (obj_t i = 1; i <= NOBJECTS; i++) {
- if (i == BEAR || !TOTING(i)) {
- continue;
- }
- if (empty) {
- rspeak(NOW_HOLDING);
- empty = false;
- }
- pspeak(i, touch, false, -1);
- }
- if (TOTING(BEAR)) {
- rspeak(TAME_BEAR);
- }
- if (empty) {
- rspeak(NO_CARRY);
- }
- return GO_CLEAROBJ;
+static phase_codes_t light(verb_t verb, obj_t obj)
+/* Light. Applicable only to lamp and urn. */
+{
+ if (obj == INTRANSITIVE) {
+ int selects = 0;
+ if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_DARK && game.limit >= 0) {
+ obj = LAMP;
+ selects++;
+ }
+ if (HERE(URN) && game.objects[URN].prop == URN_DARK) {
+ obj = URN;
+ selects++;
+ }
+ if (selects != 1)
+ return GO_UNKNOWN;
+ }
+
+ switch (obj) {
+ case URN:
+ state_change(URN, game.objects[URN].prop == URN_EMPTY ?
+ URN_EMPTY :
+ URN_LIT);
+ break;
+ case LAMP:
+ if (game.limit < 0) {
+ rspeak(LAMP_OUT);
+ break;
+ }
+ state_change(LAMP, LAMP_BRIGHT);
+ if (game.wzdark)
+ return GO_TOP;
+ break;
+ default:
+ speak(actions[verb].message);
+ }
+ return GO_CLEAROBJ;
}
-static phase_codes_t light(verb_t verb, obj_t obj) {
- /* Light. Applicable only to lamp and urn. */
- if (obj == INTRANSITIVE) {
- int selects = 0;
- if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_DARK &&
- game.limit >= 0) {
- obj = LAMP;
- selects++;
- }
- if (HERE(URN) && game.objects[URN].prop == URN_DARK) {
- obj = URN;
- selects++;
- }
- if (selects != 1) {
- return GO_UNKNOWN;
- }
- }
-
- switch (obj) {
- case URN:
- state_change(URN, game.objects[URN].prop == URN_EMPTY
- ? URN_EMPTY
- : URN_LIT);
- break;
- case LAMP:
- if (game.limit < 0) {
- rspeak(LAMP_OUT);
- break;
- }
- state_change(LAMP, LAMP_BRIGHT);
- if (game.wzdark) {
- return GO_TOP;
- }
- break;
- default:
- speak(actions[verb].message);
- }
- return GO_CLEAROBJ;
+static phase_codes_t listen(void)
+/* Listen. Intransitive only. Print stuff based on object sound properties. */
+{
+ bool soundlatch = false;
+ vocab_t sound = locations[game.loc].sound;
+ if (sound != SILENT) {
+ rspeak(sound);
+ if (!locations[game.loc].loud)
+ rspeak(NO_MESSAGE);
+ soundlatch = true;
+ }
+ for (obj_t i = 1; i <= NOBJECTS; i++) {
+ if (!HERE(i) || objects[i].sounds[0] == NULL || PROP_IS_STASHED_OR_UNSEEN(i))
+ continue;
+ int mi = game.objects[i].prop;
+ /* (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;
+ pspeak(i, hear, true, mi, game.zzword);
+ rspeak(NO_MESSAGE);
+ if (i == BIRD && mi == BIRD_ENDSTATE)
+ DESTROY(BIRD);
+ soundlatch = true;
+ }
+ if (!soundlatch)
+ rspeak(ALL_SILENT);
+ return GO_CLEAROBJ;
}
-static phase_codes_t listen(void) {
- /* Listen. Intransitive only. Print stuff based on object sound
- * properties. */
- bool soundlatch = false;
- vocab_t sound = locations[game.loc].sound;
- if (sound != SILENT) {
- rspeak(sound);
- if (!locations[game.loc].loud) {
- rspeak(NO_MESSAGE);
- }
- soundlatch = true;
- }
- for (obj_t i = 1; i <= NOBJECTS; i++) {
- if (!HERE(i) || objects[i].sounds[0] == NULL ||
- OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(i)) {
- continue;
- }
- int mi = game.objects[i].prop;
- /* (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;
- }
- pspeak(i, hear, true, mi, game.zzword);
- rspeak(NO_MESSAGE);
- if (i == BIRD && mi == BIRD_ENDSTATE) {
- DESTROY(BIRD);
- }
- soundlatch = true;
- }
- if (!soundlatch) {
- rspeak(ALL_SILENT);
- }
- return GO_CLEAROBJ;
+static phase_codes_t lock(verb_t verb, obj_t obj)
+/* Lock, unlock, no object given. Assume various things if present. */
+{
+ if (obj == INTRANSITIVE) {
+ 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;
+ if (obj == INTRANSITIVE) {
+ rspeak(NOTHING_LOCKED);
+ return GO_CLEAROBJ;
+ }
+ }
+
+ /* Lock, unlock object. Special stuff for opening clam/oyster
+ * and for chain. */
+
+ switch (obj) {
+ case CHAIN:
+ if (HERE(KEYS)) {
+ return chain(verb);
+ } else
+ rspeak(NO_KEYS);
+ break;
+ case GRATE:
+ if (HERE(KEYS)) {
+ if (game.closng) {
+ rspeak(EXIT_CLOSED);
+ if (!game.panic)
+ game.clock2 = PANICTIME;
+ game.panic = true;
+ } else {
+ state_change(GRATE, (verb == LOCK) ?
+ GRATE_CLOSED :
+ GRATE_OPEN);
+ }
+ } else
+ rspeak(NO_KEYS);
+ break;
+ case CLAM:
+ if (verb == LOCK)
+ rspeak(HUH_MAN);
+ else if (TOTING(CLAM))
+ rspeak(DROP_CLAM);
+ else if (!TOTING(TRIDENT))
+ rspeak(CLAM_OPENER);
+ else {
+ DESTROY(CLAM);
+ drop(OYSTER, game.loc);
+ drop(PEARL, LOC_CULDESAC);
+ rspeak(PEARL_FALLS);
+ }
+ break;
+ case OYSTER:
+ if (verb == LOCK)
+ rspeak(HUH_MAN);
+ else if (TOTING(OYSTER))
+ rspeak(DROP_OYSTER);
+ else if (!TOTING(TRIDENT))
+ rspeak(OYSTER_OPENER);
+ else
+ rspeak(OYSTER_OPENS);
+ break;
+ case DOOR:
+ rspeak((game.objects[DOOR].prop == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR);
+ break;
+ case CAGE:
+ rspeak( NO_LOCK);
+ break;
+ case KEYS:
+ rspeak(CANNOT_UNLOCK);
+ break;
+ default:
+ speak(actions[verb].message);
+ }
+
+ return GO_CLEAROBJ;
}
-static phase_codes_t lock(verb_t verb, obj_t obj) {
- /* Lock, unlock, no object given. Assume various things if present. */
- if (obj == INTRANSITIVE) {
- 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;
- }
- if (obj == INTRANSITIVE) {
- rspeak(NOTHING_LOCKED);
- return GO_CLEAROBJ;
- }
- }
+static phase_codes_t 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 == INTRANSITIVE)
+ obj = LIQUID();
+ if (obj == NO_OBJECT)
+ return GO_UNKNOWN;
+ if (!TOTING(obj)) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ }
- /* Lock, unlock object. Special stuff for opening clam/oyster
- * and for chain. */
-
- switch (obj) {
- case CHAIN:
- if (HERE(KEYS)) {
- return chain(verb);
- } else {
- rspeak(NO_KEYS);
- }
- break;
- case GRATE:
- if (HERE(KEYS)) {
- if (game.closng) {
- rspeak(EXIT_CLOSED);
- if (!game.panic) {
- game.clock2 = PANICTIME;
- }
- game.panic = true;
- } else {
- state_change(GRATE, (verb == LOCK)
- ? GRATE_CLOSED
- : GRATE_OPEN);
- }
- } else {
- rspeak(NO_KEYS);
- }
- break;
- case CLAM:
- if (verb == LOCK) {
- rspeak(HUH_MAN);
- } else if (TOTING(CLAM)) {
- rspeak(DROP_CLAM);
- } else if (!TOTING(TRIDENT)) {
- rspeak(CLAM_OPENER);
- } else {
- DESTROY(CLAM);
- drop(OYSTER, game.loc);
- drop(PEARL, LOC_CULDESAC);
- rspeak(PEARL_FALLS);
- }
- break;
- case OYSTER:
- if (verb == LOCK) {
- rspeak(HUH_MAN);
- } else if (TOTING(OYSTER)) {
- rspeak(DROP_OYSTER);
- } else if (!TOTING(TRIDENT)) {
- rspeak(OYSTER_OPENER);
- } else {
- rspeak(OYSTER_OPENS);
- }
- break;
- case DOOR:
- rspeak((game.objects[DOOR].prop == DOOR_UNRUSTED) ? OK_MAN
- : RUSTY_DOOR);
- break;
- case CAGE:
- rspeak(NO_LOCK);
- break;
- case KEYS:
- rspeak(CANNOT_UNLOCK);
- break;
- default:
- speak(actions[verb].message);
- }
-
- return GO_CLEAROBJ;
+ if (obj != OIL && obj != WATER) {
+ rspeak(CANT_POUR);
+ return GO_CLEAROBJ;
+ }
+ if (HERE(URN) && game.objects[URN].prop == URN_EMPTY)
+ return fill(verb, URN);
+ game.objects[BOTTLE].prop = EMPTY_BOTTLE;
+ game.objects[obj].place = LOC_NOWHERE;
+ if (!(AT(PLANT) || AT(DOOR))) {
+ rspeak(GROUND_WET);
+ return GO_CLEAROBJ;
+ }
+ if (!AT(DOOR)) {
+ if (obj == WATER) {
+ /* cycle through the three plant states */
+ state_change(PLANT, MOD(game.objects[PLANT].prop + 1, 3));
+ game.objects[PLANT2].prop = game.objects[PLANT].prop;
+ return GO_MOVE;
+ } else {
+ rspeak(SHAKING_LEAVES);
+ return GO_CLEAROBJ;
+ }
+ } else {
+ state_change(DOOR, (obj == OIL) ?
+ DOOR_UNRUSTED :
+ DOOR_RUSTED);
+ return GO_CLEAROBJ;
+ }
}
-static phase_codes_t 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 == INTRANSITIVE) {
- obj = LIQUID();
- }
- if (obj == NO_OBJECT) {
- return GO_UNKNOWN;
- }
- if (!TOTING(obj)) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- }
-
- if (obj != OIL && obj != WATER) {
- rspeak(CANT_POUR);
- return GO_CLEAROBJ;
- }
- if (HERE(URN) && game.objects[URN].prop == URN_EMPTY) {
- return fill(verb, URN);
- }
- game.objects[BOTTLE].prop = EMPTY_BOTTLE;
- game.objects[obj].place = LOC_NOWHERE;
- if (!(AT(PLANT) || AT(DOOR))) {
- rspeak(GROUND_WET);
- return GO_CLEAROBJ;
- }
- if (!AT(DOOR)) {
- if (obj == WATER) {
- /* cycle through the three plant states */
- state_change(PLANT,
- MOD(game.objects[PLANT].prop + 1, 3));
- game.objects[PLANT2].prop = game.objects[PLANT].prop;
- return GO_MOVE;
- } else {
- rspeak(SHAKING_LEAVES);
- return GO_CLEAROBJ;
- }
- } else {
- state_change(DOOR, (obj == OIL) ? DOOR_UNRUSTED : DOOR_RUSTED);
- return GO_CLEAROBJ;
- }
-}
-
-static phase_codes_t quit(void) {
- /* Quit. Intransitive only. Verify intent and exit if that's what he
- * wants. */
- if (yes_or_no(arbitrary_messages[REALLY_QUIT],
- arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) {
- terminate(quitgame);
- }
- return GO_CLEAROBJ;
+static phase_codes_t quit(void)
+/* Quit. Intransitive only. Verify intent and exit if that's what he wants. */
+{
+ if (yes_or_no(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
+ terminate(quitgame);
+ return GO_CLEAROBJ;
}
static phase_codes_t read(command_t command)
/* Read. Print stuff based on objtxt. Oyster (?) is special case. */
{
- if (command.obj == INTRANSITIVE) {
- command.obj = NO_OBJECT;
- for (int i = 1; i <= NOBJECTS; i++) {
- if (HERE(i) && objects[i].texts[0] != NULL &&
- !OBJECT_IS_STASHED(i)) {
- command.obj = command.obj * NOBJECTS + i;
- }
- }
- if (command.obj > NOBJECTS || command.obj == NO_OBJECT ||
- IS_DARK_HERE()) {
- return GO_UNKNOWN;
- }
- }
+ if (command.obj == INTRANSITIVE) {
+ command.obj = NO_OBJECT;
+ for (int i = 1; i <= NOBJECTS; i++) {
+ if (HERE(i) && objects[i].texts[0] != NULL && !PROP_IS_STASHED(i))
+ command.obj = command.obj * NOBJECTS + i;
+ }
+ if (command.obj > NOBJECTS || command.obj == NO_OBJECT || DARK(game.loc))
+ return GO_UNKNOWN;
+ }
- if (IS_DARK_HERE()) {
- sspeak(NO_SEE, command.word[0].raw);
- } else if (command.obj == OYSTER) {
- if (!TOTING(OYSTER) || !game.closed) {
- rspeak(DONT_UNDERSTAND);
- } else if (!game.clshnt) {
- game.clshnt = yes_or_no(arbitrary_messages[CLUE_QUERY],
- arbitrary_messages[WAYOUT_CLUE],
- arbitrary_messages[OK_MAN]);
- } else {
- pspeak(OYSTER, hear, true,
- 1); // Not really a sound, but oh well.
- }
- } else if (objects[command.obj].texts[0] == NULL ||
- OBJECT_IS_NOTFOUND(command.obj)) {
- speak(actions[command.verb].message);
- } else {
- pspeak(command.obj, study, true,
- game.objects[command.obj].prop);
- }
- return GO_CLEAROBJ;
+ if (DARK(game.loc)) {
+ sspeak(NO_SEE, command.word[0].raw);
+ } else if (command.obj == OYSTER) {
+ if (!TOTING(OYSTER) || !game.closed) {
+ rspeak(DONT_UNDERSTAND);
+ } else if (!game.clshnt) {
+ game.clshnt = yes_or_no(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
+ } else {
+ pspeak(OYSTER, hear, true, 1); // Not really a sound, but oh well.
+ }
+ } else if (objects[command.obj].texts[0] == NULL || PROP_IS_NOTFOUND(command.obj)) {
+ speak(actions[command.verb].message);
+ } else
+ pspeak(command.obj, study, true, game.objects[command.obj].prop);
+ return GO_CLEAROBJ;
}
-static phase_codes_t reservoir(void) {
- /* Z'ZZZ (word gets recomputed at startup; different each game). */
- if (!AT(RESER) && game.loc != LOC_RESBOTTOM) {
- rspeak(NOTHING_HAPPENS);
- return GO_CLEAROBJ;
- } else {
- state_change(RESER, game.objects[RESER].prop == WATERS_PARTED
- ? WATERS_UNPARTED
- : WATERS_PARTED);
- if (AT(RESER)) {
- return GO_CLEAROBJ;
- } else {
- game.oldlc2 = game.loc;
- game.newloc = LOC_NOWHERE;
- rspeak(NOT_BRIGHT);
- return GO_TERMINATE;
- }
- }
+static phase_codes_t reservoir(void)
+/* Z'ZZZ (word gets recomputed at startup; different each game). */
+{
+ if (!AT(RESER) && game.loc != LOC_RESBOTTOM) {
+ rspeak(NOTHING_HAPPENS);
+ return GO_CLEAROBJ;
+ } else {
+ state_change(RESER,
+ game.objects[RESER].prop == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED);
+ if (AT(RESER))
+ return GO_CLEAROBJ;
+ else {
+ game.oldlc2 = game.loc;
+ game.newloc = LOC_NOWHERE;
+ rspeak(NOT_BRIGHT);
+ return GO_TERMINATE;
+ }
+ }
}
-static phase_codes_t rub(verb_t verb, obj_t obj) {
- /* Rub. Yields various snide remarks except for lit urn. */
- if (obj == URN && game.objects[URN].prop == URN_LIT) {
- DESTROY(URN);
- drop(AMBER, game.loc);
- game.objects[AMBER].prop = AMBER_IN_ROCK;
- --game.tally;
- drop(CAVITY, game.loc);
- rspeak(URN_GENIES);
- } else if (obj != LAMP) {
- rspeak(PECULIAR_NOTHING);
- } else {
- speak(actions[verb].message);
- }
- return GO_CLEAROBJ;
+static phase_codes_t rub(verb_t verb, obj_t obj)
+/* Rub. Yields various snide remarks except for lit urn. */
+{
+ if (obj == URN && game.objects[URN].prop == URN_LIT) {
+ DESTROY(URN);
+ drop(AMBER, game.loc);
+ game.objects[AMBER].prop = AMBER_IN_ROCK;
+ --game.tally;
+ drop(CAVITY, game.loc);
+ rspeak(URN_GENIES);
+ } else if (obj != LAMP) {
+ rspeak(PECULIAR_NOTHING);
+ } else {
+ speak(actions[verb].message);
+ }
+ return GO_CLEAROBJ;
}
-static phase_codes_t say(command_t command) {
- /* Say. Echo WD2. Magic words override. */
- if (command.word[1].type == MOTION &&
- (command.word[1].id == XYZZY || command.word[1].id == PLUGH ||
- command.word[1].id == PLOVER)) {
- return GO_WORD2;
- }
- if (command.word[1].type == ACTION && command.word[1].id == PART) {
- return reservoir();
- }
+static phase_codes_t say(command_t command)
+/* Say. Echo WD2. Magic words override. */
+{
+ if (command.word[1].type == MOTION &&
+ (command.word[1].id == XYZZY ||
+ command.word[1].id == PLUGH ||
+ command.word[1].id == PLOVER)) {
+ return GO_WORD2;
+ }
+ if (command.word[1].type == ACTION && command.word[1].id == PART)
+ return reservoir();
- if (command.word[1].type == ACTION &&
- (command.word[1].id == FEE || command.word[1].id == FIE ||
- command.word[1].id == FOE || command.word[1].id == FOO ||
- command.word[1].id == FUM || command.word[1].id == PART)) {
- return bigwords(command.word[1].id);
- }
- sspeak(OKEY_DOKEY, command.word[1].raw);
- return GO_CLEAROBJ;
+ if (command.word[1].type == ACTION &&
+ (command.word[1].id == FEE ||
+ command.word[1].id == FIE ||
+ command.word[1].id == FOE ||
+ command.word[1].id == FOO ||
+ command.word[1].id == FUM ||
+ command.word[1].id == PART)) {
+ return bigwords(command.word[1].id);
+ }
+ sspeak(OKEY_DOKEY, command.word[1].raw);
+ return GO_CLEAROBJ;
}
-static phase_codes_t throw_support(vocab_t spk) {
- rspeak(spk);
- drop(AXE, game.loc);
- return GO_MOVE;
+static phase_codes_t throw_support(vocab_t spk)
+{
+ rspeak(spk);
+ drop(AXE, game.loc);
+ return GO_MOVE;
}
-static phase_codes_t throwit(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. */
- if (!TOTING(command.obj)) {
- 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, IS_FREE);
- drop(TROLL2, objects[TROLL].plac);
- drop(TROLL2 + NOBJECTS, objects[TROLL].fixd);
- juggle(CHASM);
- rspeak(TROLL_SATISFIED);
- return GO_CLEAROBJ;
- }
- if (command.obj == FOOD && HERE(BEAR)) {
- /* But throwing food is another story. */
- command.obj = BEAR;
- return (feed(command.verb, command.obj));
- }
- if (command.obj != AXE) {
- return (discard(command.verb, command.obj));
- } else {
- if (atdwrf(game.loc) <= 0) {
- if (AT(DRAGON) &&
- game.objects[DRAGON].prop == DRAGON_BARS) {
- return throw_support(DRAGON_SCALES);
- }
- if (AT(TROLL)) {
- return throw_support(TROLL_RETURNS);
- }
- if (AT(OGRE)) {
- return throw_support(OGRE_DODGE);
- }
- if (HERE(BEAR) &&
- game.objects[BEAR].prop == UNTAMED_BEAR) {
- /* This'll teach him to throw the axe at the
- * bear! */
- drop(AXE, game.loc);
- game.objects[AXE].fixed = IS_FIXED;
- juggle(BEAR);
- state_change(AXE, AXE_LOST);
- return GO_CLEAROBJ;
- }
- command.obj = INTRANSITIVE;
- return (attack(command));
- }
+static phase_codes_t throwit(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. */
+{
+ if (!TOTING(command.obj)) {
+ 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, IS_FREE);
+ drop(TROLL2, objects[TROLL].plac);
+ drop(TROLL2 + NOBJECTS, objects[TROLL].fixd);
+ juggle(CHASM);
+ rspeak(TROLL_SATISFIED);
+ return GO_CLEAROBJ;
+ }
+ if (command.obj == FOOD && HERE(BEAR)) {
+ /* But throwing food is another story. */
+ command.obj = BEAR;
+ return (feed(command.verb, command.obj));
+ }
+ if (command.obj != AXE)
+ return (discard(command.verb, command.obj));
+ else {
+ if (atdwrf(game.loc) <= 0) {
+ if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS)
+ return throw_support(DRAGON_SCALES);
+ if (AT(TROLL))
+ return throw_support(TROLL_RETURNS);
+ if (AT(OGRE))
+ return throw_support(OGRE_DODGE);
+ if (HERE(BEAR) && game.objects[BEAR].prop == UNTAMED_BEAR) {
+ /* This'll teach him to throw the axe at the bear! */
+ drop(AXE, game.loc);
+ game.objects[AXE].fixed = IS_FIXED;
+ juggle(BEAR);
+ state_change(AXE, AXE_LOST);
+ return GO_CLEAROBJ;
+ }
+ command.obj = INTRANSITIVE;
+ return (attack(command));
+ }
- if (randrange(NDWARVES + 1) < game.dflag) {
- return throw_support(DWARF_DODGES);
- } else {
- int i = atdwrf(game.loc);
- game.dwarves[i].seen = false;
- game.dwarves[i].loc = LOC_NOWHERE;
- return throw_support(
- (++game.dkill == 1) ? DWARF_SMOKE : KILLED_DWARF);
- }
- }
+ if (randrange(NDWARVES + 1) < game.dflag) {
+ return throw_support(DWARF_DODGES);
+ } else {
+ int i = atdwrf(game.loc);
+ game.dwarves[i].seen = false;
+ game.dwarves[i].loc = LOC_NOWHERE;
+ return throw_support((++game.dkill == 1) ?
+ DWARF_SMOKE :
+ KILLED_DWARF);
+ }
+ }
}
-static phase_codes_t wake(verb_t verb, obj_t obj) {
- /* Wake. Only use is to disturb the dwarves. */
- if (obj != DWARF || !game.closed) {
- speak(actions[verb].message);
- return GO_CLEAROBJ;
- } else {
- rspeak(PROD_DWARF);
- return GO_DWARFWAKE;
- }
+static phase_codes_t wake(verb_t verb, obj_t obj)
+/* Wake. Only use is to disturb the dwarves. */
+{
+ if (obj != DWARF || !game.closed) {
+ speak(actions[verb].message);
+ return GO_CLEAROBJ;
+ } else {
+ rspeak(PROD_DWARF);
+ return GO_DWARFWAKE;
+ }
}
-static phase_codes_t seed(verb_t verb, const char *arg) {
- /* Set seed */
- int32_t seed = strtol(arg, NULL, 10);
- speak(actions[verb].message, seed);
- set_seed(seed);
- --game.turns;
- return GO_TOP;
+static phase_codes_t seed(verb_t verb, const char *arg)
+/* Set seed */
+{
+ int32_t seed = strtol(arg, NULL, 10);
+ speak(actions[verb].message, seed);
+ set_seed(seed);
+ --game.turns;
+ return GO_TOP;
}
-static phase_codes_t waste(verb_t verb, turn_t turns) {
- /* Burn turns */
- game.limit -= turns;
- speak(actions[verb].message, (int)game.limit);
- return GO_TOP;
+static phase_codes_t waste(verb_t verb, turn_t turns)
+/* Burn turns */
+{
+ game.limit -= turns;
+ speak(actions[verb].message, (int)game.limit);
+ return GO_TOP;
}
-static phase_codes_t wave(verb_t verb, obj_t obj) {
- /* Wave. No effect unless waving rod at fissure or at bird. */
- if (obj != ROD || !TOTING(obj) ||
- (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
- speak(((!TOTING(obj)) && (obj != ROD || !TOTING(ROD2)))
- ? arbitrary_messages[ARENT_CARRYING]
- : actions[verb].message);
- return GO_CLEAROBJ;
- }
+static phase_codes_t wave(verb_t verb, obj_t obj)
+/* Wave. No effect unless waving rod at fissure or at bird. */
+{
+ if (obj != ROD || !TOTING(obj) || (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
+ speak(((!TOTING(obj)) && (obj != ROD ||
+ !TOTING(ROD2))) ?
+ arbitrary_messages[ARENT_CARRYING] :
+ actions[verb].message);
+ return GO_CLEAROBJ;
+ }
- if (game.objects[BIRD].prop == BIRD_UNCAGED &&
- game.loc == game.objects[STEPS].place && OBJECT_IS_NOTFOUND(JADE)) {
- drop(JADE, game.loc);
- OBJECT_SET_FOUND(JADE);
- --game.tally;
- rspeak(NECKLACE_FLY);
- return GO_CLEAROBJ;
- } else {
- if (game.closed) {
- rspeak((game.objects[BIRD].prop == BIRD_CAGED)
- ? CAGE_FLY
- : FREE_FLY);
- return GO_DWARFWAKE;
- }
- if (game.closng || !AT(FISSURE)) {
- rspeak((game.objects[BIRD].prop == BIRD_CAGED)
- ? CAGE_FLY
- : FREE_FLY);
- return GO_CLEAROBJ;
- }
- if (HERE(BIRD)) {
- rspeak((game.objects[BIRD].prop == BIRD_CAGED)
- ? CAGE_FLY
- : FREE_FLY);
- }
+ if (game.objects[BIRD].prop == BIRD_UNCAGED && game.loc == game.objects[STEPS].place
+ && PROP_IS_NOTFOUND(JADE)) {
+ drop(JADE, game.loc);
+ PROP_SET_FOUND(JADE);
+ --game.tally;
+ rspeak(NECKLACE_FLY);
+ return GO_CLEAROBJ;
+ } else {
+ if (game.closed) {
+ rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
+ CAGE_FLY :
+ FREE_FLY);
+ return GO_DWARFWAKE;
+ }
+ if (game.closng || !AT(FISSURE)) {
+ rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
+ CAGE_FLY :
+ FREE_FLY);
+ return GO_CLEAROBJ;
+ }
+ if (HERE(BIRD))
+ rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
+ CAGE_FLY :
+ FREE_FLY);
- state_change(FISSURE, game.objects[FISSURE].prop == BRIDGED
- ? UNBRIDGED
- : BRIDGED);
- return GO_CLEAROBJ;
- }
+ state_change(FISSURE,
+ game.objects[FISSURE].prop == BRIDGED ? UNBRIDGED : BRIDGED);
+ return GO_CLEAROBJ;
+ }
}
-phase_codes_t action(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.
- */
- /* Previously, actions that result in a message, but don't do anything
- * further were called "specials". Now they're handled here as normal
- * actions. If noaction is true, then we spit out the message and return
- */
- if (actions[command.verb].noaction) {
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- }
+phase_codes_t action(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.
+ */
+{
+ /* Previously, actions that result in a message, but don't do anything
+ * further were called "specials". Now they're handled here as normal
+ * actions. If noaction is true, then we spit out the message and return */
+ if (actions[command.verb].noaction) {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
- 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
- * yet to be analysed). Water and oil are also funny, since
- * 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)) {
- /* FALL THROUGH */;
- } else if (command.obj == DWARF && atdwrf(game.loc) > 0) {
- /* FALL THROUGH */;
- } else if (!game.closed &&
- ((LIQUID() == command.obj && HERE(BOTTLE)) ||
- command.obj == LIQLOC(game.loc))) {
- /* FALL THROUGH */;
- } else if (command.obj == OIL && HERE(URN) &&
- game.objects[URN].prop != URN_EMPTY) {
- command.obj = URN;
- /* FALL THROUGH */;
- } else if (command.obj == PLANT && AT(PLANT2) &&
- game.objects[PLANT2].prop != PLANT_THIRSTY) {
- command.obj = PLANT2;
- /* FALL THROUGH */;
- } else if (command.obj == KNIFE && game.knfloc == game.loc) {
- game.knfloc = -1;
- rspeak(KNIVES_VANISH);
- return GO_CLEAROBJ;
- } else if (command.obj == ROD && HERE(ROD2)) {
- command.obj = ROD2;
- /* FALL THROUGH */;
- } else if ((command.verb == FIND ||
- command.verb == INVENTORY) &&
- (command.word[1].id == WORD_EMPTY ||
- command.word[1].id == WORD_NOT_FOUND)) {
- /* FALL THROUGH */;
- } else {
- sspeak(NO_SEE, command.word[0].raw);
- return GO_CLEAROBJ;
- }
+ 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
+ * yet to be analysed). Water and oil are also funny, since
+ * 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))
+ /* FALL THROUGH */;
+ else if (command.obj == DWARF && atdwrf(game.loc) > 0)
+ /* FALL THROUGH */;
+ else if (!game.closed && ((LIQUID() == command.obj && HERE(BOTTLE)) ||
+ command.obj == LIQLOC(game.loc)))
+ /* FALL THROUGH */;
+ else if (command.obj == OIL && HERE(URN) && game.objects[URN].prop != URN_EMPTY) {
+ command.obj = URN;
+ /* FALL THROUGH */;
+ } else if (command.obj == PLANT && AT(PLANT2) && game.objects[PLANT2].prop != PLANT_THIRSTY) {
+ command.obj = PLANT2;
+ /* FALL THROUGH */;
+ } else if (command.obj == KNIFE && game.knfloc == game.loc) {
+ game.knfloc = -1;
+ rspeak(KNIVES_VANISH);
+ return GO_CLEAROBJ;
+ } else if (command.obj == ROD && HERE(ROD2)) {
+ command.obj = ROD2;
+ /* FALL THROUGH */;
+ } else if ((command.verb == FIND ||
+ command.verb == INVENTORY) && (command.word[1].id == WORD_EMPTY || command.word[1].id == WORD_NOT_FOUND))
+ /* FALL THROUGH */;
+ else {
+ sspeak(NO_SEE, command.word[0].raw);
+ return GO_CLEAROBJ;
+ }
- if (command.verb != 0) {
- command.part = transitive;
- }
- }
+ if (command.verb != 0)
+ command.part = transitive;
+ }
- switch (command.part) {
- case intransitive:
- if (command.word[1].raw[0] != '\0' && command.verb != SAY) {
- return GO_WORD2;
- }
- if (command.verb == SAY) {
- /* KEYS is not special, anything not NO_OBJECT or
- * INTRANSITIVE will do here. We're preventing
- * interpretation as an intransitive verb when the word
- * is unknown. */
- command.obj =
- command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
- }
- if (command.obj == NO_OBJECT || command.obj == INTRANSITIVE) {
- /* Analyse an intransitive verb (ie, no object given
- * yet). */
- switch (command.verb) {
- case CARRY:
- return vcarry(command.verb, INTRANSITIVE);
- case DROP:
- return GO_UNKNOWN;
- case SAY:
- return GO_UNKNOWN;
- case UNLOCK:
- return lock(command.verb, INTRANSITIVE);
- case NOTHING: {
- rspeak(OK_MAN);
- return (GO_CLEAROBJ);
- }
- case LOCK:
- return lock(command.verb, INTRANSITIVE);
- case LIGHT:
- return light(command.verb, INTRANSITIVE);
- case EXTINGUISH:
- return extinguish(command.verb, INTRANSITIVE);
- case WAVE:
- return GO_UNKNOWN;
- case TAME:
- return GO_UNKNOWN;
- case GO: {
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- }
- case ATTACK:
- command.obj = INTRANSITIVE;
- return attack(command);
- case POUR:
- return pour(command.verb, INTRANSITIVE);
- case EAT:
- return eat(command.verb, INTRANSITIVE);
- case DRINK:
- return drink(command.verb, INTRANSITIVE);
- case RUB:
- return GO_UNKNOWN;
- case THROW:
- return GO_UNKNOWN;
- case QUIT:
- return quit();
- case FIND:
- return GO_UNKNOWN;
- case INVENTORY:
- return inven();
- case FEED:
- return GO_UNKNOWN;
- case FILL:
- return fill(command.verb, INTRANSITIVE);
- case BLAST:
- blast();
- return GO_CLEAROBJ;
- case SCORE:
- score(scoregame);
- return GO_CLEAROBJ;
- case FEE:
- case FIE:
- case FOE:
- case FOO:
- case FUM:
- return bigwords(command.word[0].id);
- case BRIEF:
- return brief();
- case READ:
- command.obj = INTRANSITIVE;
- return read(command);
- case BREAK:
- return GO_UNKNOWN;
- case WAKE:
- return GO_UNKNOWN;
- case SAVE:
- return suspend();
- case RESUME:
- return resume();
- case FLY:
- return fly(command.verb, INTRANSITIVE);
- case LISTEN:
- return listen();
- case PART:
- return reservoir();
- case SEED:
- case WASTE:
- rspeak(NUMERIC_REQUIRED);
- return GO_TOP;
- default: // LCOV_EXCL_LINE
- BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
- }
- /* FALLTHRU */
- case transitive:
- /* Analyse a transitive verb. */
- switch (command.verb) {
- case CARRY:
- return vcarry(command.verb, command.obj);
- case DROP:
- return discard(command.verb, command.obj);
- case SAY:
- return say(command);
- case UNLOCK:
- return lock(command.verb, command.obj);
- case NOTHING: {
- rspeak(OK_MAN);
- return (GO_CLEAROBJ);
- }
- case LOCK:
- return lock(command.verb, command.obj);
- case LIGHT:
- return light(command.verb, command.obj);
- case EXTINGUISH:
- return extinguish(command.verb, command.obj);
- case WAVE:
- return wave(command.verb, command.obj);
- case TAME: {
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- }
- case GO: {
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- }
- case ATTACK:
- return attack(command);
- case POUR:
- return pour(command.verb, command.obj);
- case EAT:
- return eat(command.verb, command.obj);
- case DRINK:
- return drink(command.verb, command.obj);
- case RUB:
- return rub(command.verb, command.obj);
- case THROW:
- return throwit(command);
- case QUIT:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case FIND:
- return find(command.verb, command.obj);
- case INVENTORY:
- return find(command.verb, command.obj);
- case FEED:
- return feed(command.verb, command.obj);
- case FILL:
- return fill(command.verb, command.obj);
- case BLAST:
- blast();
- return GO_CLEAROBJ;
- case SCORE:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case FEE:
- case FIE:
- case FOE:
- case FOO:
- case FUM:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case BRIEF:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case READ:
- return read(command);
- case BREAK:
- return vbreak(command.verb, command.obj);
- case WAKE:
- return wake(command.verb, command.obj);
- case SAVE:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case RESUME:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- case FLY:
- return fly(command.verb, command.obj);
- case LISTEN:
- speak(actions[command.verb].message);
- return GO_CLEAROBJ;
- // LCOV_EXCL_START
- // This case should never happen - here only as placeholder
- case PART:
- return reservoir();
- // LCOV_EXCL_STOP
- case SEED:
- return seed(command.verb, command.word[1].raw);
- case WASTE:
- return waste(command.verb,
- (turn_t)atol(command.word[1].raw));
- default: // LCOV_EXCL_LINE
- BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
- case unknown:
- /* Unknown verb, couldn't deduce object - might need hint */
- sspeak(WHAT_DO, command.word[0].raw);
- return GO_CHECKHINT;
- default: // LCOV_EXCL_LINE
- BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
- }
+ switch (command.part) {
+ case intransitive:
+ if (command.word[1].raw[0] != '\0' && command.verb != SAY)
+ return GO_WORD2;
+ if (command.verb == SAY)
+ /* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE
+ * will do here. We're preventing interpretation as an intransitive
+ * verb when the word is unknown. */
+ command.obj = command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
+ if (command.obj == NO_OBJECT || command.obj == INTRANSITIVE) {
+ /* Analyse an intransitive verb (ie, no object given yet). */
+ switch (command.verb) {
+ case CARRY:
+ return vcarry(command.verb, INTRANSITIVE);
+ case DROP:
+ return GO_UNKNOWN;
+ case SAY:
+ return GO_UNKNOWN;
+ case UNLOCK:
+ return lock(command.verb, INTRANSITIVE);
+ case NOTHING: {
+ rspeak(OK_MAN);
+ return (GO_CLEAROBJ);
+ }
+ case LOCK:
+ return lock(command.verb, INTRANSITIVE);
+ case LIGHT:
+ return light(command.verb, INTRANSITIVE);
+ case EXTINGUISH:
+ return extinguish(command.verb, INTRANSITIVE);
+ case WAVE:
+ return GO_UNKNOWN;
+ case TAME:
+ return GO_UNKNOWN;
+ case GO: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case ATTACK:
+ command.obj = INTRANSITIVE;
+ return attack(command);
+ case POUR:
+ return pour(command.verb, INTRANSITIVE);
+ case EAT:
+ return eat(command.verb, INTRANSITIVE);
+ case DRINK:
+ return drink(command.verb, INTRANSITIVE);
+ case RUB:
+ return GO_UNKNOWN;
+ case THROW:
+ return GO_UNKNOWN;
+ case QUIT:
+ return quit();
+ case FIND:
+ return GO_UNKNOWN;
+ case INVENTORY:
+ return inven();
+ case FEED:
+ return GO_UNKNOWN;
+ case FILL:
+ return fill(command.verb, INTRANSITIVE);
+ case BLAST:
+ blast();
+ return GO_CLEAROBJ;
+ case SCORE:
+ score(scoregame);
+ return GO_CLEAROBJ;
+ case FEE:
+ case FIE:
+ case FOE:
+ case FOO:
+ case FUM:
+ return bigwords(command.word[0].id);
+ case BRIEF:
+ return brief();
+ case READ:
+ command.obj = INTRANSITIVE;
+ return read(command);
+ case BREAK:
+ return GO_UNKNOWN;
+ case WAKE:
+ return GO_UNKNOWN;
+ case SAVE:
+ return suspend();
+ case RESUME:
+ return resume();
+ case FLY:
+ return fly(command.verb, INTRANSITIVE);
+ case LISTEN:
+ return listen();
+ case PART:
+ return reservoir();
+ case SEED:
+ case WASTE:
+ rspeak(NUMERIC_REQUIRED);
+ return GO_TOP;
+ default: // LCOV_EXCL_LINE
+ BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ }
+ /* FALLTHRU */
+ case transitive:
+ /* Analyse a transitive verb. */
+ switch (command.verb) {
+ case CARRY:
+ return vcarry(command.verb, command.obj);
+ case DROP:
+ return discard(command.verb, command.obj);
+ case SAY:
+ return say(command);
+ case UNLOCK:
+ return lock(command.verb, command.obj);
+ case NOTHING: {
+ rspeak(OK_MAN);
+ return (GO_CLEAROBJ);
+ }
+ case LOCK:
+ return lock(command.verb, command.obj);
+ case LIGHT:
+ return light(command.verb, command.obj);
+ case EXTINGUISH:
+ return extinguish(command.verb, command.obj);
+ case WAVE:
+ return wave(command.verb, command.obj);
+ case TAME: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case GO: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case ATTACK:
+ return attack(command);
+ case POUR:
+ return pour(command.verb, command.obj);
+ case EAT:
+ return eat(command.verb, command.obj);
+ case DRINK:
+ return drink(command.verb, command.obj);
+ case RUB:
+ return rub(command.verb, command.obj);
+ case THROW:
+ return throwit(command);
+ case QUIT: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case FIND:
+ return find(command.verb, command.obj);
+ case INVENTORY:
+ return find(command.verb, command.obj);
+ case FEED:
+ return feed(command.verb, command.obj);
+ case FILL:
+ return fill(command.verb, command.obj);
+ case BLAST:
+ blast();
+ return GO_CLEAROBJ;
+ case SCORE: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case FEE:
+ case FIE:
+ case FOE:
+ case FOO:
+ case FUM: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case BRIEF: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case READ:
+ return read(command);
+ case BREAK:
+ return vbreak(command.verb, command.obj);
+ case WAKE:
+ return wake(command.verb, command.obj);
+ case SAVE: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case RESUME: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case FLY:
+ return fly(command.verb, command.obj);
+ case LISTEN: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ // LCOV_EXCL_START
+ // This case should never happen - here only as placeholder
+ case PART:
+ return reservoir();
+ // LCOV_EXCL_STOP
+ case SEED:
+ return seed(command.verb, command.word[1].raw);
+ case WASTE:
+ return waste(command.verb, (turn_t)atol(command.word[1].raw));
+ default: // LCOV_EXCL_LINE
+ BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ case unknown:
+ /* Unknown verb, couldn't deduce object - might need hint */
+ sspeak(WHAT_DO, command.word[0].raw);
+ return GO_CHECKHINT;
+ default: // LCOV_EXCL_LINE
+ BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
+ }
}
-
-// end
diff --git a/advent.adoc b/advent.adoc
index a064d1a..5ce5b3f 100644
--- a/advent.adoc
+++ b/advent.adoc
@@ -1,11 +1,8 @@
= advent(6) =
:doctype: manpage
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
-// batchspell: add advent logfile savefile roleplaying Gillogly PDP Ctrl-D
-// batchspell: add EOF autosave endianness wumpus zork nethack
-
== NAME ==
advent - Colossal Cave Adventure
@@ -23,9 +20,9 @@ adventure". To learn more about the changes since the 350-point
original, type 'news' at the command prompt.
There is an 'adventure' in the BSD games package that is a C port by
-Jim Gillogly of the 1977 version. To avoid a name collision, this game
-builds as 'advent', reflecting the fact that the PDP-10 on which the
-game originally ran limited filenames to 6 characters.
+Jim Gillogly of the Don Woods's 1977 version of this game. To avoid a name
+collision, this game builds as 'advent', reflecting the fact that the
+PDP-10 on which the game originally ran limited filenames to 6 characters.
This version is released as open source with the permission and
encouragement of the original authors.
@@ -37,8 +34,7 @@ command history.
Some minor bugs and message typos have been fixed. Otherwise, the
"version" command is almost the only way to tell you're not running
-Don's 1977 version until you get to the new cave sections added for
-2.5.
+Don's 1977 version.
To exit the game, type Ctrl-D (EOF).
@@ -48,9 +44,9 @@ There have been no gameplay changes.
-l:: Log commands to specified file.
--r:: Restore game from specified save file
+-r:: Restore game from specified file
--a:: Load from specified save file and autosave to it on exit or signal.
+-a:: Load from specified file and autosave to it on exit or signal.
-o:: Old-style. Reverts some minor cosmetic fixes in game
messages. Restores original interface, no prompt or line editing.
@@ -63,9 +59,9 @@ argument of '-' is taken as a directive to read from standard input.
== BUGS ==
-The binary save file format is fragile, dependent on your machine's
-endianness, and unlikely to survive through version bumps. There are
-version and endianness checks when attempting to restore from a save.
+The binary save file format is fragile, dependent on your machine word
+size and endianness, and unlikely to survive through version bumps. There
+is a version check.
The input parser was the first attempt *ever* at natural-language
parsing in a game and has some known deficiencies. While later text
diff --git a/advent.desktop b/advent.desktop
index 1cc38f8..3fe5420 100644
--- a/advent.desktop
+++ b/advent.desktop
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
[Desktop Entry]
Type=Application
diff --git a/advent.h b/advent.h
index fa0767b..68890da 100644
--- a/advent.h
+++ b/advent.h
@@ -1,14 +1,15 @@
/*
* Dungeon types and macros.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include
-#include
-#include
#include
#include
+#include
+#include
+#include
#include "dungeon.h"
@@ -18,35 +19,36 @@
#define LCG_C 221587L
#define LCG_M 1048576L
-#define LINESIZE 1024
-#define TOKLEN 5 // # outputting characters in a token */
-#define PIRATE NDWARVES // must be NDWARVES-1 when zero-origin
-#define DALTLC LOC_NUGGET // alternate dwarf location
-#define INVLIMIT 7 // inventory limit (# of objects)
-#define INTRANSITIVE -1 // illegal object number
-#define GAMELIMIT 330 // base limit of turns
-#define NOVICELIMIT 1000 // limit of turns for novice
-#define WARNTIME 30 // late game starts at game.limit-this
-#define FLASHTIME 50 // turns from first warning till blinding flash
-#define PANICTIME 15 // time left after closing
-#define BATTERYLIFE 2500 // turn limit increment from batteries
-#define WORD_NOT_FOUND \
- -1 // "Word not found" flag value for the vocab hash functions.
-#define WORD_EMPTY 0 // "Word empty" flag value for the vocab hash functions
-#define PIT_KILL_PROB 35 // Percentage probability of dying from fall in pit.
-#define CARRIED -1 // Player is toting it
-#define READ_MODE "rb" // b is not needed for POSIX but harmless
-#define WRITE_MODE "wb" // b is not needed for POSIX but harmless
+#define LINESIZE 1024
+#define TOKLEN 5 // # outputting characters in a token */
+#define NDWARVES 6 // number of dwarves
+#define PIRATE NDWARVES // must be NDWARVES-1 when zero-origin
+#define DALTLC LOC_NUGGET // alternate dwarf location
+#define INVLIMIT 7 // inventory limit (# of objects)
+#define INTRANSITIVE -1 // illegal object number
+#define GAMELIMIT 330 // base limit of turns
+#define NOVICELIMIT 1000 // limit of turns for novice
+#define WARNTIME 30 // late game starts at game.limit-this
+#define FLASHTIME 50 // turns from first warning till blinding flash
+#define PANICTIME 15 // time left after closing
+#define BATTERYLIFE 2500 // turn limit increment from batteries
+#define WORD_NOT_FOUND -1 // "Word not found" flag value for the vocab hash functions.
+#define WORD_EMPTY 0 // "Word empty" flag value for the vocab hash functions
+#define PIT_KILL_PROB 35 // Percentage probability of dying from fall in pit.
+#define CARRIED -1 // Player is toting it
+#define READ_MODE "rb" // b is not needed for POSIX but harmless
+#define WRITE_MODE "wb" // b is not needed for POSIX but harmless
/* Special object-state values - integers > 0 are object-specific */
-#define STATE_NOTFOUND -1 // 'Not found" state of treasures
-#define STATE_FOUND 0 // After discovered, before messed with
-#define STATE_IN_CAVITY 1 // State value common to all gemstones
+#define STATE_NOTFOUND -1 // 'Not found" state of treasures
+#define STATE_FOUND 0 // After discovered, before messed with
+#define STATE_IN_CAVITY 1 // State value common to all gemstones
/* Special fixed object-state values - integers > 0 are location */
#define IS_FIXED -1
#define IS_FREE 0
+#ifndef FOUNDBOOL
/* (ESR) It is fitting that translation of the original ADVENT should
* have left us a maze of twisty little conditionals that resists all
* understanding. Setting and use of what is now the per-object state
@@ -60,112 +62,118 @@
* STATE_NOTFOUND is only set on treasures. Non-treasures start the
* game in STATE_FOUND.
*
- * PROP_STASHIFY is supposed to map a state property value to a
+ * PROP_STASHED is supposed to map a state property value to a
* negative range, where the object cannot be picked up but the value
* can be recovered later. Various objects get this property when
* the cave starts to close. Only seems to be significant for the bird
* and readable objects, notably the clam/oyster - but the code around
- * those tests is difficult to read.
- *
- * All tests of the prop member are done with either these macros or ==.
+ * those test is difficult to read.
*/
-#define OBJECT_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND)
-#define OBJECT_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND)
-#define OBJECT_SET_FOUND(obj) (game.objects[obj].prop = STATE_FOUND)
-#define OBJECT_SET_NOT_FOUND(obj) (game.objects[obj].prop = STATE_NOTFOUND)
-#define OBJECT_IS_NOTFOUND2(g, o) (g.objects[o].prop == STATE_NOTFOUND)
-#define PROP_IS_INVALID(val) (val < -MAX_STATE - 1 || val > MAX_STATE)
-#define PROP_STASHIFY(n) (-1 - (n))
-#define OBJECT_STASHIFY(obj, pval) game.objects[obj].prop = PROP_STASHIFY(pval)
-#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND)
-#define OBJECT_STATE_EQUALS(obj, pval) \
- ((game.objects[obj].prop == pval) || \
- (game.objects[obj].prop == PROP_STASHIFY(pval)))
+#define PROP_STASHIFY(n) (-1 - (n))
+#define PROP_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND)
+#define PROP_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND)
+#define PROP_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND)
+#define PROP_IS_STASHED_OR_UNSEEN(obj) (game.objects[obj].prop < 0)
+#define PROP_SET_FOUND(obj) (game.objects[obj].prop = STATE_FOUND)
+#define PROP_SET_NOT_FOUND(obj) (game.objects[obj].prop = STATE_NOTFOUND)
+#define PROP_IS_NOTFOUND2(g, o) (g.objects[o].prop == STATE_NOTFOUND)
+#define PROP_IS_INVALID(val) (val < -MAX_STATE - 1 || val > MAX_STATE)
+#else
+/* (ESR) Only the boldest of adventurers will explore here. This
+ * alternate set of definitions for the macros above was an attempt to
+ * break from out of the state encoding a per-object "found" member
+ * telling whether or not the player has seen the object.
+ *
+ * What's broken when you try to use thus is
+ * PROP_IS_STASHED_OR_UNSEEN. The symptom is game.tally getting
+ * decremented on non-treasures.
+ */
+#define PROP_STASHIFY(n) (-(n))
+#define PROP_IS_STASHED(obj) (game.objects[obj].prop < 0)
+#define PROP_IS_NOTFOUND(obj) (!game.objects[obj].found)
+#define PROP_IS_FOUND(obj) (game.objects[obj].found && game.objects[obj].prop == 0)
+#define PROP_IS_STASHED_OR_UNSEEN(obj) (!game.objects[obj].found || game.objects[obj].prop < 0)
+#define PROP_SET_FOUND(obj) do {game.objects[obj].found = true; game.objects[obj].prop = STATE_FOUND;} while(0)
+#define PROP_SET_NOT_FOUND(obj) game.objects[obj].found = false
+#define PROP_IS_NOTFOUND2(g, o) (!g.objects[o].found)
+#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE)
+#define PROP_SET_SEEN(obj) game.objects[object].found = true
+#endif
+#define PROP_STASHED(obj) PROP_STASHIFY(game.objects[obj].prop)
-#define PROMPT "> "
+#define PROMPT "> "
/*
- * DESTROY(N) = Get rid of an item by putting it in LOC_NOWHERE
- * MOD(N,M) = Arithmetic modulus
- * TOTING(OBJ) = true if the OBJ is being carried
- * AT(OBJ) = true if on either side of two-placed object
- * HERE(OBJ) = true if the OBJ is at "LOC" (or is being carried)
- * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit)
- * LIQUID() = object number of liquid in bottle
- * LIQLOC(LOC) = object number of liquid (if any) at LOC
- * FORCED(LOC) = true if LOC moves without asking for input (COND=2)
- * IS_DARK_HERE() = true if location "LOC" is dark
- * PCT(N) = true N% of the time (N integer from 0 to 100)
- * GSTONE(OBJ) = true if OBJ is a gemstone
- * FOREST(LOC) = true if LOC is part of the forest
- * OUTSIDE(LOC) = true if location not in the cave
- * INSIDE(LOC) = true if location is in the cave or the building at the
- * beginning of the game
- * INDEEP(LOC) = true if location is in the Hall of Mists or deeper
- * BUG(X) = report bug and exit
+ * DESTROY(N) = Get rid of an item by putting it in LOC_NOWHERE
+ * MOD(N,M) = Arithmetic modulus
+ * TOTING(OBJ) = true if the OBJ is being carried
+ * AT(OBJ) = true if on either side of two-placed object
+ * HERE(OBJ) = true if the OBJ is at "LOC" (or is being carried)
+ * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit)
+ * LIQUID() = object number of liquid in bottle
+ * LIQLOC(LOC) = object number of liquid (if any) at LOC
+ * FORCED(LOC) = true if LOC moves without asking for input (COND=2)
+ * DARK(LOC) = true if location "LOC" is dark
+ * PCT(N) = true N% of the time (N integer from 0 to 100)
+ * GSTONE(OBJ) = true if OBJ is a gemstone
+ * FOREST(LOC) = true if LOC is part of the forest
+ * OUTSID(LOC) = true if location not in the cave
+ * INSIDE(LOC) = true if location is in the cave or the building at the beginning of the game
+ * INDEEP(LOC) = true if location is in the Hall of Mists or deeper
+ * BUG(X) = report bug and exit
*/
-#define DESTROY(N) move(N, LOC_NOWHERE)
-#define MOD(N, M) ((N) % (M))
-#define TOTING(OBJ) (game.objects[OBJ].place == CARRIED)
-#define AT(OBJ) \
- (game.objects[OBJ].place == game.loc || \
- game.objects[OBJ].fixed == game.loc)
-#define HERE(OBJ) (AT(OBJ) || TOTING(OBJ))
-#define CNDBIT(L, N) (tstbit(conditions[L], N))
-#define LIQUID() \
- (game.objects[BOTTLE].prop == WATER_BOTTLE ? WATER \
- : game.objects[BOTTLE].prop == OIL_BOTTLE ? OIL \
- : NO_OBJECT)
-#define LIQLOC(LOC) \
- (CNDBIT((LOC), COND_FLUID) ? CNDBIT((LOC), COND_OILY) ? OIL : WATER \
- : NO_OBJECT)
-#define FORCED(LOC) CNDBIT(LOC, COND_FORCED)
-#define IS_DARK_HERE() \
- (!CNDBIT(game.loc, COND_LIT) && \
- (game.objects[LAMP].prop == LAMP_DARK || !HERE(LAMP)))
-#define PCT(N) (randrange(100) < (N))
-#define GSTONE(OBJ) \
- ((OBJ) == EMERALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH)
-#define FOREST(LOC) CNDBIT(LOC, COND_FOREST)
-#define OUTSIDE(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC))
-#define INSIDE(LOC) (!OUTSIDE(LOC) || LOC == LOC_BUILDING)
-#define INDEEP(LOC) CNDBIT((LOC), COND_DEEP)
-#define BUG(x) bug(x, #x)
+#define DESTROY(N) move(N, LOC_NOWHERE)
+#define MOD(N,M) ((N) % (M))
+#define TOTING(OBJ) (game.objects[OBJ].place == CARRIED)
+#define AT(OBJ) (game.objects[OBJ].place == game.loc || game.objects[OBJ].fixed == game.loc)
+#define HERE(OBJ) (AT(OBJ) || TOTING(OBJ))
+#define CNDBIT(L,N) (tstbit(conditions[L],N))
+#define LIQUID() (game.objects[BOTTLE].prop == WATER_BOTTLE? WATER : game.objects[BOTTLE].prop == OIL_BOTTLE ? OIL : NO_OBJECT )
+#define LIQLOC(LOC) (CNDBIT((LOC),COND_FLUID)? CNDBIT((LOC),COND_OILY) ? OIL : WATER : NO_OBJECT)
+#define FORCED(LOC) CNDBIT(LOC, COND_FORCED)
+#define DARK(DUMMY) (!CNDBIT(game.loc,COND_LIT) && (game.objects[LAMP].prop == LAMP_DARK || !HERE(LAMP)))
+#define PCT(N) (randrange(100) < (N))
+#define GSTONE(OBJ) ((OBJ) == EMERALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH)
+#define FOREST(LOC) CNDBIT(LOC, COND_FOREST)
+#define OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC))
+#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING)
+#define INDEEP(LOC) CNDBIT((LOC),COND_DEEP)
+#define BUG(x) bug(x, #x)
enum bugtype {
- SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST,
- VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3,
- INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST,
- TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST,
- CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION,
- LOCATION_HAS_NO_TRAVEL_ENTRIES,
- HINT_NUMBER_EXCEEDS_GOTO_LIST,
- SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN,
- ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH,
+ SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST,
+ VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3,
+ INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST,
+ TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST,
+ CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION,
+ LOCATION_HAS_NO_TRAVEL_ENTRIES,
+ HINT_NUMBER_EXCEEDS_GOTO_LIST,
+ SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN,
+ ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH,
};
-enum speaktype { touch, look, hear, study, change };
+enum speaktype {touch, look, hear, study, change};
-enum termination { endgame, quitgame, scoregame };
+enum termination {endgame, quitgame, scoregame};
-enum speechpart { unknown, intransitive, transitive };
+enum speechpart {unknown, intransitive, transitive};
-typedef enum { NO_WORD_TYPE, MOTION, OBJECT, ACTION, NUMERIC } word_type_t;
+typedef enum {NO_WORD_TYPE, MOTION, OBJECT, ACTION, NUMERIC} word_type_t;
-typedef enum scorebonus { none, splatter, defeat, victory } score_t;
+typedef enum scorebonus {none, splatter, defeat, victory} score_t;
/* Phase codes for action returns.
* These were at one time FORTRAN line numbers.
*/
typedef enum {
- GO_TERMINATE,
- GO_MOVE,
- GO_TOP,
- GO_CLEAROBJ,
- GO_CHECKHINT,
- GO_WORD2,
- GO_UNKNOWN,
- GO_DWARFWAKE,
+ GO_TERMINATE,
+ GO_MOVE,
+ GO_TOP,
+ GO_CLEAROBJ,
+ GO_CHECKHINT,
+ GO_WORD2,
+ GO_UNKNOWN,
+ GO_DWARFWAKE,
} phase_codes_t;
/* Use fixed-lwength types to make the save format moore portable */
@@ -177,72 +185,75 @@ typedef int32_t turn_t; // turn counter or threshold */
typedef int32_t bool32_t; // turn counter or threshold */
struct game_t {
- int32_t lcg_x;
- int32_t abbnum; // How often to print int descriptions
- score_t bonus; // What kind of finishing bonus we are getting
- loc_t chloc; // pirate chest location
- loc_t chloc2; // pirate chest alternate location
- turn_t clock1; // # turns from finding last treasure to close
- turn_t clock2; // # turns from warning till blinding flash
- bool32_t clshnt; // has player read the clue in the endgame?
- bool32_t closed; // whether we're all the way closed
- bool32_t closng; // whether it's closing time yet
- bool32_t lmwarn; // has player been warned about lamp going dim?
- bool32_t novice; // asked for instructions at start-up?
- bool32_t panic; // has player found out he's trapped?
- bool32_t wzdark; // whether the loc he's leaving was dark
- bool32_t blooded; // has player drunk of dragon's blood?
- int32_t conds; // min value for cond[loc] if loc has any hints
- int32_t detail; // level of detail in descriptions
+ int32_t lcg_x;
+ int32_t abbnum; // How often to print int descriptions
+ score_t bonus; // What kind of finishing bonus we are getting
+ loc_t chloc; // pirate chest location
+ loc_t chloc2; // pirate chest alternate location
+ turn_t clock1; // # turns from finding last treasure to close
+ turn_t clock2; // # turns from warning till blinding flash
+ bool32_t clshnt; // has player read the clue in the endgame?
+ bool32_t closed; // whether we're all the way closed
+ bool32_t closng; // whether it's closing time yet
+ bool32_t lmwarn; // has player been warned about lamp going dim?
+ bool32_t novice; // asked for instructions at start-up?
+ bool32_t panic; // has player found out he's trapped?
+ bool32_t wzdark; // whether the loc he's leaving was dark
+ bool32_t blooded; // has player drunk of dragon's blood?
+ int32_t conds; // min value for cond[loc] if loc has any hints
+ int32_t detail; // level of detail in descriptions
- /* dflag controls the level of activation of dwarves:
- * 0 No dwarf stuff yet (wait until reaches Hall Of Mists)
- * 1 Reached Hall Of Mists, but hasn't met first dwarf
- * 2 Met 1t dwarf, others start moving, no knives thrown yet
- * 3 A knife has been thrown (first set always misses) 3+
- * Dwarves are mad (increases their accuracy) */
- int32_t dflag;
+ /* dflag controls the level of activation of dwarves:
+ * 0 No dwarf stuff yet (wait until reaches Hall Of Mists)
+ * 1 Reached Hall Of Mists, but hasn't met first dwarf
+ * 2 Met first dwarf, others start moving, no knives thrown yet
+ * 3 A knife has been thrown (first set always misses)
+ * 3+ Dwarves are mad (increases their accuracy) */
+ int32_t dflag;
- int32_t dkill; // dwarves killed
- int32_t dtotal; // total dwarves (including pirate) in loc
- int32_t foobar; // progress in saying "FEE FIE FOE FOO".
- int32_t holdng; // number of objects being carried
- int32_t igo; // # uses of "go" instead of a direction
- int32_t iwest; // # times he's said "west" instead of "w"
- loc_t knfloc; // knife location; LOC_NOWERE if none, -1 after caveat
- turn_t limit; // lifetime of lamp
- loc_t loc; // where player is now
- loc_t newloc; // where player is going
- turn_t numdie; // number of times killed so far
- loc_t oldloc; // where player was
- loc_t oldlc2; // where player was two moves ago
- obj_t oldobj; // last object player handled
- int32_t saved; // point penalty for saves
- int32_t tally; // count of treasures gained
- int32_t thresh; // current threshold for endgame scoring tier
- bool32_t seenbigwords; // have we red the graffiti in the Giant's Room?
- turn_t trnluz; // # points lost so far due to turns used
- turn_t turns; // counts commands given (ignores yes/no)
- char zzword[TOKLEN + 1]; // randomly generated magic word from bird
- struct {
- int32_t abbrev; // has location been seen?
- int32_t atloc; // head of object linked list per location
- } locs[NLOCATIONS + 1];
- struct {
- int32_t seen; // true if dwarf has seen him
- loc_t loc; // location of dwarves, initially hard-wired in
- loc_t oldloc; // prior loc of each dwarf, initially garbage
- } dwarves[NDWARVES + 1];
- struct {
- loc_t fixed; // fixed location of object (if not IS_FREE)
- int32_t prop; // object state
- loc_t place; // location of object
- } objects[NOBJECTS + 1];
- struct {
- bool32_t used; // hints[i].used = true iff hint i has been used.
- int32_t lc; // hints[i].lc = show int at LOC with cond bit i
- } hints[NHINTS];
- obj_t link[NOBJECTS * 2 + 1]; // object-list links
+ int32_t dkill; // dwarves killed
+ int32_t dtotal; // total dwarves (including pirate) in loc
+ int32_t foobar; // progress in saying "FEE FIE FOE FOO".
+ int32_t holdng; // number of objects being carried
+ int32_t igo; // # uses of "go" instead of a direction
+ int32_t iwest; // # times he's said "west" instead of "w"
+ loc_t knfloc; // knife location; LOC_NOWERE if none, -1 after caveat
+ turn_t limit; // lifetime of lamp
+ loc_t loc; // where player is now
+ loc_t newloc; // where player is going
+ turn_t numdie; // number of times killed so far
+ loc_t oldloc; // where player was
+ loc_t oldlc2; // where player was two moves ago
+ obj_t oldobj; // last object player handled
+ int32_t saved; // point penalty for saves
+ int32_t tally; // count of treasures gained
+ int32_t thresh; // current threshold for endgame scoring tier
+ bool32_t seenbigwords; // have we red the graffiti in the Giant's Room?
+ turn_t trnluz; // # points lost so far due to turns used
+ turn_t turns; // counts commands given (ignores yes/no)
+ char zzword[TOKLEN + 1]; // randomly generated magic word from bird
+ struct {
+ int32_t abbrev; // has location been seen?
+ int32_t atloc; // head of object linked list per location
+ } locs[NLOCATIONS + 1];
+ struct {
+ int32_t seen; // true if dwarf has seen him
+ loc_t loc; // location of dwarves, initially hard-wired in
+ loc_t oldloc; // prior loc of each dwarf, initially garbage
+ } dwarves[NDWARVES + 1];
+ struct {
+#ifdef FOUNDBOOL
+ bool32_t found; // has the location of this object been found?
+#endif
+ loc_t fixed; // fixed location of object (if not IS_FREE)
+ int32_t prop; // object state */
+ loc_t place; // location of object
+ } objects[NOBJECTS + 1];
+ struct {
+ bool32_t used; // hints[i].used = true iff hint i has been used.
+ int32_t lc; // hints[i].lc = show int at LOC with cond bit i
+ } hints[NHINTS];
+ obj_t link[NOBJECTS * 2 + 1];// object-list links
};
/*
@@ -250,64 +261,56 @@ struct game_t {
* This data is not saved in a saved game.
*/
struct settings_t {
- FILE *logfp;
- bool oldstyle;
- bool prompt;
- char **argv;
- int argc;
- int optind;
- FILE *scriptfp;
- int debug;
+ FILE *logfp;
+ bool oldstyle;
+ bool prompt;
+ char **argv;
+ int argc;
+ int optind;
+ FILE *scriptfp;
+ int debug;
};
typedef struct {
- char raw[LINESIZE];
- vocab_t id;
- word_type_t type;
+ char raw[LINESIZE];
+ vocab_t id;
+ word_type_t type;
} command_word_t;
-typedef enum {
- EMPTY,
- RAW,
- TOKENIZED,
- GIVEN,
- PREPROCESSED,
- PROCESSING,
- EXECUTED
-} command_state_t;
+typedef enum {EMPTY, RAW, TOKENIZED, GIVEN, PREPROCESSED, PROCESSING, EXECUTED} command_state_t;
typedef struct {
- enum speechpart part;
- command_word_t word[2];
- verb_t verb;
- obj_t obj;
- command_state_t state;
+ enum speechpart part;
+ command_word_t word[2];
+ verb_t verb;
+ obj_t obj;
+ command_state_t state;
} command_t;
/*
* Bump on save format change.
*
- * Note: Verify that the tests run clean before bumping this, then rebuild the
- * check files afterwards. Otherwise you will get a spurious failure due to the
- * old version having been generated into a check file.
+ * Note: Verify that the tests run clean before bumping this, then rebuild the check
+ * files afterwards. Otherwise you will get a spurious failure due to the old version
+ * having been generated into a check file.
*/
-#define SAVE_VERSION 31
+#define SAVE_VERSION 31
/*
* Goes at start of file so saves can be identified by file(1) and the like.
*/
-#define ADVENT_MAGIC "open-adventure\n"
+#define ADVENT_MAGIC "open-adventure\n"
/*
* If you change the first three members, the resume function may not properly
- * reject saves from older versions. Later members can change, but bump the
- * version when you do that.
+ * reject saves from older versions. Later members can change, but bump the version
+ * when you do that.
*/
struct save_t {
- char magic[sizeof(ADVENT_MAGIC)];
- int32_t version;
- int32_t canary;
- struct game_t game;
+ char magic[sizeof(ADVENT_MAGIC)];
+ int32_t version;
+ int32_t canary;
+ struct game_t game;
};
extern struct game_t game;
@@ -317,13 +320,13 @@ extern struct settings_t settings;
extern char *myreadline(const char *);
extern bool get_command_input(command_t *);
extern void clear_command(command_t *);
-extern void speak(const char *, ...);
+extern void speak(const char*, ...);
extern void sspeak(int msg, ...);
extern void pspeak(vocab_t, enum speaktype, bool, int, ...);
extern void rspeak(vocab_t, ...);
-extern void echo_input(FILE *, const char *, const char *);
+extern void echo_input(FILE*, const char*, const char*);
extern bool silent_yes_or_no(void);
-extern bool yes_or_no(const char *, const char *, const char *);
+extern bool yes_or_no(const char*, const char*, const char*);
extern void juggle(obj_t);
extern void move(obj_t, loc_t);
extern void put(obj_t, loc_t, int);
diff --git a/adventure.yaml b/adventure.yaml
index 141ffb7..2069759 100644
--- a/adventure.yaml
+++ b/adventure.yaml
@@ -1,10 +1,6 @@
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
#
-# batchspell: add XYZZY Twopit Bedquilt ne se sw nw dwarves dwarvish gouts
-# batchspell: add Crowther add axe pinin Har har meself Hmmm Adventuredom
-# batchspell: add Tsk gameplay bugfixes
-#
# This YAML file gets processed into a collection of data structures and
# variable initializers describing Colossal Cave. It replaces an ad-hoc
# text database shipped with Adventure versions up to 2.5. The format
@@ -14,7 +10,7 @@
# We define a bunch of YAML structures:
#
# motions: Motion words, grouped into synonyms. The 'oldstyle'
-# attribute, if false, means that single-letter synonyms should not be
+# attribute, if false, means that single-letter synonyms should be
# accepted in oldstyle mode; it defaults to true.
#
# actions: Action words, grouped into synonyms, and their corresponding
@@ -3436,7 +3432,7 @@ objects: !!omap
- 'There are a few recent issues of "Spelunker Today" magazine here.'
texts:
- |-
- I'm afraid the magazine is written in dwarvish. But penciled on one
+ I'm afraid the magazine is written in dwarvish. But pencilled on one
cover you see, "Please leave the magazines at the construction site."
- DWARF:
words: ['dwarf', 'dwarv']
@@ -3944,13 +3940,11 @@ obituaries:
Oh dear, you seem to have gotten yourself killed. I might be able to
help you out, but I've never really done this before. Do you want me
to try to reincarnate you?
- # batchspell: add wr
yes_response: |-
All right. But don't blame me if something goes wr......
--- POOF!! ---
You are engulfed in a cloud of orange smoke. Coughing and gasping,
you emerge from the smoke and find....
- # batchspell: remove wr
- query: |-
You clumsy oaf, you've done it again! I don't know how long I can
keep this up. Do you want me to try reincarnating you again?
@@ -4232,7 +4226,7 @@ actions: !!omap
message: |-
There is a puff of orange smoke; within it, fiery runes spell out:
- Open Adventure %V - http://www.catb.org/esr/open-adventure/
+ \tOpen Adventure %V - http://www.catb.org/esr/open-adventure/
words: ['versi']
noaction: true
diff --git a/cheat.c b/cheat.c
index 4e94025..1a415b7 100644
--- a/cheat.c
+++ b/cheat.c
@@ -4,93 +4,97 @@
* savefile(), so we know we're always outputting save files that advent
* can import.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include "advent.h"
-#include
#include
-#include
-#include
#include
+#include
+#include
+#include
+#include "advent.h"
-int main(int argc, char *argv[]) {
- int ch;
- char *savefilename = NULL;
- FILE *fp = NULL;
+int main(int argc, char *argv[])
+{
+ int ch;
+ char *savefilename = NULL;
+ FILE *fp = NULL;
- // Initialize game variables
- initialise();
+ // Initialize game variables
+ initialise();
- /* we're generating a saved game, so saved once by default,
- * unless overridden with command-line options below.
- */
- game.saved = 1;
+ /* we're generating a saved game, so saved once by default,
+ * unless overridden with command-line options below.
+ */
+ game.saved = 1;
- /* Options. */
- const char *opts = "d:l:s:t:v:o:";
- const char *usage =
- "Usage: %s [-d numdie] [-s numsaves] [-v version] -o savefilename "
- "\n"
- " -d number of deaths. Signed integer.\n"
- " -l lifetime of lamp in turns. Signed integer.\n"
- " -s number of saves. Signed integer.\n"
- " -t number of turns. Signed integer.\n"
- " -v version number of save format.\n"
- " -o required. File name of save game to write.\n";
+ /* Options. */
+ const char* opts = "d:l:s:t:v:o:";
+ const char* usage = "Usage: %s [-d numdie] [-s numsaves] [-v version] -o savefilename \n"
+ " -d number of deaths. Signed integer.\n"
+ " -l lifetime of lamp in turns. Signed integer.\n"
+ " -s number of saves. Signed integer.\n"
+ " -t number of turns. Signed integer.\n"
+ " -v version number of save format.\n"
+ " -o required. File name of save game to write.\n";
- while ((ch = getopt(argc, argv, opts)) != EOF) {
- switch (ch) {
- case 'd':
- game.numdie = (turn_t)atoi(optarg);
- printf("cheat: game.numdie = %d\n", game.numdie);
- break;
- case 'l':
- game.limit = (turn_t)atoi(optarg);
- printf("cheat: game.limit = %d\n", game.limit);
- break;
- case 's':
- game.saved = (int)atoi(optarg);
- printf("cheat: game.saved = %d\n", game.saved);
- break;
- case 't':
- game.turns = (turn_t)atoi(optarg);
- printf("cheat: game.turns = %d\n", game.turns);
- break;
- case 'v':
- save.version = atoi(optarg);
- printf("cheat: version = %d\n", save.version);
- break;
- case 'o':
- savefilename = optarg;
- break;
- default:
- fprintf(stderr, usage, argv[0]);
- exit(EXIT_FAILURE);
- break;
- }
- }
+ while ((ch = getopt(argc, argv, opts)) != EOF) {
+ switch (ch) {
+ case 'd':
+ game.numdie = (turn_t)atoi(optarg);
+ printf("cheat: game.numdie = %d\n", game.numdie);
+ break;
+ case 'l':
+ game.limit = (turn_t)atoi(optarg);
+ printf("cheat: game.limit = %d\n", game.limit);
+ break;
+ case 's':
+ game.saved = (int)atoi(optarg);
+ printf("cheat: game.saved = %d\n", game.saved);
+ break;
+ case 't':
+ game.turns = (turn_t)atoi(optarg);
+ printf("cheat: game.turns = %d\n", game.turns);
+ break;
+ case 'v':
+ save.version = atoi(optarg);
+ printf("cheat: version = %d\n", save.version);
+ break;
+ case 'o':
+ savefilename = optarg;
+ break;
+ default:
+ fprintf(stderr,
+ usage, argv[0]);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
- // Save filename required; the point of cheat is to generate save file
- if (savefilename == NULL) {
- fprintf(stderr, usage, argv[0]);
- fprintf(stderr, "ERROR: filename required\n");
- exit(EXIT_FAILURE);
- }
+ // Save filename required; the point of cheat is to generate save file
+ if (savefilename == NULL) {
+ fprintf(stderr,
+ usage, argv[0]);
+ fprintf(stderr,
+ "ERROR: filename required\n");
+ exit(EXIT_FAILURE);
+ }
- fp = fopen(savefilename, WRITE_MODE);
- if (fp == NULL) {
- fprintf(stderr, "Can't open file %s. Exiting.\n", savefilename);
- exit(EXIT_FAILURE);
- }
+ fp = fopen(savefilename, WRITE_MODE);
+ if (fp == NULL) {
+ fprintf(stderr,
+ "Can't open file %s. Exiting.\n", savefilename);
+ exit(EXIT_FAILURE);
+ }
- savefile(fp);
+ savefile(fp);
- fclose(fp);
+ fclose(fp);
- printf("cheat: %s created.\n", savefilename);
+ printf("cheat: %s created.\n", savefilename);
- return EXIT_SUCCESS;
+ return EXIT_SUCCESS;
}
// LCOV_EXCL_START
@@ -99,7 +103,12 @@ int main(int argc, char *argv[]) {
* See the actually useful version of this in main.c
*/
-char *myreadline(const char *prompt) { return readline(prompt); }
+char *myreadline(const char *prompt)
+{
+ return readline(prompt);
+}
// LCOV_EXCL_STOP
/* end */
+
+
diff --git a/control b/control
index e47484d..841e7ab 100644
--- a/control
+++ b/control
@@ -5,10 +5,11 @@ Package: open-adventure
Description: Colossal Cave Adventure, the 1995 430-point version.
This is the last descendant of the original 1976 Colossal Cave Adventure
- worked on by the original authors - Crowther & Woods; it is shipped with
- their permission and encouragement. It has sometimes been known as
- Adventure 2.5. The original PDP-10 name 'advent' is used for the
- built program to avoid collision with the BSD Games version.
+ worked on by the original authors - Crowther & Woods. It has sometimes
+ been known as Adventure 2.5. The original PDP-10 name 'advent' is used
+ for the built program to avoid collision with the BSD Games version.
+
+XBS-Destinations: mailto:ubuntu-devel-discuss@lists.ubuntu.com
Homepage: http://www.catb.org/~esr/open-adventure
diff --git a/hints.adoc b/hints.adoc
index f7e25e1..c22c11b 100644
--- a/hints.adoc
+++ b/hints.adoc
@@ -1,5 +1,5 @@
= Non-spoiler hints =
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
Say the words you see. They can have interesting effects.
diff --git a/history.adoc b/history.adoc
index 8ca5da7..5384c2c 100644
--- a/history.adoc
+++ b/history.adoc
@@ -1,6 +1,6 @@
= A brief history of Colossal Cave Adventure =
by Eric S. Raymond
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
Adventure is the fons et origo of all later dungeon-crawling computer
diff --git a/init.c b/init.c
index 372e494..aabd1cb 100644
--- a/init.c
+++ b/init.c
@@ -1,96 +1,100 @@
/*
* Initialisation
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include
-#include
-#include
-#include
-#include
#include
+#include
+#include
+#include
+#include
+#include
#include "advent.h"
-struct settings_t settings = {.logfp = NULL, .oldstyle = false, .prompt = true};
+struct settings_t settings = {
+ .logfp = NULL,
+ .oldstyle = false,
+ .prompt = true
+};
struct game_t game = {
/* Last dwarf is special (the pirate). He always starts at his
* chest's eventual location inside the maze. This loc is saved
* in chloc for ref. The dead end in the other maze has its
* loc stored in chloc2. */
- .chloc = LOC_MAZEEND12, .chloc2 = LOC_DEADEND13, .abbnum = 5,
- .clock1 = WARNTIME, .clock2 = FLASHTIME, .newloc = LOC_START,
- .loc = LOC_START, .limit = GAMELIMIT, .foobar = WORD_EMPTY,
+ .chloc = LOC_MAZEEND12,
+ .chloc2 = LOC_DEADEND13,
+ .abbnum = 5,
+ .clock1 = WARNTIME,
+ .clock2 = FLASHTIME,
+ .newloc = LOC_START,
+ .loc = LOC_START,
+ .limit = GAMELIMIT,
+ .foobar = WORD_EMPTY,
};
-int initialise(void) {
- if (settings.oldstyle) {
- printf("Initialising...\n");
- }
+int initialise(void)
+{
+ if (settings.oldstyle)
+ printf("Initialising...\n");
- srand(time(NULL));
- int seedval = (int)rand();
- set_seed(seedval);
+ srand(time(NULL));
+ int seedval = (int)rand();
+ set_seed(seedval);
- for (int i = 1; i <= NDWARVES; i++) {
- game.dwarves[i].loc = dwarflocs[i - 1];
- }
+ assert(NDWARVES == NDWARFLOCS);
+ for (int i = 1; i <= NDWARFLOCS; i++) {
+ game.dwarves[i].loc = dwarflocs[i-1];
+ }
- for (int i = 1; i <= NOBJECTS; i++) {
- game.objects[i].place = LOC_NOWHERE;
- }
+ for (int i = 1; i <= NOBJECTS; i++) {
+ game.objects[i].place = LOC_NOWHERE;
+ }
- for (int i = 1; i <= NLOCATIONS; i++) {
- if (!(locations[i].description.big == 0 || tkey[i] == 0)) {
- int k = tkey[i];
- if (travel[k].motion == HERE) {
- conditions[i] |= (1 << COND_FORCED);
- }
- }
- }
+ for (int i = 1; i <= NLOCATIONS; i++) {
+ if (!(locations[i].description.big == 0 || tkey[i] == 0)) {
+ int k = tkey[i];
+ if (travel[k].motion == HERE)
+ conditions[i] |= (1 << COND_FORCED);
+ }
+ }
- /* Set up the game.locs atloc and game.link arrays.
- * We'll use the DROP subroutine, which prefaces new objects on the
- * lists. Since we want things in the other order, we'll run the
- * loop backwards. If the object is in two locs, we drop it twice.
- * Also, since two-placed objects are typically best described
- * last, we'll drop them first. */
- for (int i = NOBJECTS; i >= 1; i--) {
- if (objects[i].fixd > 0) {
- drop(i + NOBJECTS, objects[i].fixd);
- drop(i, objects[i].plac);
- }
- }
+ /* Set up the game.locs atloc and game.link arrays.
+ * We'll use the DROP subroutine, which prefaces new objects on the
+ * lists. Since we want things in the other order, we'll run the
+ * loop backwards. If the object is in two locs, we drop it twice.
+ * Also, since two-placed objects are typically best described
+ * last, we'll drop them first. */
+ for (int i = NOBJECTS; i >= 1; i--) {
+ if (objects[i].fixd > 0) {
+ drop(i + NOBJECTS, objects[i].fixd);
+ drop(i, objects[i].plac);
+ }
+ }
- for (int i = 1; i <= NOBJECTS; i++) {
- int k = NOBJECTS + 1 - i;
- game.objects[k].fixed = objects[k].fixd;
- if (objects[k].plac != 0 && objects[k].fixd <= 0) {
- drop(k, objects[k].plac);
- }
- }
+ for (int i = 1; i <= NOBJECTS; i++) {
+ int k = NOBJECTS + 1 - i;
+ game.objects[k].fixed = objects[k].fixd;
+ if (objects[k].plac != 0 && objects[k].fixd <= 0)
+ drop(k, objects[k].plac);
+ }
- /* Treasure props are initially STATE_NOTFOUND, and are set to
- * STATE_FOUND the first time they are described. game.tally
- * keeps track of how many are not yet found, so we know when to
- * close the cave.
- * (ESR) Non-treasures are set to STATE_FOUND explicitly so we
- * don't rely on the value of uninitialized storage. This is to
- * make translation to future languages easier. */
- for (int object = 1; object <= NOBJECTS; object++) {
- if (objects[object].is_treasure) {
- ++game.tally;
- if (objects[object].inventory != NULL) {
- OBJECT_SET_NOT_FOUND(object);
- }
- } else {
- OBJECT_SET_FOUND(object);
- }
- }
- game.conds = setbit(COND_HBASE);
+ /* Treasure props are initially STATE_NOTFOUND, and are set to
+ * STATE_FOUND the first time they are described. game.tally
+ * keeps track of how many are not yet found, so we know when to
+ * close the cave. */
+ for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
+ if (objects[treasure].is_treasure) {
+ ++game.tally;
+ if (objects[treasure].inventory != 0)
+ PROP_SET_NOT_FOUND(treasure);
+ }
+ }
+ game.conds = setbit(COND_HBASE);
- return seedval;
+ return seedval;
}
diff --git a/main.c b/main.c
index 835fc1b..16cf42d 100644
--- a/main.c
+++ b/main.c
@@ -1,470 +1,401 @@
/*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include "advent.h"
-#include
-#include
+#include
+#include
+#include
#include
#include
-#include
-#include
-#include
#include
+#include
#include
+#include
+#include "advent.h"
+#include "dungeon.h"
-#define DIM(a) (sizeof(a) / sizeof(a[0]))
+#define DIM(a) (sizeof(a)/sizeof(a[0]))
#if defined ADVENT_AUTOSAVE
-static FILE *autosave_fp;
-void autosave(void) {
- if (autosave_fp != NULL) {
- rewind(autosave_fp);
- savefile(autosave_fp);
- fflush(autosave_fp);
- }
+static FILE* autosave_fp;
+void autosave(void)
+{
+ if (autosave_fp != NULL) {
+ rewind(autosave_fp);
+ savefile(autosave_fp);
+ fflush(autosave_fp);
+ }
}
#endif
// LCOV_EXCL_START
// exclude from coverage analysis because it requires interactivity to test
-static void sig_handler(int signo) {
- if (signo == SIGINT) {
- if (settings.logfp != NULL) {
- fflush(settings.logfp);
- }
- }
+static void sig_handler(int signo)
+{
+ if (signo == SIGINT) {
+ if (settings.logfp != NULL)
+ fflush(settings.logfp);
+ }
#if defined ADVENT_AUTOSAVE
- if (signo == SIGHUP || signo == SIGTERM) {
- autosave();
- }
+ if (signo == SIGHUP || signo == SIGTERM)
+ autosave();
#endif
- exit(EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
// LCOV_EXCL_STOP
-char *myreadline(const char *prompt) {
- /*
- * This function isn't required for gameplay, readline() straight
- * up would suffice for that. It's where we interpret command-line
- * logfiles for testing purposes.
- */
- /* Normal case - no script arguments */
- if (settings.argc == 0) {
- char *ln = readline(prompt);
- if (ln == NULL) {
- fputs(prompt, stdout);
- }
+char *myreadline(const char *prompt)
+{
+ /*
+ * This function isn't required for gameplay, readline() straight
+ * up would suffice for that. It's where we interpret command-line
+ * logfiles for testing purposes.
+ */
+ /* Normal case - no script arguments */
+ if (settings.argc == 0) {
+ char *ln = readline(prompt);
+ if (ln == NULL) {
+ fputs(prompt, stdout);
+ }
+ return ln;
+ }
+
+ char *buf = malloc(LINESIZE + 1);
+ for (;;) {
+ if (settings.scriptfp == NULL || feof(settings.scriptfp)) {
+ if (settings.optind >= settings.argc) {
+ free(buf);
+ return NULL;
+ }
+
+ char *next = settings.argv[settings.optind++];
+
+ if (settings.scriptfp != NULL && feof(settings.scriptfp))
+ fclose(settings.scriptfp);
+ if (strcmp(next, "-") == 0)
+ settings.scriptfp = stdin; // LCOV_EXCL_LINE
+ else
+ settings.scriptfp = fopen(next, "r");
+ }
+
+ if (isatty(fileno(settings.scriptfp))) {
+ free(buf); // LCOV_EXCL_LINE
+ return readline(prompt); // LCOV_EXCL_LINE
+ } else {
+ char *ln = fgets(buf, LINESIZE, settings.scriptfp);
+ if (ln != NULL) {
+ fputs(prompt, stdout);
+ fputs(ln, stdout);
return ln;
- }
+ }
+ }
+ }
- char *buf = malloc(LINESIZE + 1);
- for (;;) {
- if (settings.scriptfp == NULL || feof(settings.scriptfp)) {
- if (settings.optind >= settings.argc) {
- free(buf);
- return NULL;
- }
-
- char *next = settings.argv[settings.optind++];
-
- if (settings.scriptfp != NULL &&
- feof(settings.scriptfp)) {
- fclose(settings.scriptfp);
- }
- if (strcmp(next, "-") == 0) {
- settings.scriptfp = stdin; // LCOV_EXCL_LINE
- } else {
- settings.scriptfp = fopen(next, "r");
- }
- }
-
- if (isatty(fileno(settings.scriptfp)) && !settings.oldstyle) {
- free(buf); // LCOV_EXCL_LINE
- return readline(prompt); // LCOV_EXCL_LINE
- } else {
- char *ln = fgets(buf, LINESIZE, settings.scriptfp);
- if (ln != NULL) {
- fputs(prompt, stdout);
- fputs(ln, stdout);
- return ln;
- }
- }
- }
-
- return NULL;
+ return NULL;
}
/* Check if this loc is eligible for any hints. If been here int
* enough, display. Ignore "HINTS" < 4 (special stuff, see database
* notes). */
-static void checkhints(void) {
- if (conditions[game.loc] >= game.conds) {
- for (int hint = 0; hint < NHINTS; hint++) {
- if (game.hints[hint].used) {
- continue;
- }
- if (!CNDBIT(game.loc, hint + 1 + COND_HBASE)) {
- game.hints[hint].lc = -1;
- }
- ++game.hints[hint].lc;
- /* Come here if he's been int enough at required loc(s)
- * for some unused hint. */
- if (game.hints[hint].lc >= hints[hint].turns) {
- int i;
+static void checkhints(void)
+{
+ if (conditions[game.loc] >= game.conds) {
+ for (int hint = 0; hint < NHINTS; hint++) {
+ if (game.hints[hint].used)
+ continue;
+ if (!CNDBIT(game.loc, hint + 1 + COND_HBASE))
+ game.hints[hint].lc = -1;
+ ++game.hints[hint].lc;
+ /* Come here if he's been int enough at required loc(s) for some
+ * unused hint. */
+ if (game.hints[hint].lc >= hints[hint].turns) {
+ int i;
- switch (hint) {
- case 0:
- /* cave */
- if (game.objects[GRATE].prop ==
- GRATE_CLOSED &&
- !HERE(KEYS)) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- case 1: /* bird */
- if (game.objects[BIRD].place ==
- game.loc &&
- TOTING(ROD) &&
- game.oldobj == BIRD) {
- break;
- }
- return;
- case 2: /* snake */
- if (HERE(SNAKE) && !HERE(BIRD)) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- case 3: /* maze */
- if (game.locs[game.loc].atloc ==
- NO_OBJECT &&
- game.locs[game.oldloc].atloc ==
- NO_OBJECT &&
- game.locs[game.oldlc2].atloc ==
- NO_OBJECT &&
- game.holdng > 1) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- case 4: /* dark */
- if (!OBJECT_IS_NOTFOUND(EMERALD) &&
- OBJECT_IS_NOTFOUND(PYRAMID)) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- case 5: /* witt */
- break;
- case 6: /* urn */
- if (game.dflag == 0) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- case 7: /* woods */
- if (game.locs[game.loc].atloc ==
- NO_OBJECT &&
- game.locs[game.oldloc].atloc ==
- NO_OBJECT &&
- game.locs[game.oldlc2].atloc ==
- NO_OBJECT) {
- break;
- }
- return;
- case 8: /* ogre */
- i = atdwrf(game.loc);
- if (i < 0) {
- game.hints[hint].lc = 0;
- return;
- }
- if (HERE(OGRE) && i == 0) {
- break;
- }
- return;
- case 9: /* jade */
- if (game.tally == 1 &&
- (OBJECT_IS_STASHED(JADE) ||
- OBJECT_IS_NOTFOUND(JADE))) {
- break;
- }
- game.hints[hint].lc = 0;
- return;
- default: // LCOV_EXCL_LINE
- // Should never happen
- BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
+ switch (hint) {
+ case 0:
+ /* cave */
+ if (game.objects[GRATE].prop == GRATE_CLOSED && !HERE(KEYS))
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ case 1: /* bird */
+ if (game.objects[BIRD].place == game.loc && TOTING(ROD) && game.oldobj == BIRD)
+ break;
+ return;
+ case 2: /* snake */
+ if (HERE(SNAKE) && !HERE(BIRD))
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ case 3: /* maze */
+ if (game.locs[game.loc].atloc == NO_OBJECT &&
+ game.locs[game.oldloc].atloc == NO_OBJECT &&
+ game.locs[game.oldlc2].atloc == NO_OBJECT &&
+ game.holdng > 1)
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ case 4: /* dark */
+ if (!PROP_IS_NOTFOUND(EMERALD) && PROP_IS_NOTFOUND(PYRAMID))
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ case 5: /* witt */
+ break;
+ case 6: /* urn */
+ if (game.dflag == 0)
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ case 7: /* woods */
+ if (game.locs[game.loc].atloc == NO_OBJECT &&
+ game.locs[game.oldloc].atloc == NO_OBJECT &&
+ game.locs[game.oldlc2].atloc == NO_OBJECT)
+ break;
+ return;
+ case 8: /* ogre */
+ i = atdwrf(game.loc);
+ if (i < 0) {
+ game.hints[hint].lc = 0;
+ return;
+ }
+ if (HERE(OGRE) && i == 0)
+ break;
+ return;
+ case 9: /* jade */
+ if (game.tally == 1 && PROP_IS_STASHED_OR_UNSEEN(JADE))
+ break;
+ game.hints[hint].lc = 0;
+ return;
+ default: // LCOV_EXCL_LINE
+ // Should never happen
+ BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
- /* Fall through to hint display */
- game.hints[hint].lc = 0;
- if (!yes_or_no(hints[hint].question,
- arbitrary_messages[NO_MESSAGE],
- arbitrary_messages[OK_MAN])) {
- return;
- }
- rspeak(HINT_COST, hints[hint].penalty,
- hints[hint].penalty);
- game.hints[hint].used =
- yes_or_no(arbitrary_messages[WANT_HINT],
- hints[hint].hint,
- arbitrary_messages[OK_MAN]);
- if (game.hints[hint].used &&
- game.limit > WARNTIME) {
- game.limit +=
- WARNTIME * hints[hint].penalty;
- }
- }
- }
- }
+ /* Fall through to hint display */
+ game.hints[hint].lc = 0;
+ if (!yes_or_no(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
+ return;
+ rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
+ game.hints[hint].used = yes_or_no(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
+ if (game.hints[hint].used && game.limit > WARNTIME)
+ game.limit += WARNTIME * hints[hint].penalty;
+ }
+ }
+ }
}
-static bool spotted_by_pirate(int i) {
- if (i != PIRATE) {
- return false;
- }
+static bool spotted_by_pirate(int i)
+{
+ if (i != PIRATE)
+ return false;
- /* The pirate's spotted him. Pirate leaves him alone once we've
- * found chest. K counts if a treasure is here. If not, and
- * tally=1 for an unseen chest, let the pirate be spotted. Note
- * that game.objexts,place[CHEST] = LOC_NOWHERE might mean that he's
- * thrown it to the troll, but in that case he's seen the chest
- * OBJECT_IS_FOUND(CHEST) == true. */
- if (game.loc == game.chloc || !OBJECT_IS_NOTFOUND(CHEST)) {
- return true;
- }
- int snarfed = 0;
- bool movechest = false, robplayer = false;
- for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
- if (!objects[treasure].is_treasure) {
- continue;
- }
- /* Pirate won't take pyramid from plover room or dark
- * room (too easy!). */
- if (treasure == PYRAMID &&
- (game.loc == objects[PYRAMID].plac ||
- game.loc == objects[EMERALD].plac)) {
- continue;
- }
- if (TOTING(treasure) || HERE(treasure)) {
- ++snarfed;
- }
- if (TOTING(treasure)) {
- movechest = true;
- robplayer = true;
- }
- }
- /* Force chest placement before player finds last treasure */
- if (game.tally == 1 && snarfed == 0 &&
- game.objects[CHEST].place == LOC_NOWHERE && HERE(LAMP) &&
- game.objects[LAMP].prop == LAMP_BRIGHT) {
- rspeak(PIRATE_SPOTTED);
- movechest = true;
- }
- /* Do things in this order (chest move before robbery) so chest is
- * listed last at the maze location. */
- if (movechest) {
- move(CHEST, game.chloc);
- move(MESSAG, game.chloc2);
- game.dwarves[PIRATE].loc = game.chloc;
- game.dwarves[PIRATE].oldloc = game.chloc;
- game.dwarves[PIRATE].seen = false;
- } else {
- /* You might get a hint of the pirate's presence even if the
- * chest doesn't move... */
- if (game.dwarves[PIRATE].oldloc != game.dwarves[PIRATE].loc &&
- PCT(20)) {
- rspeak(PIRATE_RUSTLES);
- }
- }
- if (robplayer) {
- rspeak(PIRATE_POUNCES);
- for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
- if (!objects[treasure].is_treasure) {
- continue;
- }
- if (!(treasure == PYRAMID &&
- (game.loc == objects[PYRAMID].plac ||
- game.loc == objects[EMERALD].plac))) {
- if (AT(treasure) &&
- game.objects[treasure].fixed == IS_FREE) {
- carry(treasure, game.loc);
- }
- if (TOTING(treasure)) {
- drop(treasure, game.chloc);
- }
- }
- }
- }
+ /* The pirate's spotted him. Pirate leaves him alone once we've
+ * found chest. K counts if a treasure is here. If not, and
+ * tally=1 for an unseen chest, let the pirate be spotted. Note
+ * that game.objexts,place[CHEST] = LOC_NOWHERE might mean that he's thrown
+ * it to the troll, but in that case he's seen the chest
+ * PROP_IS_FOUND(CHEST) == true. */
+ if (game.loc == game.chloc || !PROP_IS_NOTFOUND(CHEST))
+ return true;
+ int snarfed = 0;
+ bool movechest = false, robplayer = false;
+ for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
+ if (!objects[treasure].is_treasure)
+ continue;
+ /* Pirate won't take pyramid from plover room or dark
+ * room (too easy!). */
+ if (treasure == PYRAMID && (game.loc == objects[PYRAMID].plac ||
+ game.loc == objects[EMERALD].plac)) {
+ continue;
+ }
+ if (TOTING(treasure) || HERE(treasure))
+ ++snarfed;
+ if (TOTING(treasure)) {
+ movechest = true;
+ robplayer = true;
+ }
+ }
+ /* Force chest placement before player finds last treasure */
+ if (game.tally == 1 && snarfed == 0 && game.objects[CHEST].place == LOC_NOWHERE && HERE(LAMP) && game.objects[LAMP].prop == LAMP_BRIGHT) {
+ rspeak(PIRATE_SPOTTED);
+ movechest = true;
+ }
+ /* Do things in this order (chest move before robbery) so chest is listed
+ * last at the maze location. */
+ if (movechest) {
+ move(CHEST, game.chloc);
+ move(MESSAG, game.chloc2);
+ game.dwarves[PIRATE].loc = game.chloc;
+ game.dwarves[PIRATE].oldloc = game.chloc;
+ game.dwarves[PIRATE].seen = false;
+ } else {
+ /* You might get a hint of the pirate's presence even if the
+ * chest doesn't move... */
+ if (game.dwarves[PIRATE].oldloc != game.dwarves[PIRATE].loc && PCT(20))
+ rspeak(PIRATE_RUSTLES);
+ }
+ if (robplayer) {
+ rspeak(PIRATE_POUNCES);
+ for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
+ if (!objects[treasure].is_treasure)
+ continue;
+ if (!(treasure == PYRAMID && (game.loc == objects[PYRAMID].plac ||
+ game.loc == objects[EMERALD].plac))) {
+ if (AT(treasure) && game.objects[treasure].fixed == IS_FREE)
+ carry(treasure, game.loc);
+ if (TOTING(treasure))
+ drop(treasure, game.chloc);
+ }
+ }
+ }
- return true;
+ return true;
}
-static bool dwarfmove(void) {
- /* Dwarves move. Return true if player survives, false if he dies. */
- int kk, stick, attack;
- loc_t tk[21];
+static bool dwarfmove(void)
+/* Dwarves move. Return true if player survives, false if he dies. */
+{
+ int kk, stick, attack;
+ loc_t tk[21];
- /* Dwarf stuff. See earlier comments for description of
- * variables. Remember sixth dwarf is pirate and is thus
- * very different except for motion rules. */
+ /* Dwarf stuff. See earlier comments for description of
+ * variables. Remember sixth dwarf is pirate and is thus
+ * very different except for motion rules. */
- /* First off, don't let the dwarves follow him into a pit or a
- * wall. Activate the whole mess the first time he gets as far
- * as the Hall of Mists (what INDEEP() tests). If game.newloc
- * is forbidden to pirate (in particular, if it's beyond the
- * troll bridge), bypass dwarf stuff. That way pirate can't
- * steal return toll, and dwarves can't meet the bear. Also
- * means dwarves won't follow him into dead end in maze, but
- * c'est la vie. They'll wait for him outside the dead end. */
- if (game.loc == LOC_NOWHERE || FORCED(game.loc) ||
- CNDBIT(game.newloc, COND_NOARRR)) {
- return true;
- }
+ /* First off, don't let the dwarves follow him into a pit or a
+ * wall. Activate the whole mess the first time he gets as far
+ * as the Hall of Mists (what INDEEP() tests). If game.newloc
+ * is forbidden to pirate (in particular, if it's beyond the
+ * troll bridge), bypass dwarf stuff. That way pirate can't
+ * steal return toll, and dwarves can't meet the bear. Also
+ * means dwarves won't follow him into dead end in maze, but
+ * c'est la vie. They'll wait for him outside the dead end. */
+ if (game.loc == LOC_NOWHERE || FORCED(game.loc) || CNDBIT(game.newloc, COND_NOARRR))
+ return true;
- /* Dwarf activity level ratchets up */
- if (game.dflag == 0) {
- if (INDEEP(game.loc)) {
- game.dflag = 1;
- }
- return true;
- }
+ /* Dwarf activity level ratchets up */
+ if (game.dflag == 0) {
+ if (INDEEP(game.loc))
+ game.dflag = 1;
+ return true;
+ }
- /* When we encounter the first dwarf, we kill 0, 1, or 2 of
- * the 5 dwarves. If any of the survivors is at game.loc,
- * replace him with the alternate. */
- if (game.dflag == 1) {
- if (!INDEEP(game.loc) ||
- (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85)))) {
- return true;
- }
- game.dflag = 2;
- for (int i = 1; i <= 2; i++) {
- int j = 1 + randrange(NDWARVES - 1);
- if (PCT(50)) {
- game.dwarves[j].loc = 0;
- }
- }
+ /* When we encounter the first dwarf, we kill 0, 1, or 2 of
+ * the 5 dwarves. If any of the survivors is at game.loc,
+ * replace him with the alternate. */
+ if (game.dflag == 1) {
+ if (!INDEEP(game.loc) ||
+ (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85))))
+ return true;
+ game.dflag = 2;
+ for (int i = 1; i <= 2; i++) {
+ int j = 1 + randrange(NDWARVES - 1);
+ if (PCT(50))
+ game.dwarves[j].loc = 0;
+ }
- /* Alternate initial loc for dwarf, in case one of them
- * starts out on top of the adventurer. */
- for (int i = 1; i <= NDWARVES - 1; i++) {
- if (game.dwarves[i].loc == game.loc) {
- game.dwarves[i].loc = DALTLC;
- }
- game.dwarves[i].oldloc = game.dwarves[i].loc;
- }
- rspeak(DWARF_RAN);
- drop(AXE, game.loc);
- return true;
- }
+ /* Alternate initial loc for dwarf, in case one of them
+ * starts out on top of the adventurer. */
+ for (int i = 1; i <= NDWARVES - 1; i++) {
+ if (game.dwarves[i].loc == game.loc)
+ game.dwarves[i].loc = DALTLC; //
+ game.dwarves[i].oldloc = game.dwarves[i].loc;
+ }
+ rspeak(DWARF_RAN);
+ drop(AXE, game.loc);
+ return true;
+ }
- /* Things are in full swing. Move each dwarf at random,
- * except if he's seen us he sticks with us. Dwarves stay
- * deep inside. If wandering at random, they don't back up
- * unless there's no alternative. If they don't have to
- * move, they attack. And, of course, dead dwarves don't do
- * much of anything. */
- game.dtotal = 0;
- attack = 0;
- stick = 0;
- for (int i = 1; i <= NDWARVES; i++) {
- if (game.dwarves[i].loc == 0) {
- continue;
- }
- /* Fill tk array with all the places this dwarf might go. */
- unsigned int j = 1;
- kk = tkey[game.dwarves[i].loc];
- if (kk != 0) {
- do {
- enum desttype_t desttype = travel[kk].desttype;
- game.newloc = travel[kk].destval;
- /* Have we avoided a dwarf encounter? */
- if (desttype != dest_goto) {
- continue;
- } else if (!INDEEP(game.newloc)) {
- continue;
- } else if (game.newloc ==
- game.dwarves[i].oldloc) {
- continue;
- } else if (j > 1 && game.newloc == tk[j - 1]) {
- continue;
- } else if (j >= DIM(tk) - 1) {
- /* This can't actually happen. */
- continue; // LCOV_EXCL_LINE
- } else if (game.newloc == game.dwarves[i].loc) {
- continue;
- } else if (FORCED(game.newloc)) {
- continue;
- } else if (i == PIRATE &&
- CNDBIT(game.newloc, COND_NOARRR)) {
- continue;
- } else if (travel[kk].nodwarves) {
- continue;
- }
- tk[j++] = game.newloc;
- } while (!travel[kk++].stop);
- }
- tk[j] = game.dwarves[i].oldloc;
- if (j >= 2) {
- --j;
- }
- j = 1 + randrange(j);
- game.dwarves[i].oldloc = game.dwarves[i].loc;
- game.dwarves[i].loc = tk[j];
- game.dwarves[i].seen =
- (game.dwarves[i].seen && INDEEP(game.loc)) ||
- (game.dwarves[i].loc == game.loc ||
- game.dwarves[i].oldloc == game.loc);
- if (!game.dwarves[i].seen) {
- continue;
- }
- game.dwarves[i].loc = game.loc;
- if (spotted_by_pirate(i)) {
- continue;
- }
- /* This threatening little dwarf is in the room with him! */
- ++game.dtotal;
- if (game.dwarves[i].oldloc == game.dwarves[i].loc) {
- ++attack;
- if (game.knfloc >= LOC_NOWHERE) {
- game.knfloc = game.loc;
- }
- if (randrange(1000) < 95 * (game.dflag - 2)) {
- ++stick;
- }
- }
- }
+ /* Things are in full swing. Move each dwarf at random,
+ * except if he's seen us he sticks with us. Dwarves stay
+ * deep inside. If wandering at random, they don't back up
+ * unless there's no alternative. If they don't have to
+ * move, they attack. And, of course, dead dwarves don't do
+ * much of anything. */
+ game.dtotal = 0;
+ attack = 0;
+ stick = 0;
+ for (int i = 1; i <= NDWARVES; i++) {
+ if (game.dwarves[i].loc == 0)
+ continue;
+ /* Fill tk array with all the places this dwarf might go. */
+ unsigned int j = 1;
+ kk = tkey[game.dwarves[i].loc];
+ if (kk != 0)
+ do {
+ enum desttype_t desttype = travel[kk].desttype;
+ game.newloc = travel[kk].destval;
+ /* Have we avoided a dwarf encounter? */
+ if (desttype != dest_goto)
+ continue;
+ else if (!INDEEP(game.newloc))
+ continue;
+ else if (game.newloc == game.dwarves[i].oldloc)
+ continue;
+ else if (j > 1 && game.newloc == tk[j - 1])
+ continue;
+ else if (j >= DIM(tk) - 1)
+ /* This can't actually happen. */
+ continue; // LCOV_EXCL_LINE
+ else if (game.newloc == game.dwarves[i].loc)
+ continue;
+ else if (FORCED(game.newloc))
+ continue;
+ else if (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR))
+ continue;
+ else if (travel[kk].nodwarves)
+ continue;
+ tk[j++] = game.newloc;
+ } while
+ (!travel[kk++].stop);
+ tk[j] = game.dwarves[i].oldloc;
+ if (j >= 2)
+ --j;
+ j = 1 + randrange(j);
+ game.dwarves[i].oldloc = game.dwarves[i].loc;
+ game.dwarves[i].loc = tk[j];
+ game.dwarves[i].seen = (game.dwarves[i].seen && INDEEP(game.loc)) ||
+ (game.dwarves[i].loc == game.loc ||
+ game.dwarves[i].oldloc == game.loc);
+ if (!game.dwarves[i].seen)
+ continue;
+ game.dwarves[i].loc = game.loc;
+ if (spotted_by_pirate(i))
+ continue;
+ /* This threatening little dwarf is in the room with him! */
+ ++game.dtotal;
+ if (game.dwarves[i].oldloc == game.dwarves[i].loc) {
+ ++attack;
+ if (game.knfloc >= LOC_NOWHERE)
+ game.knfloc = game.loc;
+ if (randrange(1000) < 95 * (game.dflag - 2))
+ ++stick;
+ }
+ }
- /* Now we know what's happening. Let's tell the poor sucker about it.
- */
- if (game.dtotal == 0) {
- return true;
- }
- rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
- if (attack == 0) {
- return true;
- }
- if (game.dflag == 2) {
- game.dflag = 3;
- }
- if (attack > 1) {
- rspeak(THROWN_KNIVES, attack);
- rspeak(stick > 1 ? MULTIPLE_HITS
- : (stick == 1 ? ONE_HIT : NONE_HIT),
- stick);
- } else {
- rspeak(KNIFE_THROWN);
- rspeak(stick ? GETS_YOU : MISSES_YOU);
- }
- if (stick == 0) {
- return true;
- }
- game.oldlc2 = game.loc;
- return false;
+ /* Now we know what's happening. Let's tell the poor sucker about it. */
+ if (game.dtotal == 0)
+ return true;
+ rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
+ if (attack == 0)
+ return true;
+ if (game.dflag == 2)
+ game.dflag = 3;
+ if (attack > 1) {
+ rspeak(THROWN_KNIVES, attack);
+ rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
+ } else {
+ rspeak(KNIFE_THROWN);
+ rspeak(stick ? GETS_YOU : MISSES_YOU);
+ }
+ if (stick == 0)
+ return true;
+ game.oldlc2 = game.loc;
+ return false;
}
/* "You're dead, Jim."
@@ -486,77 +417,72 @@ static bool dwarfmove(void) {
* building (and heaven help him if he tries to xyzzy back into the
* cave without the lamp!). game.oldloc is zapped so he can't just
* "retreat". */
-static void croak(void) {
- /* Okay, he's dead. Let's get on with it. */
- const char *query = obituaries[game.numdie].query;
- const char *yes_response = obituaries[game.numdie].yes_response;
+static void croak(void)
+/* Okay, he's dead. Let's get on with it. */
+{
+ const char* query = obituaries[game.numdie].query;
+ const char* yes_response = obituaries[game.numdie].yes_response;
- ++game.numdie;
+ ++game.numdie;
- if (game.closng) {
- /* He died during closing time. No resurrection. Tally up a
- * death and exit. */
- rspeak(DEATH_CLOSING);
- terminate(endgame);
- } else if (!yes_or_no(query, yes_response,
- arbitrary_messages[OK_MAN]) ||
- game.numdie == NDEATHS) {
- /* Player is asked if he wants to try again. If not, or if
- * he's already used all of his lives, we end the game */
- terminate(endgame);
- } else {
- /* If player wishes to continue, we empty the liquids in the
- * user's inventory, turn off the lamp, and drop all items
- * where he died. */
- game.objects[WATER].place = game.objects[OIL].place =
- LOC_NOWHERE;
- if (TOTING(LAMP)) {
- game.objects[LAMP].prop = LAMP_DARK;
- }
- for (int j = 1; j <= NOBJECTS; j++) {
- int i = NOBJECTS + 1 - j;
- if (TOTING(i)) {
- /* Always leave lamp where it's accessible
- * aboveground */
- drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
- }
- }
- game.oldloc = game.loc = game.newloc = LOC_BUILDING;
- }
+ if (game.closng) {
+ /* He died during closing time. No resurrection. Tally up a
+ * death and exit. */
+ rspeak(DEATH_CLOSING);
+ terminate(endgame);
+ } else if (!yes_or_no(query, yes_response, arbitrary_messages[OK_MAN])
+ || game.numdie == NDEATHS) {
+ /* Player is asked if he wants to try again. If not, or if
+ * he's already used all of his lives, we end the game */
+ terminate(endgame);
+ } else {
+ /* If player wishes to continue, we empty the liquids in the
+ * user's inventory, turn off the lamp, and drop all items
+ * where he died. */
+ game.objects[WATER].place = game.objects[OIL].place = LOC_NOWHERE;
+ if (TOTING(LAMP))
+ game.objects[LAMP].prop = LAMP_DARK;
+ for (int j = 1; j <= NOBJECTS; j++) {
+ int i = NOBJECTS + 1 - j;
+ if (TOTING(i)) {
+ /* Always leave lamp where it's accessible aboveground */
+ drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
+ }
+ }
+ game.oldloc = game.loc = game.newloc = LOC_BUILDING;
+ }
}
-static void describe_location(void) {
- /* Describe the location to the user */
- const char *msg = locations[game.loc].description.small;
+static void describe_location(void)
+/* Describe the location to the user */
+{
+ const char* msg = locations[game.loc].description.small;
- if (MOD(game.locs[game.loc].abbrev, game.abbnum) == 0 ||
- msg == NO_MESSAGE) {
- msg = locations[game.loc].description.big;
- }
+ if (MOD(game.locs[game.loc].abbrev, game.abbnum) == 0 || msg == NO_MESSAGE)
+ msg = locations[game.loc].description.big;
- if (!FORCED(game.loc) && IS_DARK_HERE()) {
- msg = arbitrary_messages[PITCH_DARK];
- }
+ if (!FORCED(game.loc) && DARK(game.loc)) {
+ msg = arbitrary_messages[PITCH_DARK];
+ }
- if (TOTING(BEAR)) {
- rspeak(TAME_BEAR);
- }
+ if (TOTING(BEAR))
+ rspeak(TAME_BEAR);
- speak(msg);
+ speak(msg);
- if (game.loc == LOC_Y2 && PCT(25) && !game.closng) {
- rspeak(SAYS_PLUGH);
- }
+ if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
+ rspeak(SAYS_PLUGH);
}
-static bool traveleq(int a, int b) {
- /* Are two travel entries equal for purposes of skip after failed
- * condition? */
- return (travel[a].condtype == travel[b].condtype) &&
- (travel[a].condarg1 == travel[b].condarg1) &&
- (travel[a].condarg2 == travel[b].condarg2) &&
- (travel[a].desttype == travel[b].desttype) &&
- (travel[a].destval == travel[b].destval);
+
+static bool traveleq(int a, int b)
+/* Are two travel entries equal for purposes of skip after failed condition? */
+{
+ return (travel[a].condtype == travel[b].condtype)
+ && (travel[a].condarg1 == travel[b].condarg1)
+ && (travel[a].condarg2 == travel[b].condarg2)
+ && (travel[a].desttype == travel[b].desttype)
+ && (travel[a].destval == travel[b].destval);
}
/* Given the current location in "game.loc", and a motion verb number in
@@ -566,353 +492,292 @@ static bool traveleq(int a, int b) {
* does, game.newloc will be limbo, and game.oldloc will be what killed
* him, so we need game.oldlc2, which is the last place he was
* safe.) */
-static void playermove(int motion) {
- int scratchloc, travel_entry = tkey[game.loc];
- game.newloc = game.loc;
- if (travel_entry == 0) {
- BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE
- }
- if (motion == NUL) {
- return;
- } else if (motion == BACK) {
- /* Handle "go back". Look for verb which goes from game.loc to
- * game.oldloc, or to game.oldlc2 If game.oldloc has
- * forced-motion. te_tmp saves entry -> forced loc -> previous
- * loc. */
- motion = game.oldloc;
- if (FORCED(motion)) {
- motion = game.oldlc2;
- }
- game.oldlc2 = game.oldloc;
- game.oldloc = game.loc;
- if (CNDBIT(game.loc, COND_NOBACK)) {
- rspeak(TWIST_TURN);
- return;
- }
- if (motion == game.loc) {
- rspeak(FORGOT_PATH);
- return;
- }
+static void playermove(int motion)
+{
+ int scratchloc, travel_entry = tkey[game.loc];
+ game.newloc = game.loc;
+ if (travel_entry == 0)
+ BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE
+ if (motion == NUL)
+ return;
+ else if (motion == BACK) {
+ /* Handle "go back". Look for verb which goes from game.loc to
+ * game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
+ * te_tmp saves entry -> forced loc -> previous loc. */
+ motion = game.oldloc;
+ if (FORCED(motion))
+ motion = game.oldlc2;
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ if (CNDBIT(game.loc, COND_NOBACK)) {
+ rspeak(TWIST_TURN);
+ return;
+ }
+ if (motion == game.loc) {
+ rspeak(FORGOT_PATH);
+ return;
+ }
- int te_tmp = 0;
- for (;;) {
- enum desttype_t desttype =
- travel[travel_entry].desttype;
- scratchloc = travel[travel_entry].destval;
- if (desttype != dest_goto || scratchloc != motion) {
- if (desttype == dest_goto) {
- if (FORCED(scratchloc) &&
- travel[tkey[scratchloc]].destval ==
- motion) {
- te_tmp = travel_entry;
- }
- }
- if (!travel[travel_entry].stop) {
- ++travel_entry; /* go to next travel
- entry for this
- location */
- continue;
- }
- /* we've reached the end of travel entries for
- * game.loc */
- travel_entry = te_tmp;
- if (travel_entry == 0) {
- rspeak(NOT_CONNECTED);
- return;
- }
- }
+ int te_tmp = 0;
+ for (;;) {
+ enum desttype_t desttype = travel[travel_entry].desttype;
+ scratchloc = travel[travel_entry].destval;
+ if (desttype != dest_goto || scratchloc != motion) {
+ if (desttype == dest_goto) {
+ if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion)
+ te_tmp = travel_entry;
+ }
+ if (!travel[travel_entry].stop) {
+ ++travel_entry; /* go to next travel entry for this location */
+ continue;
+ }
+ /* we've reached the end of travel entries for game.loc */
+ travel_entry = te_tmp;
+ if (travel_entry == 0) {
+ rspeak(NOT_CONNECTED);
+ return;
+ }
+ }
- motion = travel[travel_entry].motion;
- travel_entry = tkey[game.loc];
- break; /* fall through to ordinary travel */
- }
- } else if (motion == LOOK) {
- /* Look. Can't give more detail. Pretend it wasn't dark
- * (though it may now be dark) so he won't fall into a
- * pit while staring into the gloom. */
- if (game.detail < 3) {
- rspeak(NO_MORE_DETAIL);
- }
- ++game.detail;
- game.wzdark = false;
- game.locs[game.loc].abbrev = 0;
- return;
- } else if (motion == CAVE) {
- /* Cave. Different messages depending on whether above ground.
- */
- rspeak((OUTSIDE(game.loc) && game.loc != LOC_GRATE)
- ? FOLLOW_STREAM
- : NEED_DETAIL);
- return;
- } else {
- /* none of the specials */
- game.oldlc2 = game.oldloc;
- game.oldloc = game.loc;
- }
+ motion = travel[travel_entry].motion;
+ travel_entry = tkey[game.loc];
+ break; /* fall through to ordinary travel */
+ }
+ } else if (motion == LOOK) {
+ /* Look. Can't give more detail. Pretend it wasn't dark
+ * (though it may now be dark) so he won't fall into a
+ * pit while staring into the gloom. */
+ if (game.detail < 3)
+ rspeak(NO_MORE_DETAIL);
+ ++game.detail;
+ game.wzdark = false;
+ game.locs[game.loc].abbrev = 0;
+ return;
+ } else if (motion == CAVE) {
+ /* Cave. Different messages depending on whether above ground. */
+ rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
+ return;
+ } else {
+ /* none of the specials */
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ }
- /* Look for a way to fulfil the motion verb passed in - travel_entry
- * indexes the beginning of the motion entries for here (game.loc). */
- for (;;) {
- if ((travel[travel_entry].motion == HERE) ||
- travel[travel_entry].motion == motion) {
- break;
- }
- if (travel[travel_entry].stop) {
- /* Couldn't find an entry matching the motion word
- * passed in. Various messages depending on word given.
- */
- switch (motion) {
- case EAST:
- case WEST:
- case SOUTH:
- case NORTH:
- case NE:
- case NW:
- case SW:
- case SE:
- case UP:
- case DOWN:
- rspeak(BAD_DIRECTION);
- break;
- case FORWARD:
- case LEFT:
- case RIGHT:
- rspeak(UNSURE_FACING);
- break;
- case OUTSIDE:
- case INSIDE:
- rspeak(NO_INOUT_HERE);
- break;
- case XYZZY:
- case PLUGH:
- rspeak(NOTHING_HAPPENS);
- break;
- case CRAWL:
- rspeak(WHICH_WAY);
- break;
- default:
- rspeak(CANT_APPLY);
- }
- return;
- }
- ++travel_entry;
- }
+ /* Look for a way to fulfil the motion verb passed in - travel_entry indexes
+ * the beginning of the motion entries for here (game.loc). */
+ for (;;) {
+ if ((travel[travel_entry].motion == HERE) || travel[travel_entry].motion == motion)
+ break;
+ if (travel[travel_entry].stop) {
+ /* Couldn't find an entry matching the motion word passed
+ * in. Various messages depending on word given. */
+ switch (motion) {
+ case EAST:
+ case WEST:
+ case SOUTH:
+ case NORTH:
+ case NE:
+ case NW:
+ case SW:
+ case SE:
+ case UP:
+ case DOWN:
+ rspeak(BAD_DIRECTION);
+ break;
+ case FORWARD:
+ case LEFT:
+ case RIGHT:
+ rspeak(UNSURE_FACING);
+ break;
+ case OUTSIDE:
+ case INSIDE:
+ rspeak(NO_INOUT_HERE);
+ break;
+ case XYZZY:
+ case PLUGH:
+ rspeak(NOTHING_HAPPENS);
+ break;
+ case CRAWL:
+ rspeak(WHICH_WAY);
+ break;
+ default:
+ rspeak(CANT_APPLY);
+ }
+ return;
+ }
+ ++travel_entry;
+ }
- /* (ESR) We've found a destination that goes with the motion verb.
- * Next we need to check any conditional(s) on this destination, and
- * possibly on following entries. */
- do {
- for (;;) { /* L12 loop */
- for (;;) {
- enum condtype_t condtype =
- travel[travel_entry].condtype;
- int condarg1 = travel[travel_entry].condarg1;
- int condarg2 = travel[travel_entry].condarg2;
- if (condtype < cond_not) {
- /* YAML N and [pct N] conditionals */
- if (condtype == cond_goto ||
- condtype == cond_pct) {
- if (condarg1 == 0 ||
- PCT(condarg1)) {
- break;
- }
- /* else fall through */
- }
- /* YAML [with OBJ] clause */
- else if (TOTING(condarg1) ||
- (condtype == cond_with &&
- AT(condarg1))) {
- break;
- }
- /* else fall through to check [not OBJ
- * STATE] */
- } else if (game.objects[condarg1].prop !=
- condarg2) {
- break;
- }
+ /* (ESR) We've found a destination that goes with the motion verb.
+ * Next we need to check any conditional(s) on this destination, and
+ * possibly on following entries. */
+ do {
+ for (;;) { /* L12 loop */
+ for (;;) {
+ enum condtype_t condtype = travel[travel_entry].condtype;
+ int condarg1 = travel[travel_entry].condarg1;
+ int condarg2 = travel[travel_entry].condarg2;
+ if (condtype < cond_not) {
+ /* YAML N and [pct N] conditionals */
+ if (condtype == cond_goto || condtype == cond_pct) {
+ if (condarg1 == 0 || PCT(condarg1))
+ break;
+ /* else fall through */
+ }
+ /* YAML [with OBJ] clause */
+ else if (TOTING(condarg1) || (condtype == cond_with && AT(condarg1)))
+ break;
+ /* else fall through to check [not OBJ STATE] */
+ } else if (game.objects[condarg1].prop != condarg2)
+ break;
- /* We arrive here on conditional failure.
- * Skip to next non-matching destination */
- int te_tmp = travel_entry;
- do {
- if (travel[te_tmp].stop) {
- BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
- }
- ++te_tmp;
- } while (traveleq(travel_entry, te_tmp));
- travel_entry = te_tmp;
- }
+ /* We arrive here on conditional failure.
+ * Skip to next non-matching destination */
+ int te_tmp = travel_entry;
+ do {
+ if (travel[te_tmp].stop)
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ ++te_tmp;
+ } while
+ (traveleq(travel_entry, te_tmp));
+ travel_entry = te_tmp;
+ }
- /* Found an eligible rule, now execute it */
- enum desttype_t desttype =
- travel[travel_entry].desttype;
- game.newloc = travel[travel_entry].destval;
- if (desttype == dest_goto) {
- return;
- }
+ /* Found an eligible rule, now execute it */
+ enum desttype_t desttype = travel[travel_entry].desttype;
+ game.newloc = travel[travel_entry].destval;
+ if (desttype == dest_goto)
+ return;
- if (desttype == dest_speak) {
- /* Execute a speak rule */
- rspeak(game.newloc);
- game.newloc = game.loc;
- return;
- } else {
- switch (game.newloc) {
- case 1:
- /* Special travel 1. Plover-alcove
- * passage. Can carry only emerald.
- * Note: travel table must include
- * "useless" entries going through
- * passage, which can never be used for
- * actual motion, but can be spotted by
- * "go back". */
- game.newloc = (game.loc == LOC_PLOVER)
- ? LOC_ALCOVE
- : LOC_PLOVER;
- if (game.holdng > 1 ||
- (game.holdng == 1 &&
- !TOTING(EMERALD))) {
- game.newloc = game.loc;
- rspeak(MUST_DROP);
- }
- return;
- case 2:
- /* Special travel 2. Plover transport.
- * Drop the emerald (only use special
- * travel if toting it), so he's forced
- * to use the plover-passage to get it
- * out. Having dropped it, go back and
- * pretend he wasn't carrying it after
- * all. */
- drop(EMERALD, game.loc);
- {
- int te_tmp = travel_entry;
- do {
- if (travel[te_tmp]
- .stop) {
- BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
- }
- ++te_tmp;
- } while (traveleq(travel_entry,
- te_tmp));
- travel_entry = te_tmp;
- }
- continue; /* goto L12 */
- case 3:
- /* Special travel 3. Troll bridge. Must
- * be done only as special motion so
- * that dwarves won't wander across and
- * encounter the bear. (They won't
- * follow the player there because that
- * region is forbidden to the pirate.)
- * If game.prop[TROLL]=TROLL_PAIDONCE,
- * he's crossed since paying, so step
- * out and block him. (standard travel
- * entries check for
- * game.prop[TROLL]=TROLL_UNPAID.)
- * Special stuff for bear. */
- if (game.objects[TROLL].prop ==
- TROLL_PAIDONCE) {
- pspeak(TROLL, look, true,
- TROLL_PAIDONCE);
- game.objects[TROLL].prop =
- TROLL_UNPAID;
- DESTROY(TROLL2);
- move(TROLL2 + NOBJECTS,
- IS_FREE);
- move(TROLL,
- objects[TROLL].plac);
- move(TROLL + NOBJECTS,
- objects[TROLL].fixd);
- juggle(CHASM);
- game.newloc = game.loc;
- return;
- } else {
- game.newloc =
- objects[TROLL].plac +
- objects[TROLL].fixd -
- game.loc;
- if (game.objects[TROLL].prop ==
- TROLL_UNPAID) {
- game.objects[TROLL]
- .prop =
- TROLL_PAIDONCE;
- }
- if (!TOTING(BEAR)) {
- return;
- }
- state_change(CHASM,
- BRIDGE_WRECKED);
- game.objects[TROLL].prop =
- TROLL_GONE;
- drop(BEAR, game.newloc);
- game.objects[BEAR].fixed =
- IS_FIXED;
- game.objects[BEAR].prop =
- BEAR_DEAD;
- game.oldlc2 = game.newloc;
- croak();
- return;
- }
- default: // LCOV_EXCL_LINE
- BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
- }
- break; /* Leave L12 loop */
- }
- } while (false);
+ if (desttype == dest_speak) {
+ /* Execute a speak rule */
+ rspeak(game.newloc);
+ game.newloc = game.loc;
+ return;
+ } else {
+ switch (game.newloc) {
+ case 1:
+ /* Special travel 1. Plover-alcove passage. Can carry only
+ * emerald. Note: travel table must include "useless"
+ * entries going through passage, which can never be used
+ * for actual motion, but can be spotted by "go back". */
+ game.newloc = (game.loc == LOC_PLOVER)
+ ? LOC_ALCOVE
+ : LOC_PLOVER;
+ if (game.holdng > 1 || (game.holdng == 1 && !TOTING(EMERALD))) {
+ game.newloc = game.loc;
+ rspeak(MUST_DROP);
+ }
+ return;
+ case 2:
+ /* Special travel 2. Plover transport. Drop the
+ * emerald (only use special travel if toting
+ * it), so he's forced to use the plover-passage
+ * to get it out. Having dropped it, go back and
+ * pretend he wasn't carrying it after all. */
+ drop(EMERALD, game.loc);
+ {
+ int te_tmp = travel_entry;
+ do {
+ if (travel[te_tmp].stop)
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ ++te_tmp;
+ } while
+ (traveleq(travel_entry, te_tmp));
+ travel_entry = te_tmp;
+ }
+ continue; /* goto L12 */
+ case 3:
+ /* Special travel 3. Troll bridge. Must be done
+ * only as special motion so that dwarves won't
+ * wander across and encounter the bear. (They
+ * won't follow the player there because that
+ * region is forbidden to the pirate.) If
+ * game.prop[TROLL]=TROLL_PAIDONCE, he's crossed
+ * since paying, so step out and block him.
+ * (standard travel entries check for
+ * game.prop[TROLL]=TROLL_UNPAID.) Special stuff
+ * for bear. */
+ if (game.objects[TROLL].prop == TROLL_PAIDONCE) {
+ pspeak(TROLL, look, true, TROLL_PAIDONCE);
+ game.objects[TROLL].prop = TROLL_UNPAID;
+ DESTROY(TROLL2);
+ move(TROLL2 + NOBJECTS, IS_FREE);
+ move(TROLL, objects[TROLL].plac);
+ move(TROLL + NOBJECTS, objects[TROLL].fixd);
+ juggle(CHASM);
+ game.newloc = game.loc;
+ return;
+ } else {
+ game.newloc = objects[TROLL].plac + objects[TROLL].fixd - game.loc;
+ if (game.objects[TROLL].prop == TROLL_UNPAID)
+ game.objects[TROLL].prop = TROLL_PAIDONCE;
+ if (!TOTING(BEAR))
+ return;
+ state_change(CHASM, BRIDGE_WRECKED);
+ game.objects[TROLL].prop = TROLL_GONE;
+ drop(BEAR, game.newloc);
+ game.objects[BEAR].fixed = IS_FIXED;
+ game.objects[BEAR].prop = BEAR_DEAD;
+ game.oldlc2 = game.newloc;
+ croak();
+ return;
+ }
+ default: // LCOV_EXCL_LINE
+ BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ }
+ break; /* Leave L12 loop */
+ }
+ } while
+ (false);
}
-static void lampcheck(void) {
- /* Check game limit and lamp timers */
- if (game.objects[LAMP].prop == LAMP_BRIGHT) {
- --game.limit;
- }
+static void lampcheck(void)
+/* Check game limit and lamp timers */
+{
+ if (game.objects[LAMP].prop == LAMP_BRIGHT)
+ --game.limit;
- /* Another way we can force an end to things is by having the
- * lamp give out. When it gets close, we come here to warn him.
- * First following arm checks if the lamp and fresh batteries are
- * here, in which case we replace the batteries and continue.
- * Second is for other cases of lamp dying. Even after it goes
- * out, he can explore outside for a while if desired. */
- if (game.limit <= WARNTIME) {
- if (HERE(BATTERY) &&
- game.objects[BATTERY].prop == FRESH_BATTERIES &&
- HERE(LAMP)) {
- rspeak(REPLACE_BATTERIES);
- game.objects[BATTERY].prop = DEAD_BATTERIES;
+ /* Another way we can force an end to things is by having the
+ * lamp give out. When it gets close, we come here to warn him.
+ * First following arm checks if the lamp and fresh batteries are
+ * here, in which case we replace the batteries and continue.
+ * Second is for other cases of lamp dying. Even after it goes
+ * out, he can explore outside for a while if desired. */
+ if (game.limit <= WARNTIME) {
+ if (HERE(BATTERY) && game.objects[BATTERY].prop == FRESH_BATTERIES && HERE(LAMP)) {
+ rspeak(REPLACE_BATTERIES);
+ game.objects[BATTERY].prop = DEAD_BATTERIES;
#ifdef __unused__
- /* This code from the original game seems to have been
- * faulty. No tests ever passed the guard, and with the
- * guard removed the game hangs when the lamp limit is
- * reached.
- */
- if (TOTING(BATTERY)) {
- drop(BATTERY, game.loc);
- }
+ /* This code from the original game seems to have been faulty.
+ * No tests ever passed the guard, and with the guard removed
+ * the game hangs when the lamp limit is reached.
+ */
+ if (TOTING(BATTERY))
+ drop(BATTERY, game.loc);
#endif
- game.limit += BATTERYLIFE;
- game.lmwarn = false;
- } else if (!game.lmwarn && HERE(LAMP)) {
- game.lmwarn = true;
- if (game.objects[BATTERY].prop == DEAD_BATTERIES) {
- rspeak(MISSING_BATTERIES);
- } else if (game.objects[BATTERY].place == LOC_NOWHERE) {
- rspeak(LAMP_DIM);
- } else {
- rspeak(GET_BATTERIES);
- }
- }
- }
- if (game.limit == 0) {
- game.limit = -1;
- game.objects[LAMP].prop = LAMP_DARK;
- if (HERE(LAMP)) {
- rspeak(LAMP_OUT);
- }
- }
+ game.limit += BATTERYLIFE;
+ game.lmwarn = false;
+ } else if (!game.lmwarn && HERE(LAMP)) {
+ game.lmwarn = true;
+ if (game.objects[BATTERY].prop == DEAD_BATTERIES)
+ rspeak(MISSING_BATTERIES);
+ else if (game.objects[BATTERY].place == LOC_NOWHERE)
+ rspeak(LAMP_DIM);
+ else
+ rspeak(GET_BATTERIES);
+ }
+ }
+ if (game.limit == 0) {
+ game.limit = -1;
+ game.objects[LAMP].prop = LAMP_DARK;
+ if (HERE(LAMP))
+ rspeak(LAMP_OUT);
+ }
}
+static bool closecheck(void)
/* Handle the closing of the cave. The cave closes "clock1" turns
* after the last treasure has been located (including the pirate's
* chest, which may of course never show up). Note that the
@@ -931,510 +796,448 @@ static void lampcheck(void) {
* separating him from all the treasures. Most of these problems
* arise from the use of negative prop numbers to suppress the object
* descriptions until he's actually moved the objects. */
-static bool closecheck(void) {
- /* If a turn threshold has been met, apply penalties and tell
- * the player about it. */
- for (int i = 0; i < NTHRESHOLDS; ++i) {
- if (game.turns == turn_thresholds[i].threshold + 1) {
- game.trnluz += turn_thresholds[i].point_loss;
- speak(turn_thresholds[i].message);
- }
- }
+{
+ /* If a turn threshold has been met, apply penalties and tell
+ * the player about it. */
+ for (int i = 0; i < NTHRESHOLDS; ++i) {
+ if (game.turns == turn_thresholds[i].threshold + 1) {
+ game.trnluz += turn_thresholds[i].point_loss;
+ speak(turn_thresholds[i].message);
+ }
+ }
- /* Don't tick game.clock1 unless well into cave (and not at Y2). */
- if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2) {
- --game.clock1;
- }
+ /* Don't tick game.clock1 unless well into cave (and not at Y2). */
+ if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
+ --game.clock1;
- /* When the first warning comes, we lock the grate, destroy
- * the bridge, kill all the dwarves (and the pirate), remove
- * the troll and bear (unless dead), and set "closng" to
- * true. Leave the dragon; too much trouble to move it.
- * from now until clock2 runs out, he cannot unlock the
- * grate, move to any location outside the cave, or create
- * the bridge. Nor can he be resurrected if he dies. Note
- * that the snake is already gone, since he got to the
- * treasure accessible only via the hall of the mountain
- * king. Also, he's been in giant room (to get eggs), so we
- * can refer to it. Also also, he's gotten the pearl, so we
- * know the bivalve is an oyster. *And*, the dwarves must
- * have been activated, since we've found chest. */
- if (game.clock1 == 0) {
- game.objects[GRATE].prop = GRATE_CLOSED;
- game.objects[FISSURE].prop = UNBRIDGED;
- for (int i = 1; i <= NDWARVES; i++) {
- game.dwarves[i].seen = false;
- game.dwarves[i].loc = LOC_NOWHERE;
- }
- DESTROY(TROLL);
- move(TROLL + NOBJECTS, IS_FREE);
- move(TROLL2, objects[TROLL].plac);
- move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
- juggle(CHASM);
- if (game.objects[BEAR].prop != BEAR_DEAD) {
- DESTROY(BEAR);
- }
- game.objects[CHAIN].prop = CHAIN_HEAP;
- game.objects[CHAIN].fixed = IS_FREE;
- game.objects[AXE].prop = AXE_HERE;
- game.objects[AXE].fixed = IS_FREE;
- rspeak(CAVE_CLOSING);
- game.clock1 = -1;
- game.closng = true;
- return game.closed;
- } else if (game.clock1 < 0) {
- --game.clock2;
- }
- if (game.clock2 == 0) {
- /* Once he's panicked, and clock2 has run out, we come here
- * to set up the storage room. The room has two locs,
- * hardwired as LOC_NE and LOC_SW. At the ne end, we
- * place empty bottles, a nursery of plants, a bed of
- * oysters, a pile of lamps, rods with stars, sleeping
- * dwarves, and him. At the sw end we place grate over
- * treasures, snake pit, covey of caged birds, more rods, and
- * pillows. A mirror stretches across one wall. Many of the
- * objects come from known locations and/or states (e.g. the
- * snake is known to have been destroyed and needn't be
- * carried away from its old "place"), making the various
- * objects be handled differently. We also drop all other
- * objects he might be carrying (lest he has some which
- * could cause trouble, such as the keys). We describe the
- * flash of light and trundle back. */
- put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
- put(PLANT, LOC_NE, PLANT_THIRSTY);
- put(OYSTER, LOC_NE, STATE_FOUND);
- put(LAMP, LOC_NE, LAMP_DARK);
- put(ROD, LOC_NE, STATE_FOUND);
- put(DWARF, LOC_NE, STATE_FOUND);
- game.loc = LOC_NE;
- game.oldloc = LOC_NE;
- game.newloc = LOC_NE;
- /* Leave the grate with normal (non-negative) property.
- * Reuse sign. */
- move(GRATE, LOC_SW);
- move(SIGN, LOC_SW);
- game.objects[SIGN].prop = ENDGAME_SIGN;
- put(SNAKE, LOC_SW, SNAKE_CHASED);
- put(BIRD, LOC_SW, BIRD_CAGED);
- put(CAGE, LOC_SW, STATE_FOUND);
- put(ROD2, LOC_SW, STATE_FOUND);
- put(PILLOW, LOC_SW, STATE_FOUND);
+ /* When the first warning comes, we lock the grate, destroy
+ * the bridge, kill all the dwarves (and the pirate), remove
+ * the troll and bear (unless dead), and set "closng" to
+ * true. Leave the dragon; too much trouble to move it.
+ * from now until clock2 runs out, he cannot unlock the
+ * grate, move to any location outside the cave, or create
+ * the bridge. Nor can he be resurrected if he dies. Note
+ * that the snake is already gone, since he got to the
+ * treasure accessible only via the hall of the mountain
+ * king. Also, he's been in giant room (to get eggs), so we
+ * can refer to it. Also also, he's gotten the pearl, so we
+ * know the bivalve is an oyster. *And*, the dwarves must
+ * have been activated, since we've found chest. */
+ if (game.clock1 == 0) {
+ game.objects[GRATE].prop = GRATE_CLOSED;
+ game.objects[FISSURE].prop = UNBRIDGED;
+ for (int i = 1; i <= NDWARVES; i++) {
+ game.dwarves[i].seen = false;
+ game.dwarves[i].loc = LOC_NOWHERE;
+ }
+ DESTROY(TROLL);
+ move(TROLL + NOBJECTS, IS_FREE);
+ move(TROLL2, objects[TROLL].plac);
+ move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
+ juggle(CHASM);
+ if (game.objects[BEAR].prop != BEAR_DEAD)
+ DESTROY(BEAR);
+ game.objects[CHAIN].prop = CHAIN_HEAP;
+ game.objects[CHAIN].fixed = IS_FREE;
+ game.objects[AXE].prop = AXE_HERE;
+ game.objects[AXE].fixed = IS_FREE;
+ rspeak(CAVE_CLOSING);
+ game.clock1 = -1;
+ game.closng = true;
+ return game.closed;
+ } else if (game.clock1 < 0)
+ --game.clock2;
+ if (game.clock2 == 0) {
+ /* Once he's panicked, and clock2 has run out, we come here
+ * to set up the storage room. The room has two locs,
+ * hardwired as LOC_NE and LOC_SW. At the ne end, we
+ * place empty bottles, a nursery of plants, a bed of
+ * oysters, a pile of lamps, rods with stars, sleeping
+ * dwarves, and him. At the sw end we place grate over
+ * treasures, snake pit, covey of caged birds, more rods, and
+ * pillows. A mirror stretches across one wall. Many of the
+ * objects come from known locations and/or states (e.g. the
+ * snake is known to have been destroyed and needn't be
+ * carried away from its old "place"), making the various
+ * objects be handled differently. We also drop all other
+ * objects he might be carrying (lest he has some which
+ * could cause trouble, such as the keys). We describe the
+ * flash of light and trundle back. */
+ put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
+ put(PLANT, LOC_NE, PLANT_THIRSTY);
+ put(OYSTER, LOC_NE, STATE_FOUND);
+ put(LAMP, LOC_NE, LAMP_DARK);
+ put(ROD, LOC_NE, STATE_FOUND);
+ put(DWARF, LOC_NE, STATE_FOUND);
+ game.loc = LOC_NE;
+ game.oldloc = LOC_NE;
+ game.newloc = LOC_NE;
+ /* Leave the grate with normal (non-negative) property.
+ * Reuse sign. */
+ move(GRATE, LOC_SW);
+ move(SIGN, LOC_SW);
+ game.objects[SIGN].prop = ENDGAME_SIGN;
+ put(SNAKE, LOC_SW, SNAKE_CHASED);
+ put(BIRD, LOC_SW, BIRD_CAGED);
+ put(CAGE, LOC_SW, STATE_FOUND);
+ put(ROD2, LOC_SW, STATE_FOUND);
+ put(PILLOW, LOC_SW, STATE_FOUND);
- put(MIRROR, LOC_NE, STATE_FOUND);
- game.objects[MIRROR].fixed = LOC_SW;
+ put(MIRROR, LOC_NE, STATE_FOUND);
+ game.objects[MIRROR].fixed = LOC_SW;
- for (int i = 1; i <= NOBJECTS; i++) {
- if (TOTING(i)) {
- DESTROY(i);
- }
- }
+ for (int i = 1; i <= NOBJECTS; i++) {
+ if (TOTING(i))
+ DESTROY(i);
+ }
- rspeak(CAVE_CLOSED);
- game.closed = true;
- return game.closed;
- }
+ rspeak(CAVE_CLOSED);
+ game.closed = true;
+ return game.closed;
+ }
- lampcheck();
- return false;
+ lampcheck();
+ return false;
}
-static void listobjects(void) {
- /* Print out descriptions of objects at this location. If
- * not closing and property value is negative, tally off
- * another treasure. Rug is special case; once seen, its
- * game.prop is RUG_DRAGON (dragon on it) till dragon is killed.
- * Similarly for chain; game.prop is initially CHAINING_BEAR (locked to
- * bear). These hacks are because game.prop=0 is needed to
- * get full score. */
- if (!IS_DARK_HERE()) {
- ++game.locs[game.loc].abbrev;
- for (int i = game.locs[game.loc].atloc; i != 0;
- i = game.link[i]) {
- obj_t obj = i;
- if (obj > NOBJECTS) {
- obj = obj - NOBJECTS;
- }
- if (obj == STEPS && TOTING(NUGGET)) {
- continue;
- }
- /* (ESR) Warning: it looks like you could get away with
- * running this code only on objects with the treasure
- * property set. Nope. There is mystery here.
- */
- if (OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(obj)) {
- if (game.closed) {
- continue;
- }
- OBJECT_SET_FOUND(obj);
- if (obj == RUG) {
- game.objects[RUG].prop = RUG_DRAGON;
- }
- if (obj == CHAIN) {
- game.objects[CHAIN].prop =
- CHAINING_BEAR;
- }
- if (obj == EGGS) {
- game.seenbigwords = true;
- }
- --game.tally;
- /* Note: There used to be a test here to see
- * whether the player had blown it so badly that
- * he could never ever see the remaining
- * treasures, and if so the lamp was zapped to
- * 35 turns. But the tests were too
- * simple-minded; things like killing the bird
- * before the snake was gone (can never see
- * jewelry), and doing it "right" was hopeless.
- * E.G., could cross troll bridge several times,
- * using up all available treasures, breaking
- * vase, using coins to buy batteries, etc., and
- * eventually never be able to get across again.
- * If bottle were left on far side, could then
- * never get eggs or trident, and the effects
- * propagate. So the whole thing was flushed.
- * anyone who makes such a gross blunder isn't
- * likely to find everything else anyway (so
- * goes the rationalisation). */
- }
- int kk = game.objects[obj].prop;
- if (obj == STEPS) {
- kk = (game.loc == game.objects[STEPS].fixed)
- ? STEPS_UP
- : STEPS_DOWN;
- }
- pspeak(obj, look, true, kk);
- }
- }
+static void listobjects(void)
+/* Print out descriptions of objects at this location. If
+ * not closing and property value is negative, tally off
+ * another treasure. Rug is special case; once seen, its
+ * game.prop is RUG_DRAGON (dragon on it) till dragon is killed.
+ * Similarly for chain; game.prop is initially CHAINING_BEAR (locked to
+ * bear). These hacks are because game.prop=0 is needed to
+ * get full score. */
+{
+ if (!DARK(game.loc)) {
+ ++game.locs[game.loc].abbrev;
+ for (int i = game.locs[game.loc].atloc; i != 0; i = game.link[i]) {
+ obj_t obj = i;
+ if (obj > NOBJECTS)
+ obj = obj - NOBJECTS;
+ if (obj == STEPS && TOTING(NUGGET))
+ continue;
+ /* (ESR) Warning: it looks like you could get away with
+ * running this code only on objects with the treasure
+ * property set. Nope. There is mystery here.
+ */
+ if (PROP_IS_STASHED_OR_UNSEEN(obj)) {
+ if (game.closed)
+ continue;
+ PROP_SET_FOUND(obj);
+ if (obj == RUG)
+ game.objects[RUG].prop = RUG_DRAGON;
+ if (obj == CHAIN)
+ game.objects[CHAIN].prop = CHAINING_BEAR;
+ if (obj == EGGS)
+ game.seenbigwords = true;
+ --game.tally;
+ /* Note: There used to be a test here to see whether the
+ * player had blown it so badly that he could never ever see
+ * the remaining treasures, and if so the lamp was zapped to
+ * 35 turns. But the tests were too simple-minded; things
+ * like killing the bird before the snake was gone (can never
+ * see jewelry), and doing it "right" was hopeless. E.G.,
+ * could cross troll bridge several times, using up all
+ * available treasures, breaking vase, using coins to buy
+ * batteries, etc., and eventually never be able to get
+ * across again. If bottle were left on far side, could then
+ * never get eggs or trident, and the effects propagate. So
+ * the whole thing was flushed. anyone who makes such a
+ * gross blunder isn't likely to find everything else anyway
+ * (so goes the rationalisation). */
+ }
+ int kk = game.objects[obj].prop;
+ if (obj == STEPS)
+ kk = (game.loc == game.objects[STEPS].fixed)
+ ? STEPS_UP
+ : STEPS_DOWN;
+ pspeak(obj, look, true, kk);
+ }
+ }
}
-/* Pre-processes a command input to see if we need to tease out a few specific
- * cases:
+static bool preprocess_command(command_t *command)
+/* Pre-processes a command input to see if we need to tease out a few specific cases:
* - "enter water" or "enter stream":
- * weird specific case that gets the user wet, and then kicks us back to get
- * another command
+ * weird specific case that gets the user wet, and then kicks us back to get another command
* - :
- * Irregular form of input, but should be allowed. We switch back to
- * form for further processing.
+ * Irregular form of input, but should be allowed. We switch back to form for
+ * further processing.
* - "grate":
- * If in location with grate, we move to that grate. If we're in a number of
- * other places, we move to the entrance.
+ * If in location with grate, we move to that grate. If we're in a number of other places,
+ * we move to the entrance.
* - "water plant", "oil plant", "water door", "oil door":
* Change to "pour water" or "pour oil" based on context
* - "cage bird":
* If bird is present, we change to "carry bird"
*
- * Returns true if pre-processing is complete, and we're ready to move to the
- * primary command processing, false otherwise. */
-static bool preprocess_command(command_t *command) {
- if (command->word[0].type == MOTION && command->word[0].id == ENTER &&
- (command->word[1].id == STREAM || command->word[1].id == WATER)) {
- if (LIQLOC(game.loc) == WATER) {
- rspeak(FEET_WET);
- } else {
- rspeak(WHERE_QUERY);
- }
- } else {
- if (command->word[0].type == OBJECT) {
- /* From OV to VO form */
- if (command->word[1].type == ACTION) {
- command_word_t stage = command->word[0];
- command->word[0] = command->word[1];
- command->word[1] = stage;
- }
+ * Returns true if pre-processing is complete, and we're ready to move to the primary command
+ * processing, false otherwise. */
+{
+ if (command->word[0].type == MOTION && command->word[0].id == ENTER
+ && (command->word[1].id == STREAM || command->word[1].id == WATER)) {
+ if (LIQLOC(game.loc) == WATER)
+ rspeak(FEET_WET);
+ else
+ rspeak(WHERE_QUERY);
+ } else {
+ if (command->word[0].type == OBJECT) {
+ /* From OV to VO form */
+ if (command->word[1].type == ACTION) {
+ command_word_t stage = command->word[0];
+ command->word[0] = command->word[1];
+ command->word[1] = stage;
+ }
- if (command->word[0].id == GRATE) {
- command->word[0].type = MOTION;
- if (game.loc == LOC_START ||
- game.loc == LOC_VALLEY ||
- game.loc == LOC_SLIT) {
- command->word[0].id = DEPRESSION;
- }
- if (game.loc == LOC_COBBLE ||
- game.loc == LOC_DEBRIS ||
- game.loc == LOC_AWKWARD ||
- game.loc == LOC_BIRDCHAMBER ||
- game.loc == LOC_PITTOP) {
- command->word[0].id = ENTRANCE;
- }
- }
- if ((command->word[0].id == WATER ||
- command->word[0].id == OIL) &&
- (command->word[1].id == PLANT ||
- command->word[1].id == DOOR)) {
- if (AT(command->word[1].id)) {
- command->word[1] = command->word[0];
- command->word[0].id = POUR;
- command->word[0].type = ACTION;
- strncpy(command->word[0].raw, "pour",
- LINESIZE - 1);
- }
- }
- if (command->word[0].id == CAGE &&
- command->word[1].id == BIRD && HERE(CAGE) &&
- HERE(BIRD)) {
- command->word[0].id = CARRY;
- command->word[0].type = ACTION;
- }
- }
+ if (command->word[0].id == GRATE) {
+ command->word[0].type = MOTION;
+ if (game.loc == LOC_START ||
+ game.loc == LOC_VALLEY ||
+ game.loc == LOC_SLIT) {
+ command->word[0].id = DEPRESSION;
+ }
+ if (game.loc == LOC_COBBLE ||
+ game.loc == LOC_DEBRIS ||
+ game.loc == LOC_AWKWARD ||
+ game.loc == LOC_BIRDCHAMBER ||
+ game.loc == LOC_PITTOP) {
+ command->word[0].id = ENTRANCE;
+ }
+ }
+ if ((command->word[0].id == WATER || command->word[0].id == OIL) &&
+ (command->word[1].id == PLANT || command->word[1].id == DOOR)) {
+ if (AT(command->word[1].id)) {
+ command->word[1] = command->word[0];
+ command->word[0].id = POUR;
+ command->word[0].type = ACTION;
+ strncpy(command->word[0].raw, "pour", LINESIZE - 1);
+ }
+ }
+ if (command->word[0].id == CAGE && command->word[1].id == BIRD && HERE(CAGE) && HERE(BIRD)) {
+ command->word[0].id = CARRY;
+ command->word[0].type = ACTION;
+ }
+ }
- /* If no word type is given for the first word, we assume it's a
- * motion. */
- if (command->word[0].type == NO_WORD_TYPE) {
- command->word[0].type = MOTION;
- }
+ /* If no word type is given for the first word, we assume it's a motion. */
+ if (command->word[0].type == NO_WORD_TYPE)
+ command->word[0].type = MOTION;
- command->state = PREPROCESSED;
- return true;
- }
- return false;
+ command->state = PREPROCESSED;
+ return true;
+ }
+ return false;
}
-static bool do_move(void) {
- /* Actually execute the move to the new location and dwarf movement */
- /* Can't leave cave once it's closing (except by main office). */
- if (OUTSIDE(game.newloc) && game.newloc != 0 && game.closng) {
- rspeak(EXIT_CLOSED);
- game.newloc = game.loc;
- if (!game.panic) {
- game.clock2 = PANICTIME;
- }
- game.panic = true;
- }
+static bool do_move(void)
+/* Actually execute the move to the new location and dwarf movement */
+{
+ /* Can't leave cave once it's closing (except by main office). */
+ if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
+ rspeak(EXIT_CLOSED);
+ game.newloc = game.loc;
+ if (!game.panic)
+ game.clock2 = PANICTIME;
+ game.panic = true;
+ }
- /* See if a dwarf has seen him and has come from where he
- * wants to go. If so, the dwarf's blocking his way. If
- * coming from place forbidden to pirate (dwarves rooted in
- * place) let him get out (and attacked). */
- if (game.newloc != game.loc && !FORCED(game.loc) &&
- !CNDBIT(game.loc, COND_NOARRR)) {
- for (size_t i = 1; i <= NDWARVES - 1; i++) {
- if (game.dwarves[i].oldloc == game.newloc &&
- game.dwarves[i].seen) {
- game.newloc = game.loc;
- rspeak(DWARF_BLOCK);
- break;
- }
- }
- }
- game.loc = game.newloc;
+ /* See if a dwarf has seen him and has come from where he
+ * wants to go. If so, the dwarf's blocking his way. If
+ * coming from place forbidden to pirate (dwarves rooted in
+ * place) let him get out (and attacked). */
+ if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) {
+ for (size_t i = 1; i <= NDWARVES - 1; i++) {
+ if (game.dwarves[i].oldloc == game.newloc && game.dwarves[i].seen) {
+ game.newloc = game.loc;
+ rspeak(DWARF_BLOCK);
+ break;
+ }
+ }
+ }
+ game.loc = game.newloc;
- if (!dwarfmove()) {
- croak();
- }
+ if (!dwarfmove())
+ croak();
- if (game.loc == LOC_NOWHERE) {
- croak();
- }
+ if (game.loc == LOC_NOWHERE)
+ croak();
- /* The easiest way to get killed is to fall into a pit in
- * pitch darkness. */
- if (!FORCED(game.loc) && IS_DARK_HERE() && game.wzdark &&
- PCT(PIT_KILL_PROB)) {
- rspeak(PIT_FALL);
- game.oldlc2 = game.loc;
- croak();
- return false;
- }
+ /* The easiest way to get killed is to fall into a pit in
+ * pitch darkness. */
+ if (!FORCED(game.loc) && DARK(game.loc) && game.wzdark && PCT(PIT_KILL_PROB)) {
+ rspeak(PIT_FALL);
+ game.oldlc2 = game.loc;
+ croak();
+ return false;
+ }
- return true;
+ return true;
}
-static bool do_command(void) {
- /* Get and execute a command */
- static command_t command;
- clear_command(&command);
+static bool do_command(void)
+/* Get and execute a command */
+{
+ static command_t command;
+ clear_command(&command);
- /* Describe the current location and (maybe) get next command. */
- while (command.state != EXECUTED) {
- describe_location();
+ /* Describe the current location and (maybe) get next command. */
+ while (command.state != EXECUTED) {
+ describe_location();
- if (FORCED(game.loc)) {
- playermove(HERE);
- return true;
- }
+ if (FORCED(game.loc)) {
+ playermove(HERE);
+ return true;
+ }
- listobjects();
+ listobjects();
- /* Command not yet given; keep getting commands from user
- * until valid command is both given and executed. */
- clear_command(&command);
- while (command.state <= GIVEN) {
+ /* Command not yet given; keep getting commands from user
+ * until valid command is both given and executed. */
+ clear_command(&command);
+ while (command.state <= GIVEN) {
- if (game.closed) {
- /* If closing time, check for any stashed
- * objects being toted and unstash them. This
- * way objects won't be described until they've
- * been picked up and put down separate from
- * their respective piles. */
- if ((OBJECT_IS_NOTFOUND(OYSTER) ||
- OBJECT_IS_STASHED(OYSTER)) &&
- TOTING(OYSTER)) {
- pspeak(OYSTER, look, true, 1);
- }
- for (size_t i = 1; i <= NOBJECTS; i++) {
- if (TOTING(i) &&
- (OBJECT_IS_NOTFOUND(i) ||
- OBJECT_IS_STASHED(i))) {
- OBJECT_STASHIFY(
- i, game.objects[i].prop);
- }
- }
- }
+ if (game.closed) {
+ /* If closing time, check for any stashed objects
+ * being toted and unstash them. This way objects
+ * won't be described until they've been picked up
+ * and put down separate from their respective
+ * piles. */
+ if ((PROP_IS_NOTFOUND(OYSTER) || PROP_IS_STASHED(OYSTER)) && TOTING(OYSTER))
+ pspeak(OYSTER, look, true, 1);
+ for (size_t i = 1; i <= NOBJECTS; i++) {
+ if (TOTING(i) && (PROP_IS_NOTFOUND(i) || PROP_IS_STASHED(i)))
+ game.objects[i].prop = PROP_STASHED(i);
+ }
+ }
- /* Check to see if the room is dark. */
- game.wzdark = IS_DARK_HERE();
+ /* Check to see if the room is dark. If the knife is here,
+ * and it's dark, the knife permanently disappears */
+ game.wzdark = DARK(game.loc);
+ if (game.knfloc != LOC_NOWHERE && game.knfloc != game.loc)
+ game.knfloc = LOC_NOWHERE;
- /* If the knife is not here it permanently disappears.
- * Possibly this should fire if the knife is here but
- * the room is dark? */
- if (game.knfloc > LOC_NOWHERE &&
- game.knfloc != game.loc) {
- game.knfloc = LOC_NOWHERE;
- }
+ /* Check some for hints, get input from user, increment
+ * turn, and pre-process commands. Keep going until
+ * pre-processing is done. */
+ while ( command.state < PREPROCESSED ) {
+ checkhints();
- /* Check some for hints, get input from user, increment
- * turn, and pre-process commands. Keep going until
- * pre-processing is done. */
- while (command.state < PREPROCESSED) {
- checkhints();
+ /* Get command input from user */
+ if (!get_command_input(&command))
+ return false;
- /* Get command input from user */
- if (!get_command_input(&command)) {
- return false;
- }
+ /* Every input, check "foobar" flag. If zero, nothing's going
+ * on. If pos, make neg. If neg, he skipped a word, so make it
+ * zero.
+ */
+ game.foobar = (game.foobar > WORD_EMPTY) ? -game.foobar : WORD_EMPTY;
- /* Every input, check "foobar" flag. If zero,
- * nothing's going on. If pos, make neg. If neg,
- * he skipped a word, so make it zero.
- */
- game.foobar = (game.foobar > WORD_EMPTY)
- ? -game.foobar
- : WORD_EMPTY;
+ ++game.turns;
+ preprocess_command(&command);
+ }
- ++game.turns;
- preprocess_command(&command);
- }
+ /* check if game is closed, and exit if it is */
+ if (closecheck() )
+ return true;
- /* check if game is closed, and exit if it is */
- if (closecheck()) {
- return true;
- }
+ /* loop until all words in command are processed */
+ while (command.state == PREPROCESSED ) {
+ command.state = PROCESSING;
- /* loop until all words in command are processed */
- while (command.state == PREPROCESSED) {
- command.state = PROCESSING;
+ if (command.word[0].id == WORD_NOT_FOUND) {
+ /* Gee, I don't understand. */
+ sspeak(DONT_KNOW, command.word[0].raw);
+ clear_command(&command);
+ continue;
+ }
- if (command.word[0].id == WORD_NOT_FOUND) {
- /* Gee, I don't understand. */
- sspeak(DONT_KNOW, command.word[0].raw);
- clear_command(&command);
- continue;
- }
+ /* Give user hints of shortcuts */
+ if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) {
+ if (++game.iwest == 10)
+ rspeak(W_IS_WEST);
+ }
+ if (strncasecmp(command.word[0].raw, "go", sizeof("go")) == 0 && command.word[1].id != WORD_EMPTY) {
+ if (++game.igo == 10)
+ rspeak(GO_UNNEEDED);
+ }
- /* Give user hints of shortcuts */
- if (strncasecmp(command.word[0].raw, "west",
- sizeof("west")) == 0) {
- if (++game.iwest == 10) {
- rspeak(W_IS_WEST);
- }
- }
- if (strncasecmp(command.word[0].raw, "go",
- sizeof("go")) == 0 &&
- command.word[1].id != WORD_EMPTY) {
- if (++game.igo == 10) {
- rspeak(GO_UNNEEDED);
- }
- }
+ switch (command.word[0].type) {
+ case MOTION:
+ playermove(command.word[0].id);
+ command.state = EXECUTED;
+ continue;
+ case OBJECT:
+ command.part = unknown;
+ command.obj = command.word[0].id;
+ break;
+ case ACTION:
+ if (command.word[1].type == NUMERIC)
+ command.part = transitive;
+ else
+ command.part = intransitive;
+ command.verb = command.word[0].id;
+ break;
+ case NUMERIC:
+ if (!settings.oldstyle) {
+ sspeak(DONT_KNOW, command.word[0].raw);
+ clear_command(&command);
+ continue;
+ }
+ break;// LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ case NO_WORD_TYPE: // LCOV_EXCL_LINE
+ BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
+ }
- switch (command.word[0].type) {
- case MOTION:
- playermove(command.word[0].id);
- command.state = EXECUTED;
- continue;
- case OBJECT:
- command.part = unknown;
- command.obj = command.word[0].id;
- break;
- case ACTION:
- if (command.word[1].type == NUMERIC) {
- command.part = transitive;
- } else {
- command.part = intransitive;
- }
- command.verb = command.word[0].id;
- break;
- case NUMERIC:
- if (!settings.oldstyle) {
- sspeak(DONT_KNOW,
- command.word[0].raw);
- clear_command(&command);
- continue;
- }
- break; // LCOV_EXCL_LINE
- default: // LCOV_EXCL_LINE
- case NO_WORD_TYPE: // LCOV_EXCL_LINE
- BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
- }
-
- switch (action(command)) {
- case GO_TERMINATE:
- command.state = EXECUTED;
- break;
- case GO_MOVE:
- playermove(NUL);
- command.state = EXECUTED;
- break;
- case GO_WORD2:
+ switch (action(command)) {
+ case GO_TERMINATE:
+ command.state = EXECUTED;
+ break;
+ case GO_MOVE:
+ playermove(NUL);
+ command.state = EXECUTED;
+ break;
+ case GO_WORD2:
#ifdef GDEBUG
- printf("Word shift\n");
+ printf("Word shift\n");
#endif /* GDEBUG */
- /* Get second word for analysis. */
- command.word[0] = command.word[1];
- command.word[1] = empty_command_word;
- command.state = PREPROCESSED;
- break;
- case GO_UNKNOWN:
- /* Random intransitive verbs come here.
- * Clear obj just in case (see
- * attack()). */
- command.word[0].raw[0] =
- toupper(command.word[0].raw[0]);
- sspeak(DO_WHAT, command.word[0].raw);
- command.obj = NO_OBJECT;
+ /* Get second word for analysis. */
+ command.word[0] = command.word[1];
+ command.word[1] = empty_command_word;
+ command.state = PREPROCESSED;
+ break;
+ case GO_UNKNOWN:
+ /* Random intransitive verbs come here. Clear obj just in case
+ * (see attack()). */
+ command.word[0].raw[0] = toupper(command.word[0].raw[0]);
+ sspeak(DO_WHAT, command.word[0].raw);
+ command.obj = NO_OBJECT;
- /* object cleared; we need to go back to
- * the preprocessing step */
- command.state = GIVEN;
- break;
- case GO_CHECKHINT: // FIXME: re-name to be more
- // contextual; this was
- // previously a label
- command.state = GIVEN;
- break;
- case GO_DWARFWAKE:
- /* Oh dear, he's disturbed the dwarves.
- */
- rspeak(DWARVES_AWAKEN);
- terminate(endgame);
- case GO_CLEAROBJ: // FIXME: re-name to be more
- // contextual; this was
- // previously a label
- clear_command(&command);
- break;
- case GO_TOP: // FIXME: re-name to be more
- // contextual; this was previously
- // a label
- break;
- default: // LCOV_EXCL_LINE
- BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
- }
- } /* while command has not been fully processed */
- } /* while command is not yet given */
- } /* while command is not executed */
+ /* object cleared; we need to go back to the preprocessing step */
+ command.state = GIVEN;
+ break;
+ case GO_CHECKHINT: // FIXME: re-name to be more contextual; this was previously a label
+ command.state = GIVEN;
+ break;
+ case GO_DWARFWAKE:
+ /* Oh dear, he's disturbed the dwarves. */
+ rspeak(DWARVES_AWAKEN);
+ terminate(endgame);
+ case GO_CLEAROBJ: // FIXME: re-name to be more contextual; this was previously a label
+ clear_command(&command);
+ break;
+ case GO_TOP: // FIXME: re-name to be more contextual; this was previously a label
+ break;
+ default: // LCOV_EXCL_LINE
+ BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
+ }
+ } /* while command has not been fully processed */
+ } /* while command is not yet given */
+ } /* while command is not executed */
- /* command completely executed; we return true. */
- return true;
+ /* command completely executed; we return true. */
+ return true;
}
/*
@@ -1448,141 +1251,126 @@ static bool do_command(void) {
* Revived 2017 as Open Adventure.
*/
-int main(int argc, char *argv[]) {
- int ch;
+int main(int argc, char *argv[])
+{
+ int ch;
- /* Options. */
+ /* Options. */
#if defined ADVENT_AUTOSAVE
- const char *opts = "dl:oa:";
- const char *usage =
- "Usage: %s [-l logfilename] [-o] [-a filename] [script...]\n";
- FILE *rfp = NULL;
- const char *autosave_filename = NULL;
+ const char* opts = "dl:oa:";
+ const char* usage = "Usage: %s [-l logfilename] [-o] [-a filename] [script...]\n";
+ FILE *rfp = NULL;
+ const char* autosave_filename = NULL;
#elif !defined ADVENT_NOSAVE
- const char *opts = "dl:or:";
- const char *usage = "Usage: %s [-l logfilename] [-o] [-r "
- "restorefilename] [script...]\n";
- FILE *rfp = NULL;
+ const char* opts = "dl:or:";
+ const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [script...]\n";
+ FILE *rfp = NULL;
#else
- const char *opts = "dl:o";
- const char *usage = "Usage: %s [-l logfilename] [-o] [script...]\n";
+ const char* opts = "dl:o";
+ const char* usage = "Usage: %s [-l logfilename] [-o] [script...]\n";
#endif
- while ((ch = getopt(argc, argv, opts)) != EOF) {
- switch (ch) {
- case 'd': // LCOV_EXCL_LINE
- settings.debug += 1; // LCOV_EXCL_LINE
- break; // LCOV_EXCL_LINE
- case 'l':
- settings.logfp = fopen(optarg, "w");
- if (settings.logfp == NULL) {
- fprintf(
- stderr,
- "advent: can't open logfile %s for write\n",
- optarg);
- }
- signal(SIGINT, sig_handler);
- break;
- case 'o':
- settings.oldstyle = true;
- settings.prompt = false;
- break;
+ while ((ch = getopt(argc, argv, opts)) != EOF) {
+ switch (ch) {
+ case 'd': // LCOV_EXCL_LINE
+ settings.debug +=1; // LCOV_EXCL_LINE
+ break; // LCOV_EXCL_LINE
+ case 'l':
+ settings.logfp = fopen(optarg, "w");
+ if (settings.logfp == NULL)
+ fprintf(stderr,
+ "advent: can't open logfile %s for write\n",
+ optarg);
+ signal(SIGINT, sig_handler);
+ break;
+ case 'o':
+ settings.oldstyle = true;
+ settings.prompt = false;
+ break;
#ifdef ADVENT_AUTOSAVE
- case 'a':
- rfp = fopen(optarg, READ_MODE);
- autosave_filename = optarg;
- signal(SIGHUP, sig_handler);
- signal(SIGTERM, sig_handler);
- break;
+ case 'a':
+ rfp = fopen(optarg, READ_MODE);
+ autosave_filename = optarg;
+ signal(SIGHUP, sig_handler);
+ signal(SIGTERM, sig_handler);
+ break;
#elif !defined ADVENT_NOSAVE
- case 'r':
- rfp = fopen(optarg, "r");
- if (rfp == NULL) {
- fprintf(stderr,
- "advent: can't open save file %s for "
- "read\n",
- optarg);
- }
- break;
+ case 'r':
+ rfp = fopen(optarg, "r");
+ if (rfp == NULL)
+ fprintf(stderr,
+ "advent: can't open save file %s for read\n",
+ optarg);
+ break;
#endif
- default:
- fprintf(stderr, usage, argv[0]);
- fprintf(stderr, " -l create a log file of your "
- "game named as specified'\n");
- fprintf(stderr,
- " -o 'oldstyle' (no prompt, no command "
- "editing, displays 'Initialising...')\n");
+ default:
+ fprintf(stderr,
+ usage, argv[0]);
+ fprintf(stderr,
+ " -l create a log file of your game named as specified'\n");
+ fprintf(stderr,
+ " -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n");
#if defined ADVENT_AUTOSAVE
- fprintf(stderr, " -a automatic save/restore "
- "from specified saved game file\n");
+ fprintf(stderr,
+ " -a automatic save/restore from specified saved game file\n");
#elif !defined ADVENT_NOSAVE
- fprintf(stderr, " -r restore from specified "
- "saved game file\n");
+ fprintf(stderr,
+ " -r restore from specified saved game file\n");
#endif
- exit(EXIT_FAILURE);
- break;
- }
- }
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
- /* copy invocation line part after switches */
- settings.argc = argc - optind;
- settings.argv = argv + optind;
- settings.optind = 0;
+ /* copy invocation line part after switches */
+ settings.argc = argc - optind;
+ settings.argv = argv + optind;
+ settings.optind = 0;
- /* Initialize game variables */
- int seedval = initialise();
+ /* Initialize game variables */
+ int seedval = initialise();
#if !defined ADVENT_NOSAVE
- if (!rfp) {
- game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU],
- arbitrary_messages[CAVE_NEARBY],
- arbitrary_messages[NO_MESSAGE]);
- if (game.novice) {
- game.limit = NOVICELIMIT;
- }
- } else {
- restore(rfp);
+ if (!rfp) {
+ game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
+ if (game.novice)
+ game.limit = NOVICELIMIT;
+ } else {
+ restore(rfp);
#if defined ADVENT_AUTOSAVE
- score(scoregame);
+ score(scoregame);
#endif
- }
+ }
#if defined ADVENT_AUTOSAVE
- if (autosave_filename != NULL) {
- if ((autosave_fp = fopen(autosave_filename, WRITE_MODE)) ==
- NULL) {
- perror(autosave_filename);
- return EXIT_FAILURE;
- }
- autosave();
- }
+ if (autosave_filename != NULL) {
+ if ((autosave_fp = fopen(autosave_filename, WRITE_MODE)) == NULL) {
+ perror(autosave_filename);
+ return EXIT_FAILURE;
+ }
+ autosave();
+ }
#endif
#else
- game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU],
- arbitrary_messages[CAVE_NEARBY],
- arbitrary_messages[NO_MESSAGE]);
- if (game.novice) {
- game.limit = NOVICELIMIT;
- }
+ game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
+ if (game.novice)
+ game.limit = NOVICELIMIT;
#endif
- if (settings.logfp) {
- fprintf(settings.logfp, "seed %d\n", seedval);
- }
+ if (settings.logfp)
+ fprintf(settings.logfp, "seed %d\n", seedval);
- /* interpret commands until EOF or interrupt */
- for (;;) {
- // if we're supposed to move, move
- if (!do_move()) {
- continue;
- }
+ /* interpret commands until EOF or interrupt */
+ for (;;) {
+ // if we're supposed to move, move
+ if (!do_move())
+ continue;
- // get command
- if (!do_command()) {
- break;
- }
- }
- /* show score and exit */
- terminate(quitgame);
+ // get command
+ if (!do_command())
+ break;
+ }
+ /* show score and exit */
+ terminate(quitgame);
}
/* end */
diff --git a/make_dungeon.py b/make_dungeon.py
index 8d7f719..0b8bc53 100755
--- a/make_dungeon.py
+++ b/make_dungeon.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
"""
This is the open-adventure dungeon generator. It consumes a YAML description of
diff --git a/make_graph.py b/make_graph.py
index 5a58895..35dd92f 100755
--- a/make_graph.py
+++ b/make_graph.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
"""\
usage: make_graph.py [-a] [-d] [-m] [-s] [-v]
diff --git a/misc.c b/misc.c
index b28949e..d2d0b8f 100644
--- a/misc.c
+++ b/misc.c
@@ -1,775 +1,751 @@
/*
* I/O and support routines.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
#include "advent.h"
#include "dungeon.h"
-static void *xcalloc(size_t size) {
- void *ptr = calloc(size, 1);
- if (ptr == NULL) {
- // LCOV_EXCL_START
- // exclude from coverage analysis because we can't simulate an
- // out of memory error in testing
- fprintf(stderr, "Out of memory!\n");
- exit(EXIT_FAILURE);
- // LCOV_EXCL_STOP
- }
- return (ptr);
+static void* xcalloc(size_t size)
+{
+ void* ptr = calloc(size, 1);
+ if (ptr == NULL) {
+ // LCOV_EXCL_START
+ // exclude from coverage analysis because we can't simulate an out of memory error in testing
+ fprintf(stderr, "Out of memory!\n");
+ exit(EXIT_FAILURE);
+ // LCOV_EXCL_STOP
+ }
+ return (ptr);
}
/* I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */
-static void vspeak(const char *msg, bool blank, va_list ap) {
- /* Engine for various speak functions */
- // Do nothing if we got a null pointer.
- if (msg == NULL) {
- return;
- }
+static void vspeak(const char* msg, bool blank, va_list ap)
+/* Engine for various speak functions */
+{
+ // Do nothing if we got a null pointer.
+ if (msg == NULL)
+ return;
- // Do nothing if we got an empty string.
- if (strlen(msg) == 0) {
- return;
- }
+ // Do nothing if we got an empty string.
+ if (strlen(msg) == 0)
+ return;
- if (blank == true) {
- printf("\n");
- }
+ if (blank == true)
+ printf("\n");
- int msglen = strlen(msg);
+ int msglen = strlen(msg);
- // Rendered string
- ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */
- char *rendered = xcalloc(size);
- char *renderp = rendered;
+ // Rendered string
+ ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */
+ char* rendered = xcalloc(size);
+ char* renderp = rendered;
- // Handle format specifiers (including the custom %S) by
- // adjusting the parameter accordingly, and replacing the
- // specifier with %s.
- bool pluralize = false;
- for (int i = 0; i < msglen; i++) {
- if (msg[i] != '%') {
- /* Ugh. Least obtrusive way to deal with artifacts "on
- * the floor" being dropped outside of both cave and
- * building. */
- if (strncmp(msg + i, "floor", 5) == 0 &&
- strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) {
- strcpy(renderp, "ground");
- renderp += 6;
- i += 4;
- size -= 5;
- } else {
- *renderp++ = msg[i];
- size--;
- }
- } else {
- i++;
- // Integer specifier.
- if (msg[i] == 'd') {
- int32_t arg = va_arg(ap, int32_t);
- int ret =
- snprintf(renderp, size, "%" PRId32, arg);
- if (ret < size) {
- renderp += ret;
- size -= ret;
- }
- pluralize = (arg != 1);
- }
+ // Handle format specifiers (including the custom %S) by
+ // adjusting the parameter accordingly, and replacing the
+ // specifier with %s.
+ bool pluralize = false;
+ for (int i = 0; i < msglen; i++) {
+ if (msg[i] != '%') {
+ /* Ugh. Least obtrusive way to deal with artifacts "on the floor"
+ * being dropped outside of both cave and building. */
+ if (strncmp(msg + i, "floor", 5) == 0 && strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) {
+ strcpy(renderp, "ground");
+ renderp += 6;
+ i += 4;
+ size -= 5;
+ } else {
+ *renderp++ = msg[i];
+ size--;
+ }
+ } else {
+ i++;
+ // Integer specifier.
+ if (msg[i] == 'd') {
+ int32_t arg = va_arg(ap, int32_t);
+ int ret = snprintf(renderp, size, "%" PRId32, arg);
+ if (ret < size) {
+ renderp += ret;
+ size -= ret;
+ }
+ pluralize = (arg != 1);
+ }
- // Unmodified string specifier.
- if (msg[i] == 's') {
- char *arg = va_arg(ap, char *);
- strncat(renderp, arg, size - 1);
- size_t len = strlen(renderp);
- renderp += len;
- size -= len;
- }
+ // Unmodified string specifier.
+ if (msg[i] == 's') {
+ char *arg = va_arg(ap, char *);
+ strncat(renderp, arg, size - 1);
+ size_t len = strlen(renderp);
+ renderp += len;
+ size -= len;
+ }
- // Singular/plural specifier.
- if (msg[i] == 'S') {
- // look at the *previous* numeric parameter
- if (pluralize) {
- *renderp++ = 's';
- size--;
- }
- }
+ // Singular/plural specifier.
+ if (msg[i] == 'S') {
+ // look at the *previous* numeric parameter
+ if (pluralize) {
+ *renderp++ = 's';
+ size--;
+ }
+ }
- // LCOV_EXCL_START - doesn't occur in test suite.
- /* Version specifier */
- if (msg[i] == 'V') {
- strcpy(renderp, VERSION);
- size_t len = strlen(VERSION);
- renderp += len;
- size -= len;
- }
- // LCOV_EXCL_STOP
- }
- }
- *renderp = 0;
+ // LCOV_EXCL_START - doesn't occur in test suite.
+ /* Version specifier */
+ if (msg[i] == 'V') {
+ strcpy(renderp, VERSION);
+ size_t len = strlen(VERSION);
+ renderp += len;
+ size -= len;
+ }
+ // LCOV_EXCL_STOP
+ }
+ }
+ *renderp = 0;
- // Print the message.
- printf("%s\n", rendered);
+ // Print the message.
+ printf("%s\n", rendered);
- free(rendered);
+ free(rendered);
}
-void speak(const char *msg, ...) {
- /* speak a specified string */
- va_list ap;
- va_start(ap, msg);
- vspeak(msg, true, ap);
- va_end(ap);
+void speak(const char* msg, ...)
+/* speak a specified string */
+{
+ va_list ap;
+ va_start(ap, msg);
+ vspeak(msg, true, ap);
+ va_end(ap);
}
-void sspeak(const int msg, ...) {
- /* Speak a message from the arbitrary-messages list */
- va_list ap;
- va_start(ap, msg);
- fputc('\n', stdout);
- vprintf(arbitrary_messages[msg], ap);
- fputc('\n', stdout);
- va_end(ap);
+void sspeak(const int msg, ...)
+/* Speak a message from the arbitrary-messages list */
+{
+ va_list ap;
+ va_start(ap, msg);
+ fputc('\n', stdout);
+ vprintf(arbitrary_messages[msg], ap);
+ fputc('\n', stdout);
+ va_end(ap);
}
-void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...) {
- /* Find the skip+1st message from msg and print it. Modes are:
- * feel = for inventory, what you can touch
- * look = the full description for the state the object is in
- * listen = the sound for the state the object is in
- * study = text on the object. */
- va_list ap;
- va_start(ap, skip);
- switch (mode) {
- case touch:
- vspeak(objects[msg].inventory, blank, ap);
- break;
- case look:
- vspeak(objects[msg].descriptions[skip], blank, ap);
- break;
- case hear:
- vspeak(objects[msg].sounds[skip], blank, ap);
- break;
- case study:
- vspeak(objects[msg].texts[skip], blank, ap);
- break;
- case change:
- vspeak(objects[msg].changes[skip], blank, ap);
- break;
- }
- va_end(ap);
+void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...)
+/* Find the skip+1st message from msg and print it. Modes are:
+ * feel = for inventory, what you can touch
+ * look = the full description for the state the object is in
+ * listen = the sound for the state the object is in
+ * study = text on the object. */
+{
+ va_list ap;
+ va_start(ap, skip);
+ switch (mode) {
+ case touch:
+ vspeak(objects[msg].inventory, blank, ap);
+ break;
+ case look:
+ vspeak(objects[msg].descriptions[skip], blank, ap);
+ break;
+ case hear:
+ vspeak(objects[msg].sounds[skip], blank, ap);
+ break;
+ case study:
+ vspeak(objects[msg].texts[skip], blank, ap);
+ break;
+ case change:
+ vspeak(objects[msg].changes[skip], blank, ap);
+ break;
+ }
+ va_end(ap);
}
-void rspeak(vocab_t i, ...) {
- /* Print the i-th "random" message (section 6 of database). */
- va_list ap;
- va_start(ap, i);
- vspeak(arbitrary_messages[i], true, ap);
- va_end(ap);
+void rspeak(vocab_t i, ...)
+/* Print the i-th "random" message (section 6 of database). */
+{
+ va_list ap;
+ va_start(ap, i);
+ vspeak(arbitrary_messages[i], true, ap);
+ va_end(ap);
}
-void echo_input(FILE *destination, const char *input_prompt,
- const char *input) {
- size_t len = strlen(input_prompt) + strlen(input) + 1;
- char *prompt_and_input = (char *)xcalloc(len);
- strcpy(prompt_and_input, input_prompt);
- strcat(prompt_and_input, input);
- fprintf(destination, "%s\n", prompt_and_input);
- free(prompt_and_input);
+void echo_input(FILE* destination, const char* input_prompt, const char* input)
+{
+ size_t len = strlen(input_prompt) + strlen(input) + 1;
+ char* prompt_and_input = (char*) xcalloc(len);
+ strcpy(prompt_and_input, input_prompt);
+ strcat(prompt_and_input, input);
+ fprintf(destination, "%s\n", prompt_and_input);
+ free(prompt_and_input);
}
-static int word_count(char *str) {
- char delims[] = " \t";
- int count = 0;
- int inblanks = true;
+static int word_count(char* str)
+{
+ char delims[] = " \t";
+ int count = 0;
+ int inblanks = true;
- for (char *s = str; *s; s++) {
- if (inblanks) {
- if (strchr(delims, *s) == 0) {
- ++count;
- inblanks = false;
- }
- } else {
- if (strchr(delims, *s) != 0) {
- inblanks = true;
- }
- }
- }
+ for (char *s = str; *s; s++)
+ if (inblanks) {
+ if (strchr(delims, *s) == 0) {
+ ++count;
+ inblanks = false;
+ }
+ } else {
+ if (strchr(delims, *s) != 0) {
+ inblanks = true;
+ }
+ }
- return (count);
+ return (count);
}
-static char *get_input(void) {
- // Set up the prompt
- char input_prompt[] = PROMPT;
- if (!settings.prompt) {
- input_prompt[0] = '\0';
- }
+static char* get_input(void)
+{
+ // Set up the prompt
+ char input_prompt[] = PROMPT;
+ if (!settings.prompt)
+ input_prompt[0] = '\0';
- // Print a blank line
- printf("\n");
+ // Print a blank line
+ printf("\n");
- char *input;
- for (;;) {
- input = myreadline(input_prompt);
+ char* input;
+ for (;;) {
+ input = myreadline(input_prompt);
- if (input == NULL) { // Got EOF; return with it.
- return (input);
- }
- if (input[0] == '#') { // Ignore comments.
- free(input);
- continue;
- }
- // We have a 'normal' line; leave the loop.
- break;
- }
+ if (input == NULL) // Got EOF; return with it.
+ return (input);
+ if (input[0] == '#') { // Ignore comments.
+ free(input);
+ continue;
+ }
+ // We have a 'normal' line; leave the loop.
+ break;
+ }
- // Strip trailing newlines from the input
- input[strcspn(input, "\n")] = 0;
+ // Strip trailing newlines from the input
+ input[strcspn(input, "\n")] = 0;
- add_history(input);
+ add_history(input);
- if (!isatty(0)) {
- echo_input(stdout, input_prompt, input);
- }
+ if (!isatty(0))
+ echo_input(stdout, input_prompt, input);
- if (settings.logfp) {
- echo_input(settings.logfp, "", input);
- }
+ if (settings.logfp)
+ echo_input(settings.logfp, "", input);
- return (input);
+ return (input);
}
-bool silent_yes_or_no(void) {
- bool outcome = false;
+bool silent_yes_or_no(void)
+{
+ bool outcome = false;
- for (;;) {
- char *reply = get_input();
- if (reply == NULL) {
- // LCOV_EXCL_START
- // Should be unreachable. Reply should never be NULL
- free(reply);
- exit(EXIT_SUCCESS);
- // LCOV_EXCL_STOP
- }
- if (strlen(reply) == 0) {
- free(reply);
- rspeak(PLEASE_ANSWER);
- continue;
- }
+ for (;;) {
+ char* reply = get_input();
+ if (reply == NULL) {
+ // LCOV_EXCL_START
+ // Should be unreachable. Reply should never be NULL
+ free(reply);
+ exit(EXIT_SUCCESS);
+ // LCOV_EXCL_STOP
+ }
+ if (strlen(reply) == 0) {
+ free(reply);
+ rspeak(PLEASE_ANSWER);
+ continue;
+ }
- char *firstword = (char *)xcalloc(strlen(reply) + 1);
- sscanf(reply, "%s", firstword);
+ char* firstword = (char*) xcalloc(strlen(reply) + 1);
+ sscanf(reply, "%s", firstword);
- free(reply);
+ free(reply);
- for (int i = 0; i < (int)strlen(firstword); ++i) {
- firstword[i] = tolower(firstword[i]);
- }
+ for (int i = 0; i < (int)strlen(firstword); ++i)
+ firstword[i] = tolower(firstword[i]);
- int yes = strncmp("yes", firstword, sizeof("yes") - 1);
- int y = strncmp("y", firstword, sizeof("y") - 1);
- int no = strncmp("no", firstword, sizeof("no") - 1);
- int n = strncmp("n", firstword, sizeof("n") - 1);
+ int yes = strncmp("yes", firstword, sizeof("yes") - 1);
+ int y = strncmp("y", firstword, sizeof("y") - 1);
+ int no = strncmp("no", firstword, sizeof("no") - 1);
+ int n = strncmp("n", firstword, sizeof("n") - 1);
- free(firstword);
+ free(firstword);
- if (yes == 0 || y == 0) {
- outcome = true;
- break;
- } else if (no == 0 || n == 0) {
- outcome = false;
- break;
- } else {
- rspeak(PLEASE_ANSWER);
- }
- }
- return (outcome);
+ if (yes == 0 || y == 0) {
+ outcome = true;
+ break;
+ } else if (no == 0 || n == 0) {
+ outcome = false;
+ break;
+ } else
+ rspeak(PLEASE_ANSWER);
+ }
+ return (outcome);
}
-bool yes_or_no(const char *question, const char *yes_response,
- const char *no_response) {
- /* Print message X, wait for yes/no answer. If yes, print Y and return
- * true; if no, print Z and return false. */
- bool outcome = false;
- for (;;) {
- speak(question);
+bool yes_or_no(const char* question, const char* yes_response, const char* no_response)
+/* Print message X, wait for yes/no answer. If yes, print Y and return true;
+ * if no, print Z and return false. */
+{
+ bool outcome = false;
- char *reply = get_input();
- if (reply == NULL) {
- // LCOV_EXCL_START
- // Should be unreachable. Reply should never be NULL
- free(reply);
- exit(EXIT_SUCCESS);
- // LCOV_EXCL_STOP
- }
+ for (;;) {
+ speak(question);
- if (strlen(reply) == 0) {
- free(reply);
- rspeak(PLEASE_ANSWER);
- continue;
- }
+ char* reply = get_input();
+ if (reply == NULL) {
+ // LCOV_EXCL_START
+ // Should be unreachable. Reply should never be NULL
+ free(reply);
+ exit(EXIT_SUCCESS);
+ // LCOV_EXCL_STOP
+ }
- char *firstword = (char *)xcalloc(strlen(reply) + 1);
- sscanf(reply, "%s", firstword);
+ if (strlen(reply) == 0) {
+ free(reply);
+ rspeak(PLEASE_ANSWER);
+ continue;
+ }
- free(reply);
+ char* firstword = (char*) xcalloc(strlen(reply) + 1);
+ sscanf(reply, "%s", firstword);
- for (int i = 0; i < (int)strlen(firstword); ++i) {
- firstword[i] = tolower(firstword[i]);
- }
+ free(reply);
- int yes = strncmp("yes", firstword, sizeof("yes") - 1);
- int y = strncmp("y", firstword, sizeof("y") - 1);
- int no = strncmp("no", firstword, sizeof("no") - 1);
- int n = strncmp("n", firstword, sizeof("n") - 1);
+ for (int i = 0; i < (int)strlen(firstword); ++i)
+ firstword[i] = tolower(firstword[i]);
- free(firstword);
+ int yes = strncmp("yes", firstword, sizeof("yes") - 1);
+ int y = strncmp("y", firstword, sizeof("y") - 1);
+ int no = strncmp("no", firstword, sizeof("no") - 1);
+ int n = strncmp("n", firstword, sizeof("n") - 1);
- if (yes == 0 || y == 0) {
- speak(yes_response);
- outcome = true;
- break;
- } else if (no == 0 || n == 0) {
- speak(no_response);
- outcome = false;
- break;
- } else {
- rspeak(PLEASE_ANSWER);
- }
- }
+ free(firstword);
- return (outcome);
+ if (yes == 0 || y == 0) {
+ speak(yes_response);
+ outcome = true;
+ break;
+ } else if (no == 0 || n == 0) {
+ speak(no_response);
+ outcome = false;
+ break;
+ } else
+ rspeak(PLEASE_ANSWER);
+
+ }
+
+ return (outcome);
}
-/* Data structure routines */
+/* Data structure routines */
-static int get_motion_vocab_id(const char *word) {
- // Return the first motion number that has 'word' as one of its words.
- for (int i = 0; i < NMOTIONS; ++i) {
- for (int j = 0; j < motions[i].words.n; ++j) {
- if (strncasecmp(word, motions[i].words.strs[j],
- TOKLEN) == 0 &&
- (strlen(word) > 1 ||
- strchr(ignore, word[0]) == NULL ||
- !settings.oldstyle)) {
- return (i);
- }
- }
- }
- // If execution reaches here, we didn't find the word.
- return (WORD_NOT_FOUND);
+static int get_motion_vocab_id(const char* word)
+// Return the first motion number that has 'word' as one of its words.
+{
+ for (int i = 0; i < NMOTIONS; ++i) {
+ for (int j = 0; j < motions[i].words.n; ++j) {
+ if (strncasecmp(word, motions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 ||
+ strchr(ignore, word[0]) == NULL ||
+ !settings.oldstyle))
+ return (i);
+ }
+ }
+ // If execution reaches here, we didn't find the word.
+ return (WORD_NOT_FOUND);
}
-static int get_object_vocab_id(const char *word) {
- // Return the first object number that has 'word' as one of its words.
- for (int i = 0; i < NOBJECTS + 1;
- ++i) { // FIXME: the + 1 should go when 1-indexing for objects is
- // removed
- for (int j = 0; j < objects[i].words.n; ++j) {
- if (strncasecmp(word, objects[i].words.strs[j],
- TOKLEN) == 0) {
- return (i);
- }
- }
- }
- // If execution reaches here, we didn't find the word.
- return (WORD_NOT_FOUND);
+static int get_object_vocab_id(const char* word)
+// Return the first object number that has 'word' as one of its words.
+{
+ for (int i = 0; i < NOBJECTS + 1; ++i) { // FIXME: the + 1 should go when 1-indexing for objects is removed
+ for (int j = 0; j < objects[i].words.n; ++j) {
+ if (strncasecmp(word, objects[i].words.strs[j], TOKLEN) == 0)
+ return (i);
+ }
+ }
+ // If execution reaches here, we didn't find the word.
+ return (WORD_NOT_FOUND);
}
-static int get_action_vocab_id(const char *word) {
- // Return the first motion number that has 'word' as one of its words.
- for (int i = 0; i < NACTIONS; ++i) {
- for (int j = 0; j < actions[i].words.n; ++j) {
- if (strncasecmp(word, actions[i].words.strs[j],
- TOKLEN) == 0 &&
- (strlen(word) > 1 ||
- strchr(ignore, word[0]) == NULL ||
- !settings.oldstyle)) {
- return (i);
- }
- }
- }
- // If execution reaches here, we didn't find the word.
- return (WORD_NOT_FOUND);
+static int get_action_vocab_id(const char* word)
+// Return the first motion number that has 'word' as one of its words.
+{
+ for (int i = 0; i < NACTIONS; ++i) {
+ for (int j = 0; j < actions[i].words.n; ++j) {
+ if (strncasecmp(word, actions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 ||
+ strchr(ignore, word[0]) == NULL ||
+ !settings.oldstyle))
+ return (i);
+ }
+ }
+ // If execution reaches here, we didn't find the word.
+ return (WORD_NOT_FOUND);
}
-static bool is_valid_int(const char *str) {
- /* Returns true if the string passed in is represents a valid integer,
- * that could then be parsed by atoi() */
- // Handle negative number
- if (*str == '-') {
- ++str;
- }
+static bool is_valid_int(const char *str)
+/* Returns true if the string passed in is represents a valid integer,
+ * that could then be parsed by atoi() */
+{
+ // Handle negative number
+ if (*str == '-')
+ ++str;
- // Handle empty string or just "-". Should never reach this
- // point, because this is only used with transitive verbs.
- if (!*str) {
- return false; // LCOV_EXCL_LINE
- }
+ // Handle empty string or just "-". Should never reach this
+ // point, because this is only used with transitive verbs.
+ if (!*str)
+ return false; // LCOV_EXCL_LINE
- // Check for non-digit chars in the rest of the string.
- while (*str) {
- if (!isdigit(*str)) {
- return false;
- } else {
- ++str;
- }
- }
+ // Check for non-digit chars in the rest of the string.
+ while (*str) {
+ if (!isdigit(*str))
+ return false;
+ else
+ ++str;
+ }
- return true;
+ return true;
}
-static void get_vocab_metadata(const char *word, vocab_t *id,
- word_type_t *type) {
- /* Check for an empty string */
- if (strncmp(word, "", sizeof("")) == 0) {
- *id = WORD_EMPTY;
- *type = NO_WORD_TYPE;
- return;
- }
+static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type)
+{
+ /* Check for an empty string */
+ if (strncmp(word, "", sizeof("")) == 0) {
+ *id = WORD_EMPTY;
+ *type = NO_WORD_TYPE;
+ return;
+ }
- vocab_t ref_num;
+ vocab_t ref_num;
- ref_num = get_motion_vocab_id(word);
- // Second conjunct is because the magic-word placeholder is a bit
- // special
- if (ref_num != WORD_NOT_FOUND) {
- *id = ref_num;
- *type = MOTION;
- return;
- }
+ ref_num = get_motion_vocab_id(word);
+ // Second conjunct is because the magic-word placeholder is a bit special
+ if (ref_num != WORD_NOT_FOUND) {
+ *id = ref_num;
+ *type = MOTION;
+ return;
+ }
- ref_num = get_object_vocab_id(word);
- if (ref_num != WORD_NOT_FOUND) {
- *id = ref_num;
- *type = OBJECT;
- return;
- }
+ ref_num = get_object_vocab_id(word);
+ if (ref_num != WORD_NOT_FOUND) {
+ *id = ref_num;
+ *type = OBJECT;
+ return;
+ }
- ref_num = get_action_vocab_id(word);
- if (ref_num != WORD_NOT_FOUND && ref_num != PART) {
- *id = ref_num;
- *type = ACTION;
- return;
- }
+ ref_num = get_action_vocab_id(word);
+ if (ref_num != WORD_NOT_FOUND && ref_num != PART) {
+ *id = ref_num;
+ *type = ACTION;
+ return;
+ }
- // Check for the reservoir magic word.
- if (strcasecmp(word, game.zzword) == 0) {
- *id = PART;
- *type = ACTION;
- return;
- }
+ // Check for the reservoir magic word.
+ if (strcasecmp(word, game.zzword) == 0) {
+ *id = PART;
+ *type = ACTION;
+ return;
+ }
- // Check words that are actually numbers.
- if (is_valid_int(word)) {
- *id = WORD_EMPTY;
- *type = NUMERIC;
- return;
- }
+ // Check words that are actually numbers.
+ if (is_valid_int(word)) {
+ *id = WORD_EMPTY;
+ *type = NUMERIC;
+ return;
+ }
- *id = WORD_NOT_FOUND;
- *type = NO_WORD_TYPE;
- return;
+ *id = WORD_NOT_FOUND;
+ *type = NO_WORD_TYPE;
+ return;
}
-static void tokenize(char *raw, command_t *cmd) {
- /*
- * Be careful about modifying this. We do not want to nuke the
- * the speech part or ID from the previous turn.
- */
- memset(&cmd->word[0].raw, '\0', sizeof(cmd->word[0].raw));
- memset(&cmd->word[1].raw, '\0', sizeof(cmd->word[1].raw));
+static void tokenize(char* raw, command_t *cmd)
+{
+ /*
+ * Be careful about modifying this. We do not want to nuke the
+ * the speech part or ID from the previous turn.
+ */
+ memset(&cmd->word[0].raw, '\0', sizeof(cmd->word[0].raw));
+ memset(&cmd->word[1].raw, '\0', sizeof(cmd->word[1].raw));
- /* Bound prefix on the %s would be needed to prevent buffer
- * overflow. We shortstop this more simply by making each
- * raw-input buffer as long as the entire input buffer. */
- sscanf(raw, "%s%s", cmd->word[0].raw, cmd->word[1].raw);
+ /* Bound prefix on the %s would be needed to prevent buffer
+ * overflow. but we shortstop this more simply by making each
+ * raw-input buffer as int as the entire input buffer. */
+ sscanf(raw, "%s%s", cmd->word[0].raw, cmd->word[1].raw);
- /* (ESR) In oldstyle mode, simulate the uppercasing and truncating
- * effect on raw tokens of packing them into sixbit characters, 5
- * to a 32-bit word. This is something the FORTRAN version did
- * because archaic FORTRAN had no string types. Don Wood's
- * mechanical translation of 2.5 to C retained the packing and
- * thus this misfeature.
- *
- * It's philosophically questionable whether this is the right
- * thing to do even in oldstyle mode. On one hand, the text
- * mangling was not authorial intent, but a result of limitations
- * in their tools. On the other, not simulating this misbehavior
- * goes against the goal of making oldstyle as accurate as
- * possible an emulation of the original UI.
- *
- * The definition of TRUNCLEN is dubious. It accurately reflects the
- * FORTRAN, but it's possible that was a bug and the proper definition
- * is (TOKLEN).
- */
-#define TRUNCLEN (TOKLEN + TOKLEN)
- if (settings.oldstyle) {
- cmd->word[0].raw[TRUNCLEN] = cmd->word[1].raw[TRUNCLEN] = '\0';
- for (size_t i = 0; i < strlen(cmd->word[0].raw); i++) {
- cmd->word[0].raw[i] = toupper(cmd->word[0].raw[i]);
- }
- for (size_t i = 0; i < strlen(cmd->word[1].raw); i++) {
- cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]);
- }
- }
+ /* (ESR) In oldstyle mode, simulate the uppercasing and truncating
+ * effect on raw tokens of packing them into sixbit characters, 5
+ * to a 32-bit word. This is something the FORTRAN version did
+ * because archaic FORTRAN had no string types. Don Wood's
+ * mechanical translation of 2.5 to C retained the packing and
+ * thus this misfeature.
+ *
+ * It's philosophically questionable whether this is the right
+ * thing to do even in oldstyle mode. On one hand, the text
+ * mangling was not authorial intent, but a result of limitations
+ * in their tools. On the other, not simulating this misbehavior
+ * goes against the goal of making oldstyle as accurate as
+ * possible an emulation of the original UI.
+ */
+ if (settings.oldstyle) {
+ cmd->word[0].raw[TOKLEN + TOKLEN] = cmd->word[1].raw[TOKLEN + TOKLEN] = '\0';
+ for (size_t i = 0; i < strlen(cmd->word[0].raw); i++)
+ cmd->word[0].raw[i] = toupper(cmd->word[0].raw[i]);
+ for (size_t i = 0; i < strlen(cmd->word[1].raw); i++)
+ cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]);
+ }
- /* populate command with parsed vocabulary metadata */
- get_vocab_metadata(cmd->word[0].raw, &(cmd->word[0].id),
- &(cmd->word[0].type));
- get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id),
- &(cmd->word[1].type));
- cmd->state = TOKENIZED;
+ /* populate command with parsed vocabulary metadata */
+ get_vocab_metadata(cmd->word[0].raw, &(cmd->word[0].id), &(cmd->word[0].type));
+ get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id), &(cmd->word[1].type));
+ cmd->state = TOKENIZED;
}
-bool get_command_input(command_t *command) {
- /* Get user input on stdin, parse and map to command */
- char inputbuf[LINESIZE];
- char *input;
+bool get_command_input(command_t *command)
+/* Get user input on stdin, parse and map to command */
+{
+ char inputbuf[LINESIZE];
+ char* input;
- for (;;) {
- input = get_input();
- if (input == NULL) {
- return false;
- }
- if (word_count(input) > 2) {
- rspeak(TWO_WORDS);
- free(input);
- continue;
- }
- if (strcmp(input, "") != 0) {
- break;
- }
- free(input);
- }
+ for (;;) {
+ input = get_input();
+ if (input == NULL)
+ return false;
+ if (word_count(input) > 2) {
+ rspeak(TWO_WORDS);
+ free(input);
+ continue;
+ }
+ if (strcmp(input, "") != 0)
+ break;
+ free(input);
+ }
- strncpy(inputbuf, input, LINESIZE - 1);
- free(input);
+ strncpy(inputbuf, input, LINESIZE - 1);
+ free(input);
- tokenize(inputbuf, command);
+ tokenize(inputbuf, command);
#ifdef GDEBUG
- /* Needs to stay synced with enum word_type_t */
- const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION",
- "NUMERIC"};
- /* needs to stay synced with enum speechpart */
- const char *roles[] = {"unknown", "intransitive", "transitive"};
- printf(
- "Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n",
- roles[command->part], types[command->word[0].type],
- command->word[0].id, types[command->word[1].type],
- command->word[1].id);
+ /* Needs to stay synced with enum word_type_t */
+ const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", "NUMERIC"};
+ /* needs to stay synced with enum speechpart */
+ const char *roles[] = {"unknown", "intransitive", "transitive"};
+ printf("Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n",
+ roles[command->part],
+ types[command->word[0].type],
+ command->word[0].id,
+ types[command->word[1].type],
+ command->word[1].id);
#endif
- command->state = GIVEN;
- return true;
+ command->state = GIVEN;
+ return true;
}
-void clear_command(command_t *cmd) {
- /* Resets the state of the command to empty */
- cmd->verb = ACT_NULL;
- cmd->part = unknown;
- game.oldobj = cmd->obj;
- cmd->obj = NO_OBJECT;
- cmd->state = EMPTY;
+void clear_command(command_t *cmd)
+/* Resets the state of the command to empty */
+{
+ cmd->verb = ACT_NULL;
+ cmd->part = unknown;
+ game.oldobj = cmd->obj;
+ cmd->obj = NO_OBJECT;
+ cmd->state = EMPTY;
}
-void juggle(obj_t object) {
- /* Juggle an object by picking it up and putting it down again, the
- * purpose being to get the object to the front of the chain of things
- * at its loc. */
- loc_t i, j;
+void juggle(obj_t object)
+/* Juggle an object by picking it up and putting it down again, the purpose
+ * being to get the object to the front of the chain of things at its loc. */
+{
+ loc_t i, j;
- i = game.objects[object].place;
- j = game.objects[object].fixed;
- move(object, i);
- move(object + NOBJECTS, j);
+ i = game.objects[object].place;
+ j = game.objects[object].fixed;
+ move(object, i);
+ move(object + NOBJECTS, j);
}
-void move(obj_t object, loc_t where) {
- /* Place any object anywhere by picking it up and dropping it. May
- * already be toting, in which case the carry is a no-op. Mustn't
- * pick up objects which are not at any loc, since carry wants to
- * remove objects from game atloc chains. */
- loc_t from;
+void move(obj_t object, loc_t where)
+/* Place any object anywhere by picking it up and dropping it. May
+ * already be toting, in which case the carry is a no-op. Mustn't
+ * pick up objects which are not at any loc, since carry wants to
+ * remove objects from game atloc chains. */
+{
+ loc_t from;
- if (object > NOBJECTS) {
- from = game.objects[object - NOBJECTS].fixed;
- } else {
- from = game.objects[object].place;
- }
- /* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong...
+ if (object > NOBJECTS)
+ from = game.objects[object - NOBJECTS].fixed;
+ else
+ from = game.objects[object].place;
+ /* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong... */
+ if (from != LOC_NOWHERE && from != CARRIED)
+ carry(object, from);
+ drop(object, where);
+}
+
+void put(obj_t object, loc_t where, int pval)
+/* put() is the same as move(), except it returns a value used to set up the
+ * negated game.prop values for the repository objects. */
+{
+ move(object, where);
+ /* (ESR) Read this in combination with the macro defintions in advebt.h.
+ */
+ game.objects[object].prop = PROP_STASHIFY(pval);
+#ifdef PROP_SET_SEEN
+ PROP_SET_SEEN(object);
+#endif
+}
+
+void carry(obj_t object, loc_t where)
+/* Start toting an object, removing it from the list of things at its former
+ * location. Incr holdng unless it was already being toted. If object>NOBJECTS
+ * (moving "fixed" second loc), don't change game.place or game.holdng. */
+{
+ int temp;
+
+ if (object <= NOBJECTS) {
+ if (game.objects[object].place == CARRIED)
+ return;
+ game.objects[object].place = CARRIED;
+
+ /*
+ * Without this conditional your inventory is overcounted
+ * when you pick up the bird while it's caged. This fixes
+ * a cosmetic bug in the original.
+ *
+ * Possibly this check should be skipped whwn oldstyle is on.
*/
- if (from != LOC_NOWHERE && from != CARRIED) {
- carry(object, from);
- }
- drop(object, where);
+ if (object != BIRD)
+ ++game.holdng;
+ }
+ if (game.locs[where].atloc == object) {
+ game.locs[where].atloc = game.link[object];
+ return;
+ }
+ temp = game.locs[where].atloc;
+ while (game.link[temp] != object) {
+ temp = game.link[temp];
+ }
+ game.link[temp] = game.link[object];
}
-void put(obj_t object, loc_t where, int pval) {
- /* put() is the same as move(), except the object is stashed and
- * can no longer be picked up. */
- move(object, where);
- OBJECT_STASHIFY(object, pval);
-#ifdef OBJECT_SET_SEEN
- OBJECT_SET_SEEN(object);
-#endif
+void drop(obj_t object, loc_t where)
+/* Place an object at a given loc, prefixing it onto the game atloc list. Decr
+ * game.holdng if the object was being toted. No state change on the object. */
+{
+ if (object > NOBJECTS)
+ game.objects[object - NOBJECTS].fixed = where;
+ else {
+ if (game.objects[object].place == CARRIED)
+ if (object != BIRD)
+ /* The bird has to be weightless. This ugly hack (and the
+ * corresponding code in the carry function) brought to you
+ * by the fact that when the bird is caged, we need to be able
+ * to either 'take bird' or 'take cage' and have the right thing
+ * happen.
+ */
+ --game.holdng;
+ game.objects[object].place = where;
+ }
+ if (where == LOC_NOWHERE || where == CARRIED)
+ return;
+ game.link[object] = game.locs[where].atloc;
+ game.locs[where].atloc = object;
}
-void carry(obj_t object, loc_t where) {
- /* Start toting an object, removing it from the list of things at its
- * former location. Incr holdng unless it was already being toted. If
- * object>NOBJECTS (moving "fixed" second loc), don't change game.place
- * or game.holdng. */
- int temp;
+int atdwrf(loc_t where)
+/* Return the index of first dwarf at the given location, zero if no dwarf is
+ * there (or if dwarves not active yet), -1 if all dwarves are dead. Ignore
+ * the pirate (6th dwarf). */
+{
+ int at;
- if (object <= NOBJECTS) {
- if (game.objects[object].place == CARRIED) {
- return;
- }
- game.objects[object].place = CARRIED;
-
- /*
- * Without this conditional your inventory is overcounted
- * when you pick up the bird while it's caged. This fixes
- * a cosmetic bug in the original.
- *
- * Possibly this check should be skipped whwn oldstyle is on.
- */
- if (object != BIRD) {
- ++game.holdng;
- }
- }
- if (game.locs[where].atloc == object) {
- game.locs[where].atloc = game.link[object];
- return;
- }
- temp = game.locs[where].atloc;
- while (game.link[temp] != object) {
- temp = game.link[temp];
- }
- game.link[temp] = game.link[object];
-}
-
-void drop(obj_t object, loc_t where) {
- /* Place an object at a given loc, prefixing it onto the game atloc
- * list. Decr game.holdng if the object was being toted. No state
- * change on the object. */
- if (object > NOBJECTS) {
- game.objects[object - NOBJECTS].fixed = where;
- } else {
- if (game.objects[object].place == CARRIED) {
- if (object != BIRD) {
- /* The bird has to be weightless. This ugly
- * hack (and the corresponding code in the carry
- * function) brought to you by the fact that
- * when the bird is caged, we need to be able to
- * either 'take bird' or 'take cage' and have
- * the right thing happen.
- */
- --game.holdng;
- }
- }
- game.objects[object].place = where;
- }
- if (where == LOC_NOWHERE || where == CARRIED) {
- return;
- }
- game.link[object] = game.locs[where].atloc;
- game.locs[where].atloc = object;
-}
-
-int atdwrf(loc_t where) {
- /* Return the index of first dwarf at the given location, zero if no
- * dwarf is there (or if dwarves not active yet), -1 if all dwarves are
- * dead. Ignore the pirate (6th dwarf). */
- int at;
-
- at = 0;
- if (game.dflag < 2) {
- return at;
- }
- at = -1;
- for (int i = 1; i <= NDWARVES - 1; i++) {
- if (game.dwarves[i].loc == where) {
- return i;
- }
- if (game.dwarves[i].loc != 0) {
- at = 0;
- }
- }
- return at;
+ at = 0;
+ if (game.dflag < 2)
+ return at;
+ at = -1;
+ for (int i = 1; i <= NDWARVES - 1; i++) {
+ if (game.dwarves[i].loc == where)
+ return i;
+ if (game.dwarves[i].loc != 0)
+ at = 0;
+ }
+ return at;
}
/* Utility routines (setbit, tstbit, set_seed, get_next_lcg_value,
* randrange) */
-int setbit(int bit) {
- /* Returns 2**bit for use in constructing bit-masks. */
- return (1L << bit);
+int setbit(int bit)
+/* Returns 2**bit for use in constructing bit-masks. */
+{
+ return (1L << bit);
}
-bool tstbit(int mask, int bit) {
- /* Returns true if the specified bit is set in the mask. */
- return (mask & (1 << bit)) != 0;
+bool tstbit(int mask, int bit)
+/* Returns true if the specified bit is set in the mask. */
+{
+ return (mask & (1 << bit)) != 0;
}
-void set_seed(int32_t seedval) {
- /* Set the LCG1 seed */
- game.lcg_x = seedval % LCG_M;
- if (game.lcg_x < 0) {
- game.lcg_x = LCG_M + game.lcg_x;
- }
- // once seed is set, we need to generate the Z`ZZZ word
- for (int i = 0; i < 5; ++i) {
- game.zzword[i] = 'A' + randrange(26);
- }
- game.zzword[1] = '\''; // force second char to apostrophe
- game.zzword[5] = '\0';
+void set_seed(int32_t seedval)
+/* Set the LCG1 seed */
+{
+ game.lcg_x = seedval % LCG_M;
+ if (game.lcg_x < 0) {
+ game.lcg_x = LCG_M + game.lcg_x;
+ }
+ // once seed is set, we need to generate the Z`ZZZ word
+ for (int i = 0; i < 5; ++i) {
+ game.zzword[i] = 'A' + randrange(26);
+ }
+ game.zzword[1] = '\''; // force second char to apostrophe
+ game.zzword[5] = '\0';
}
-static int32_t get_next_lcg_value(void) {
- /* Return the LCG's current value, and then iterate it. */
- int32_t old_x = game.lcg_x;
- game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M;
- if (settings.debug) {
- printf("# random %d\n", old_x); // LCOV_EXCL_LINE
- }
- return old_x;
+static int32_t get_next_lcg_value(void)
+/* Return the LCG's current value, and then iterate it. */
+{
+ int32_t old_x = game.lcg_x;
+ game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M;
+ if (settings.debug) {
+ printf("# random %d\n", old_x); // LCOV_EXCL_LINE
+ }
+ return old_x;
}
-int32_t randrange(int32_t range) {
- /* Return a random integer from [0, range). */
- return range * get_next_lcg_value() / LCG_M;
+int32_t randrange(int32_t range)
+/* Return a random integer from [0, range). */
+{
+ return range * get_next_lcg_value() / LCG_M;
}
// LCOV_EXCL_START
-void bug(enum bugtype num, const char *error_string) {
- fprintf(stderr, "Fatal error %d, %s.\n", num, error_string);
- exit(EXIT_FAILURE);
+void bug(enum bugtype num, const char *error_string)
+{
+ fprintf(stderr, "Fatal error %d, %s.\n", num, error_string);
+ exit(EXIT_FAILURE);
}
// LCOV_EXCL_STOP
-void state_change(obj_t obj, int state) {
- /* Object must have a change-message list for this to be useful; only
- * some do */
- game.objects[obj].prop = state;
- pspeak(obj, change, true, state);
+void state_change(obj_t obj, int state)
+/* Object must have a change-message list for this to be useful; only some do */
+{
+ game.objects[obj].prop = state;
+ pspeak(obj, change, true, state);
}
/* end */
diff --git a/notes.adoc b/notes.adoc
index a247e49..c369177 100644
--- a/notes.adoc
+++ b/notes.adoc
@@ -1,6 +1,6 @@
= Open Adventure Maintainer's Notes =
by Eric S. Raymond
-// SPDX-FileCopyrightText: (C) Eric S. Raymond
+// SPDX-FileCopyrightText: Copyright Eric S. Raymond
// SPDX-License-Identifier: CC-BY-4.0
In which we explain what has been done to this code since Don Woods
@@ -16,8 +16,8 @@ of Peje Nilsson in restructuring some particularly grotty gotos is
gratefully acknowledged. Petr Voropaev contributed fuzz testing and
code cleanups. Aaron Traas did a lot of painstaking work to improve
test coverage, and factored out the last handful of gotos. Ryan
-Sarson nudged us into fixing a longstanding minor bug in the
-handling of incorrect magic-word sequences,
+Sarson nudged us into fixing a longstannding minor bug in the
+handling of incorrect magic-word sequebcesm,
== Nomenclature ==
@@ -75,15 +75,10 @@ Bug fixes:
* A few minor typos have been corrected: absence of capitalization on
"Swiss" and "Persian", inconsistent spelling of "imbedded" vs. "embedded",
- "eying" for "eyeing", "thresholds" for "threshholds", "pencilled"
- for "penciled".
+ "eying" for "eyeing", "thresholds" for "threshholds".
* Under odd circumstances (dropping rug or vase outdoors) the game could
- formerly say "floor" when it should say "ground" (or "dirt", or
- something).
-
-* The "knives vanish" message could formerly be emitted when "I see no
- knife here." would be appropriate.
+ formerly say "floor" when it should say "ground" (or "dirt", or something).
Enhancements:
@@ -144,7 +139,7 @@ whitespace before processing.
A -r command-line option has been added. When it is given (with a file
path argument) it is functionally equivalent to a RESTORE command.
-An -a command-line option has been added (conditionally on
+An -a command-line option has been added (comditionally on
ADVENT_AUTOSAVE) for use in BBS door systems. When this option is
given, the game roads from the specified filename argument on startup
and saves to it on quit or a received signal. There is a new nmessage
diff --git a/saveresume.c b/saveresume.c
index 1c778ef..a2c10a0 100644
--- a/saveresume.c
+++ b/saveresume.c
@@ -4,264 +4,249 @@
* (ESR) This replaces a bunch of particularly nasty FORTRAN-derived code;
* see the history.adoc file in the source distribution for discussion.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include
-#include
#include
#include
+#include
#include
+#include
#include "advent.h"
+#include "dungeon.h"
/*
* Use this to detect endianness mismatch. Can't be unchanged by byte-swapping.
*/
-#define ENDIAN_MAGIC 2317
+#define ENDIAN_MAGIC 2317
struct save_t save;
-#define IGNORE(r) \
- do { \
- if (r) { \
- } \
- } while (0)
+#define IGNORE(r) do{if (r){}}while(0)
-int savefile(FILE *fp) {
- /* Save game to file. No input or output from user. */
- memcpy(&save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC));
- if (save.version == 0) {
- save.version = SAVE_VERSION;
- }
- if (save.canary == 0) {
- save.canary = ENDIAN_MAGIC;
- }
- save.game = game;
- IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
- return (0);
+int savefile(FILE *fp)
+/* Save game to file. No input or output from user. */
+{
+ memcpy(&save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC));
+ if (save.version == 0)
+ save.version = SAVE_VERSION;
+ if (save.canary == 0)
+ save.canary = ENDIAN_MAGIC;
+
+ save.game = game;
+ IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
+ return (0);
}
/* Suspend and resume */
-static char *strip(char *name) {
- // Trim leading whitespace
- while (isspace((unsigned char)*name)) {
- name++; // LCOV_EXCL_LINE
- }
- if (*name != '\0') {
- // Trim trailing whitespace;
- // might be left there by autocomplete
- char *end = name + strlen(name) - 1;
- while (end > name && isspace((unsigned char)*end)) {
- end--;
- }
- // Write new null terminator character
- end[1] = '\0';
- }
+static char *strip(char *name)
+{
+ // Trim leading whitespace
+ while(isspace((unsigned char)*name))
+ name++; // LCOV_EXCL_LINE
+ if(*name != '\0') {
+ // Trim trailing whitespace;
+ // might be left there by autocomplete
+ char *end = name + strlen(name) - 1;
+ while(end > name && isspace((unsigned char)*end))
+ end--;
+ // Write new null terminator character
+ end[1] = '\0';
+ }
- return name;
+ return name;
}
-int suspend(void) {
- /* Suspend. Offer to save things in a file, but charging
- * some points (so can't win by using saved games to retry
- * battles or to start over after learning zzword).
- * If ADVENT_NOSAVE is defined, gripe instead. */
+int suspend(void)
+{
+ /* Suspend. Offer to save things in a file, but charging
+ * some points (so can't win by using saved games to retry
+ * battles or to start over after learning zzword).
+ * If ADVENT_NOSAVE is defined, gripe instead. */
#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE
- rspeak(SAVERESUME_DISABLED);
- return GO_TOP;
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
#endif
- FILE *fp = NULL;
+ FILE *fp = NULL;
- rspeak(SUSPEND_WARNING);
- if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE],
- arbitrary_messages[OK_MAN],
- arbitrary_messages[OK_MAN])) {
- return GO_CLEAROBJ;
- }
- game.saved = game.saved + 5;
+ rspeak(SUSPEND_WARNING);
+ if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
+ return GO_CLEAROBJ;
+ game.saved = game.saved + 5;
- while (fp == NULL) {
- char *name = myreadline("\nFile name: ");
- if (name == NULL) {
- return GO_TOP;
- }
- name = strip(name);
- if (strlen(name) == 0) {
- return GO_TOP; // LCOV_EXCL_LINE
- }
- fp = fopen(strip(name), WRITE_MODE);
- if (fp == NULL) {
- printf("Can't open file %s, try again.\n", name);
- }
- free(name);
- }
+ while (fp == NULL) {
+ char* name = myreadline("\nFile name: ");
+ if (name == NULL)
+ return GO_TOP;
+ name = strip(name);
+ if (strlen(name) == 0)
+ return GO_TOP; // LCOV_EXCL_LINE
+ fp = fopen(strip(name), WRITE_MODE);
+ if (fp == NULL)
+ printf("Can't open file %s, try again.\n", name);
+ free(name);
+ }
- savefile(fp);
- fclose(fp);
- rspeak(RESUME_HELP);
- exit(EXIT_SUCCESS);
+ savefile(fp);
+ fclose(fp);
+ rspeak(RESUME_HELP);
+ exit(EXIT_SUCCESS);
}
-int resume(void) {
- /* Resume. Read a suspended game back from a file.
- * If ADVENT_NOSAVE is defined, gripe instead. */
+int resume(void)
+{
+ /* Resume. Read a suspended game back from a file.
+ * If ADVENT_NOSAVE is defined, gripe instead. */
#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE
- rspeak(SAVERESUME_DISABLED);
- return GO_TOP;
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
#endif
- FILE *fp = NULL;
+ FILE *fp = NULL;
- if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) {
- rspeak(RESUME_ABANDON);
- if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE],
- arbitrary_messages[OK_MAN],
- arbitrary_messages[OK_MAN])) {
- return GO_CLEAROBJ;
- }
- }
+ if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) {
+ rspeak(RESUME_ABANDON);
+ if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
+ return GO_CLEAROBJ;
+ }
- while (fp == NULL) {
- char *name = myreadline("\nFile name: ");
- if (name == NULL) {
- return GO_TOP;
- }
- name = strip(name);
- if (strlen(name) == 0) {
- return GO_TOP; // LCOV_EXCL_LINE
- }
- fp = fopen(name, READ_MODE);
- if (fp == NULL) {
- printf("Can't open file %s, try again.\n", name);
- }
- free(name);
- }
+ while (fp == NULL) {
+ char* name = myreadline("\nFile name: ");
+ if (name == NULL)
+ return GO_TOP;
+ name = strip(name);
+ if (strlen(name) == 0)
+ return GO_TOP; // LCOV_EXCL_LINE
+ fp = fopen(name, READ_MODE);
+ if (fp == NULL)
+ printf("Can't open file %s, try again.\n", name);
+ free(name);
+ }
- return restore(fp);
+ return restore(fp);
}
-int restore(FILE *fp) {
- /* Read and restore game state from file, assuming
- * sane initial state.
- * If ADVENT_NOSAVE is defined, gripe instead. */
+int restore(FILE* fp)
+{
+ /* Read and restore game state from file, assuming
+ * sane initial state.
+ * If ADVENT_NOSAVE is defined, gripe instead. */
#ifdef ADVENT_NOSAVE
- rspeak(SAVERESUME_DISABLED);
- return GO_TOP;
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
#endif
- IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
- fclose(fp);
- if (memcmp(save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC)) != 0 ||
- save.canary != ENDIAN_MAGIC) {
- rspeak(BAD_SAVE);
- } else if (save.version != SAVE_VERSION) {
- rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10),
- SAVE_VERSION / 10, MOD(SAVE_VERSION, 10));
- } else if (!is_valid(save.game)) {
- rspeak(SAVE_TAMPERING);
- exit(EXIT_SUCCESS);
- } else {
- game = save.game;
- }
- return GO_TOP;
+ IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
+ fclose(fp);
+ if (memcmp(save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC)) != 0 || save.canary != ENDIAN_MAGIC)
+ rspeak(BAD_SAVE);
+ else if (save.version != SAVE_VERSION) {
+ rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10), SAVE_VERSION / 10, MOD(SAVE_VERSION, 10));
+ } else if (!is_valid(save.game)) {
+ rspeak(SAVE_TAMPERING);
+ exit(EXIT_SUCCESS);
+ } else {
+ game = save.game;
+ }
+ return GO_TOP;
}
-bool is_valid(struct game_t valgame) {
- /* Save files can be roughly grouped into three groups:
- * With valid, reachable state, with valid, but unreachable
- * state and with invalid state. We check that state is
- * valid: no states are outside minimal or maximal value
- */
+bool is_valid(struct game_t valgame)
+{
+ /* Save files can be roughly grouped into three groups:
+ * With valid, reachable state, with valid, but unreachable
+ * state and with invalid state. We check that state is
+ * valid: no states are outside minimal or maximal value
+ */
- /* Prevent division by zero */
- if (valgame.abbnum == 0) {
- return false; // LCOV_EXCL_LINE
- }
+ /* Prevent division by zero */
+ if (valgame.abbnum == 0) {
+ return false; // LCOV_EXCL_LINE
+ }
- /* Check for RNG overflow. Truncate */
- if (valgame.lcg_x >= LCG_M) {
- return false;
- }
+ /* Check for RNG overflow. Truncate */
+ if (valgame.lcg_x >= LCG_M) {
+ valgame.lcg_x %= LCG_M; // LCOV_EXCL_LINE
+ }
- /* Bounds check for locations */
- if (valgame.chloc < -1 || valgame.chloc > NLOCATIONS ||
- valgame.chloc2 < -1 || valgame.chloc2 > NLOCATIONS ||
- valgame.loc < 0 || valgame.loc > NLOCATIONS || valgame.newloc < 0 ||
- valgame.newloc > NLOCATIONS || valgame.oldloc < 0 ||
- valgame.oldloc > NLOCATIONS || valgame.oldlc2 < 0 ||
- valgame.oldlc2 > NLOCATIONS) {
- return false; // LCOV_EXCL_LINE
- }
- /* Bounds check for location arrays */
- for (int i = 0; i <= NDWARVES; i++) {
- if (valgame.dwarves[i].loc < -1 ||
- valgame.dwarves[i].loc > NLOCATIONS ||
- valgame.dwarves[i].oldloc < -1 ||
- valgame.dwarves[i].oldloc > NLOCATIONS) {
- return false; // LCOV_EXCL_LINE
- }
- }
+ /* Check for RNG underflow. Transpose */
+ if (valgame.lcg_x < LCG_M) {
+ valgame.lcg_x = LCG_M + (valgame.lcg_x % LCG_M);
+ }
- for (int i = 0; i <= NOBJECTS; i++) {
- if (valgame.objects[i].place < -1 ||
- valgame.objects[i].place > NLOCATIONS ||
- valgame.objects[i].fixed < -1 ||
- valgame.objects[i].fixed > NLOCATIONS) {
- return false; // LCOV_EXCL_LINE
- }
- }
+ /* Bounds check for locations */
+ if ( valgame.chloc < -1 || valgame.chloc > NLOCATIONS ||
+ valgame.chloc2 < -1 || valgame.chloc2 > NLOCATIONS ||
+ valgame.loc < 0 || valgame.loc > NLOCATIONS ||
+ valgame.newloc < 0 || valgame.newloc > NLOCATIONS ||
+ valgame.oldloc < 0 || valgame.oldloc > NLOCATIONS ||
+ valgame.oldlc2 < 0 || valgame.oldlc2 > NLOCATIONS) {
+ return false; // LCOV_EXCL_LINE
+ }
+ /* Bounds check for location arrays */
+ for (int i = 0; i <= NDWARVES; i++) {
+ if (valgame.dwarves[i].loc < -1 || valgame.dwarves[i].loc > NLOCATIONS ||
+ valgame.dwarves[i].oldloc < -1 || valgame.dwarves[i].oldloc > NLOCATIONS) {
+ return false; // LCOV_EXCL_LINE
+ }
+ }
- /* Bounds check for dwarves */
- if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES ||
- valgame.dkill < 0 || valgame.dkill > NDWARVES) {
- return false; // LCOV_EXCL_LINE
- }
+ for (int i = 0; i <= NOBJECTS; i++) {
+ if (valgame.objects[i].place < -1 || valgame.objects[i].place > NLOCATIONS ||
+ valgame.objects[i].fixed < -1 || valgame.objects[i].fixed > NLOCATIONS) {
+ return false; // LCOV_EXCL_LINE
+ }
+ }
- /* Validate that we didn't die too many times in save */
- if (valgame.numdie >= NDEATHS) {
- return false; // LCOV_EXCL_LINE
- }
+ /* Bounds check for dwarves */
+ if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES ||
+ valgame.dkill < 0 || valgame.dkill > NDWARVES) {
+ return false; // LCOV_EXCL_LINE
+ }
- /* Recalculate tally, throw the towel if in disagreement */
- int temp_tally = 0;
- for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
- if (objects[treasure].is_treasure) {
- if (OBJECT_IS_NOTFOUND2(valgame, treasure)) {
- ++temp_tally;
- }
- }
- }
- if (temp_tally != valgame.tally) {
- return false; // LCOV_EXCL_LINE
- }
+ /* Validate that we didn't die too many times in save */
+ if (valgame.numdie >= NDEATHS) {
+ return false; // LCOV_EXCL_LINE
+ }
- /* Check that properties of objects aren't beyond expected */
- for (obj_t obj = 0; obj <= NOBJECTS; obj++) {
- if (PROP_IS_INVALID(valgame.objects[obj].prop)) {
- return false; // LCOV_EXCL_LINE
- }
- }
+ /* Recalculate tally, throw the towel if in disagreement */
+ int temp_tally = 0;
+ for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
+ if (objects[treasure].is_treasure) {
+ if (PROP_IS_NOTFOUND2(valgame, treasure)) {
+ ++temp_tally;
+ }
+ }
+ }
+ if (temp_tally != valgame.tally) {
+ return false; // LCOV_EXCL_LINE
+ }
- /* Check that values in linked lists for objects in locations are inside
- * bounds */
- for (loc_t loc = LOC_NOWHERE; loc <= NLOCATIONS; loc++) {
- if (valgame.locs[loc].atloc < NO_OBJECT ||
- valgame.locs[loc].atloc > NOBJECTS * 2) {
- return false; // LCOV_EXCL_LINE
- }
- }
- for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++) {
- if (valgame.link[obj] < NO_OBJECT ||
- valgame.link[obj] > NOBJECTS * 2) {
- return false; // LCOV_EXCL_LINE
- }
- }
+ /* Check that properties of objects aren't beyond expected */
+ for (obj_t obj = 0; obj <= NOBJECTS; obj++) {
+ if (PROP_IS_INVALID(valgame.objects[obj].prop)) {
+ return false; // LCOV_EXCL_LINE
+ }
+ }
- return true;
+ /* Check that values in linked lists for objects in locations are inside bounds */
+ for (loc_t loc = LOC_NOWHERE; loc <= NLOCATIONS; loc++) {
+ if (valgame.locs[loc].atloc < NO_OBJECT || valgame.locs[loc].atloc > NOBJECTS * 2) {
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+ for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++ ) {
+ if (valgame.link[obj] < NO_OBJECT || valgame.link[obj] > NOBJECTS * 2) {
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+
+ return true;
}
/* end */
diff --git a/score.c b/score.c
index ad1c00a..c0a0277 100644
--- a/score.c
+++ b/score.c
@@ -1,162 +1,145 @@
/*
* Scoring and wrap-up.
*
- * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods
+ * SPDX-FileCopyrightText: 2017 by Eric S. Raymond
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
#include "advent.h"
#include "dungeon.h"
-#include
-static int mxscor; /* ugh..the price for having score() not exit. */
+static int mxscor; /* ugh..the price for having score() not exit. */
-int score(enum termination mode) {
- /* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if
- * died or won */
- int score = 0;
+int score(enum termination mode)
+/* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if died
+ * or won */
+{
+ int score = 0;
- /* The present scoring algorithm is as follows:
- * Objective: Points: Present total possible:
- * Getting well into cave 25 25
- * Each treasure < chest 12 60
- * Treasure chest itself 14 14
- * Each treasure > chest 16 224
- * Surviving (MAX-NUM)*10 30
- * Not quitting 4 4
- * Reaching "game.closng" 25 25
- * "Closed": Quit/Killed 10
- * Klutzed 25
- * Wrong way 30
- * Success 45 45
- * Came to Witt's End 1 1
- * Round out the total 2 2
- * TOTAL: 430
- * Points can also be deducted for using hints or too many turns, or
- * for saving intermediate positions. */
+ /* The present scoring algorithm is as follows:
+ * Objective: Points: Present total possible:
+ * Getting well into cave 25 25
+ * Each treasure < chest 12 60
+ * Treasure chest itself 14 14
+ * Each treasure > chest 16 224
+ * Surviving (MAX-NUM)*10 30
+ * Not quitting 4 4
+ * Reaching "game.closng" 25 25
+ * "Closed": Quit/Killed 10
+ * Klutzed 25
+ * Wrong way 30
+ * Success 45 45
+ * Came to Witt's End 1 1
+ * Round out the total 2 2
+ * TOTAL: 430
+ * Points can also be deducted for using hints or too many turns, or for
+ * saving intermediate positions. */
- /* First tally up the treasures. Must be in building and not broken.
- * Give the poor guy 2 points just for finding each treasure. */
- mxscor = 0;
- for (int i = 1; i <= NOBJECTS; i++) {
- if (!objects[i].is_treasure) {
- continue;
- }
- if (objects[i].inventory != 0) {
- int k = 12;
- if (i == CHEST) {
- k = 14;
- }
- if (i > CHEST) {
- k = 16;
- }
- if (!OBJECT_IS_STASHED(i) && !OBJECT_IS_NOTFOUND(i)) {
- score += 2;
- }
- if (game.objects[i].place == LOC_BUILDING &&
- OBJECT_IS_FOUND(i)) {
- score += k - 2;
- }
- mxscor += k;
- }
- }
+ /* First tally up the treasures. Must be in building and not broken.
+ * Give the poor guy 2 points just for finding each treasure. */
+ mxscor = 0;
+ for (int i = 1; i <= NOBJECTS; i++) {
+ if (!objects[i].is_treasure)
+ continue;
+ if (objects[i].inventory != 0) {
+ int k = 12;
+ if (i == CHEST)
+ k = 14;
+ if (i > CHEST)
+ k = 16;
+ if (!PROP_IS_STASHED(i) && !PROP_IS_NOTFOUND(i))
+ score += 2;
+ if (game.objects[i].place == LOC_BUILDING && PROP_IS_FOUND(i))
+ score += k - 2;
+ mxscor += k;
+ }
+ }
- /* Now look at how he finished and how far he got. NDEATHS and
- * game.numdie tell us how well he survived. game.dflag will tell us
- * if he ever got suitably deep into the cave. game.closng still
- * indicates whether he reached the endgame. And if he got as far as
- * "cave closed" (indicated by "game.closed"), then bonus is zero for
- * mundane exits or 133, 134, 135 if he blew it (so to speak). */
- score += (NDEATHS - game.numdie) * 10;
- mxscor += NDEATHS * 10;
- if (mode == endgame) {
- score += 4;
- }
- mxscor += 4;
- if (game.dflag != 0) {
- score += 25;
- }
- mxscor += 25;
- if (game.closng) {
- score += 25;
- }
- mxscor += 25;
- if (game.closed) {
- if (game.bonus == none) {
- score += 10;
- }
- if (game.bonus == splatter) {
- score += 25;
- }
- if (game.bonus == defeat) {
- score += 30;
- }
- if (game.bonus == victory) {
- score += 45;
- }
- }
- mxscor += 45;
+ /* Now look at how he finished and how far he got. NDEATHS and
+ * game.numdie tell us how well he survived. game.dflag will tell us
+ * if he ever got suitably deep into the cave. game.closng still
+ * indicates whether he reached the endgame. And if he got as far as
+ * "cave closed" (indicated by "game.closed"), then bonus is zero for
+ * mundane exits or 133, 134, 135 if he blew it (so to speak). */
+ score += (NDEATHS - game.numdie) * 10;
+ mxscor += NDEATHS * 10;
+ if (mode == endgame)
+ score += 4;
+ mxscor += 4;
+ if (game.dflag != 0)
+ score += 25;
+ mxscor += 25;
+ if (game.closng)
+ score += 25;
+ mxscor += 25;
+ if (game.closed) {
+ if (game.bonus == none)
+ score += 10;
+ if (game.bonus == splatter)
+ score += 25;
+ if (game.bonus == defeat)
+ score += 30;
+ if (game.bonus == victory)
+ score += 45;
+ }
+ mxscor += 45;
- /* Did he come to Witt's End as he should? */
- if (game.objects[MAGAZINE].place == LOC_WITTSEND) {
- score += 1;
- }
- mxscor += 1;
+ /* Did he come to Witt's End as he should? */
+ if (game.objects[MAGAZINE].place == LOC_WITTSEND)
+ score += 1;
+ mxscor += 1;
- /* Round it off. */
- score += 2;
- mxscor += 2;
+ /* Round it off. */
+ score += 2;
+ mxscor += 2;
- /* Deduct for hints/turns/saves. Hints < 4 are special; see database
- * desc. */
- for (int i = 0; i < NHINTS; i++) {
- if (game.hints[i].used) {
- score = score - hints[i].penalty;
- }
- }
- if (game.novice) {
- score -= 5;
- }
- if (game.clshnt) {
- score -= 10;
- }
- score = score - game.trnluz - game.saved;
+ /* Deduct for hints/turns/saves. Hints < 4 are special; see database desc. */
+ for (int i = 0; i < NHINTS; i++) {
+ if (game.hints[i].used)
+ score = score - hints[i].penalty;
+ }
+ if (game.novice)
+ score -= 5;
+ if (game.clshnt)
+ score -= 10;
+ score = score - game.trnluz - game.saved;
- /* Return to score command if that's where we came from. */
- if (mode == scoregame) {
- rspeak(GARNERED_POINTS, score, mxscor, game.turns, game.turns);
- }
+ /* Return to score command if that's where we came from. */
+ if (mode == scoregame) {
+ rspeak(GARNERED_POINTS, score, mxscor, game.turns, game.turns);
+ }
- return score;
+ return score;
}
-void terminate(enum termination mode) {
- /* End of game. Let's tell him all about it. */
- int points = score(mode);
+void terminate(enum termination mode)
+/* End of game. Let's tell him all about it. */
+{
+ int points = score(mode);
#if defined ADVENT_AUTOSAVE
- autosave();
+ autosave();
#endif
- if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0) {
- rspeak(TOOK_LONG);
- }
- if (points + game.saved + 1 >= mxscor && game.saved != 0) {
- rspeak(WITHOUT_SUSPENDS);
- }
- rspeak(TOTAL_SCORE, points, mxscor, game.turns, game.turns);
- for (int i = 1; i <= (int)NCLASSES; i++) {
- if (classes[i].threshold >= points) {
- speak(classes[i].message);
- if (i < (int)NCLASSES) {
- int nxt = classes[i].threshold + 1 - points;
- rspeak(NEXT_HIGHER, nxt, nxt);
- } else {
- rspeak(NO_HIGHER);
- }
- exit(EXIT_SUCCESS);
- }
- }
- rspeak(OFF_SCALE);
- exit(EXIT_SUCCESS);
+ if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0)
+ rspeak(TOOK_LONG);
+ if (points + game.saved + 1 >= mxscor && game.saved != 0)
+ rspeak(WITHOUT_SUSPENDS);
+ rspeak(TOTAL_SCORE, points, mxscor, game.turns, game.turns);
+ for (int i = 1; i <= (int)NCLASSES; i++) {
+ if (classes[i].threshold >= points) {
+ speak(classes[i].message);
+ if (i < (int)NCLASSES) {
+ int nxt = classes[i].threshold + 1 - points;
+ rspeak(NEXT_HIGHER, nxt, nxt);
+ } else {
+ rspeak(NO_HIGHER);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ }
+ rspeak(OFF_SCALE);
+ exit(EXIT_SUCCESS);
}
/* end */
diff --git a/templates/dungeon.c.tpl b/templates/dungeon.c.tpl
index 03640d5..a192adb 100644
--- a/templates/dungeon.c.tpl
+++ b/templates/dungeon.c.tpl
@@ -54,6 +54,6 @@ const travelop_t travel[] = {{
const char *ignore = "{ignore}";
/* Dwarf starting locations */
-const int dwarflocs[NDWARVES] = {{{dwarflocs}}};
+const int dwarflocs[NDWARFLOCS] = {{{dwarflocs}}};
/* end */
diff --git a/templates/dungeon.h.tpl b/templates/dungeon.h.tpl
index cf637c8..2a26917 100644
--- a/templates/dungeon.h.tpl
+++ b/templates/dungeon.h.tpl
@@ -35,8 +35,9 @@ SPDX-License-Identifier: BSD-2-Clause
#define COND_HOGRE 20 /* Trying to deal with ogre */
#define COND_HJADE 21 /* Found all treasures except jade */
-#define NDWARVES {ndwarflocs} // number of dwarves
-extern const int dwarflocs[NDWARVES];
+/* Count of dwarf starting locations */
+#define NDWARFLOCS {ndwarflocs}
+extern const int dwarflocs[NDWARFLOCS];
typedef struct {{
const char** strs;
diff --git a/tests/Makefile b/tests/Makefile
index 997096a..c39bcbe 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -124,6 +124,10 @@ tap: count $(SGAMES) $(TEST_TARGETS)
count:
@echo 1..$(words $(TEST_TARGETS))
+foobar:
+ exit 1
+
+
# The following machinery tests the game against a binary made from
# the advent430 branch To use it, switch to that branch, build the
# binary, run it once to generate adventure.data, then switch back to
@@ -170,7 +174,7 @@ oldcompare:
echo 1..$(words $(shell ls *.log))) | $(TAPFILTER)
@rm *.ochk *-new advent430 adventure.data
-# List all NOCOMPARE tests.
+# List all NOMPARE tests.
residuals:
@grep -n NOCOMPARE *.log
diff --git a/tests/knife.chk b/tests/knife.chk
deleted file mode 100644
index 76a3b6b..0000000
--- a/tests/knife.chk
+++ /dev/null
@@ -1,386 +0,0 @@
-
-Welcome to Adventure!! Would you like instructions?
-
-> no
-
-You are standing at the end of a road before a small brick building.
-Around you is a forest. A small stream flows out of the building and
-down a gully.
-
-> seed 1640849217
-
-Seed set to 1640849217
-
-You're in front of building.
-
-> e
-
-You are inside a building, a well house for a large spring.
-
-There are some keys on the ground here.
-
-There is a shiny brass lamp nearby.
-
-There is food here.
-
-There is a bottle of water here.
-
-> get lamp
-
-OK
-
-> xyzzy
-
->>Foof!<<
-
-It is now pitch dark. If you proceed you will likely fall into a pit.
-
-> get rod
-
-OK
-
-> on
-
-Your lamp is now on.
-
-You are in a debris room filled with stuff washed in from the surface.
-A low wide passage with cobbles becomes plugged with mud and debris
-here, but an awkward canyon leads upward and west. In the mud someone
-has scrawled, "MAGIC WORD XYZZY".
-
-> w
-
-You are in an awkward sloping east/west canyon.
-
-> w
-
-You are in a splendid chamber thirty feet high. The walls are frozen
-rivers of orange stone. An awkward canyon and a good passage exit
-from east and west sides of the chamber.
-
-A cheerful little bird is sitting here singing.
-
-> w
-
-At your feet is a small pit breathing traces of white mist. An east
-passage ends here except for a small crack leading on.
-
-Rough stone steps lead down the pit.
-
-> d
-
-You are at one end of a vast hall stretching forward out of sight to
-the west. There are openings to either side. Nearby, a wide stone
-staircase leads downward. The hall is filled with wisps of white mist
-swaying to and fro almost as if alive. A cold wind blows up the
-staircase. There is a passage at the top of a dome behind you.
-
-Rough stone steps lead up the dome.
-
-> w
-
-You are on the east bank of a fissure slicing clear across the hall.
-The mist is quite thick here, and the fissure is too wide to jump.
-
-> wave rod
-
-A crystal bridge now spans the fissure.
-
-> w
-
-You are on the west side of the fissure in the Hall of Mists.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You are on the east bank of a fissure slicing clear across the hall.
-The mist is quite thick here, and the fissure is too wide to jump.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You are on the west side of the fissure in the Hall of Mists.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You are on the east bank of a fissure slicing clear across the hall.
-The mist is quite thick here, and the fissure is too wide to jump.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You are on the west side of the fissure in the Hall of Mists.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You are on the east bank of a fissure slicing clear across the hall.
-The mist is quite thick here, and the fissure is too wide to jump.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You are on the west side of the fissure in the Hall of Mists.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-You're on east bank of fissure.
-
-A crystal bridge spans the fissure.
-
-> w
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> e
-
-A little dwarf just walked around a corner, saw you, threw a little
-axe at you which missed, cursed, and ran away.
-
-You're on east bank of fissure.
-
-There is a little axe here.
-
-A crystal bridge spans the fissure.
-
-> w
-
-There is a threatening little dwarf in the room with you!
-
-One sharp nasty knife is thrown at you!
-
-It misses!
-
-You're on west bank of fissure.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> get knife
-
-The dwarves' knives vanish as they strike the walls of the cave.
-
-> look
-
-Sorry, but I am not allowed to give more detail. I will repeat the
-long description of your location.
-
-There is a threatening little dwarf in the room with you!
-
-One sharp nasty knife is thrown at you!
-
-It misses!
-
-You are on the west side of the fissure in the Hall of Mists.
-
-There are diamonds here!
-
-A crystal bridge spans the fissure.
-
-> get knife
-
-I see no knife here.
-
-> quit
-
-Do you really want to quit now?
-
-> yes
-
-OK
-
-You scored 59 out of a possible 430, using 50 turns.
-
-Your score qualifies you as a novice class adventurer.
-
-To achieve the next higher rating, you need 62 more points.
diff --git a/tests/knife.log b/tests/knife.log
deleted file mode 100644
index 1c811ea..0000000
--- a/tests/knife.log
+++ /dev/null
@@ -1,57 +0,0 @@
-## Test whether KNIVES_VANISH can be issued twice
-# SPDX-FileCopyrightText: Copyright Eric S. Raymond
-# SPDX-License-Identifier: BSD-2-Clause
-#NOCOMPARE avoide spuriuous failure on second "get knife"
-no
-seed 1640849217
-e
-get lamp
-xyzzy
-get rod
-on
-w
-w
-w
-d
-w
-wave rod
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-e
-w
-get knife
-look
-get knife
-quit
-yes
diff --git a/tests/tapdiffer b/tests/tapdiffer
index ea1cfa7..679867e 100755
--- a/tests/tapdiffer
+++ b/tests/tapdiffer
@@ -1,4 +1,6 @@
#! /bin/sh
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
+# SPDX-License-Identifier: MIT-0
#
# tapdiffer - Render diff between input and checkfile as a TAP report
#
@@ -14,23 +16,17 @@
# OSD-compliant; otherwise the following SPDX tag incorporates the
# MIT No Attribution license by reference.
#
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
-# SPDX-License-Identifier: MIT-0
-#
# A newer version may be available at https://gitlab.com/esr/tapview
# Check your last commit dqte for this file against the commit list
# there to see if it might be a good idea to update.
#
-diffopts=-u
-while getopts bn opt
-do
- case $opt in
- b) diffopts=-ub;;
- *) echo "tapdiffer: unknown option ${opt}."; exit 1;;
- esac
-done
-# shellcheck disable=SC2004
-shift $(($OPTIND - 1))
+if [ "$1" = "-b" ]
+then
+ diffopts=-ub
+ shift
+else
+ diffopts=-u
+fi
legend=$1
checkfile=$2
@@ -41,7 +37,7 @@ if diff --text "${diffopts}" "${checkfile}" - >/tmp/tapdiff$$
then
echo "ok - ${legend}"
else
- echo "not ok - ${legend}"
+ echo "not ok - ${checkfile}: ${legend}"
if [ ! "${QUIET}" = 1 ]
then
echo " --- |"
diff --git a/tests/tapview b/tests/tapview
index 0a36f88..b96b436 100755
--- a/tests/tapview
+++ b/tests/tapview
@@ -4,15 +4,14 @@
# This code is intended to be embedded in your project. The author
# grants permission for it to be distributed under the prevailing
# license of your project if you choose, provided that license is
-# OSD-compliant; otherwise the following SPDX tag incorporates the
-# MIT No Attribution license by reference.
+# OSD-compliant; otherwise the following SPDX tag incorporates a
+# license by reference.
#
-# SPDX-FileCopyrightText: (C) Eric S. Raymond
-# SPDX-License-Identifier: MIT-0
+# SPDX-FileCopyrightText: Copyright Eric S. Raymond
+# SPDX-License-Identifier: BSD-2-Clause
#
+# This is version 1.6
# A newer version may be available at https://gitlab.com/esr/tapview
-# Check your last commit date for this file against the commit list
-# there to see if it might be a good idea to update.
#
OK="."
FAIL="F"
@@ -20,16 +19,13 @@ SKIP="s"
TODO_NOT_OK="x"
TODO_OK="u"
-LF='
-'
-
ship_char() {
# shellcheck disable=SC2039
printf '%s' "$1" # https://www.etalabs.net/sh_tricks.html
}
ship_line() {
- report="${report}${1}$LF"
+ report="${report}${1}\n"
}
ship_error() {
@@ -38,7 +34,7 @@ ship_error() {
then
echo ""
fi
- report="${report}${1}$LF"
+ report="${report}${1}\n"
echo "${report}"
exit 1
}
@@ -80,10 +76,6 @@ context_pop () {
then
ship_line "Expected $(context_get expect) tests but only ${testcount} ran."
status=1
- elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -lt "$(context_get count)" ]
- then
- ship_line "${testcount} ran but $(context_get expect) expected."
- status=1
fi
}