Compare commits

...

25 commits
1.19 ... 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
13 changed files with 130 additions and 129 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

@ -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,9 @@
// 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.

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

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 tests 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 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.
* 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);

48
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,26 +1267,27 @@ 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. */
game.wzdark = DARK(game.loc);
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? */
* 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,7 +75,8 @@ 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

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;