Compare commits

...

32 commits
1.18 ... master

Author SHA1 Message Date
Eric S. Raymond
7d848c89e1 Add spell-checking to the regression tests. 2025-05-25 11:06:46 -04:00
Eric S. Raymond
7bbf994fce Spellcheck the manual page a well. No errors. 2025-05-25 11:03:34 -04:00
Eric S. Raymond
3e989aec53 Spellcheck the YAML - yielded one trivial fix. 2025-05-25 10:57:34 -04:00
Eric S. Raymond
9df69fe034 Ready to ship 1.20. 2024-09-23 18:48:11 -04:00
Eric S. Raymond
a2bb39dc7e Eliminate a confusing dummy argument in a macro. 2024-09-23 14:22:25 -04:00
Eric S. Raymond
5c90880f0a Comment and macro cleanup. 2024-09-23 06:06:32 -04:00
Eric S. Raymond
92451f1fff Eliminate thew last property inequality outside a macro. 2024-09-23 05:08:11 -04:00
Eric S. Raymond
96ad6c6245 Reflow. 2024-09-23 04:38:24 -04:00
Eric S. Raymond
40742e112b Expand a macro to simplify code. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
a4fd14caf7 Back away from trying for the found member until we make stashed work. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
9a6e4406f5 Confine uses of PROP_STASHIFY() to advent.h
Now it shouyld be possible to manipulate a stashed flag by only
changing macros.
2024-09-23 04:26:59 -04:00
Eric S. Raymond
08f0351817 Introduce OBJECT_STASHIFY. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
0157e58668 Remove an unneeded layer of macro indirection. 2024-09-23 03:35:52 -04:00
Eric S. Raymond
354e56a69b Clean up some comments. 2024-09-23 03:35:52 -04:00
Eric S. Raymond
9c3f4b0c90 Reflow. 2024-09-22 13:08:31 -04:00
Eric S. Raymond
20fd7c589f Typo fix. 2024-09-20 22:19:14 -04:00
Eric S. Raymond
8c553af53e Avoid a GNUism, POSIX strncasecmp() is declarted in strings.h. 2024-09-20 22:05:25 -04:00
Eric S. Raymond
f1cb740c41 Define TRUNCLEN and explain its issues. 2024-09-20 22:04:16 -04:00
Eric S. Raymond
cf4adf8d02 Repair truncation in oldstyle mode.
Sure would be nice to remember while this code had TOKEN + TOKEN
where one would think it should just say TOKEN.
2024-09-20 10:49:44 -04:00
Eric S. Raymond
acdfa96315 Fix a busted comment. 2024-09-20 10:40:22 -04:00
Eric S. Raymond
3d6c689ffa Make oldstyle correctly suppress line editing. 2024-09-20 10:29:37 -04:00
Eric S. Raymond
8dcc6e6641 Correct missing negative. 2024-09-20 10:03:15 -04:00
Eric S. Raymond
e17ff128da Remove obsolete comment part. 2024-06-30 17:20:46 -04:00
Eric S. Raymond
cb293f4aa4 Rename some macos for clarity. 2024-06-30 14:48:44 -04:00
Eric S. Raymond
124e7768b4 Cease relying on C storage starting zeroed. 2024-06-27 19:50:56 -04:00
Eric S. Raymond
63e8579f74 Ready to shp 1.19. 2024-06-27 13:39:27 -04:00
Eric S. Raymond
1080b45d39 Comment typo fix. 2024-06-27 13:29:28 -04:00
Eric S. Raymond
86fe4bd121 Verify that tesrts still match advent430 where expected. 2024-06-27 13:04:33 -04:00
Eric S. Raymond
bd499dc532 Fix GitLab issue #69: repeated knive caveat message 2024-06-25 16:42:14 -04:00
Eric S. Raymond
ae6eced72d Incorporate Ryan Sarson's test for correct knife message. 2024-06-25 13:08:50 -04:00
Eric S. Raymond
7903ac1bb8 More validation, with -Wall and -Wextra. 2024-06-03 21:12:41 -04:00
Eric S. Raymond
b51612131d Typo fix. 2024-04-29 11:43:41 -04:00
16 changed files with 588 additions and 135 deletions

View file

@ -23,3 +23,5 @@ You can also use pip to install PyYAML: `pip3 install PyYAML`.
5. Run `./advent` to play.
6. If you want to buld the documentation you will need asciidoctor.
7. Running the regression tests requires batchspell

View file

@ -12,7 +12,7 @@ VERS=$(shell sed -n <NEWS.adoc '/^[0-9]/s/:.*//p' | head -1)
.PHONY: check coverage
CC?=gcc
CCFLAGS+=-std=c99 -D_DEFAULT_SOURCE -DVERSION=\"$(VERS)\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all $(CFLAGS) -g $(EXTRA)
CCFLAGS+=-std=c99 -Wall -Wextra -D_DEFAULT_SOURCE -DVERSION=\"$(VERS)\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all $(CFLAGS) -g $(EXTRA)
LIBS=$(shell pkg-config --libs libedit)
INC+=$(shell pkg-config --cflags libedit)
@ -67,14 +67,17 @@ cheat: $(CHEAT_OBJS) dungeon.o
CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf
cppcheck:
@-cppcheck -I. --quiet --template gcc -UPROP_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch]
@-cppcheck -I. --quiet --template gcc -UOBJECT_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch]
pylint:
@-pylint --score=n *.py */*.py
check: advent cheat pylint cppcheck
check: advent cheat pylint cppcheck spellcheck
cd tests; $(MAKE) --quiet
spellcheck:
@batchspell adventure.yaml advent.adoc
reflow:
@clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]")
@black --quiet *.py

View file

@ -2,6 +2,12 @@
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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.

View file

@ -246,7 +246,7 @@ static phase_codes_t bigwords(vocab_t id) {
static void blast(void) {
/* Blast. No effect unless you've got dynamite, which is a neat trick!
*/
if (PROP_IS_NOTFOUND(ROD2) || !game.closed) {
if (OBJECT_IS_NOTFOUND(ROD2) || !game.closed) {
rspeak(REQUIRES_DYNAMITE);
} else {
if (HERE(ROD2)) {
@ -329,8 +329,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
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
rspeak((game.objects[PLANT].prop == PLANT_THIRSTY ||
OBJECT_IS_STASHED(PLANT))
? DEEP_ROOTS
: YOU_JOKING);
break;
@ -389,7 +389,7 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
}
if (obj == BIRD && game.objects[BIRD].prop != BIRD_CAGED &&
!PROP_IS_STASHED(BIRD)) {
!OBJECT_IS_STASHED(BIRD)) {
if (game.objects[BIRD].prop == BIRD_FOREST_UNCAGED) {
DESTROY(BIRD);
rspeak(BIRD_CRAP);
@ -406,8 +406,7 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
game.objects[BIRD].prop = BIRD_CAGED;
}
if ((obj == BIRD || obj == CAGE) &&
(game.objects[BIRD].prop == BIRD_CAGED ||
PROP_STASHED(BIRD) == BIRD_CAGED)) {
OBJECT_STATE_EQUALS(BIRD, BIRD_CAGED)) {
/* expression maps BIRD to CAGE and CAGE to BIRD */
carry(BIRD + CAGE - obj, game.loc);
}
@ -418,8 +417,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
game.objects[LIQUID()].place = CARRIED;
}
if (GSTONE(obj) && !PROP_IS_FOUND(obj)) {
PROP_SET_FOUND(obj);
if (GSTONE(obj) && !OBJECT_IS_FOUND(obj)) {
OBJECT_SET_FOUND(obj);
game.objects[CAVITY].prop = CAVITY_EMPTY;
}
rspeak(OK_MAN);
@ -677,7 +676,7 @@ static phase_codes_t extinguish(verb_t verb, obj_t obj) {
break;
case LAMP:
state_change(LAMP, LAMP_DARK);
rspeak(DARK(game.loc) ? PITCH_DARK : NO_MESSAGE);
rspeak(IS_DARK_HERE() ? PITCH_DARK : NO_MESSAGE);
break;
case DRAGON:
case VOLCANO:
@ -971,7 +970,7 @@ static phase_codes_t listen(void) {
}
for (obj_t i = 1; i <= NOBJECTS; i++) {
if (!HERE(i) || objects[i].sounds[0] == NULL ||
PROP_IS_STASHED_OR_UNSEEN(i)) {
OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(i)) {
continue;
}
int mi = game.objects[i].prop;
@ -1151,17 +1150,17 @@ static phase_codes_t read(command_t command)
command.obj = NO_OBJECT;
for (int i = 1; i <= NOBJECTS; i++) {
if (HERE(i) && objects[i].texts[0] != NULL &&
!PROP_IS_STASHED(i)) {
!OBJECT_IS_STASHED(i)) {
command.obj = command.obj * NOBJECTS + i;
}
}
if (command.obj > NOBJECTS || command.obj == NO_OBJECT ||
DARK(game.loc)) {
IS_DARK_HERE()) {
return GO_UNKNOWN;
}
}
if (DARK(game.loc)) {
if (IS_DARK_HERE()) {
sspeak(NO_SEE, command.word[0].raw);
} else if (command.obj == OYSTER) {
if (!TOTING(OYSTER) || !game.closed) {
@ -1175,7 +1174,7 @@ static phase_codes_t read(command_t command)
1); // Not really a sound, but oh well.
}
} else if (objects[command.obj].texts[0] == NULL ||
PROP_IS_NOTFOUND(command.obj)) {
OBJECT_IS_NOTFOUND(command.obj)) {
speak(actions[command.verb].message);
} else {
pspeak(command.obj, study, true,
@ -1351,9 +1350,9 @@ static phase_codes_t wave(verb_t verb, obj_t obj) {
}
if (game.objects[BIRD].prop == BIRD_UNCAGED &&
game.loc == game.objects[STEPS].place && PROP_IS_NOTFOUND(JADE)) {
game.loc == game.objects[STEPS].place && OBJECT_IS_NOTFOUND(JADE)) {
drop(JADE, game.loc);
PROP_SET_FOUND(JADE);
OBJECT_SET_FOUND(JADE);
--game.tally;
rspeak(NECKLACE_FLY);
return GO_CLEAROBJ;

View file

@ -3,6 +3,9 @@
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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
@ -62,7 +65,7 @@ argument of '-' is taken as a directive to read from standard input.
The binary save file format is fragile, dependent on your machine's
endianness, and unlikely to survive through version bumps. There are
version and emdianness checks when attempting to restore from a save.
version and endianness checks when attempting to restore from a save.
The input parser was the first attempt *ever* at natural-language
parsing in a game and has some known deficiencies. While later text

100
advent.h
View file

@ -47,7 +47,6 @@
#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
@ -61,71 +60,49 @@
* STATE_NOTFOUND is only set on treasures. Non-treasures start the
* game in STATE_FOUND.
*
* PROP_STASHED is supposed to map a state property value to a
* PROP_STASHIFY 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 test is difficult to read.
*/
#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.
* those tests is difficult to read.
*
* 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.
* All tests of the prop member are done with either these macros or ==.
*/
#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 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 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)
* 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
* 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
*/
#define DESTROY(N) move(N, LOC_NOWHERE)
#define MOD(N, M) ((N) % (M))
@ -143,15 +120,15 @@
(CNDBIT((LOC), COND_FLUID) ? CNDBIT((LOC), COND_OILY) ? OIL : WATER \
: NO_OBJECT)
#define FORCED(LOC) CNDBIT(LOC, COND_FORCED)
#define DARK(DUMMY) \
#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 OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC))
#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING)
#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)
@ -257,11 +234,8 @@ struct game_t {
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 */
int32_t prop; // object state
loc_t place; // location of object
} objects[NOBJECTS + 1];
struct {

View file

@ -1,6 +1,10 @@
# SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
# 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
@ -10,7 +14,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 be
# attribute, if false, means that single-letter synonyms should not be
# accepted in oldstyle mode; it defaults to true.
#
# actions: Action words, grouped into synonyms, and their corresponding
@ -3432,7 +3436,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 pencilled on one
I'm afraid the magazine is written in dwarvish. But penciled on one
cover you see, "Please leave the magazines at the construction site."
- DWARF:
words: ['dwarf', 'dwarv']
@ -3940,11 +3944,13 @@ 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?
@ -4226,7 +4232,7 @@ actions: !!omap
message: |-
There is a puff of orange smoke; within it, fiery runes spell out:
\tOpen Adventure %V - http://www.catb.org/esr/open-adventure/
Open Adventure %V - http://www.catb.org/esr/open-adventure/
words: ['versi']
noaction: true

17
init.c
View file

@ -1,7 +1,7 @@
/*
* Initialisation
*
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woodsm
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -76,13 +76,18 @@ int initialise(void) {
/* 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) {
* 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[treasure].inventory != 0) {
PROP_SET_NOT_FOUND(treasure);
if (objects[object].inventory != NULL) {
OBJECT_SET_NOT_FOUND(object);
}
} else {
OBJECT_SET_FOUND(object);
}
}
game.conds = setbit(COND_HBASE);

52
main.c
View file

@ -81,7 +81,7 @@ char *myreadline(const char *prompt) {
}
}
if (isatty(fileno(settings.scriptfp))) {
if (isatty(fileno(settings.scriptfp)) && !settings.oldstyle) {
free(buf); // LCOV_EXCL_LINE
return readline(prompt); // LCOV_EXCL_LINE
} else {
@ -152,8 +152,8 @@ static void checkhints(void) {
game.hints[hint].lc = 0;
return;
case 4: /* dark */
if (!PROP_IS_NOTFOUND(EMERALD) &&
PROP_IS_NOTFOUND(PYRAMID)) {
if (!OBJECT_IS_NOTFOUND(EMERALD) &&
OBJECT_IS_NOTFOUND(PYRAMID)) {
break;
}
game.hints[hint].lc = 0;
@ -188,7 +188,8 @@ static void checkhints(void) {
return;
case 9: /* jade */
if (game.tally == 1 &&
PROP_IS_STASHED_OR_UNSEEN(JADE)) {
(OBJECT_IS_STASHED(JADE) ||
OBJECT_IS_NOTFOUND(JADE))) {
break;
}
game.hints[hint].lc = 0;
@ -231,8 +232,8 @@ static bool spotted_by_pirate(int i) {
* 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)) {
* OBJECT_IS_FOUND(CHEST) == true. */
if (game.loc == game.chloc || !OBJECT_IS_NOTFOUND(CHEST)) {
return true;
}
int snarfed = 0;
@ -533,7 +534,7 @@ static void describe_location(void) {
msg = locations[game.loc].description.big;
}
if (!FORCED(game.loc) && DARK(game.loc)) {
if (!FORCED(game.loc) && IS_DARK_HERE()) {
msg = arbitrary_messages[PITCH_DARK];
}
@ -639,7 +640,7 @@ static void playermove(int motion) {
} else if (motion == CAVE) {
/* Cave. Different messages depending on whether above ground.
*/
rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE)
rspeak((OUTSIDE(game.loc) && game.loc != LOC_GRATE)
? FOLLOW_STREAM
: NEED_DETAIL);
return;
@ -1046,7 +1047,7 @@ static void listobjects(void) {
* 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)) {
if (!IS_DARK_HERE()) {
++game.locs[game.loc].abbrev;
for (int i = game.locs[game.loc].atloc; i != 0;
i = game.link[i]) {
@ -1061,11 +1062,11 @@ static void listobjects(void) {
* running this code only on objects with the treasure
* property set. Nope. There is mystery here.
*/
if (PROP_IS_STASHED_OR_UNSEEN(obj)) {
if (OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(obj)) {
if (game.closed) {
continue;
}
PROP_SET_FOUND(obj);
OBJECT_SET_FOUND(obj);
if (obj == RUG) {
game.objects[RUG].prop = RUG_DRAGON;
}
@ -1192,7 +1193,7 @@ static bool preprocess_command(command_t *command) {
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) {
if (OUTSIDE(game.newloc) && game.newloc != 0 && game.closng) {
rspeak(EXIT_CLOSED);
game.newloc = game.loc;
if (!game.panic) {
@ -1228,7 +1229,7 @@ static bool do_move(void) {
/* The easiest way to get killed is to fall into a pit in
* pitch darkness. */
if (!FORCED(game.loc) && DARK(game.loc) && game.wzdark &&
if (!FORCED(game.loc) && IS_DARK_HERE() && game.wzdark &&
PCT(PIT_KILL_PROB)) {
rspeak(PIT_FALL);
game.oldlc2 = game.loc;
@ -1266,25 +1267,28 @@ static bool do_command(void) {
* 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)) &&
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) && (PROP_IS_NOTFOUND(i) ||
PROP_IS_STASHED(i))) {
game.objects[i].prop =
PROP_STASHED(i);
if (TOTING(i) &&
(OBJECT_IS_NOTFOUND(i) ||
OBJECT_IS_STASHED(i))) {
OBJECT_STASHIFY(
i, game.objects[i].prop);
}
}
}
/* 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 &&
/* Check to see if the room is dark. */
game.wzdark = IS_DARK_HERE();
/* 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;
}

25
misc.c
View file

@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
@ -495,8 +496,8 @@ static void tokenize(char *raw, command_t *cmd) {
memset(&cmd->word[1].raw, '\0', sizeof(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. */
* 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);
/* (ESR) In oldstyle mode, simulate the uppercasing and truncating
@ -512,10 +513,14 @@ static void tokenize(char *raw, command_t *cmd) {
* 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[TOKLEN + TOKLEN] =
cmd->word[1].raw[TOKLEN + TOKLEN] = '\0';
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]);
}
@ -617,14 +622,12 @@ void move(obj_t object, loc_t 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. */
/* put() is the same as move(), except the object is stashed and
* can no longer be picked up. */
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);
OBJECT_STASHIFY(object, pval);
#ifdef OBJECT_SET_SEEN
OBJECT_SET_SEEN(object);
#endif
}

View file

@ -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 longstannding minor bug in the
handling of incorrect magic-word sequebcesm,
Sarson nudged us into fixing a longstanding minor bug in the
handling of incorrect magic-word sequences,
== Nomenclature ==
@ -75,10 +75,15 @@ 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".
"eying" for "eyeing", "thresholds" for "threshholds", "pencilled"
for "penciled".
* Under odd circumstances (dropping rug or vase outdoors) the game could
formerly say "floor" when it should say "ground" (or "dirt", or something).
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.
Enhancements:

View file

@ -230,7 +230,7 @@ bool is_valid(struct game_t valgame) {
int temp_tally = 0;
for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
if (objects[treasure].is_treasure) {
if (PROP_IS_NOTFOUND2(valgame, treasure)) {
if (OBJECT_IS_NOTFOUND2(valgame, treasure)) {
++temp_tally;
}
}

View file

@ -49,11 +49,11 @@ int score(enum termination mode) {
if (i > CHEST) {
k = 16;
}
if (!PROP_IS_STASHED(i) && !PROP_IS_NOTFOUND(i)) {
if (!OBJECT_IS_STASHED(i) && !OBJECT_IS_NOTFOUND(i)) {
score += 2;
}
if (game.objects[i].place == LOC_BUILDING &&
PROP_IS_FOUND(i)) {
OBJECT_IS_FOUND(i)) {
score += k - 2;
}
mxscor += k;

View file

@ -170,7 +170,7 @@ oldcompare:
echo 1..$(words $(shell ls *.log))) | $(TAPFILTER)
@rm *.ochk *-new advent430 adventure.data
# List all NOMPARE tests.
# List all NOCOMPARE tests.
residuals:
@grep -n NOCOMPARE *.log

386
tests/knife.chk Normal file
View file

@ -0,0 +1,386 @@
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.

57
tests/knife.log Normal file
View file

@ -0,0 +1,57 @@
## Test whether KNIVES_VANISH can be issued twice
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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