From 75bc031b46097725c83b63bbdb520ac3c907ef05 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Wed, 3 Jan 2024 05:55:57 -0500 Subject: [PATCH 01/55] Reduce include complexity. --- actions.c | 4 ++-- main.c | 1 - saveresume.c | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/actions.c b/actions.c index 4f4ced1..f5a7abd 100644 --- a/actions.c +++ b/actions.c @@ -9,10 +9,10 @@ #include #include #include -#include "advent.h" -#include "dungeon.h" #include +#include "advent.h" + static phase_codes_t fill(verb_t, obj_t); static phase_codes_t attack(command_t command) diff --git a/main.c b/main.c index 16cf42d..3fadf10 100644 --- a/main.c +++ b/main.c @@ -14,7 +14,6 @@ #include #include #include "advent.h" -#include "dungeon.h" #define DIM(a) (sizeof(a)/sizeof(a[0])) diff --git a/saveresume.c b/saveresume.c index a2c10a0..3b94b48 100644 --- a/saveresume.c +++ b/saveresume.c @@ -16,7 +16,6 @@ #include #include "advent.h" -#include "dungeon.h" /* * Use this to detect endianness mismatch. Can't be unchanged by byte-swapping. From a6ec8a9595e988db977f0075402ab5279e653af9 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Wed, 3 Jan 2024 06:15:04 -0500 Subject: [PATCH 02/55] Simplify some dependencies. --- advent.h | 1 - init.c | 3 +-- templates/dungeon.c.tpl | 2 +- templates/dungeon.h.tpl | 5 ++--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/advent.h b/advent.h index 68890da..394a389 100644 --- a/advent.h +++ b/advent.h @@ -21,7 +21,6 @@ #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) diff --git a/init.c b/init.c index aabd1cb..715f3ef 100644 --- a/init.c +++ b/init.c @@ -46,8 +46,7 @@ int initialise(void) int seedval = (int)rand(); set_seed(seedval); - assert(NDWARVES == NDWARFLOCS); - for (int i = 1; i <= NDWARFLOCS; i++) { + for (int i = 1; i <= NDWARVES; i++) { game.dwarves[i].loc = dwarflocs[i-1]; } diff --git a/templates/dungeon.c.tpl b/templates/dungeon.c.tpl index a192adb..03640d5 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[NDWARFLOCS] = {{{dwarflocs}}}; +const int dwarflocs[NDWARVES] = {{{dwarflocs}}}; /* end */ diff --git a/templates/dungeon.h.tpl b/templates/dungeon.h.tpl index 2a26917..cf637c8 100644 --- a/templates/dungeon.h.tpl +++ b/templates/dungeon.h.tpl @@ -35,9 +35,8 @@ SPDX-License-Identifier: BSD-2-Clause #define COND_HOGRE 20 /* Trying to deal with ogre */ #define COND_HJADE 21 /* Found all treasures except jade */ -/* Count of dwarf starting locations */ -#define NDWARFLOCS {ndwarflocs} -extern const int dwarflocs[NDWARFLOCS]; +#define NDWARVES {ndwarflocs} // number of dwarves +extern const int dwarflocs[NDWARVES]; typedef struct {{ const char** strs; From 2db3bed3f1ee0a220bf1d1b818716afb90b30b35 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Wed, 3 Jan 2024 06:30:40 -0500 Subject: [PATCH 03/55] Fix up copyright notices. SPDX wants only one per file. --- actions.c | 3 +-- advent.h | 3 +-- cheat.c | 3 +-- init.c | 3 +-- main.c | 3 +-- misc.c | 3 +-- saveresume.c | 3 +-- score.c | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/actions.c b/actions.c index f5a7abd..9239b50 100644 --- a/actions.c +++ b/actions.c @@ -1,8 +1,7 @@ /* * Actions for the dungeon-running code. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/advent.h b/advent.h index 394a389..e3be917 100644 --- a/advent.h +++ b/advent.h @@ -1,8 +1,7 @@ /* * Dungeon types and macros. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright, 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ #include diff --git a/cheat.c b/cheat.c index 1a415b7..9842d5c 100644 --- a/cheat.c +++ b/cheat.c @@ -4,8 +4,7 @@ * savefile(), so we know we're always outputting save files that advent * can import. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyrighy * SPDX-License-Identifier: BSD-2-Clause */ #include diff --git a/init.c b/init.c index 715f3ef..4be4b62 100644 --- a/init.c +++ b/init.c @@ -1,8 +1,7 @@ /* * Initialisation * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copright 1977, 2005 by Will Crowther and Don Woodsm, Copyright 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/main.c b/main.c index 3fadf10..2e7fdf2 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,5 @@ /* - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/misc.c b/misc.c index d2d0b8f..432d5d8 100644 --- a/misc.c +++ b/misc.c @@ -1,8 +1,7 @@ /* * I/O and support routines. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/saveresume.c b/saveresume.c index 3b94b48..0d1fbf4 100644 --- a/saveresume.c +++ b/saveresume.c @@ -4,8 +4,7 @@ * (ESR) This replaces a bunch of particularly nasty FORTRAN-derived code; * see the history.adoc file in the source distribution for discussion. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/score.c b/score.c index c0a0277..9d3d63f 100644 --- a/score.c +++ b/score.c @@ -1,8 +1,7 @@ /* * Scoring and wrap-up. * - * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods - * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: Copyright 977, 2005 by Will Crowther and Don Woods, Copyright, 2017 by Eric S. Raymond * SPDX-License-Identifier: BSD-2-Clause */ #include From cf7ea0cd89952ff4bbd9f820399ee7bc473916ba Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 15 Jan 2024 04:57:47 -0500 Subject: [PATCH 04/55] Remove debugging debris. --- tests/Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index c39bcbe..2ed7431 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -124,10 +124,6 @@ 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 From f26514b5dd3624076f59801cfca66cc7cbdd3313 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 15 Jan 2024 04:58:21 -0500 Subject: [PATCH 05/55] Fix SPDX headers. --- .gitignore | 2 +- .gitlab-ci.yml | 2 +- .shipper | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7aa1928..fcfc9f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause advent *.gcda diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6b6bd6d..b8f4b91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause stages: - ci-build diff --git a/.shipper b/.shipper index d3ed58d..5e5904c 100644 --- a/.shipper +++ b/.shipper @@ -1,4 +1,4 @@ -#SPDX-FileCopyrightText: Eric S. Raymond +#SPDX-FileCopyrightText: (C) Eric S. Raymond #SPDX-License-Identifier: BSD-2-Clause extralines="""

There is a code coverage analysis and a symbol coverage analysis

From 3c09c25cc3343546143447547297d23201f2140c Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 15 Jan 2024 05:03:53 -0500 Subject: [PATCH 06/55] Update tapview and tapdiffer. --- tests/tapdiffer | 24 ++++++++++++++---------- tests/tapview | 22 +++++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/tests/tapdiffer b/tests/tapdiffer index 679867e..ea1cfa7 100755 --- a/tests/tapdiffer +++ b/tests/tapdiffer @@ -1,6 +1,4 @@ #! /bin/sh -# SPDX-FileCopyrightText: Copyright Eric S. Raymond -# SPDX-License-Identifier: MIT-0 # # tapdiffer - Render diff between input and checkfile as a TAP report # @@ -16,17 +14,23 @@ # 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. # -if [ "$1" = "-b" ] -then - diffopts=-ub - shift -else - diffopts=-u -fi +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)) legend=$1 checkfile=$2 @@ -37,7 +41,7 @@ if diff --text "${diffopts}" "${checkfile}" - >/tmp/tapdiff$$ then echo "ok - ${legend}" else - echo "not ok - ${checkfile}: ${legend}" + echo "not ok - ${legend}" if [ ! "${QUIET}" = 1 ] then echo " --- |" diff --git a/tests/tapview b/tests/tapview index b96b436..0a36f88 100755 --- a/tests/tapview +++ b/tests/tapview @@ -4,14 +4,15 @@ # 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 a -# license by reference. +# OSD-compliant; otherwise the following SPDX tag incorporates the +# MIT No Attribution license by reference. # -# SPDX-FileCopyrightText: Copyright Eric S. Raymond -# SPDX-License-Identifier: BSD-2-Clause +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: MIT-0 # -# 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" @@ -19,13 +20,16 @@ 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}\n" + report="${report}${1}$LF" } ship_error() { @@ -34,7 +38,7 @@ ship_error() { then echo "" fi - report="${report}${1}\n" + report="${report}${1}$LF" echo "${report}" exit 1 } @@ -76,6 +80,10 @@ 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 } From 83c6a66887002170cec56f0e5c562288cb19b3a0 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 15 Jan 2024 05:10:46 -0500 Subject: [PATCH 07/55] Typo fix. --- notes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes.adoc b/notes.adoc index c369177..cc57885 100644 --- a/notes.adoc +++ b/notes.adoc @@ -139,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 (comditionally on +An -a command-line option has been added (conditionally 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 From 258b7703f22045be1b6e9fb74bd3391ed94ed86b Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 28 Jan 2024 05:55:33 -0500 Subject: [PATCH 08/55] Simplify SPDX copyright lines to the shortest canonical form... ...because if we leave them longer than 80 chars, reflow is going to mess them up. --- Dockerfile.ci | 2 +- INSTALL.adoc | 2 +- Makefile | 2 +- NEWS.adoc | 2 +- README.adoc | 2 +- actions.c | 2 +- advent.adoc | 2 +- advent.desktop | 2 +- advent.h | 2 +- adventure.yaml | 2 +- cheat.c | 2 +- hints.adoc | 2 +- history.adoc | 2 +- init.c | 2 +- main.c | 2 +- make_dungeon.py | 2 +- make_graph.py | 2 +- misc.c | 2 +- notes.adoc | 2 +- saveresume.c | 2 +- score.c | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index c8f29a1..598687b 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: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause FROM ubuntu diff --git a/INSTALL.adoc b/INSTALL.adoc index b6f5b4b..683e2f0 100644 --- a/INSTALL.adoc +++ b/INSTALL.adoc @@ -1,5 +1,5 @@ = Installing Open Adventure = -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 Installation now requires Python3 due to a security issue diff --git a/Makefile b/Makefile index dfd3ff2..49c909e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile for the open-source release of adventure 2.5 -# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause # To build with save/resume disabled, pass CFLAGS="-DADVENT_NOSAVE" diff --git a/NEWS.adoc b/NEWS.adoc index b61a477..68aa36c 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -1,5 +1,5 @@ = Open Adventure project news = -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 1.17: 2024-01-02:: diff --git a/README.adoc b/README.adoc index ba67510..6b25f7f 100644 --- a/README.adoc +++ b/README.adoc @@ -1,5 +1,5 @@ = README for Open Adventure = -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) 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 9239b50..ec62447 100644 --- a/actions.c +++ b/actions.c @@ -1,7 +1,7 @@ /* * Actions for the dungeon-running code. * - * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/advent.adoc b/advent.adoc index 5ce5b3f..9e628e0 100644 --- a/advent.adoc +++ b/advent.adoc @@ -1,6 +1,6 @@ = advent(6) = :doctype: manpage -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 == NAME == diff --git a/advent.desktop b/advent.desktop index 3fe5420..1cc38f8 100644 --- a/advent.desktop +++ b/advent.desktop @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause [Desktop Entry] Type=Application diff --git a/advent.h b/advent.h index e3be917..55518cb 100644 --- a/advent.h +++ b/advent.h @@ -1,7 +1,7 @@ /* * Dungeon types and macros. * - * SPDX-FileCopyrightText: Copyright, 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ #include diff --git a/adventure.yaml b/adventure.yaml index 2069759..31988de 100644 --- a/adventure.yaml +++ b/adventure.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) Eric S. Raymond # SPDX-License-Identifier: BSD-2-Clause # # This YAML file gets processed into a collection of data structures and diff --git a/cheat.c b/cheat.c index 9842d5c..279bcd9 100644 --- a/cheat.c +++ b/cheat.c @@ -4,7 +4,7 @@ * savefile(), so we know we're always outputting save files that advent * can import. * - * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyrighy + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ #include diff --git a/hints.adoc b/hints.adoc index c22c11b..f7e25e1 100644 --- a/hints.adoc +++ b/hints.adoc @@ -1,5 +1,5 @@ = Non-spoiler hints = -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) 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 5384c2c..8ca5da7 100644 --- a/history.adoc +++ b/history.adoc @@ -1,6 +1,6 @@ = A brief history of Colossal Cave Adventure = by Eric S. Raymond -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) 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 4be4b62..a620b2c 100644 --- a/init.c +++ b/init.c @@ -1,7 +1,7 @@ /* * Initialisation * - * SPDX-FileCopyrightText: Copright 1977, 2005 by Will Crowther and Don Woodsm, Copyright 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woodsm * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/main.c b/main.c index 2e7fdf2..e0fdb69 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/make_dungeon.py b/make_dungeon.py index 0b8bc53..8d7f719 100755 --- a/make_dungeon.py +++ b/make_dungeon.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) 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 35dd92f..5a58895 100755 --- a/make_graph.py +++ b/make_graph.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-FileCopyrightText: (C) 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 432d5d8..c853237 100644 --- a/misc.c +++ b/misc.c @@ -1,7 +1,7 @@ /* * I/O and support routines. * - * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/notes.adoc b/notes.adoc index cc57885..b970e7c 100644 --- a/notes.adoc +++ b/notes.adoc @@ -1,6 +1,6 @@ = Open Adventure Maintainer's Notes = by Eric S. Raymond -// SPDX-FileCopyrightText: Copyright Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 In which we explain what has been done to this code since Don Woods diff --git a/saveresume.c b/saveresume.c index 0d1fbf4..34404c6 100644 --- a/saveresume.c +++ b/saveresume.c @@ -4,7 +4,7 @@ * (ESR) This replaces a bunch of particularly nasty FORTRAN-derived code; * see the history.adoc file in the source distribution for discussion. * - * SPDX-FileCopyrightText: Copyright 1977, 2005 by Will Crowther and Don Woods, Copyright 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/score.c b/score.c index 9d3d63f..1c9a397 100644 --- a/score.c +++ b/score.c @@ -1,7 +1,7 @@ /* * Scoring and wrap-up. * - * SPDX-FileCopyrightText: Copyright 977, 2005 by Will Crowther and Don Woods, Copyright, 2017 by Eric S. Raymond + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ #include From c11938aed5e261ac40a562781325996628663610 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 28 Jan 2024 07:11:21 -0500 Subject: [PATCH 09/55] Place1TBS mandatory braces. --- actions.c | 381 ++++++++++++++++++++++++++++-------------------------- init.c | 9 +- main.c | 319 +++++++++++++++++++++++++++------------------ score.c | 72 +++++++---- 4 files changed, 438 insertions(+), 343 deletions(-) diff --git a/actions.c b/actions.c index ec62447..fed70b9 100644 --- a/actions.c +++ b/actions.c @@ -14,12 +14,11 @@ static phase_codes_t fill(verb_t, obj_t); -static phase_codes_t attack(command_t command) +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; @@ -137,9 +136,9 @@ static phase_codes_t attack(command_t command) if (obj == OGRE) { rspeak(OGRE_DODGE); - if (atdwrf(game.loc) == 0) + if (atdwrf(game.loc) == 0) { return GO_CLEAROBJ; - + } rspeak(KNIFE_THROWN); DESTROY(OGRE); int dwarves = 0; @@ -185,12 +184,11 @@ static phase_codes_t attack(command_t command) return GO_CLEAROBJ; } -static phase_codes_t bigwords(vocab_t id) +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. */ @@ -219,30 +217,31 @@ static phase_codes_t bigwords(vocab_t id) 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)) + if (HERE(EGGS)) { pspeak(EGGS, look, true, EGGS_VANISHED); - else if (game.loc == objects[EGGS].plac) + } else if (game.loc == objects[EGGS].plac) { pspeak(EGGS, look, true, EGGS_HERE); - else + } 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 + if (settings.oldstyle || game.seenbigwords) { + rspeak(START_OVER); + } else { rspeak(WELL_POINTLESS); + } game.foobar = WORD_EMPTY; return GO_CLEAROBJ; } } -static void blast(void) +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 { @@ -260,9 +259,8 @@ static void blast(void) } } -static phase_codes_t vbreak(verb_t verb, obj_t obj) +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) { @@ -287,26 +285,25 @@ static phase_codes_t vbreak(verb_t verb, obj_t obj) return (GO_CLEAROBJ); } -static phase_codes_t brief(void) +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) +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) + atdwrf(game.loc) > 0) { return GO_UNKNOWN; + } obj = game.locs[game.loc].atloc; } @@ -362,8 +359,9 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) } if (game.objects[BOTTLE].prop == EMPTY_BOTTLE) { return (fill(verb, BOTTLE)); - } else + } else { rspeak(BOTTLE_FULL); + } return GO_CLEAROBJ; } obj = BOTTLE; @@ -399,8 +397,9 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) carry(obj, game.loc); - if (obj == BOTTLE && LIQUID() != NO_OBJECT) + if (obj == BOTTLE && LIQUID() != NO_OBJECT) { game.objects[LIQUID()].place = CARRIED; + } if (GSTONE(obj) && !PROP_IS_FOUND(obj)) { PROP_SET_FOUND(obj); @@ -410,9 +409,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static int chain(verb_t verb) +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); @@ -462,11 +460,10 @@ static int chain(verb_t verb) return GO_CLEAROBJ; } -static phase_codes_t discard(verb_t verb, obj_t obj) +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; } @@ -482,17 +479,19 @@ static phase_codes_t discard(verb_t verb, obj_t obj) 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 (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) + if (k == RUG_HOVER) { k = objects[SAPPH].plac; + } move(RUG + NOBJECTS, k); } } @@ -507,8 +506,9 @@ static phase_codes_t discard(verb_t verb, obj_t obj) return GO_CLEAROBJ; } - if (LIQUID() == obj) + if (LIQUID() == obj) { obj = BOTTLE; + } if (obj == BOTTLE && LIQUID() != NO_OBJECT) { game.objects[LIQUID()].place = LOC_NOWHERE; } @@ -529,8 +529,9 @@ static phase_codes_t discard(verb_t verb, obj_t obj) state_change(VASE, AT(PILLOW) ? VASE_WHOLE : VASE_DROPPED); - if (game.objects[VASE].prop != VASE_WHOLE) + if (game.objects[VASE].prop != VASE_WHOLE) { game.objects[VASE].fixed = IS_FIXED; + } drop(obj, game.loc); return GO_CLEAROBJ; } @@ -548,13 +549,15 @@ static phase_codes_t discard(verb_t verb, obj_t obj) } if (HERE(SNAKE)) { rspeak(BIRD_ATTACKS); - if (game.closed) + if (game.closed) { return GO_DWARFWAKE; + } DESTROY(SNAKE); /* Set game.prop for use by travel options */ game.objects[SNAKE].prop = SNAKE_CHASED; - } else + } else { rspeak(OK_MAN); + } game.objects[BIRD].prop = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED; drop(obj, game.loc); @@ -566,10 +569,9 @@ static phase_codes_t discard(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t drink(verb_t verb, obj_t obj) +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; @@ -596,10 +598,9 @@ static phase_codes_t drink(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t eat(verb_t verb, obj_t obj) +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)) @@ -626,16 +627,18 @@ static phase_codes_t eat(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t extinguish(verb_t verb, obj_t obj) +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; + 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) { @@ -662,26 +665,27 @@ static phase_codes_t extinguish(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t feed(verb_t verb, obj_t obj) +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); + 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 + } else { rspeak(NOTHING_EDIBLE); + } break; case TROLL: rspeak(TROLL_VICES); @@ -690,8 +694,9 @@ static phase_codes_t feed(verb_t verb, obj_t obj) if (HERE(FOOD)) { game.dflag += 2; rspeak(REALLY_MAD); - } else + } else { speak(actions[verb].message); + } break; case BEAR: if (game.objects[BEAR].prop == BEAR_DEAD) { @@ -704,17 +709,19 @@ static phase_codes_t feed(verb_t verb, obj_t obj) game.objects[AXE].fixed = IS_FREE; game.objects[AXE].prop = AXE_HERE; state_change(BEAR, SITTING_BEAR); - } else + } else { rspeak(NOTHING_EDIBLE); + } break; } speak(actions[verb].message); break; case OGRE: - if (HERE(FOOD)) - rspeak(OGRE_FULL); - else - speak(actions[verb].message); + if (HERE(FOOD)) { + rspeak(OGRE_FULL); + } else { + speak(actions[verb].message); + } break; default: rspeak(AM_GAME); @@ -722,10 +729,9 @@ static phase_codes_t feed(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -phase_codes_t fill(verb_t verb, obj_t obj) +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); @@ -793,14 +799,14 @@ phase_codes_t fill(verb_t verb, obj_t obj) state_change(BOTTLE, (LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE); - if (TOTING(BOTTLE)) + if (TOTING(BOTTLE)) { game.objects[LIQUID()].place = CARRIED; + } return GO_CLEAROBJ; } -static phase_codes_t find(verb_t verb, obj_t obj) +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; @@ -822,9 +828,8 @@ static phase_codes_t find(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t fly(verb_t verb, obj_t obj) +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); @@ -865,9 +870,8 @@ static phase_codes_t fly(verb_t verb, obj_t obj) return GO_TERMINATE; } -static phase_codes_t inven(void) +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)) @@ -885,9 +889,8 @@ static phase_codes_t inven(void) return GO_CLEAROBJ; } -static phase_codes_t light(verb_t verb, obj_t obj) +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) { @@ -914,8 +917,9 @@ static phase_codes_t light(verb_t verb, obj_t obj) break; } state_change(LAMP, LAMP_BRIGHT); - if (game.wzdark) + if (game.wzdark) { return GO_TOP; + } break; default: speak(actions[verb].message); @@ -923,53 +927,61 @@ static phase_codes_t light(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t listen(void) +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) + 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; + 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) + if (i == BIRD) { mi += 3 * game.blooded; + } pspeak(i, hear, true, mi, game.zzword); rspeak(NO_MESSAGE); - if (i == BIRD && mi == BIRD_ENDSTATE) + if (i == BIRD && mi == BIRD_ENDSTATE) { DESTROY(BIRD); + } soundlatch = true; } - if (!soundlatch) + if (!soundlatch) { rspeak(ALL_SILENT); + } return GO_CLEAROBJ; } -static phase_codes_t lock(verb_t verb, obj_t obj) +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 (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; @@ -983,32 +995,35 @@ static phase_codes_t lock(verb_t verb, obj_t obj) case CHAIN: if (HERE(KEYS)) { return chain(verb); - } else + } else { rspeak(NO_KEYS); + } break; case GRATE: if (HERE(KEYS)) { if (game.closng) { rspeak(EXIT_CLOSED); - if (!game.panic) + if (!game.panic) { game.clock2 = PANICTIME; + } game.panic = true; } else { state_change(GRATE, (verb == LOCK) ? GRATE_CLOSED : GRATE_OPEN); } - } else + } 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 { + 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); @@ -1016,14 +1031,15 @@ static phase_codes_t lock(verb_t verb, obj_t obj) } 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); + 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); @@ -1041,14 +1057,15 @@ static phase_codes_t lock(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t pour(verb_t verb, obj_t obj) +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 (obj == BOTTLE || obj == INTRANSITIVE) { + obj = LIQUID(); + } + if (obj == NO_OBJECT) { + return GO_UNKNOWN; + } if (!TOTING(obj)) { speak(actions[verb].message); return GO_CLEAROBJ; @@ -1058,8 +1075,9 @@ static phase_codes_t pour(verb_t verb, obj_t obj) rspeak(CANT_POUR); return GO_CLEAROBJ; } - if (HERE(URN) && game.objects[URN].prop == URN_EMPTY) + 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))) { @@ -1084,11 +1102,11 @@ static phase_codes_t pour(verb_t verb, obj_t obj) } } -static phase_codes_t quit(void) +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); + if (yes_or_no(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) { + terminate(quitgame); + } return GO_CLEAROBJ; } @@ -1098,11 +1116,13 @@ static phase_codes_t read(command_t command) 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 (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)) + if (command.obj > NOBJECTS || command.obj == NO_OBJECT || DARK(game.loc)) { return GO_UNKNOWN; + } } if (DARK(game.loc)) { @@ -1117,14 +1137,14 @@ static phase_codes_t read(command_t command) } } else if (objects[command.obj].texts[0] == NULL || PROP_IS_NOTFOUND(command.obj)) { speak(actions[command.verb].message); - } else + } else { pspeak(command.obj, study, true, game.objects[command.obj].prop); + } return GO_CLEAROBJ; } -static phase_codes_t reservoir(void) +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; @@ -1142,9 +1162,8 @@ static phase_codes_t reservoir(void) } } -static phase_codes_t rub(verb_t verb, obj_t obj) +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); @@ -1160,17 +1179,17 @@ static phase_codes_t rub(verb_t verb, obj_t obj) return GO_CLEAROBJ; } -static phase_codes_t say(command_t command) +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) + if (command.word[1].type == ACTION && command.word[1].id == PART) { return reservoir(); + } if (command.word[1].type == ACTION && (command.word[1].id == FEE || @@ -1192,12 +1211,11 @@ static phase_codes_t throw_support(vocab_t spk) return GO_MOVE; } -static phase_codes_t throwit(command_t 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; @@ -1218,16 +1236,18 @@ static phase_codes_t throwit(command_t command) command.obj = BEAR; return (feed(command.verb, command.obj)); } - if (command.obj != AXE) + if (command.obj != AXE) { return (discard(command.verb, command.obj)); - else { + } 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)) + 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); @@ -1253,9 +1273,8 @@ static phase_codes_t throwit(command_t command) } } -static phase_codes_t wake(verb_t verb, obj_t obj) +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; @@ -1265,9 +1284,8 @@ static phase_codes_t wake(verb_t verb, obj_t obj) } } -static phase_codes_t seed(verb_t verb, const char *arg) +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); @@ -1275,17 +1293,15 @@ static phase_codes_t seed(verb_t verb, const char *arg) return GO_TOP; } -static phase_codes_t waste(verb_t verb, turn_t turns) +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) +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))) ? @@ -1325,11 +1341,10 @@ static phase_codes_t wave(verb_t verb, obj_t obj) } } -phase_codes_t action(command_t command) +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 */ @@ -1346,14 +1361,14 @@ phase_codes_t action(command_t command) * they are never actually dropped at any location, but might * be here inside the bottle or urn or as a feature of the * location. */ - if (HERE(command.obj)) - /* 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) { + 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) { @@ -1367,26 +1382,29 @@ phase_codes_t action(command_t command) 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)) + command.verb == INVENTORY) && (command.word[1].id == WORD_EMPTY || command.word[1].id == WORD_NOT_FOUND)) { /* FALL THROUGH */; - else { + } else { sspeak(NO_SEE, command.word[0].raw); return GO_CLEAROBJ; } - if (command.verb != 0) + 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.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) { @@ -1522,10 +1540,9 @@ phase_codes_t action(command_t command) return rub(command.verb, command.obj); case THROW: return throwit(command); - case QUIT: { + case QUIT: speak(actions[command.verb].message); return GO_CLEAROBJ; - } case FIND: return find(command.verb, command.obj); case INVENTORY: @@ -1537,42 +1554,36 @@ phase_codes_t action(command_t command) case BLAST: blast(); return GO_CLEAROBJ; - case SCORE: { + case SCORE: speak(actions[command.verb].message); return GO_CLEAROBJ; - } case FEE: case FIE: case FOE: case FOO: - case FUM: { + case FUM: speak(actions[command.verb].message); return GO_CLEAROBJ; - } - case BRIEF: { + 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: { + case SAVE: speak(actions[command.verb].message); return GO_CLEAROBJ; - } - case RESUME: { + case RESUME: speak(actions[command.verb].message); return GO_CLEAROBJ; - } case FLY: return fly(command.verb, command.obj); - case LISTEN: { + case LISTEN: speak(actions[command.verb].message); return GO_CLEAROBJ; - } // LCOV_EXCL_START // This case should never happen - here only as placeholder case PART: @@ -1593,3 +1604,5 @@ phase_codes_t action(command_t command) BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE } } + +// end diff --git a/init.c b/init.c index a620b2c..94c972d 100644 --- a/init.c +++ b/init.c @@ -56,8 +56,9 @@ int initialise(void) 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) + if (travel[k].motion == HERE) { conditions[i] |= (1 << COND_FORCED); + } } } @@ -77,8 +78,9 @@ int initialise(void) 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) + if (objects[k].plac != 0 && objects[k].fixd <= 0) { drop(k, objects[k].plac); + } } /* Treasure props are initially STATE_NOTFOUND, and are set to @@ -88,8 +90,9 @@ int initialise(void) for (int treasure = 1; treasure <= NOBJECTS; treasure++) { if (objects[treasure].is_treasure) { ++game.tally; - if (objects[treasure].inventory != 0) + if (objects[treasure].inventory != 0) { PROP_SET_NOT_FOUND(treasure); + } } } game.conds = setbit(COND_HBASE); diff --git a/main.c b/main.c index e0fdb69..fd0852f 100644 --- a/main.c +++ b/main.c @@ -38,8 +38,9 @@ static void sig_handler(int signo) } #if defined ADVENT_AUTOSAVE - if (signo == SIGHUP || signo == SIGTERM) + if (signo == SIGHUP || signo == SIGTERM) { autosave(); + } #endif exit(EXIT_FAILURE); } @@ -71,12 +72,14 @@ char *myreadline(const char *prompt) char *next = settings.argv[settings.optind++]; - if (settings.scriptfp != NULL && feof(settings.scriptfp)) + if (settings.scriptfp != NULL && feof(settings.scriptfp)) { fclose(settings.scriptfp); - if (strcmp(next, "-") == 0) + } + if (strcmp(next, "-") == 0) { settings.scriptfp = stdin; // LCOV_EXCL_LINE - else + } else { settings.scriptfp = fopen(next, "r"); + } } if (isatty(fileno(settings.scriptfp))) { @@ -102,10 +105,12 @@ 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)) + 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. */ @@ -115,17 +120,20 @@ static void checkhints(void) switch (hint) { case 0: /* cave */ - if (game.objects[GRATE].prop == GRATE_CLOSED && !HERE(KEYS)) - break; + 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; + if (game.objects[BIRD].place == game.loc && TOTING(ROD) && game.oldobj == BIRD) { + break; + } return; case 2: /* snake */ - if (HERE(SNAKE) && !HERE(BIRD)) - break; + if (HERE(SNAKE) && !HERE(BIRD)) { + break; + } game.hints[hint].lc = 0; return; case 3: /* maze */ @@ -137,15 +145,17 @@ static void checkhints(void) game.hints[hint].lc = 0; return; case 4: /* dark */ - if (!PROP_IS_NOTFOUND(EMERALD) && PROP_IS_NOTFOUND(PYRAMID)) - break; + 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; + if (game.dflag == 0) { + break; + } game.hints[hint].lc = 0; return; case 7: /* woods */ @@ -160,12 +170,14 @@ static void checkhints(void) game.hints[hint].lc = 0; return; } - if (HERE(OGRE) && i == 0) + if (HERE(OGRE) && i == 0) { break; + } return; case 9: /* jade */ - if (game.tally == 1 && PROP_IS_STASHED_OR_UNSEEN(JADE)) - break; + if (game.tally == 1 && PROP_IS_STASHED_OR_UNSEEN(JADE)) { + break; + } game.hints[hint].lc = 0; return; default: // LCOV_EXCL_LINE @@ -175,12 +187,14 @@ static void checkhints(void) /* 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])) + 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) + if (game.hints[hint].used && game.limit > WARNTIME) { game.limit += WARNTIME * hints[hint].penalty; + } } } } @@ -188,8 +202,9 @@ static void checkhints(void) static bool spotted_by_pirate(int i) { - if (i != PIRATE) - return false; + 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 @@ -197,21 +212,24 @@ static bool spotted_by_pirate(int i) * 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)) + 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) + 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)) + if (TOTING(treasure) || HERE(treasure)) { ++snarfed; + } if (TOTING(treasure)) { movechest = true; robplayer = true; @@ -233,20 +251,24 @@ static bool spotted_by_pirate(int i) } 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 (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 (!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); + if (AT(treasure) && game.objects[treasure].fixed == IS_FREE) { + carry(treasure, game.loc); + } + if (TOTING(treasure)) { + drop(treasure, game.chloc); + } } } } @@ -254,9 +276,8 @@ static bool spotted_by_pirate(int i) return true; } -static bool dwarfmove(void) +static bool dwarfmove(void) { /* Dwarves move. Return true if player survives, false if he dies. */ -{ int kk, stick, attack; loc_t tk[21]; @@ -272,8 +293,9 @@ static bool dwarfmove(void) * 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)) + if (game.loc == LOC_NOWHERE || FORCED(game.loc) || CNDBIT(game.newloc, COND_NOARRR)) { return true; + } /* Dwarf activity level ratchets up */ if (game.dflag == 0) { @@ -287,20 +309,23 @@ static bool dwarfmove(void) * replace him with the alternate. */ if (game.dflag == 1) { if (!INDEEP(game.loc) || - (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85)))) + (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)) + 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; // + if (game.dwarves[i].loc == game.loc) { + game.dwarves[i].loc = DALTLC; + } game.dwarves[i].oldloc = game.dwarves[i].loc; } rspeak(DWARF_RAN); @@ -318,8 +343,9 @@ static bool dwarfmove(void) attack = 0; stick = 0; for (int i = 1; i <= NDWARVES; i++) { - if (game.dwarves[i].loc == 0) - continue; + 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]; @@ -351,38 +377,46 @@ static bool dwarfmove(void) } while (!travel[kk++].stop); tk[j] = game.dwarves[i].oldloc; - if (j >= 2) + 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) + if (!game.dwarves[i].seen) { continue; + } game.dwarves[i].loc = game.loc; - if (spotted_by_pirate(i)) + 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) + if (game.knfloc >= LOC_NOWHERE) { game.knfloc = game.loc; - if (randrange(1000) < 95 * (game.dflag - 2)) + } + 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) + if (game.dtotal == 0) { return true; + } rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal); - if (attack == 0) + if (attack == 0) { return true; - if (game.dflag == 2) + } + 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); @@ -390,8 +424,9 @@ static bool dwarfmove(void) rspeak(KNIFE_THROWN); rspeak(stick ? GETS_YOU : MISSES_YOU); } - if (stick == 0) + if (stick == 0) { return true; + } game.oldlc2 = game.loc; return false; } @@ -415,9 +450,8 @@ 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) +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; @@ -451,9 +485,8 @@ static void croak(void) } } -static void describe_location(void) +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) @@ -463,8 +496,9 @@ static void describe_location(void) msg = arbitrary_messages[PITCH_DARK]; } - if (TOTING(BEAR)) + if (TOTING(BEAR)) { rspeak(TAME_BEAR); + } speak(msg); @@ -473,9 +507,8 @@ static void describe_location(void) } -static bool traveleq(int a, int b) +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) @@ -494,11 +527,12 @@ static void playermove(int motion) { int scratchloc, travel_entry = tkey[game.loc]; game.newloc = game.loc; - if (travel_entry == 0) + if (travel_entry == 0) { BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE - if (motion == NUL) + } + if (motion == NUL) { return; - else if (motion == BACK) { + } 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. */ @@ -545,8 +579,9 @@ static void playermove(int motion) /* 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); + if (game.detail < 3) { + rspeak(NO_MORE_DETAIL); + } ++game.detail; game.wzdark = false; game.locs[game.loc].abbrev = 0; @@ -626,8 +661,9 @@ static void playermove(int motion) 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) + } else if (game.objects[condarg1].prop != condarg2) { break; + } /* We arrive here on conditional failure. * Skip to next non-matching destination */ @@ -644,8 +680,9 @@ static void playermove(int motion) /* 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) + if (desttype == dest_goto) { return; + } if (desttype == dest_speak) { /* Execute a speak rule */ @@ -710,8 +747,9 @@ static void playermove(int motion) 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)) + if (!TOTING(BEAR)) { return; + } state_change(CHASM, BRIDGE_WRECKED); game.objects[TROLL].prop = TROLL_GONE; drop(BEAR, game.newloc); @@ -731,11 +769,11 @@ static void playermove(int motion) (false); } -static void lampcheck(void) +static void lampcheck(void) { /* Check game limit and lamp timers */ -{ - if (game.objects[LAMP].prop == LAMP_BRIGHT) - --game.limit; + 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. @@ -752,30 +790,32 @@ static void lampcheck(void) * No tests ever passed the guard, and with the guard removed * the game hangs when the lamp limit is reached. */ - if (TOTING(BATTERY)) + 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) + if (game.objects[BATTERY].prop == DEAD_BATTERIES) { rspeak(MISSING_BATTERIES); - else if (game.objects[BATTERY].place == LOC_NOWHERE) + } else if (game.objects[BATTERY].place == LOC_NOWHERE) { rspeak(LAMP_DIM); - else + } else { rspeak(GET_BATTERIES); + } } } if (game.limit == 0) { game.limit = -1; game.objects[LAMP].prop = LAMP_DARK; - if (HERE(LAMP)) + 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 @@ -794,7 +834,7 @@ static bool closecheck(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) { @@ -805,8 +845,9 @@ static bool closecheck(void) } /* Don't tick game.clock1 unless well into cave (and not at Y2). */ - if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_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 @@ -833,8 +874,9 @@ static bool closecheck(void) move(TROLL2, objects[TROLL].plac); move(TROLL2 + NOBJECTS, objects[TROLL].fixd); juggle(CHASM); - if (game.objects[BEAR].prop != BEAR_DEAD) + 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; @@ -885,8 +927,9 @@ static bool closecheck(void) game.objects[MIRROR].fixed = LOC_SW; for (int i = 1; i <= NOBJECTS; i++) { - if (TOTING(i)) - DESTROY(i); + if (TOTING(i)) { + DESTROY(i); + } } rspeak(CAVE_CLOSED); @@ -898,7 +941,7 @@ static bool closecheck(void) return false; } -static void listobjects(void) +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 @@ -906,29 +949,34 @@ 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)) { ++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) + if (obj > NOBJECTS) { obj = obj - NOBJECTS; - if (obj == STEPS && TOTING(NUGGET)) + } + 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; + if (game.closed) { + continue; + } PROP_SET_FOUND(obj); - if (obj == RUG) + if (obj == RUG) { game.objects[RUG].prop = RUG_DRAGON; - if (obj == CHAIN) + } + if (obj == CHAIN) { game.objects[CHAIN].prop = CHAINING_BEAR; - if (obj == EGGS) + } + 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 @@ -946,16 +994,16 @@ static void listobjects(void) * (so goes the rationalisation). */ } int kk = game.objects[obj].prop; - if (obj == STEPS) + if (obj == STEPS) { kk = (game.loc == game.objects[STEPS].fixed) ? STEPS_UP : STEPS_DOWN; + } pspeak(obj, look, true, kk); } } } -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 @@ -972,13 +1020,14 @@ static bool preprocess_command(command_t *command) * * 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); + if (LIQLOC(game.loc) == WATER) { + rspeak(FEET_WET); + } else { + rspeak(WHERE_QUERY); + } } else { if (command->word[0].type == OBJECT) { /* From OV to VO form */ @@ -1028,15 +1077,15 @@ static bool preprocess_command(command_t *command) return false; } -static bool do_move(void) +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) + if (!game.panic) { game.clock2 = PANICTIME; + } game.panic = true; } @@ -1055,11 +1104,13 @@ static bool do_move(void) } game.loc = game.newloc; - if (!dwarfmove()) + if (!dwarfmove()) { croak(); + } - if (game.loc == LOC_NOWHERE) + if (game.loc == LOC_NOWHERE) { croak(); + } /* The easiest way to get killed is to fall into a pit in * pitch darkness. */ @@ -1073,9 +1124,8 @@ static bool do_move(void) return true; } -static bool do_command(void) +static bool do_command(void) { /* Get and execute a command */ -{ static command_t command; clear_command(&command); @@ -1101,8 +1151,9 @@ static bool do_command(void) * 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); + 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); @@ -1112,8 +1163,9 @@ static bool do_command(void) /* 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) + 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 @@ -1122,8 +1174,9 @@ static bool do_command(void) checkhints(); /* Get command input from user */ - if (!get_command_input(&command)) + 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 @@ -1136,8 +1189,9 @@ static bool do_command(void) } /* check if game is closed, and exit if it is */ - if (closecheck() ) + if (closecheck()) { return true; + } /* loop until all words in command are processed */ while (command.state == PREPROCESSED ) { @@ -1152,12 +1206,14 @@ static bool do_command(void) /* Give user hints of shortcuts */ if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) { - if (++game.iwest == 10) - rspeak(W_IS_WEST); + 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); + if (++game.igo == 10) { + rspeak(GO_UNNEEDED); + } } switch (command.word[0].type) { @@ -1170,10 +1226,11 @@ static bool do_command(void) command.obj = command.word[0].id; break; case ACTION: - if (command.word[1].type == NUMERIC) - command.part = transitive; - else - command.part = intransitive; + if (command.word[1].type == NUMERIC) { + command.part = transitive; + } else { + command.part = intransitive; + } command.verb = command.word[0].id; break; case NUMERIC: @@ -1275,10 +1332,11 @@ int main(int argc, char *argv[]) break; // LCOV_EXCL_LINE case 'l': settings.logfp = fopen(optarg, "w"); - if (settings.logfp == NULL) + if (settings.logfp == NULL) { fprintf(stderr, "advent: can't open logfile %s for write\n", optarg); + } signal(SIGINT, sig_handler); break; case 'o': @@ -1295,10 +1353,11 @@ int main(int argc, char *argv[]) #elif !defined ADVENT_NOSAVE case 'r': rfp = fopen(optarg, "r"); - if (rfp == NULL) + if (rfp == NULL) { fprintf(stderr, "advent: can't open save file %s for read\n", optarg); + } break; #endif default: @@ -1331,8 +1390,9 @@ int main(int argc, char *argv[]) #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) + if (game.novice) { game.limit = NOVICELIMIT; + } } else { restore(rfp); #if defined ADVENT_AUTOSAVE @@ -1354,18 +1414,21 @@ int main(int argc, char *argv[]) game.limit = NOVICELIMIT; #endif - if (settings.logfp) + 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; + if (!do_move()) { + continue; + } - // get command - if (!do_command()) - break; + // get command + if (!do_command()) { + break; + } } /* show score and exit */ terminate(quitgame); diff --git a/score.c b/score.c index 1c9a397..473ec6a 100644 --- a/score.c +++ b/score.c @@ -10,10 +10,9 @@ static int mxscor; /* ugh..the price for having score() not exit. */ -int score(enum termination mode) +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: @@ -39,18 +38,23 @@ int score(enum termination mode) * 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].is_treasure) { + continue; + } if (objects[i].inventory != 0) { int k = 12; - if (i == CHEST) + if (i == CHEST) { k = 14; - if (i > CHEST) + } + if (i > CHEST) { k = 16; - if (!PROP_IS_STASHED(i) && !PROP_IS_NOTFOUND(i)) + } + if (!PROP_IS_STASHED(i) && !PROP_IS_NOTFOUND(i)) { score += 2; - if (game.objects[i].place == LOC_BUILDING && PROP_IS_FOUND(i)) + } + if (game.objects[i].place == LOC_BUILDING && PROP_IS_FOUND(i)) { score += k - 2; + } mxscor += k; } } @@ -63,30 +67,38 @@ int score(enum termination mode) * mundane exits or 133, 134, 135 if he blew it (so to speak). */ score += (NDEATHS - game.numdie) * 10; mxscor += NDEATHS * 10; - if (mode == endgame) + if (mode == endgame) { score += 4; + } mxscor += 4; - if (game.dflag != 0) + if (game.dflag != 0) { score += 25; + } mxscor += 25; - if (game.closng) + 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; + 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) + if (game.objects[MAGAZINE].place == LOC_WITTSEND) { score += 1; + } mxscor += 1; /* Round it off. */ @@ -95,13 +107,16 @@ int score(enum termination mode) /* 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.hints[i].used) { + score = score - hints[i].penalty; + } } - if (game.novice) + if (game.novice) { score -= 5; - if (game.clshnt) + } + if (game.clshnt) { score -= 10; + } score = score - game.trnluz - game.saved; /* Return to score command if that's where we came from. */ @@ -112,18 +127,19 @@ int score(enum termination mode) return score; } -void terminate(enum termination 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(); #endif - if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0) + if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0) { rspeak(TOOK_LONG); - if (points + game.saved + 1 >= mxscor && game.saved != 0) + } + 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) { From be429016afc256ce6731867ffed6f2d6f2d876f2 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sat, 27 Jan 2024 06:17:02 -0500 Subject: [PATCH 10/55] 1TBS reflow with clang-format. --- Makefile | 3 + actions.c | 2920 +++++++++++++++++++++++++------------------------- advent.h | 393 +++---- cheat.c | 154 ++- init.c | 141 ++- main.c | 2570 +++++++++++++++++++++++--------------------- misc.c | 1220 ++++++++++----------- saveresume.c | 414 +++---- score.c | 258 ++--- 9 files changed, 4137 insertions(+), 3936 deletions(-) diff --git a/Makefile b/Makefile index 49c909e..9c837d2 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,9 @@ cheat: $(CHEAT_OBJS) dungeon.o check: advent cheat cd tests; $(MAKE) --quiet +reflow: + @clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]") + # Requires gcov, lcov, libasan6, and libubsan1 # The last two are Ubuntu names, might vary on other distributions. # After this, run your browser on coverage/open-adventure/index.html diff --git a/actions.c b/actions.c index fed70b9..97e5a48 100644 --- a/actions.c +++ b/actions.c @@ -5,1604 +5,1642 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include #include +#include +#include +#include #include "advent.h" 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; + /* 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 == 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; - } - - 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; + 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; } - 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; + 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; + } + + 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; + } + + 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 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); + /* 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; } - 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); - } + /* 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 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); + /* 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; + /* 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; + /* 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; } - obj = game.locs[game.loc].atloc; - } - if (TOTING(obj)) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } + if (TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } - if (obj == MESSAG) { - rspeak(REMOVE_MESSAGE); - DESTROY(MESSAG); - 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 (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 (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 (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); + } - 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); - carry(obj, game.loc); + if (obj == BOTTLE && LIQUID() != NO_OBJECT) { + game.objects[LIQUID()].place = CARRIED; + } - 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; + if (GSTONE(obj) && !PROP_IS_FOUND(obj)) { + PROP_SET_FOUND(obj); + game.objects[CAVITY].prop = CAVITY_EMPTY; + } + rspeak(OK_MAN); + 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; + /* 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; - 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; - } + 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 (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[CHAIN].prop != CHAIN_HEAP) { + rspeak(ALREADY_LOCKED); + return GO_CLEAROBJ; + } + if (game.loc != objects[CHAIN].plac) { + rspeak(NO_LOCKSITE); + return GO_CLEAROBJ; + } - game.objects[CHAIN].prop = CHAIN_FIXED; + game.objects[CHAIN].prop = CHAIN_FIXED; - if (TOTING(CHAIN)) - drop(CHAIN, game.loc); - game.objects[CHAIN].fixed = IS_FIXED; + if (TOTING(CHAIN)) + drop(CHAIN, game.loc); + game.objects[CHAIN].fixed = IS_FIXED; - rspeak(CHAIN_LOCKED); - return GO_CLEAROBJ; + rspeak(CHAIN_LOCKED); + 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; - } - - if (!TOTING(obj)) { - speak(actions[verb].message); - 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 == 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); + /* 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; } - game.objects[BIRD].prop = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED; - drop(obj, game.loc); - return GO_CLEAROBJ; - } + if (!TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } - rspeak(OK_MAN); - drop(obj, game.loc); - 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 == 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; } 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; - } + /* 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 == 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; - } + 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; + 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; + /* 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; - } + /* Extinguish. Lamp, urn, dragon/volcano (nice try). */ if (obj == INTRANSITIVE) { - return GO_UNKNOWN; + 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; + 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 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); + /* 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); } - 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; + 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; - } + /* 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 (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; - } + 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; + state_change(BOTTLE, + (LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE); + if (TOTING(BOTTLE)) { + game.objects[LIQUID()].place = CARRIED; + } + 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; - } + /* Find. Might be carrying it, or it might be here. Else give caveat. + */ + if (TOTING(obj)) { + rspeak(ALREADY_CARRYING); + return GO_CLEAROBJ; + } - if (game.closed) { - rspeak(NEEDED_NEARBY); - return GO_CLEAROBJ; - } + if (game.closed) { + rspeak(NEEDED_NEARBY); + 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; - } + 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; + speak(actions[verb].message); + 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; - } + /* 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 (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; + 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; + /* 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; + /* 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; } - break; - default: - speak(actions[verb].message); - } - return GO_CLEAROBJ; + + 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); + /* 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; } - 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; + 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; } - 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; + if (!soundlatch) { + rspeak(ALL_SILENT); } - 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; + 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; + /* 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; } - 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; + /* 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 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; - } + /* 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; - } + 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; + /* 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 && !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 (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 (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; + 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; - } - } + /* 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; + /* 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(); - } + /* 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)); - } + /* 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; - } + /* 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; + /* 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; + /* 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; - } + /* 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 - && 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); + 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; - } - - 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; + /* 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; } - } - switch (command.part) { - case intransitive: - if (command.word[1].raw[0] != '\0' && command.verb != SAY) { - return GO_WORD2; + 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 == 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; + + 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 } - 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.h b/advent.h index 55518cb..b263645 100644 --- a/advent.h +++ b/advent.h @@ -4,11 +4,11 @@ * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include -#include -#include -#include #include "dungeon.h" @@ -18,29 +18,30 @@ #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 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 @@ -67,39 +68,45 @@ * 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) +#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. + * 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 +#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 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 @@ -116,62 +123,72 @@ * 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 + * 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 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) +#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 */ @@ -183,75 +200,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 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; + /* 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 { + 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? + 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 + 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 }; /* @@ -259,56 +276,64 @@ 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; @@ -318,13 +343,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/cheat.c b/cheat.c index 279bcd9..4e94025 100644 --- a/cheat.c +++ b/cheat.c @@ -7,93 +7,90 @@ * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include #include "advent.h" +#include +#include +#include +#include +#include -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 @@ -102,12 +99,7 @@ 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/init.c b/init.c index 94c972d..d00255d 100644 --- a/init.c +++ b/init.c @@ -5,97 +5,86 @@ * 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]; - } - - 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); - } - } - } - - /* 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 <= NDWARVES; i++) { + game.dwarves[i].loc = dwarflocs[i - 1]; } - } - /* 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); + for (int i = 1; i <= NOBJECTS; i++) { + game.objects[i].place = LOC_NOWHERE; + } - return seedval; + 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); + } + } + + 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. */ + 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; } diff --git a/main.c b/main.c index fd0852f..cff0780 100644 --- a/main.c +++ b/main.c @@ -3,432 +3,460 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include +#include "advent.h" +#include +#include #include #include +#include +#include +#include #include -#include #include -#include -#include "advent.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); - } - 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); +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; - } - } - } + } - return NULL; + 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; + } + } + } + + 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 (!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 - } + 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; + /* 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; + } + } } - 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 - * 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; + /* 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; } - 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); + 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)) { - drop(treasure, game.chloc); + 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]; + /* 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; - } - - /* 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; + /* 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; } - 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; - } + /* Dwarf activity level ratchets up */ + if (game.dflag == 0) { + if (INDEEP(game.loc)) + game.dflag = 1; + 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; - } - } - } + /* 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; + } + } - /* 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; + /* 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; + } + } + } + + /* 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." @@ -451,69 +479,73 @@ static bool dwarfmove(void) { * 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; + /* 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; + /* 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) && DARK(game.loc)) { - 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); + /* 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 @@ -523,297 +555,342 @@ 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; - } - - 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); +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 } - ++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; - } - - /* (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; + 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; } - /* 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; - } - - 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; + 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; + } } - 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); + + 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; + } + + /* (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; + } + + /* 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); } 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; -#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); - } -#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); + /* 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; +#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); + } +#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); + } } - } } /* Handle the closing of the cave. The cave closes "clock1" turns @@ -835,464 +912,503 @@ static void lampcheck(void) { * 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); - } - } - - /* 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); + /* 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); + } } - 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; + /* 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; + } - for (int i = 1; i <= NOBJECTS; i++) { - if (TOTING(i)) { - DESTROY(i); - } - } + /* 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); - rspeak(CAVE_CLOSED); - game.closed = true; - return game.closed; - } + put(MIRROR, LOC_NE, STATE_FOUND); + game.objects[MIRROR].fixed = LOC_SW; - lampcheck(); - return false; + for (int i = 1; i <= NOBJECTS; i++) { + if (TOTING(i)) { + DESTROY(i); + } + } + + rspeak(CAVE_CLOSED); + game.closed = true; + return game.closed; + } + + 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 (!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; + /* 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); } - 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: +/* 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. */ + * 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); + 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 { - rspeak(WHERE_QUERY); + 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 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; } - } 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 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; + 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 (OUTSID(game.newloc) && game.newloc != 0 && game.closng) { - rspeak(EXIT_CLOSED); - game.newloc = game.loc; - if (!game.panic) { - game.clock2 = PANICTIME; + /* 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; } - 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) && DARK(game.loc) && 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); + /* 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; - } - - 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) { - - 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. 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; - } - - /* 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; + if (FORCED(game.loc)) { + playermove(HERE); + return true; } - /* 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; + listobjects(); - ++game.turns; - preprocess_command(&command); - } + /* Command not yet given; keep getting commands from user + * until valid command is both given and executed. */ + clear_command(&command); + while (command.state <= GIVEN) { - /* check if game is closed, and exit if it is */ - if (closecheck()) { - return true; - } + 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); + } + } - /* loop until all words in command are processed */ - while (command.state == PREPROCESSED ) { - command.state = PROCESSING; + /* 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 (command.word[0].id == WORD_NOT_FOUND) { - /* Gee, I don't understand. */ - sspeak(DONT_KNOW, command.word[0].raw); - clear_command(&command); - continue; - } + /* 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(); - /* 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); - } - } + /* Get command input from user */ + if (!get_command_input(&command)) { + return false; + } - 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 - } + /* 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; - switch (action(command)) { - case GO_TERMINATE: - command.state = EXECUTED; - break; - case GO_MOVE: - playermove(NUL); - command.state = EXECUTED; - break; - case GO_WORD2: + ++game.turns; + preprocess_command(&command); + } + + /* 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; + + 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); + } + } + + 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: #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; } /* @@ -1306,132 +1422,140 @@ 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); - } - - /* interpret commands until EOF or interrupt */ - for (;;) { - // if we're supposed to move, move - if (!do_move()) { - continue; + if (settings.logfp) { + fprintf(settings.logfp, "seed %d\n", seedval); } - // get command - if (!do_command()) { - break; + /* 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); + /* show score and exit */ + terminate(quitgame); } /* end */ diff --git a/misc.c b/misc.c index c853237..ad110d9 100644 --- a/misc.c +++ b/misc.c @@ -5,746 +5,754 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#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; -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); - for (;;) { - speak(question); + 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* 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; + } - 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) { + speak(yes_response); + outcome = true; + break; + } else if (no == 0 || n == 0) { + speak(no_response); + outcome = false; + break; + } else + rspeak(PLEASE_ANSWER); + } - 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); + 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. 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); + /* 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. - */ - 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]); - } + /* (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 (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 (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 != 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]; + if (from != LOC_NOWHERE && from != CARRIED) { + carry(object, from); + } + drop(object, where); } -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 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 } -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; +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; - 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; + 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; } /* 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/saveresume.c b/saveresume.c index 34404c6..ea30e4d 100644 --- a/saveresume.c +++ b/saveresume.c @@ -8,243 +8,263 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include -#include #include -#include #include "advent.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); -} - -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; -#endif - 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; - } - - 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); -} - -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; -#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); + savefile(fp); + fclose(fp); + rspeak(RESUME_HELP); 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 - */ +int resume(void) { + /* Resume. Read a suspended game back from a file. + * If ADVENT_NOSAVE is defined, gripe instead. */ - /* Prevent division by zero */ - if (valgame.abbnum == 0) { - return false; // LCOV_EXCL_LINE - } +#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE + rspeak(SAVERESUME_DISABLED); + return GO_TOP; +#endif + FILE *fp = NULL; - /* Check for RNG overflow. Truncate */ - if (valgame.lcg_x >= LCG_M) { - valgame.lcg_x %= LCG_M; // LCOV_EXCL_LINE - } + 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; + } + } - /* Check for RNG underflow. Transpose */ - if (valgame.lcg_x < LCG_M) { - valgame.lcg_x = LCG_M + (valgame.lcg_x % LCG_M); - } + 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); + } - /* 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 - } - } + return restore(fp); +} - 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 - } - } +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; +#endif - /* Bounds check for dwarves */ - if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES || - valgame.dkill < 0 || valgame.dkill > NDWARVES) { - return false; // LCOV_EXCL_LINE - } + 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; +} - /* Validate that we didn't die too many times in save */ - if (valgame.numdie >= NDEATHS) { - return false; // LCOV_EXCL_LINE - } +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 + */ - /* 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 - } + /* Prevent division by zero */ + if (valgame.abbnum == 0) { + 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 - } - } + /* Check for RNG overflow. Truncate */ + if (valgame.lcg_x >= LCG_M) { + valgame.lcg_x %= LCG_M; // 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 for RNG underflow. Transpose */ + if (valgame.lcg_x < LCG_M) { + valgame.lcg_x = LCG_M + (valgame.lcg_x % LCG_M); + } - return true; + /* 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 + } + } + + 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 dwarves */ + if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES || + valgame.dkill < 0 || valgame.dkill > NDWARVES) { + 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 + } + + /* 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 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 + } + } + + /* 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 473ec6a..1d84c79 100644 --- a/score.c +++ b/score.c @@ -4,157 +4,159 @@ * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods * 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; + /* 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; + /* 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; + } } - 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; + /* 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; } - if (game.bonus == splatter) { - score += 25; + mxscor += 4; + if (game.dflag != 0) { + score += 25; } - if (game.bonus == defeat) { - score += 30; + mxscor += 25; + if (game.closng) { + score += 25; } - if (game.bonus == victory) { - score += 45; + 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; + mxscor += 45; - /* 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; - - /* 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; + /* Did he come to Witt's End as he should? */ + if (game.objects[MAGAZINE].place == LOC_WITTSEND) { + score += 1; } - } - if (game.novice) { - score -= 5; - } - if (game.clshnt) { - score -= 10; - } - score = score - game.trnluz - game.saved; + mxscor += 1; - /* Return to score command if that's where we came from. */ - if (mode == scoregame) { - rspeak(GARNERED_POINTS, score, mxscor, game.turns, game.turns); - } + /* Round it off. */ + score += 2; + mxscor += 2; - return score; + /* 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 score; } void terminate(enum termination mode) { -/* End of game. Let's tell him all about it. */ - int points = score(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 */ From 0175344caaadc9a7c80c17834f8b21b490a102a1 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 29 Jan 2024 12:14:56 -0500 Subject: [PATCH 11/55] 1TBS reflow, the bracening. --- actions.c | 50 +++++++++++++++++++------------ advent.h | 6 ++-- init.c | 3 +- main.c | 83 +++++++++++++++++++++++++++++++++------------------- misc.c | 42 +++++++++++++++++--------- saveresume.c | 6 ++-- 6 files changed, 122 insertions(+), 68 deletions(-) diff --git a/actions.c b/actions.c index 97e5a48..31a09c8 100644 --- a/actions.c +++ b/actions.c @@ -66,8 +66,9 @@ static phase_codes_t attack(command_t command) { ++changes; } } - if (changes >= 2) + if (changes >= 2) { return GO_UNKNOWN; + } } if (obj == BIRD) { @@ -128,8 +129,9 @@ static phase_codes_t attack(command_t command) { 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) + game.objects[i].place == objects[DRAGON].fixd) { move(i, LOC_SECRET5); + } } game.loc = LOC_SECRET5; return GO_MOVE; @@ -215,8 +217,9 @@ static phase_codes_t bigwords(vocab_t id) { * 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_UNPAID) { game.objects[TROLL].prop = TROLL_PAIDONCE; + } if (HERE(EGGS)) { pspeak(EGGS, look, true, EGGS_VANISHED); } else if (game.loc == objects[EGGS].plac) { @@ -243,9 +246,9 @@ 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 (PROP_IS_NOTFOUND(ROD2) || !game.closed) { rspeak(REQUIRES_DYNAMITE); - else { + } else { if (HERE(ROD2)) { game.bonus = splatter; rspeak(SPLATTER_MESSAGE); @@ -274,8 +277,9 @@ static phase_codes_t vbreak(verb_t verb, obj_t obj) { } case VASE: if (game.objects[VASE].prop == VASE_WHOLE) { - if (TOTING(VASE)) + if (TOTING(VASE)) { drop(VASE, game.loc); + } state_change(VASE, VASE_BROKEN); game.objects[VASE].fixed = IS_FIXED; break; @@ -435,8 +439,9 @@ static int chain(verb_t verb) { } game.objects[CHAIN].prop = CHAIN_HEAP; game.objects[CHAIN].fixed = IS_FREE; - if (game.objects[BEAR].prop != BEAR_DEAD) + if (game.objects[BEAR].prop != BEAR_DEAD) { game.objects[BEAR].prop = CONTENTED_BEAR; + } switch (game.objects[BEAR].prop) { // LCOV_EXCL_START @@ -465,8 +470,9 @@ static int chain(verb_t verb) { game.objects[CHAIN].prop = CHAIN_FIXED; - if (TOTING(CHAIN)) + if (TOTING(CHAIN)) { drop(CHAIN, game.loc); + } game.objects[CHAIN].fixed = IS_FIXED; rspeak(CHAIN_LOCKED); @@ -622,8 +628,9 @@ static phase_codes_t eat(verb_t verb, obj_t obj) { */ switch (obj) { case INTRANSITIVE: - if (!HERE(FOOD)) + if (!HERE(FOOD)) { return GO_UNKNOWN; + } /* FALLTHRU */ case FOOD: DESTROY(FOOD); @@ -797,8 +804,9 @@ phase_codes_t fill(verb_t verb, obj_t obj) { speak(actions[verb].message); return GO_CLEAROBJ; } - if (obj == INTRANSITIVE && !HERE(BOTTLE)) + if (obj == INTRANSITIVE && !HERE(BOTTLE)) { return GO_UNKNOWN; + } if (HERE(URN) && game.objects[URN].prop != URN_EMPTY) { rspeak(URN_NOPOUR); @@ -891,18 +899,21 @@ static phase_codes_t inven(void) { * burden. */ bool empty = true; for (obj_t i = 1; i <= NOBJECTS; i++) { - if (i == BEAR || !TOTING(i)) + if (i == BEAR || !TOTING(i)) { continue; + } if (empty) { rspeak(NOW_HOLDING); empty = false; } pspeak(i, touch, false, -1); } - if (TOTING(BEAR)) + if (TOTING(BEAR)) { rspeak(TAME_BEAR); - if (empty) + } + if (empty) { rspeak(NO_CARRY); + } return GO_CLEAROBJ; } @@ -919,8 +930,9 @@ static phase_codes_t light(verb_t verb, obj_t obj) { obj = URN; selects++; } - if (selects != 1) + if (selects != 1) { return GO_UNKNOWN; + } } switch (obj) { @@ -1181,9 +1193,9 @@ static phase_codes_t reservoir(void) { state_change(RESER, game.objects[RESER].prop == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED); - if (AT(RESER)) + if (AT(RESER)) { return GO_CLEAROBJ; - else { + } else { game.oldlc2 = game.loc; game.newloc = LOC_NOWHERE; rspeak(NOT_BRIGHT); @@ -1266,8 +1278,9 @@ static phase_codes_t throwit(command_t command) { } else { if (atdwrf(game.loc) <= 0) { if (AT(DRAGON) && - game.objects[DRAGON].prop == DRAGON_BARS) + game.objects[DRAGON].prop == DRAGON_BARS) { return throw_support(DRAGON_SCALES); + } if (AT(TROLL)) { return throw_support(TROLL_RETURNS); } @@ -1357,10 +1370,11 @@ static phase_codes_t wave(verb_t verb, obj_t obj) { : FREE_FLY); return GO_CLEAROBJ; } - if (HERE(BIRD)) + if (HERE(BIRD)) { rspeak((game.objects[BIRD].prop == BIRD_CAGED) ? CAGE_FLY : FREE_FLY); + } state_change(FISSURE, game.objects[FISSURE].prop == BRIDGED ? UNBRIDGED diff --git a/advent.h b/advent.h index b263645..2a23eb9 100644 --- a/advent.h +++ b/advent.h @@ -221,9 +221,9 @@ struct game_t { /* 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) */ + * 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; int32_t dkill; // dwarves killed diff --git a/init.c b/init.c index d00255d..e413fff 100644 --- a/init.c +++ b/init.c @@ -27,8 +27,9 @@ struct game_t game = { }; int initialise(void) { - if (settings.oldstyle) + if (settings.oldstyle) { printf("Initialising...\n"); + } srand(time(NULL)); int seedval = (int)rand(); diff --git a/main.c b/main.c index cff0780..7f5d8a1 100644 --- a/main.c +++ b/main.c @@ -31,8 +31,9 @@ void autosave(void) { // exclude from coverage analysis because it requires interactivity to test static void sig_handler(int signo) { if (signo == SIGINT) { - if (settings.logfp != NULL) + if (settings.logfp != NULL) { fflush(settings.logfp); + } } #if defined ADVENT_AUTOSAVE @@ -145,8 +146,9 @@ static void checkhints(void) { NO_OBJECT && game.locs[game.oldlc2].atloc == NO_OBJECT && - game.holdng > 1) + game.holdng > 1) { break; + } game.hints[hint].lc = 0; return; case 4: /* dark */ @@ -170,8 +172,9 @@ static void checkhints(void) { game.locs[game.oldloc].atloc == NO_OBJECT && game.locs[game.oldlc2].atloc == - NO_OBJECT) + NO_OBJECT) { break; + } return; case 8: /* ogre */ i = atdwrf(game.loc); @@ -323,8 +326,9 @@ static bool dwarfmove(void) { /* Dwarf activity level ratchets up */ if (game.dflag == 0) { - if (INDEEP(game.loc)) + if (INDEEP(game.loc)) { game.dflag = 1; + } return true; } @@ -373,33 +377,36 @@ static bool dwarfmove(void) { /* Fill tk array with all the places this dwarf might go. */ unsigned int j = 1; kk = tkey[game.dwarves[i].loc]; - if (kk != 0) + 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) + if (desttype != dest_goto) { continue; - else if (!INDEEP(game.newloc)) + } else if (!INDEEP(game.newloc)) { continue; - else if (game.newloc == game.dwarves[i].oldloc) + } else if (game.newloc == + game.dwarves[i].oldloc) { continue; - else if (j > 1 && game.newloc == tk[j - 1]) + } else if (j > 1 && game.newloc == tk[j - 1]) { continue; - else if (j >= DIM(tk) - 1) + } else if (j >= DIM(tk) - 1) { /* This can't actually happen. */ continue; // LCOV_EXCL_LINE - else if (game.newloc == game.dwarves[i].loc) + } else if (game.newloc == game.dwarves[i].loc) { continue; - else if (FORCED(game.newloc)) + } else if (FORCED(game.newloc)) { continue; - else if (i == PIRATE && - CNDBIT(game.newloc, COND_NOARRR)) + } else if (i == PIRATE && + CNDBIT(game.newloc, COND_NOARRR)) { continue; - else if (travel[kk].nodwarves) + } else if (travel[kk].nodwarves) { continue; + } tk[j++] = game.newloc; } while (!travel[kk++].stop); + } tk[j] = game.dwarves[i].oldloc; if (j >= 2) { --j; @@ -502,8 +509,9 @@ static void croak(void) { * where he died. */ game.objects[WATER].place = game.objects[OIL].place = LOC_NOWHERE; - if (TOTING(LAMP)) + if (TOTING(LAMP)) { game.objects[LAMP].prop = LAMP_DARK; + } for (int j = 1; j <= NOBJECTS; j++) { int i = NOBJECTS + 1 - j; if (TOTING(i)) { @@ -521,8 +529,9 @@ static void describe_location(void) { const char *msg = locations[game.loc].description.small; if (MOD(game.locs[game.loc].abbrev, game.abbnum) == 0 || - msg == NO_MESSAGE) + msg == NO_MESSAGE) { msg = locations[game.loc].description.big; + } if (!FORCED(game.loc) && DARK(game.loc)) { msg = arbitrary_messages[PITCH_DARK]; @@ -534,8 +543,9 @@ static void describe_location(void) { speak(msg); - if (game.loc == LOC_Y2 && PCT(25) && !game.closng) + if (game.loc == LOC_Y2 && PCT(25) && !game.closng) { rspeak(SAYS_PLUGH); + } } static bool traveleq(int a, int b) { @@ -569,8 +579,9 @@ static void playermove(int motion) { * forced-motion. te_tmp saves entry -> forced loc -> previous * loc. */ motion = game.oldloc; - if (FORCED(motion)) + if (FORCED(motion)) { motion = game.oldlc2; + } game.oldlc2 = game.oldloc; game.oldloc = game.loc; if (CNDBIT(game.loc, COND_NOBACK)) { @@ -591,8 +602,9 @@ static void playermove(int motion) { if (desttype == dest_goto) { if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == - motion) + motion) { te_tmp = travel_entry; + } } if (!travel[travel_entry].stop) { ++travel_entry; /* go to next travel @@ -641,8 +653,9 @@ static void playermove(int motion) { * indexes the beginning of the motion entries for here (game.loc). */ for (;;) { if ((travel[travel_entry].motion == HERE) || - travel[travel_entry].motion == motion) + 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. @@ -699,15 +712,17 @@ static void playermove(int motion) { if (condtype == cond_goto || condtype == cond_pct) { if (condarg1 == 0 || - PCT(condarg1)) + PCT(condarg1)) { break; + } /* else fall through */ } /* YAML [with OBJ] clause */ else if (TOTING(condarg1) || (condtype == cond_with && - AT(condarg1))) + AT(condarg1))) { break; + } /* else fall through to check [not OBJ * STATE] */ } else if (game.objects[condarg1].prop != @@ -719,8 +734,9 @@ static void playermove(int motion) { * Skip to next non-matching destination */ int te_tmp = travel_entry; do { - if (travel[te_tmp].stop) + 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; @@ -771,8 +787,10 @@ static void playermove(int motion) { { int te_tmp = travel_entry; do { - if (travel[te_tmp].stop) + if (travel[te_tmp] + .stop) { BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE + } ++te_tmp; } while (traveleq(travel_entry, te_tmp)); @@ -814,10 +832,11 @@ static void playermove(int motion) { objects[TROLL].fixd - game.loc; if (game.objects[TROLL].prop == - TROLL_UNPAID) + TROLL_UNPAID) { game.objects[TROLL] .prop = TROLL_PAIDONCE; + } if (!TOTING(BEAR)) { return; } @@ -962,8 +981,9 @@ static bool closecheck(void) { game.clock1 = -1; game.closng = true; return game.closed; - } else if (game.clock1 < 0) + } 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, @@ -1159,8 +1179,9 @@ static bool preprocess_command(command_t *command) { /* If no word type is given for the first word, we assume it's a * motion. */ - if (command->word[0].type == NO_WORD_TYPE) + if (command->word[0].type == NO_WORD_TYPE) { command->word[0].type = MOTION; + } command->state = PREPROCESSED; return true; @@ -1252,9 +1273,10 @@ static bool do_command(void) { } for (size_t i = 1; i <= NOBJECTS; i++) { if (TOTING(i) && (PROP_IS_NOTFOUND(i) || - PROP_IS_STASHED(i))) + PROP_IS_STASHED(i))) { game.objects[i].prop = PROP_STASHED(i); + } } } @@ -1534,8 +1556,9 @@ int main(int argc, char *argv[]) { game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); - if (game.novice) + if (game.novice) { game.limit = NOVICELIMIT; + } #endif if (settings.logfp) { diff --git a/misc.c b/misc.c index ad110d9..9e90d05 100644 --- a/misc.c +++ b/misc.c @@ -195,7 +195,7 @@ static int word_count(char *str) { int count = 0; int inblanks = true; - for (char *s = str; *s; s++) + for (char *s = str; *s; s++) { if (inblanks) { if (strchr(delims, *s) == 0) { ++count; @@ -206,6 +206,7 @@ static int word_count(char *str) { inblanks = true; } } + } return (count); } @@ -213,8 +214,9 @@ static int word_count(char *str) { static char *get_input(void) { // Set up the prompt char input_prompt[] = PROMPT; - if (!settings.prompt) + if (!settings.prompt) { input_prompt[0] = '\0'; + } // Print a blank line printf("\n"); @@ -223,8 +225,9 @@ static char *get_input(void) { for (;;) { input = myreadline(input_prompt); - if (input == NULL) // Got EOF; return with it. + if (input == NULL) { // Got EOF; return with it. return (input); + } if (input[0] == '#') { // Ignore comments. free(input); continue; @@ -238,11 +241,13 @@ static char *get_input(void) { add_history(input); - if (!isatty(0)) + if (!isatty(0)) { echo_input(stdout, input_prompt, input); + } - if (settings.logfp) + if (settings.logfp) { echo_input(settings.logfp, "", input); + } return (input); } @@ -270,8 +275,9 @@ bool silent_yes_or_no(void) { free(reply); - for (int i = 0; i < (int)strlen(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); @@ -286,8 +292,9 @@ bool silent_yes_or_no(void) { } else if (no == 0 || n == 0) { outcome = false; break; - } else + } else { rspeak(PLEASE_ANSWER); + } } return (outcome); } @@ -340,8 +347,9 @@ bool yes_or_no(const char *question, const char *yes_response, speak(no_response); outcome = false; break; - } else + } else { rspeak(PLEASE_ANSWER); + } } return (outcome); @@ -357,8 +365,9 @@ static int get_motion_vocab_id(const char *word) { TOKLEN) == 0 && (strlen(word) > 1 || strchr(ignore, word[0]) == NULL || - !settings.oldstyle)) + !settings.oldstyle)) { return (i); + } } } // If execution reaches here, we didn't find the word. @@ -372,8 +381,9 @@ static int get_object_vocab_id(const char *word) { // removed for (int j = 0; j < objects[i].words.n; ++j) { if (strncasecmp(word, objects[i].words.strs[j], - TOKLEN) == 0) + TOKLEN) == 0) { return (i); + } } } // If execution reaches here, we didn't find the word. @@ -529,8 +539,9 @@ bool get_command_input(command_t *command) { for (;;) { input = get_input(); - if (input == NULL) + if (input == NULL) { return false; + } if (word_count(input) > 2) { rspeak(TWO_WORDS); free(input); @@ -637,8 +648,9 @@ void carry(obj_t object, loc_t where) { * * Possibly this check should be skipped whwn oldstyle is on. */ - if (object != BIRD) + if (object != BIRD) { ++game.holdng; + } } if (game.locs[where].atloc == object) { game.locs[where].atloc = game.link[object]; @@ -658,8 +670,8 @@ void drop(obj_t object, loc_t where) { if (object > NOBJECTS) { game.objects[object - NOBJECTS].fixed = where; } else { - if (game.objects[object].place == CARRIED) - if (object != BIRD) + 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 @@ -668,6 +680,8 @@ void drop(obj_t object, loc_t where) { * the right thing happen. */ --game.holdng; + } + } game.objects[object].place = where; } if (where == LOC_NOWHERE || where == CARRIED) { diff --git a/saveresume.c b/saveresume.c index ea30e4d..9822b76 100644 --- a/saveresume.c +++ b/saveresume.c @@ -127,11 +127,13 @@ int resume(void) { while (fp == NULL) { char *name = myreadline("\nFile name: "); - if (name == NULL) + if (name == NULL) { return GO_TOP; + } name = strip(name); - if (strlen(name) == 0) + 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); From 1ef39055f3e4fb7c1bd84804a994c20b87301521 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 10:07:58 -0500 Subject: [PATCH 12/55] Make reflow run black. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9c837d2..2e81dfc 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,7 @@ check: advent cheat reflow: @clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]") + @black --quiet *.py # Requires gcov, lcov, libasan6, and libubsan1 # The last two are Ubuntu names, might vary on other distributions. From 5b917084b035b815cc7b8282219ae7266f239d68 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 11:43:36 -0500 Subject: [PATCH 13/55] Minor repair of savefile validation code. --- saveresume.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/saveresume.c b/saveresume.c index 9822b76..100c925 100644 --- a/saveresume.c +++ b/saveresume.c @@ -184,12 +184,7 @@ bool is_valid(struct game_t valgame) { /* Check for RNG overflow. Truncate */ if (valgame.lcg_x >= LCG_M) { - valgame.lcg_x %= LCG_M; // LCOV_EXCL_LINE - } - - /* Check for RNG underflow. Transpose */ - if (valgame.lcg_x < LCG_M) { - valgame.lcg_x = LCG_M + (valgame.lcg_x % LCG_M); + return false; } /* Bounds check for locations */ From 580f409ee63779f79494188db3afdd0f455d9a6d Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 11:44:04 -0500 Subject: [PATCH 14/55] At this revision, make cppcheck runs clean. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2e81dfc..7521a43 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ debug: linty CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf cppcheck: - cppcheck -I. --template gcc --enable=all $(CSUPPRESSIONS) *.[ch] + cppcheck -I. --template gcc -UPROP_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch] pylint: @pylint --score=n *.py */*.py From f24fcd297176c7a95e999bd34e627bb0f4159c3a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 12:48:33 -0500 Subject: [PATCH 15/55] Perform full code validation on every make check. --- Makefile | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7521a43..978d5a8 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,14 @@ clean: cheat: $(CHEAT_OBJS) dungeon.o $(CC) $(CCFLAGS) $(DBX) -o cheat $(CHEAT_OBJS) dungeon.o $(LDFLAGS) $(LIBS) -check: advent cheat +CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf +cppcheck: + @-cppcheck -I. --quiet --template gcc -UPROP_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch] + +pylint: + @-pylint --score=n *.py */*.py + +check: advent cheat pylint cppcheck cd tests; $(MAKE) --quiet reflow: @@ -151,9 +158,3 @@ debug: CCFLAGS += -fsanitize=address debug: CCFLAGS += -fsanitize=undefined debug: linty -CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf -cppcheck: - cppcheck -I. --template gcc -UPROP_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch] - -pylint: - @pylint --score=n *.py */*.py From 43a08621e4404bb47e3bde1d0b6c12bf79d7066e Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 15:52:03 -0500 Subject: [PATCH 16/55] asciidoc -> asciidoctor. --- Dockerfile.ci | 2 +- Makefile | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 598687b..b8eeef5 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,4 +9,4 @@ FROM ubuntu ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -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 +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 diff --git a/Makefile b/Makefile index 978d5a8..ea8fcd4 100644 --- a/Makefile +++ b/Makefile @@ -87,15 +87,16 @@ reflow: coverage: clean debug cd tests; $(MAKE) coverage --quiet -.SUFFIXES: .adoc .html .6 +# Note: to suppress the footers with timestamps being generated in HTML, +# we use "-a nofooter". +# To debug asciidoc problems, you may need to run "xmllint --nonet --noout --valid" +# on the intermediate XML that throws an error. +.SUFFIXES: .html .adoc .6 -# Requires asciidoc and xsltproc/docbook stylesheets. .adoc.6: - a2x --doctype manpage --format manpage $< + asciidoctor -D. -a nofooter -b manpage $< .adoc.html: - asciidoc $< -.adoc: - asciidoc $< + asciidoctor -D. -a nofooter -a webfonts! $< html: advent.html history.html hints.html From a2fa136988609cfd57a0aed53b42630339e052ac Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 16:16:05 -0500 Subject: [PATCH 17/55] Remove unused production. --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index ea8fcd4..0da7fb8 100644 --- a/Makefile +++ b/Makefile @@ -112,9 +112,6 @@ advent-$(VERS).tar.gz: $(SOURCES) $(DOCS) (tar -T MANIFEST -czvf advent-$(VERS).tar.gz) @(rm advent-$(VERS)) -indent: - astyle -n -A3 --pad-header --min-conditional-indent=1 --pad-oper *.c - release: advent-$(VERS).tar.gz advent.html history.html hints.html notes.html shipper version=$(VERS) | sh -e -x From 6174129116ca191c5435c14853047908d05caf5a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 4 Feb 2024 16:46:43 -0500 Subject: [PATCH 18/55] Add a detail to the installation instructiions. --- INSTALL.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/INSTALL.adoc b/INSTALL.adoc index 683e2f0..5184d6b 100644 --- a/INSTALL.adoc +++ b/INSTALL.adoc @@ -21,3 +21,5 @@ 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. From b610a62685444a565fda09374c7d21957081f7e4 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 5 Feb 2024 07:43:23 -0500 Subject: [PATCH 19/55] Documentation polishing. --- advent.adoc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/advent.adoc b/advent.adoc index 9e628e0..d7231f6 100644 --- a/advent.adoc +++ b/advent.adoc @@ -20,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 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. +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. This version is released as open source with the permission and encouragement of the original authors. @@ -34,7 +34,8 @@ 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. +Don's 1977 version until you get to the new cave sections added for +2.5. To exit the game, type Ctrl-D (EOF). @@ -44,9 +45,9 @@ There have been no gameplay changes. -l:: Log commands to specified file. --r:: Restore game from specified file +-r:: Restore game from specified save file --a:: Load from specified file and autosave to it on exit or signal. +-a:: Load from specified save 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. @@ -59,9 +60,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 word -size and endianness, and unlikely to survive through version bumps. There -is a version check. +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. The input parser was the first attempt *ever* at natural-language parsing in a game and has some known deficiencies. While later text From fcf6935689a83d363771c05660ecc9ff5b1e013b Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 5 Feb 2024 07:47:01 -0500 Subject: [PATCH 20/55] Ready to ship 1.18. --- NEWS.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.adoc b/NEWS.adoc index 68aa36c..92c2f10 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,6 +2,9 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 +1.18: 2024-02-05:: + 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. From 0fafbe379f0d72a72bcecc2f946c29b562fba161 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 5 Feb 2024 07:51:00 -0500 Subject: [PATCH 21/55] Ubuntu-discuss doesn't want to see release notifications. --- control | 2 -- 1 file changed, 2 deletions(-) diff --git a/control b/control index 841e7ab..9b7fe43 100644 --- a/control +++ b/control @@ -9,8 +9,6 @@ Description: Colossal Cave Adventure, the 1995 430-point version. 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 XBS-HTML-Target: index.html From bad34acf1ef988f77a38c7f6cad2cf1546c83d4c Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 5 Feb 2024 08:07:31 -0500 Subject: [PATCH 22/55] Improve the project summary. --- control | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/control b/control index 9b7fe43..e47484d 100644 --- a/control +++ b/control @@ -5,9 +5,10 @@ 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 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 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. Homepage: http://www.catb.org/~esr/open-adventure From 62cd0c78dab69bef371aec943fe78ff464ae4f15 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 15 Feb 2024 12:56:01 -0500 Subject: [PATCH 23/55] Reissue 1.18 - same code, corrected metadata. --- NEWS.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index 92c2f10..624f6ce 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 -1.18: 2024-02-05:: +1.18: 2024-02-15:: Bring the manual page fully up to date. 1.17: 2024-01-02:: From b51612131d7552c9629a1e843b236b5b9be096d5 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 29 Apr 2024 11:43:41 -0400 Subject: [PATCH 24/55] Typo fix. --- advent.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advent.adoc b/advent.adoc index d7231f6..f132b0c 100644 --- a/advent.adoc +++ b/advent.adoc @@ -62,7 +62,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 From 7903ac1bb879208dbe4a4e3e24f21997d53f68d6 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 3 Jun 2024 21:12:41 -0400 Subject: [PATCH 25/55] More validation, with -Wall and -Wextra. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0da7fb8..a7acbd8 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ VERS=$(shell sed -n Date: Tue, 25 Jun 2024 13:08:50 -0400 Subject: [PATCH 26/55] Incorporate Ryan Sarson's test for correct knife message. --- tests/knife.chk | 386 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/knife.log | 56 +++++++ 2 files changed, 442 insertions(+) create mode 100644 tests/knife.chk create mode 100644 tests/knife.log diff --git a/tests/knife.chk b/tests/knife.chk new file mode 100644 index 0000000..0c150ec --- /dev/null +++ b/tests/knife.chk @@ -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 + +The dwarves' knives vanish as they strike the walls of the cave. + +> 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 new file mode 100644 index 0000000..e7477e2 --- /dev/null +++ b/tests/knife.log @@ -0,0 +1,56 @@ +## Test whether KNIVES_VANISH can be issued twice +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +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 From bd499dc53276f28994b7ffc833fb8499fb05c5eb Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 25 Jun 2024 13:23:21 -0400 Subject: [PATCH 27/55] Fix GitLab issue #69: repeated knive caveat message --- NEWS.adoc | 3 +++ main.c | 10 ++++++---- notes.adoc | 6 +++++- tests/knife.chk | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 624f6ce..28dd018 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,6 +2,9 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 +Repository head:: + Ensore that the KNIVES_VANISH message can't issue twice. + 1.18: 2024-02-15:: Bring the manual page fully up to date. diff --git a/main.c b/main.c index 7f5d8a1..6d904f2 100644 --- a/main.c +++ b/main.c @@ -1280,11 +1280,13 @@ static bool do_command(void) { } } - /* Check to see if the room is dark. If the knife is - * here, and it's dark, the knife permanently disappears - */ + /* Check to see if the room is dark. */ game.wzdark = DARK(game.loc); - if (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; } diff --git a/notes.adoc b/notes.adoc index b970e7c..833682c 100644 --- a/notes.adoc +++ b/notes.adoc @@ -78,7 +78,11 @@ Bug fixes: "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). + 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: diff --git a/tests/knife.chk b/tests/knife.chk index 0c150ec..76a3b6b 100644 --- a/tests/knife.chk +++ b/tests/knife.chk @@ -369,7 +369,7 @@ A crystal bridge spans the fissure. > get knife -The dwarves' knives vanish as they strike the walls of the cave. +I see no knife here. > quit From 86fe4bd1212c346de237755139eaf7bfcb00946f Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 27 Jun 2024 13:04:33 -0400 Subject: [PATCH 28/55] Verify that tesrts still match advent430 where expected. --- tests/Makefile | 2 +- tests/knife.log | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 2ed7431..997096a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 diff --git a/tests/knife.log b/tests/knife.log index e7477e2..1c811ea 100644 --- a/tests/knife.log +++ b/tests/knife.log @@ -1,6 +1,7 @@ ## 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 From 1080b45d391062bbaadd16900d2259c6d1b888ed Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 27 Jun 2024 13:29:28 -0400 Subject: [PATCH 29/55] Comment typo fix. --- advent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advent.h b/advent.h index 2a23eb9..21a521f 100644 --- a/advent.h +++ b/advent.h @@ -66,7 +66,7 @@ * 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. + * those tests is difficult to read. */ #define PROP_STASHIFY(n) (-1 - (n)) #define PROP_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND) @@ -80,7 +80,7 @@ #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 + * 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 From 63e8579f74956ca2e729311920c90a929b39176b Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 27 Jun 2024 13:39:27 -0400 Subject: [PATCH 30/55] Ready to shp 1.19. --- NEWS.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index 28dd018..2d0dcee 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 -Repository head:: +1.19: 2024-06-27:: Ensore that the KNIVES_VANISH message can't issue twice. 1.18: 2024-02-15:: From 124e7768b45712a7c2dbd979bda88e2e54fd0ed6 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 27 Jun 2024 19:50:56 -0400 Subject: [PATCH 31/55] Cease relying on C storage starting zeroed. --- init.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/init.c b/init.c index e413fff..2fffc14 100644 --- a/init.c +++ b/init.c @@ -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-trreasures are set to STATE_FOUND explicity 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) { + PROP_SET_NOT_FOUND(object); } + } else { + PROP_SET_FOUND(object); } } game.conds = setbit(COND_HBASE); From cb293f4aa4f29f0da11c1ddae9ee0712f4e1dd0a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 30 Jun 2024 14:42:06 -0400 Subject: [PATCH 32/55] Rename some macos for clarity. --- Makefile | 2 +- actions.c | 20 ++++++++++---------- advent.h | 38 +++++++++++++++++++------------------- init.c | 4 ++-- main.c | 24 ++++++++++++------------ misc.c | 6 +++--- saveresume.c | 2 +- score.c | 4 ++-- 8 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index a7acbd8..88c59e6 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ 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 diff --git a/actions.c b/actions.c index 31a09c8..27b7904 100644 --- a/actions.c +++ b/actions.c @@ -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)) { @@ -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); @@ -407,7 +407,7 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) { } if ((obj == BIRD || obj == CAGE) && (game.objects[BIRD].prop == BIRD_CAGED || - PROP_STASHED(BIRD) == BIRD_CAGED)) { + OBJECT_STASHED(BIRD) == BIRD_CAGED)) { /* expression maps BIRD to CAGE and CAGE to BIRD */ carry(BIRD + CAGE - obj, game.loc); } @@ -418,8 +418,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); @@ -971,7 +971,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_OR_UNSEEN(i)) { continue; } int mi = game.objects[i].prop; @@ -1151,7 +1151,7 @@ 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; } } @@ -1175,7 +1175,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 +1351,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; diff --git a/advent.h b/advent.h index 21a521f..f561e89 100644 --- a/advent.h +++ b/advent.h @@ -61,7 +61,7 @@ * 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 + * OBJECT_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 @@ -69,14 +69,14 @@ * 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) +#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND) +#define OBJECT_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND) +#define OBJECT_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND) +#define OBJECT_IS_STASHED_OR_UNSEEN(obj) (game.objects[obj].prop < 0) +#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) #else /* (ESR) Only the boldest of adventurers will explore here. This * alternate set of definitions for the macros above was an attempt to @@ -84,27 +84,27 @@ * 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 + * OBJECT_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) \ +#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE) +#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < 0) +#define OBJECT_IS_NOTFOUND(obj) (!game.objects[obj].found) +#define OBJECT_IS_FOUND(obj) \ (game.objects[obj].found && game.objects[obj].prop == 0) -#define PROP_IS_STASHED_OR_UNSEEN(obj) \ +#define OBJECT_IS_STASHED_OR_UNSEEN(obj) \ (!game.objects[obj].found || game.objects[obj].prop < 0) -#define PROP_SET_FOUND(obj) \ +#define OBJECT_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 +#define OBJECT_SET_NOT_FOUND(obj) game.objects[obj].found = false +#define OBJECT_IS_NOTFOUND2(g, o) (!g.objects[o].found) +#define OBJECT_SET_SEEN(obj) game.objects[object].found = true #endif -#define PROP_STASHED(obj) PROP_STASHIFY(game.objects[obj].prop) +#define OBJECT_STASHED(obj) PROP_STASHIFY(game.objects[obj].prop) #define PROMPT "> " diff --git a/init.c b/init.c index 2fffc14..58bb0e8 100644 --- a/init.c +++ b/init.c @@ -84,10 +84,10 @@ int initialise(void) { if (objects[object].is_treasure) { ++game.tally; if (objects[object].inventory != NULL) { - PROP_SET_NOT_FOUND(object); + OBJECT_SET_NOT_FOUND(object); } } else { - PROP_SET_FOUND(object); + OBJECT_SET_FOUND(object); } } game.conds = setbit(COND_HBASE); diff --git a/main.c b/main.c index 6d904f2..2dfa880 100644 --- a/main.c +++ b/main.c @@ -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,7 @@ static void checkhints(void) { return; case 9: /* jade */ if (game.tally == 1 && - PROP_IS_STASHED_OR_UNSEEN(JADE)) { + OBJECT_IS_STASHED_OR_UNSEEN(JADE)) { break; } game.hints[hint].lc = 0; @@ -231,8 +231,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; @@ -1061,11 +1061,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_OR_UNSEEN(obj)) { if (game.closed) { continue; } - PROP_SET_FOUND(obj); + OBJECT_SET_FOUND(obj); if (obj == RUG) { game.objects[RUG].prop = RUG_DRAGON; } @@ -1266,16 +1266,16 @@ 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))) { + if (TOTING(i) && (OBJECT_IS_NOTFOUND(i) || + OBJECT_IS_STASHED(i))) { game.objects[i].prop = - PROP_STASHED(i); + OBJECT_STASHED(i); } } } diff --git a/misc.c b/misc.c index 9e90d05..14d0622 100644 --- a/misc.c +++ b/misc.c @@ -620,11 +620,11 @@ 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. + /* (ESR) Read this in combination with the macro defintions in advent.h. */ game.objects[object].prop = PROP_STASHIFY(pval); -#ifdef PROP_SET_SEEN - PROP_SET_SEEN(object); +#ifdef OBJECT_SET_SEEN + OBJECT_SET_SEEN(object); #endif } diff --git a/saveresume.c b/saveresume.c index 100c925..1c778ef 100644 --- a/saveresume.c +++ b/saveresume.c @@ -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; } } diff --git a/score.c b/score.c index 1d84c79..ad1c00a 100644 --- a/score.c +++ b/score.c @@ -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; From e17ff128da5fc447806308eb0c62a05a9a56787b Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 30 Jun 2024 17:20:46 -0400 Subject: [PATCH 33/55] Remove obsolete comment part. --- advent.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/advent.h b/advent.h index f561e89..5aadcbb 100644 --- a/advent.h +++ b/advent.h @@ -82,10 +82,6 @@ * 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 - * OBJECT_IS_STASHED_OR_UNSEEN. The symptom is game.tally getting - * decremented on non-treasures. */ #define PROP_STASHIFY(n) (-(n)) #define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE) From 8dcc6e66415771d76a09a87bd5e97d21d80dc84e Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 10:03:15 -0400 Subject: [PATCH 34/55] Correct missing negative. --- adventure.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adventure.yaml b/adventure.yaml index 31988de..75d415a 100644 --- a/adventure.yaml +++ b/adventure.yaml @@ -10,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 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 From 3d6c689ffa6ab95e2b59c0fc284038a41f7f598e Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 10:29:37 -0400 Subject: [PATCH 35/55] Make oldstyle correctly suppress line editing. --- NEWS.adoc | 3 +++ main.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index 2d0dcee..f9c70d6 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,6 +2,9 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 +Repository head:: + Make oldstyle correctly suppress line editing. + 1.19: 2024-06-27:: Ensore that the KNIVES_VANISH message can't issue twice. diff --git a/main.c b/main.c index 2dfa880..e0e4c58 100644 --- a/main.c +++ b/main.c @@ -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 { From acdfa96315f7c7bafce54d0e0530f4c95f3bb390 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 10:40:22 -0400 Subject: [PATCH 36/55] Fix a busted comment. --- misc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc.c b/misc.c index 14d0622..aa11786 100644 --- a/misc.c +++ b/misc.c @@ -495,8 +495,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 From cf4adf8d028e75e041abe4850259edad54171264 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 10:49:44 -0400 Subject: [PATCH 37/55] 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. --- misc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc.c b/misc.c index aa11786..533735b 100644 --- a/misc.c +++ b/misc.c @@ -514,8 +514,7 @@ static void tokenize(char *raw, command_t *cmd) { * possible an emulation of the original UI. */ if (settings.oldstyle) { - cmd->word[0].raw[TOKLEN + TOKLEN] = - cmd->word[1].raw[TOKLEN + TOKLEN] = '\0'; + cmd->word[0].raw[TOKLEN] = cmd->word[1].raw[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]); } From f1cb740c41a992c6b25a83d098b0b580cf85486c Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 22:04:16 -0400 Subject: [PATCH 38/55] Define TRUNCLEN and explain its issues. --- misc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/misc.c b/misc.c index 533735b..af88737 100644 --- a/misc.c +++ b/misc.c @@ -512,9 +512,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] = cmd->word[1].raw[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]); } From 8c553af53e6d04462716d50a654102b028e86e96 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 22:05:25 -0400 Subject: [PATCH 39/55] Avoid a GNUism, POSIX strncasecmp() is declarted in strings.h. --- misc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/misc.c b/misc.c index af88737..7760cc5 100644 --- a/misc.c +++ b/misc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include From 20fd7c589fcd1e9817db541e988341b4501f8305 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 20 Sep 2024 22:19:14 -0400 Subject: [PATCH 40/55] Typo fix. --- init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init.c b/init.c index 58bb0e8..b4ee643 100644 --- a/init.c +++ b/init.c @@ -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 */ From 9c3f4b0c90690f35669b802a3661c3ef31460900 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 13:08:31 -0400 Subject: [PATCH 41/55] Reflow. --- advent.h | 6 +++--- main.c | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/advent.h b/advent.h index 5aadcbb..17e02eb 100644 --- a/advent.h +++ b/advent.h @@ -87,11 +87,11 @@ #define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE) #define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < 0) #define OBJECT_IS_NOTFOUND(obj) (!game.objects[obj].found) -#define OBJECT_IS_FOUND(obj) \ +#define OBJECT_IS_FOUND(obj) \ (game.objects[obj].found && game.objects[obj].prop == 0) -#define OBJECT_IS_STASHED_OR_UNSEEN(obj) \ +#define OBJECT_IS_STASHED_OR_UNSEEN(obj) \ (!game.objects[obj].found || game.objects[obj].prop < 0) -#define OBJECT_SET_FOUND(obj) \ +#define OBJECT_SET_FOUND(obj) \ do { \ game.objects[obj].found = true; \ game.objects[obj].prop = STATE_FOUND; \ diff --git a/main.c b/main.c index e0e4c58..4db3836 100644 --- a/main.c +++ b/main.c @@ -1272,8 +1272,9 @@ static bool do_command(void) { pspeak(OYSTER, look, true, 1); } for (size_t i = 1; i <= NOBJECTS; i++) { - if (TOTING(i) && (OBJECT_IS_NOTFOUND(i) || - OBJECT_IS_STASHED(i))) { + if (TOTING(i) && + (OBJECT_IS_NOTFOUND(i) || + OBJECT_IS_STASHED(i))) { game.objects[i].prop = OBJECT_STASHED(i); } @@ -1282,10 +1283,10 @@ static bool do_command(void) { /* Check to see if the room is dark. */ game.wzdark = DARK(game.loc); - + /* 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; From 354e56a69ba6671cf56a0e51950162abb2542bfd Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 22:06:02 -0400 Subject: [PATCH 42/55] Clean up some comments. --- advent.h | 2 +- init.c | 2 +- misc.c | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/advent.h b/advent.h index 17e02eb..9f306c1 100644 --- a/advent.h +++ b/advent.h @@ -257,7 +257,7 @@ struct game_t { 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 { diff --git a/init.c b/init.c index b4ee643..372e494 100644 --- a/init.c +++ b/init.c @@ -77,7 +77,7 @@ int initialise(void) { * 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-trreasures are set to STATE_FOUND explicity so we + * (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++) { diff --git a/misc.c b/misc.c index 7760cc5..9fddfd5 100644 --- a/misc.c +++ b/misc.c @@ -622,11 +622,9 @@ 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 advent.h. - */ game.objects[object].prop = PROP_STASHIFY(pval); #ifdef OBJECT_SET_SEEN OBJECT_SET_SEEN(object); From 0157e5866872f9b2e6f0b8f875ad9696f658944b Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 22:21:47 -0400 Subject: [PATCH 43/55] Remove an unneeded layer of macro indirection. --- actions.c | 2 +- advent.h | 3 +-- main.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/actions.c b/actions.c index 27b7904..107b298 100644 --- a/actions.c +++ b/actions.c @@ -407,7 +407,7 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) { } if ((obj == BIRD || obj == CAGE) && (game.objects[BIRD].prop == BIRD_CAGED || - OBJECT_STASHED(BIRD) == BIRD_CAGED)) { + PROP_STASHIFY(game.objects[BIRD].prop) == BIRD_CAGED)) { /* expression maps BIRD to CAGE and CAGE to BIRD */ carry(BIRD + CAGE - obj, game.loc); } diff --git a/advent.h b/advent.h index 9f306c1..9841037 100644 --- a/advent.h +++ b/advent.h @@ -61,7 +61,7 @@ * STATE_NOTFOUND is only set on treasures. Non-treasures start the * game in STATE_FOUND. * - * OBJECT_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 @@ -100,7 +100,6 @@ #define OBJECT_IS_NOTFOUND2(g, o) (!g.objects[o].found) #define OBJECT_SET_SEEN(obj) game.objects[object].found = true #endif -#define OBJECT_STASHED(obj) PROP_STASHIFY(game.objects[obj].prop) #define PROMPT "> " diff --git a/main.c b/main.c index 4db3836..688cfc7 100644 --- a/main.c +++ b/main.c @@ -1276,7 +1276,7 @@ static bool do_command(void) { (OBJECT_IS_NOTFOUND(i) || OBJECT_IS_STASHED(i))) { game.objects[i].prop = - OBJECT_STASHED(i); + PROP_STASHIFY(game.objects[i].prop); } } } From 08f0351817b54e76f33609b12deec52e8efbcd4c Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 22:34:07 -0400 Subject: [PATCH 44/55] Introduce OBJECT_STASHIFY. --- advent.h | 1 + main.c | 3 +-- misc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/advent.h b/advent.h index 9841037..d1504f0 100644 --- a/advent.h +++ b/advent.h @@ -100,6 +100,7 @@ #define OBJECT_IS_NOTFOUND2(g, o) (!g.objects[o].found) #define OBJECT_SET_SEEN(obj) game.objects[object].found = true #endif +#define OBJECT_STASHIFY(obj, pval) game.objects[obj].prop = PROP_STASHIFY(pval) #define PROMPT "> " diff --git a/main.c b/main.c index 688cfc7..14c9065 100644 --- a/main.c +++ b/main.c @@ -1275,8 +1275,7 @@ static bool do_command(void) { if (TOTING(i) && (OBJECT_IS_NOTFOUND(i) || OBJECT_IS_STASHED(i))) { - game.objects[i].prop = - PROP_STASHIFY(game.objects[i].prop); + OBJECT_STASHIFY(i, game.objects[i].prop); } } } diff --git a/misc.c b/misc.c index 9fddfd5..b28949e 100644 --- a/misc.c +++ b/misc.c @@ -625,7 +625,7 @@ 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); - game.objects[object].prop = PROP_STASHIFY(pval); + OBJECT_STASHIFY(object, pval); #ifdef OBJECT_SET_SEEN OBJECT_SET_SEEN(object); #endif From 9a6e4406f55df92d448fcad0b54aff1902787e11 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 23:09:16 -0400 Subject: [PATCH 45/55] Confine uses of PROP_STASHIFY() to advent.h Now it shouyld be possible to manipulate a stashed flag by only changing macros. --- actions.c | 3 +-- advent.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions.c b/actions.c index 107b298..6f93eb0 100644 --- a/actions.c +++ b/actions.c @@ -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_STASHIFY(game.objects[BIRD].prop) == BIRD_CAGED)) { + OBJECT_STATE_EQUALS(BIRD, BIRD_CAGED)) { /* expression maps BIRD to CAGE and CAGE to BIRD */ carry(BIRD + CAGE - obj, game.loc); } diff --git a/advent.h b/advent.h index d1504f0..6686dfc 100644 --- a/advent.h +++ b/advent.h @@ -77,6 +77,7 @@ #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 OBJECT_STATE_EQUALS(obj, pval) ((game.objects[obj].prop == pval) || (game.objects[obj].prop == PROP_STASHIFY(pval))) #else /* (ESR) Only the boldest of adventurers will explore here. This * alternate set of definitions for the macros above was an attempt to From a4fd14caf710b8327d631a99c49b81f411f0161a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 22 Sep 2024 23:32:23 -0400 Subject: [PATCH 46/55] Back away from trying for the found member until we make stashed work. --- advent.h | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/advent.h b/advent.h index 6686dfc..fa3c185 100644 --- a/advent.h +++ b/advent.h @@ -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 @@ -78,29 +77,6 @@ #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 OBJECT_STATE_EQUALS(obj, pval) ((game.objects[obj].prop == pval) || (game.objects[obj].prop == PROP_STASHIFY(pval))) -#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. - */ -#define PROP_STASHIFY(n) (-(n)) -#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE) -#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < 0) -#define OBJECT_IS_NOTFOUND(obj) (!game.objects[obj].found) -#define OBJECT_IS_FOUND(obj) \ - (game.objects[obj].found && game.objects[obj].prop == 0) -#define OBJECT_IS_STASHED_OR_UNSEEN(obj) \ - (!game.objects[obj].found || game.objects[obj].prop < 0) -#define OBJECT_SET_FOUND(obj) \ - do { \ - game.objects[obj].found = true; \ - game.objects[obj].prop = STATE_FOUND; \ - } while (0) -#define OBJECT_SET_NOT_FOUND(obj) game.objects[obj].found = false -#define OBJECT_IS_NOTFOUND2(g, o) (!g.objects[o].found) -#define OBJECT_SET_SEEN(obj) game.objects[object].found = true -#endif #define OBJECT_STASHIFY(obj, pval) game.objects[obj].prop = PROP_STASHIFY(pval) #define PROMPT "> " @@ -254,9 +230,6 @@ 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 loc_t place; // location of object From 40742e112ba73e1fd98c9b898f6ce5812c7c74b7 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 03:53:28 -0400 Subject: [PATCH 47/55] Expand a macro to simplify code. --- actions.c | 2 +- advent.h | 9 ++++----- main.c | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/actions.c b/actions.c index 6f93eb0..e5f2e70 100644 --- a/actions.c +++ b/actions.c @@ -970,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 || - OBJECT_IS_STASHED_OR_UNSEEN(i)) { + OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(i)) { continue; } int mi = game.objects[i].prop; diff --git a/advent.h b/advent.h index fa3c185..204fce7 100644 --- a/advent.h +++ b/advent.h @@ -67,17 +67,16 @@ * 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_INVALID(val) (val < -MAX_STATE - 1 || val > MAX_STATE) -#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND) #define OBJECT_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND) #define OBJECT_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND) -#define OBJECT_IS_STASHED_OR_UNSEEN(obj) (game.objects[obj].prop < 0) #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 OBJECT_STATE_EQUALS(obj, pval) ((game.objects[obj].prop == pval) || (game.objects[obj].prop == PROP_STASHIFY(pval))) +#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 "> " diff --git a/main.c b/main.c index 14c9065..c823280 100644 --- a/main.c +++ b/main.c @@ -188,7 +188,7 @@ static void checkhints(void) { return; case 9: /* jade */ if (game.tally == 1 && - OBJECT_IS_STASHED_OR_UNSEEN(JADE)) { + (OBJECT_IS_STASHED(JADE) || OBJECT_IS_NOTFOUND(JADE))) { break; } game.hints[hint].lc = 0; @@ -1061,7 +1061,7 @@ static void listobjects(void) { * running this code only on objects with the treasure * property set. Nope. There is mystery here. */ - if (OBJECT_IS_STASHED_OR_UNSEEN(obj)) { + if (OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(obj)) { if (game.closed) { continue; } From 96ad6c6245bfc18fb36116717143a299b98a9c77 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 04:38:24 -0400 Subject: [PATCH 48/55] Reflow. --- advent.h | 4 +++- main.c | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/advent.h b/advent.h index 204fce7..b9c98d9 100644 --- a/advent.h +++ b/advent.h @@ -76,7 +76,9 @@ #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 OBJECT_STATE_EQUALS(obj, pval) \ + ((game.objects[obj].prop == pval) || \ + (game.objects[obj].prop == PROP_STASHIFY(pval))) #define PROMPT "> " diff --git a/main.c b/main.c index c823280..d383095 100644 --- a/main.c +++ b/main.c @@ -188,7 +188,8 @@ static void checkhints(void) { return; case 9: /* jade */ if (game.tally == 1 && - (OBJECT_IS_STASHED(JADE) || OBJECT_IS_NOTFOUND(JADE))) { + (OBJECT_IS_STASHED(JADE) || + OBJECT_IS_NOTFOUND(JADE))) { break; } game.hints[hint].lc = 0; @@ -1275,7 +1276,8 @@ static bool do_command(void) { if (TOTING(i) && (OBJECT_IS_NOTFOUND(i) || OBJECT_IS_STASHED(i))) { - OBJECT_STASHIFY(i, game.objects[i].prop); + OBJECT_STASHIFY( + i, game.objects[i].prop); } } } From 92451f1fff194355ccc4c770ddc54944ec7fda6a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 05:08:11 -0400 Subject: [PATCH 49/55] Eliminate thew last property inequality outside a macro. --- actions.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/actions.c b/actions.c index e5f2e70..1786c53 100644 --- a/actions.c +++ b/actions.c @@ -329,8 +329,7 @@ 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; From 5c90880f0a20ec7e06740e7699cd5ff9c94b44b4 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 06:04:40 -0400 Subject: [PATCH 50/55] Comment and macro cleanup. --- actions.c | 3 ++- advent.h | 39 ++++++++++++++++++++------------------- main.c | 4 ++-- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/actions.c b/actions.c index 1786c53..96dabb3 100644 --- a/actions.c +++ b/actions.c @@ -329,7 +329,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) { if (game.objects[obj].fixed != IS_FREE) { switch (obj) { case PLANT: - rspeak((game.objects[PLANT].prop == PLANT_THIRSTY || OBJECT_IS_STASHED(PLANT)) + rspeak((game.objects[PLANT].prop == PLANT_THIRSTY || + OBJECT_IS_STASHED(PLANT)) ? DEEP_ROOTS : YOU_JOKING); break; diff --git a/advent.h b/advent.h index b9c98d9..4a6d4d3 100644 --- a/advent.h +++ b/advent.h @@ -83,23 +83,24 @@ #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) + * 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 + * 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)) @@ -124,8 +125,8 @@ #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) diff --git a/main.c b/main.c index d383095..3ecf65a 100644 --- a/main.c +++ b/main.c @@ -640,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; @@ -1193,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) { From a2bb39dc7e9379900d4c87d8084c196ddd61b32f Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 14:17:52 -0400 Subject: [PATCH 51/55] Eliminate a confusing dummy argument in a macro. --- actions.c | 6 +++--- advent.h | 38 +++++++++++++++++++------------------- main.c | 8 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/actions.c b/actions.c index 96dabb3..469e92f 100644 --- a/actions.c +++ b/actions.c @@ -676,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: @@ -1155,12 +1155,12 @@ static phase_codes_t read(command_t command) } } 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) { diff --git a/advent.h b/advent.h index 4a6d4d3..3ae9012 100644 --- a/advent.h +++ b/advent.h @@ -83,24 +83,24 @@ #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 - * 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) + * 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)) @@ -118,7 +118,7 @@ (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)) diff --git a/main.c b/main.c index 3ecf65a..835fc1b 100644 --- a/main.c +++ b/main.c @@ -534,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]; } @@ -1047,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]) { @@ -1229,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; @@ -1283,7 +1283,7 @@ static bool do_command(void) { } /* 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 From 9df69fe0347d4c02956336d3659d8aa06a91dce9 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Mon, 23 Sep 2024 18:48:11 -0400 Subject: [PATCH 52/55] Ready to ship 1.20. --- NEWS.adoc | 2 +- advent.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index f9c70d6..0140574 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: (C) Eric S. Raymond // SPDX-License-Identifier: CC-BY-4.0 -Repository head:: +1.20: 2024-09-23:: Make oldstyle correctly suppress line editing. 1.19: 2024-06-27:: diff --git a/advent.h b/advent.h index 3ae9012..fa0767b 100644 --- a/advent.h +++ b/advent.h @@ -66,6 +66,8 @@ * 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 ==. */ #define OBJECT_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND) #define OBJECT_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND) From 3e989aec5370beb49d49a837fc92f5b991970ece Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 25 May 2025 10:57:34 -0400 Subject: [PATCH 53/55] Spellcheck the YAML - yielded one trivial fix. --- Makefile | 3 +++ adventure.yaml | 10 ++++++++-- notes.adoc | 7 ++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 88c59e6..b0a752a 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,9 @@ pylint: check: advent cheat pylint cppcheck cd tests; $(MAKE) --quiet +spellcheck: + @batchspell adventure.yaml + reflow: @clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]") @black --quiet *.py diff --git a/adventure.yaml b/adventure.yaml index 75d415a..141ffb7 100644 --- a/adventure.yaml +++ b/adventure.yaml @@ -1,6 +1,10 @@ # SPDX-FileCopyrightText: (C) 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 @@ -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 diff --git a/notes.adoc b/notes.adoc index 833682c..a247e49 100644 --- a/notes.adoc +++ b/notes.adoc @@ -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 From 7bbf994fce39e62af8d2feee66d767f7fa9b41b0 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 25 May 2025 11:03:34 -0400 Subject: [PATCH 54/55] Spellcheck the manual page a well. No errors. --- advent.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/advent.adoc b/advent.adoc index f132b0c..a064d1a 100644 --- a/advent.adoc +++ b/advent.adoc @@ -3,6 +3,9 @@ // SPDX-FileCopyrightText: (C) 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 From 7d848c89e1f771c043d99a0495836a8575d8995e Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 25 May 2025 11:06:46 -0400 Subject: [PATCH 55/55] Add spell-checking to the regression tests. --- INSTALL.adoc | 2 ++ Makefile | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/INSTALL.adoc b/INSTALL.adoc index 5184d6b..fb93b7d 100644 --- a/INSTALL.adoc +++ b/INSTALL.adoc @@ -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 diff --git a/Makefile b/Makefile index b0a752a..e4e31cb 100644 --- a/Makefile +++ b/Makefile @@ -72,11 +72,11 @@ cppcheck: 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 + @batchspell adventure.yaml advent.adoc reflow: @clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]")