1TBS reflow with clang-format.
This commit is contained in:
parent
c11938aed5
commit
be429016af
9 changed files with 4137 additions and 3936 deletions
3
Makefile
3
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
|
||||
|
|
352
actions.c
352
actions.c
|
@ -5,17 +5,17 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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.
|
||||
/* 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. */
|
||||
|
@ -59,8 +59,8 @@ static phase_codes_t attack(command_t command) {
|
|||
obj = VEND;
|
||||
++changes;
|
||||
}
|
||||
/* Clam and oyster both treated as clam for intransitive case;
|
||||
* no harm done. */
|
||||
/* Clam and oyster both treated as clam for intransitive
|
||||
* case; no harm done. */
|
||||
if (HERE(CLAM) || HERE(OYSTER)) {
|
||||
obj = CLAM;
|
||||
++changes;
|
||||
|
@ -80,8 +80,9 @@ static phase_codes_t attack(command_t command) {
|
|||
return GO_CLEAROBJ;
|
||||
}
|
||||
if (obj == VEND) {
|
||||
state_change(VEND,
|
||||
game.objects[VEND].prop == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
|
||||
state_change(VEND, game.objects[VEND].prop == VEND_BLOCKS
|
||||
? VEND_UNBLOCKS
|
||||
: VEND_BLOCKS);
|
||||
|
||||
return GO_CLEAROBJ;
|
||||
}
|
||||
|
@ -115,10 +116,10 @@ static phase_codes_t attack(command_t command) {
|
|||
}
|
||||
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.
|
||||
/* 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);
|
||||
|
@ -149,9 +150,7 @@ static phase_codes_t attack(command_t command) {
|
|||
game.dwarves[i].seen = false;
|
||||
}
|
||||
}
|
||||
rspeak((dwarves > 1) ?
|
||||
OGRE_PANIC1 :
|
||||
OGRE_PANIC2);
|
||||
rspeak((dwarves > 1) ? OGRE_PANIC1 : OGRE_PANIC2);
|
||||
return GO_CLEAROBJ;
|
||||
}
|
||||
|
||||
|
@ -185,21 +184,21 @@ static phase_codes_t attack(command_t command) {
|
|||
}
|
||||
|
||||
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). */
|
||||
/* 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)) {
|
||||
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 == FEE && id == FIE) || (foobar == FIE && id == FOE) ||
|
||||
(foobar == FOE && id == FOO)) {
|
||||
game.foobar = id;
|
||||
if (id != FOO) {
|
||||
|
@ -212,10 +211,11 @@ static phase_codes_t bigwords(vocab_t id) {
|
|||
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)
|
||||
/* 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);
|
||||
|
@ -241,7 +241,8 @@ 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! */
|
||||
/* 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,7 +261,8 @@ static void blast(void) {
|
|||
}
|
||||
|
||||
static phase_codes_t vbreak(verb_t verb, obj_t obj) {
|
||||
/* Break. Only works for mirror in repository and, of course, the vase. */
|
||||
/* Break. Only works for mirror in repository and, of course, the
|
||||
* vase. */
|
||||
switch (obj) {
|
||||
case MIRROR:
|
||||
if (game.closed) {
|
||||
|
@ -286,7 +288,8 @@ static phase_codes_t vbreak(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static phase_codes_t brief(void) {
|
||||
/* Brief. Intransitive only. Suppress full descriptions after first time. */
|
||||
/* Brief. Intransitive only. Suppress full descriptions after first
|
||||
* time. */
|
||||
game.abbnum = 10000;
|
||||
game.detail = 3;
|
||||
rspeak(BRIEF_CONFIRM);
|
||||
|
@ -294,11 +297,12 @@ static phase_codes_t brief(void) {
|
|||
}
|
||||
|
||||
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. */
|
||||
/* 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. */
|
||||
/* 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) {
|
||||
|
@ -322,16 +326,24 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
|||
switch (obj) {
|
||||
case PLANT:
|
||||
/* Next guard tests whether plant is tiny or stashed */
|
||||
rspeak(game.objects[PLANT].prop <= PLANT_THIRSTY ? DEEP_ROOTS : YOU_JOKING);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
rspeak(game.objects[RUG].prop == RUG_HOVER
|
||||
? RUG_HOVERS
|
||||
: YOU_JOKING);
|
||||
break;
|
||||
case URN:
|
||||
rspeak(URN_NOBUDGE);
|
||||
|
@ -370,10 +382,10 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
|||
if (game.holdng >= INVLIMIT) {
|
||||
rspeak(CARRY_LIMIT);
|
||||
return GO_CLEAROBJ;
|
||||
|
||||
}
|
||||
|
||||
if (obj == BIRD && game.objects[BIRD].prop != BIRD_CAGED && !PROP_IS_STASHED(BIRD)) {
|
||||
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);
|
||||
|
@ -390,7 +402,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
|||
game.objects[BIRD].prop = BIRD_CAGED;
|
||||
}
|
||||
if ((obj == BIRD || obj == CAGE) &&
|
||||
(game.objects[BIRD].prop == BIRD_CAGED || PROP_STASHED(BIRD) == BIRD_CAGED)) {
|
||||
(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);
|
||||
}
|
||||
|
@ -410,7 +423,7 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static int chain(verb_t verb) {
|
||||
/* Do something to the bear's chain */
|
||||
/* Do something to the bear's chain */
|
||||
if (verb != LOCK) {
|
||||
if (game.objects[BEAR].prop == UNTAMED_BEAR) {
|
||||
rspeak(BEAR_BLOCKS);
|
||||
|
@ -428,9 +441,9 @@ static int chain(verb_t verb) {
|
|||
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. */
|
||||
/* 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
|
||||
|
@ -461,9 +474,9 @@ static int chain(verb_t verb) {
|
|||
}
|
||||
|
||||
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. */
|
||||
/* 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;
|
||||
}
|
||||
|
@ -473,11 +486,13 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
|||
return GO_CLEAROBJ;
|
||||
}
|
||||
|
||||
if (GSTONE(obj) && AT(CAVITY) && game.objects[CAVITY].prop != CAVITY_FULL) {
|
||||
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) ||
|
||||
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);
|
||||
|
@ -487,7 +502,9 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
|||
rspeak(RUG_RISES);
|
||||
}
|
||||
if (!TOTING(RUG) || obj == RUBY) {
|
||||
int k = (game.objects[RUG].prop == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER;
|
||||
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;
|
||||
|
@ -526,9 +543,8 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
|||
|
||||
if (obj == VASE) {
|
||||
if (game.loc != objects[PILLOW].plac) {
|
||||
state_change(VASE, AT(PILLOW)
|
||||
? VASE_WHOLE
|
||||
: VASE_DROPPED);
|
||||
state_change(VASE,
|
||||
AT(PILLOW) ? VASE_WHOLE : VASE_DROPPED);
|
||||
if (game.objects[VASE].prop != VASE_WHOLE) {
|
||||
game.objects[VASE].fixed = IS_FIXED;
|
||||
}
|
||||
|
@ -559,7 +575,8 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
|||
rspeak(OK_MAN);
|
||||
}
|
||||
|
||||
game.objects[BIRD].prop = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
|
||||
game.objects[BIRD].prop =
|
||||
FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
|
||||
drop(obj, game.loc);
|
||||
return GO_CLEAROBJ;
|
||||
}
|
||||
|
@ -570,8 +587,9 @@ static phase_codes_t discard(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. */
|
||||
/* 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;
|
||||
|
@ -599,8 +617,9 @@ static phase_codes_t drink(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. */
|
||||
/* 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))
|
||||
|
@ -628,7 +647,7 @@ static phase_codes_t eat(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static phase_codes_t extinguish(verb_t verb, obj_t obj) {
|
||||
/* Extinguish. Lamp, urn, dragon/volcano (nice try). */
|
||||
/* Extinguish. Lamp, urn, dragon/volcano (nice try). */
|
||||
if (obj == INTRANSITIVE) {
|
||||
if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_BRIGHT) {
|
||||
obj = LAMP;
|
||||
|
@ -651,9 +670,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(DARK(game.loc) ? PITCH_DARK : NO_MESSAGE);
|
||||
break;
|
||||
case DRAGON:
|
||||
case VOLCANO:
|
||||
|
@ -666,8 +683,8 @@ static phase_codes_t extinguish(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. */
|
||||
/* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf,
|
||||
* make him mad. Bear, special. */
|
||||
switch (obj) {
|
||||
case BIRD:
|
||||
rspeak(BIRD_PINING);
|
||||
|
@ -730,7 +747,7 @@ static phase_codes_t feed(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
|
||||
/* Fill. Bottle or urn must be empty, and liquid available. (Vase
|
||||
* is nasty.) */
|
||||
if (obj == VASE) {
|
||||
if (LIQLOC(game.loc) == NO_OBJECT) {
|
||||
|
@ -796,9 +813,8 @@ phase_codes_t fill(verb_t verb, obj_t obj) {
|
|||
return GO_CLEAROBJ;
|
||||
}
|
||||
|
||||
state_change(BOTTLE, (LIQLOC(game.loc) == OIL)
|
||||
? OIL_BOTTLE
|
||||
: WATER_BOTTLE);
|
||||
state_change(BOTTLE,
|
||||
(LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE);
|
||||
if (TOTING(BOTTLE)) {
|
||||
game.objects[LIQUID()].place = CARRIED;
|
||||
}
|
||||
|
@ -806,7 +822,8 @@ phase_codes_t fill(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. */
|
||||
/* Find. Might be carrying it, or it might be here. Else give caveat.
|
||||
*/
|
||||
if (TOTING(obj)) {
|
||||
rspeak(ALREADY_CARRYING);
|
||||
return GO_CLEAROBJ;
|
||||
|
@ -823,13 +840,12 @@ static phase_codes_t find(verb_t verb, obj_t obj) {
|
|||
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. */
|
||||
/* Fly. Snide remarks unless hovering rug is here. */
|
||||
if (obj == INTRANSITIVE) {
|
||||
if (!HERE(RUG)) {
|
||||
rspeak(FLAP_ARMS);
|
||||
|
@ -862,16 +878,17 @@ static phase_codes_t fly(verb_t verb, obj_t obj) {
|
|||
game.newloc = LOC_CLIFF;
|
||||
rspeak(RUG_RETURNS);
|
||||
} else {
|
||||
// LCOV_EXCL_START
|
||||
// LCOV_EXCL_START
|
||||
/* should never happen */
|
||||
rspeak(NOTHING_HAPPENS);
|
||||
// LCOV_EXCL_STOP
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return GO_TERMINATE;
|
||||
}
|
||||
|
||||
static phase_codes_t inven(void) {
|
||||
/* Inventory. If object, treat same as find. Else report on current burden. */
|
||||
/* 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))
|
||||
|
@ -890,10 +907,11 @@ static phase_codes_t inven(void) {
|
|||
}
|
||||
|
||||
static phase_codes_t light(verb_t verb, obj_t obj) {
|
||||
/* Light. Applicable only to lamp and urn. */
|
||||
/* 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) {
|
||||
if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_DARK &&
|
||||
game.limit >= 0) {
|
||||
obj = LAMP;
|
||||
selects++;
|
||||
}
|
||||
|
@ -907,9 +925,9 @@ static phase_codes_t light(verb_t verb, obj_t obj) {
|
|||
|
||||
switch (obj) {
|
||||
case URN:
|
||||
state_change(URN, game.objects[URN].prop == URN_EMPTY ?
|
||||
URN_EMPTY :
|
||||
URN_LIT);
|
||||
state_change(URN, game.objects[URN].prop == URN_EMPTY
|
||||
? URN_EMPTY
|
||||
: URN_LIT);
|
||||
break;
|
||||
case LAMP:
|
||||
if (game.limit < 0) {
|
||||
|
@ -928,7 +946,8 @@ static phase_codes_t light(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static phase_codes_t listen(void) {
|
||||
/* Listen. Intransitive only. Print stuff based on object sound properties. */
|
||||
/* Listen. Intransitive only. Print stuff based on object sound
|
||||
* properties. */
|
||||
bool soundlatch = false;
|
||||
vocab_t sound = locations[game.loc].sound;
|
||||
if (sound != SILENT) {
|
||||
|
@ -939,7 +958,8 @@ static phase_codes_t listen(void) {
|
|||
soundlatch = true;
|
||||
}
|
||||
for (obj_t i = 1; i <= NOBJECTS; i++) {
|
||||
if (!HERE(i) || objects[i].sounds[0] == NULL || PROP_IS_STASHED_OR_UNSEEN(i)) {
|
||||
if (!HERE(i) || objects[i].sounds[0] == NULL ||
|
||||
PROP_IS_STASHED_OR_UNSEEN(i)) {
|
||||
continue;
|
||||
}
|
||||
int mi = game.objects[i].prop;
|
||||
|
@ -965,7 +985,7 @@ static phase_codes_t listen(void) {
|
|||
}
|
||||
|
||||
static phase_codes_t lock(verb_t verb, obj_t obj) {
|
||||
/* Lock, unlock, no object given. Assume various things if present. */
|
||||
/* Lock, unlock, no object given. Assume various things if present. */
|
||||
if (obj == INTRANSITIVE) {
|
||||
if (HERE(CLAM)) {
|
||||
obj = CLAM;
|
||||
|
@ -1008,9 +1028,9 @@ static phase_codes_t lock(verb_t verb, obj_t obj) {
|
|||
}
|
||||
game.panic = true;
|
||||
} else {
|
||||
state_change(GRATE, (verb == LOCK) ?
|
||||
GRATE_CLOSED :
|
||||
GRATE_OPEN);
|
||||
state_change(GRATE, (verb == LOCK)
|
||||
? GRATE_CLOSED
|
||||
: GRATE_OPEN);
|
||||
}
|
||||
} else {
|
||||
rspeak(NO_KEYS);
|
||||
|
@ -1042,10 +1062,11 @@ static phase_codes_t lock(verb_t verb, obj_t obj) {
|
|||
}
|
||||
break;
|
||||
case DOOR:
|
||||
rspeak((game.objects[DOOR].prop == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR);
|
||||
rspeak((game.objects[DOOR].prop == DOOR_UNRUSTED) ? OK_MAN
|
||||
: RUSTY_DOOR);
|
||||
break;
|
||||
case CAGE:
|
||||
rspeak( NO_LOCK);
|
||||
rspeak(NO_LOCK);
|
||||
break;
|
||||
case KEYS:
|
||||
rspeak(CANNOT_UNLOCK);
|
||||
|
@ -1058,7 +1079,7 @@ static phase_codes_t lock(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.
|
||||
/* 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();
|
||||
|
@ -1087,7 +1108,8 @@ static phase_codes_t pour(verb_t verb, obj_t obj) {
|
|||
if (!AT(DOOR)) {
|
||||
if (obj == WATER) {
|
||||
/* cycle through the three plant states */
|
||||
state_change(PLANT, MOD(game.objects[PLANT].prop + 1, 3));
|
||||
state_change(PLANT,
|
||||
MOD(game.objects[PLANT].prop + 1, 3));
|
||||
game.objects[PLANT2].prop = game.objects[PLANT].prop;
|
||||
return GO_MOVE;
|
||||
} else {
|
||||
|
@ -1095,16 +1117,16 @@ static phase_codes_t pour(verb_t verb, obj_t obj) {
|
|||
return GO_CLEAROBJ;
|
||||
}
|
||||
} else {
|
||||
state_change(DOOR, (obj == OIL) ?
|
||||
DOOR_UNRUSTED :
|
||||
DOOR_RUSTED);
|
||||
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])) {
|
||||
/* 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;
|
||||
|
@ -1116,11 +1138,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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1131,26 +1155,32 @@ static phase_codes_t read(command_t command)
|
|||
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]);
|
||||
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.
|
||||
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)) {
|
||||
} 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);
|
||||
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). */
|
||||
/* 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);
|
||||
state_change(RESER, game.objects[RESER].prop == WATERS_PARTED
|
||||
? WATERS_UNPARTED
|
||||
: WATERS_PARTED);
|
||||
if (AT(RESER))
|
||||
return GO_CLEAROBJ;
|
||||
else {
|
||||
|
@ -1163,7 +1193,7 @@ static phase_codes_t reservoir(void) {
|
|||
}
|
||||
|
||||
static phase_codes_t rub(verb_t verb, obj_t obj) {
|
||||
/* Rub. Yields various snide remarks except for lit urn. */
|
||||
/* Rub. Yields various snide remarks except for lit urn. */
|
||||
if (obj == URN && game.objects[URN].prop == URN_LIT) {
|
||||
DESTROY(URN);
|
||||
drop(AMBER, game.loc);
|
||||
|
@ -1180,10 +1210,9 @@ static phase_codes_t rub(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static phase_codes_t say(command_t command) {
|
||||
/* Say. Echo WD2. Magic words override. */
|
||||
/* 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 == XYZZY || command.word[1].id == PLUGH ||
|
||||
command.word[1].id == PLOVER)) {
|
||||
return GO_WORD2;
|
||||
}
|
||||
|
@ -1192,27 +1221,23 @@ static phase_codes_t say(command_t command) {
|
|||
}
|
||||
|
||||
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)) {
|
||||
(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)
|
||||
{
|
||||
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
|
||||
/* 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. */
|
||||
|
@ -1240,7 +1265,8 @@ static phase_codes_t throwit(command_t command) {
|
|||
return (discard(command.verb, command.obj));
|
||||
} else {
|
||||
if (atdwrf(game.loc) <= 0) {
|
||||
if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS)
|
||||
if (AT(DRAGON) &&
|
||||
game.objects[DRAGON].prop == DRAGON_BARS)
|
||||
return throw_support(DRAGON_SCALES);
|
||||
if (AT(TROLL)) {
|
||||
return throw_support(TROLL_RETURNS);
|
||||
|
@ -1248,8 +1274,10 @@ static phase_codes_t throwit(command_t command) {
|
|||
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! */
|
||||
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);
|
||||
|
@ -1266,15 +1294,14 @@ static phase_codes_t throwit(command_t command) {
|
|||
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);
|
||||
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. */
|
||||
/* Wake. Only use is to disturb the dwarves. */
|
||||
if (obj != DWARF || !game.closed) {
|
||||
speak(actions[verb].message);
|
||||
return GO_CLEAROBJ;
|
||||
|
@ -1285,7 +1312,7 @@ static phase_codes_t wake(verb_t verb, obj_t obj) {
|
|||
}
|
||||
|
||||
static phase_codes_t seed(verb_t verb, const char *arg) {
|
||||
/* Set seed */
|
||||
/* Set seed */
|
||||
int32_t seed = strtol(arg, NULL, 10);
|
||||
speak(actions[verb].message, seed);
|
||||
set_seed(seed);
|
||||
|
@ -1294,24 +1321,24 @@ static phase_codes_t seed(verb_t verb, const char *arg) {
|
|||
}
|
||||
|
||||
static phase_codes_t waste(verb_t verb, turn_t turns) {
|
||||
/* Burn turns */
|
||||
/* Burn turns */
|
||||
game.limit -= turns;
|
||||
speak(actions[verb].message, (int)game.limit);
|
||||
return GO_TOP;
|
||||
}
|
||||
|
||||
static phase_codes_t wave(verb_t verb, obj_t obj) {
|
||||
/* Wave. No effect unless waving rod at fissure or at bird. */
|
||||
if (obj != ROD || !TOTING(obj) || (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
|
||||
speak(((!TOTING(obj)) && (obj != ROD ||
|
||||
!TOTING(ROD2))) ?
|
||||
arbitrary_messages[ARENT_CARRYING] :
|
||||
actions[verb].message);
|
||||
/* 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)) {
|
||||
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;
|
||||
|
@ -1319,35 +1346,37 @@ static phase_codes_t wave(verb_t verb, obj_t obj) {
|
|||
return GO_CLEAROBJ;
|
||||
} else {
|
||||
if (game.closed) {
|
||||
rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
|
||||
CAGE_FLY :
|
||||
FREE_FLY);
|
||||
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);
|
||||
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);
|
||||
rspeak((game.objects[BIRD].prop == BIRD_CAGED)
|
||||
? CAGE_FLY
|
||||
: FREE_FLY);
|
||||
|
||||
state_change(FISSURE,
|
||||
game.objects[FISSURE].prop == BRIDGED ? UNBRIDGED : BRIDGED);
|
||||
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.
|
||||
/* 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 */
|
||||
* 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;
|
||||
|
@ -1365,13 +1394,16 @@ phase_codes_t action(command_t command) {
|
|||
/* FALL THROUGH */;
|
||||
} else if (command.obj == DWARF && atdwrf(game.loc) > 0) {
|
||||
/* FALL THROUGH */;
|
||||
} else if (!game.closed && ((LIQUID() == command.obj && HERE(BOTTLE)) ||
|
||||
} 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) {
|
||||
} 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) {
|
||||
} 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) {
|
||||
|
@ -1382,7 +1414,9 @@ 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 {
|
||||
sspeak(NO_SEE, command.word[0].raw);
|
||||
|
@ -1400,13 +1434,16 @@ phase_codes_t action(command_t command) {
|
|||
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;
|
||||
/* 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). */
|
||||
/* Analyse an intransitive verb (ie, no object given
|
||||
* yet). */
|
||||
switch (command.verb) {
|
||||
case CARRY:
|
||||
return vcarry(command.verb, INTRANSITIVE);
|
||||
|
@ -1592,7 +1629,8 @@ phase_codes_t action(command_t command) {
|
|||
case SEED:
|
||||
return seed(command.verb, command.word[1].raw);
|
||||
case WASTE:
|
||||
return waste(command.verb, (turn_t)atol(command.word[1].raw));
|
||||
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
|
||||
}
|
||||
|
|
97
advent.h
97
advent.h
|
@ -4,11 +4,11 @@
|
|||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "dungeon.h"
|
||||
|
||||
|
@ -30,7 +30,8 @@
|
|||
#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_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
|
||||
|
@ -89,9 +90,15 @@
|
|||
#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_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)
|
||||
|
@ -116,26 +123,36 @@
|
|||
* 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 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 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 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 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 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 INDEEP(LOC) CNDBIT((LOC), COND_DEEP)
|
||||
#define BUG(x) bug(x, #x)
|
||||
|
||||
enum bugtype {
|
||||
|
@ -150,15 +167,15 @@ enum bugtype {
|
|||
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.
|
||||
|
@ -204,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 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
|
||||
|
@ -251,7 +268,7 @@ struct game_t {
|
|||
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
|
||||
obj_t link[NOBJECTS * 2 + 1]; // object-list links
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -275,7 +292,15 @@ typedef struct {
|
|||
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;
|
||||
|
@ -288,9 +313,9 @@ typedef struct {
|
|||
/*
|
||||
* 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
|
||||
|
||||
|
@ -301,8 +326,8 @@ typedef struct {
|
|||
|
||||
/*
|
||||
* 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)];
|
||||
|
@ -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);
|
||||
|
|
38
cheat.c
38
cheat.c
|
@ -7,15 +7,14 @@
|
|||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <editline/readline.h>
|
||||
#include "advent.h"
|
||||
#include <editline/readline.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int main(int argc, char *argv[]) {
|
||||
int ch;
|
||||
char *savefilename = NULL;
|
||||
FILE *fp = NULL;
|
||||
|
@ -29,8 +28,10 @@ int main(int argc, char *argv[])
|
|||
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"
|
||||
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"
|
||||
|
@ -64,8 +65,7 @@ int main(int argc, char *argv[])
|
|||
savefilename = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
usage, argv[0]);
|
||||
fprintf(stderr, usage, argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
@ -73,17 +73,14 @@ int main(int argc, char *argv[])
|
|||
|
||||
// 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");
|
||||
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);
|
||||
fprintf(stderr, "Can't open file %s. Exiting.\n", savefilename);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
|
33
init.c
33
init.c
|
@ -5,39 +5,28 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
int initialise(void) {
|
||||
if (settings.oldstyle)
|
||||
printf("Initialising...\n");
|
||||
|
||||
|
@ -46,7 +35,7 @@ int initialise(void)
|
|||
set_seed(seedval);
|
||||
|
||||
for (int i = 1; i <= NDWARVES; i++) {
|
||||
game.dwarves[i].loc = dwarflocs[i-1];
|
||||
game.dwarves[i].loc = dwarflocs[i - 1];
|
||||
}
|
||||
|
||||
for (int i = 1; i <= NOBJECTS; i++) {
|
||||
|
|
330
misc.c
330
misc.c
|
@ -5,25 +5,25 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <editline/readline.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "advent.h"
|
||||
#include "dungeon.h"
|
||||
|
||||
static void* xcalloc(size_t size)
|
||||
{
|
||||
void* ptr = calloc(size, 1);
|
||||
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
|
||||
// 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
|
||||
|
@ -33,26 +33,28 @@ static void* xcalloc(size_t size)
|
|||
|
||||
/* 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 */
|
||||
{
|
||||
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)
|
||||
if (msg == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing if we got an empty string.
|
||||
if (strlen(msg) == 0)
|
||||
if (strlen(msg) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blank == true)
|
||||
if (blank == true) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int msglen = strlen(msg);
|
||||
|
||||
// Rendered string
|
||||
ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */
|
||||
char* rendered = xcalloc(size);
|
||||
char* renderp = rendered;
|
||||
char *rendered = xcalloc(size);
|
||||
char *renderp = rendered;
|
||||
|
||||
// Handle format specifiers (including the custom %S) by
|
||||
// adjusting the parameter accordingly, and replacing the
|
||||
|
@ -60,9 +62,11 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
|||
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)) {
|
||||
/* 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;
|
||||
|
@ -76,7 +80,8 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
|||
// Integer specifier.
|
||||
if (msg[i] == 'd') {
|
||||
int32_t arg = va_arg(ap, int32_t);
|
||||
int ret = snprintf(renderp, size, "%" PRId32, arg);
|
||||
int ret =
|
||||
snprintf(renderp, size, "%" PRId32, arg);
|
||||
if (ret < size) {
|
||||
renderp += ret;
|
||||
size -= ret;
|
||||
|
@ -121,18 +126,16 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
|||
free(rendered);
|
||||
}
|
||||
|
||||
void speak(const char* msg, ...)
|
||||
/* speak a specified string */
|
||||
{
|
||||
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 */
|
||||
{
|
||||
void sspeak(const int msg, ...) {
|
||||
/* Speak a message from the arbitrary-messages list */
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
fputc('\n', stdout);
|
||||
|
@ -141,13 +144,12 @@ void sspeak(const int msg, ...)
|
|||
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:
|
||||
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) {
|
||||
|
@ -170,27 +172,25 @@ void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...)
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
void rspeak(vocab_t i, ...)
|
||||
/* Print the i-th "random" message (section 6 of database). */
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
static int word_count(char *str) {
|
||||
char delims[] = " \t";
|
||||
int count = 0;
|
||||
int inblanks = true;
|
||||
|
@ -210,8 +210,7 @@ static int word_count(char* str)
|
|||
return (count);
|
||||
}
|
||||
|
||||
static char* get_input(void)
|
||||
{
|
||||
static char *get_input(void) {
|
||||
// Set up the prompt
|
||||
char input_prompt[] = PROMPT;
|
||||
if (!settings.prompt)
|
||||
|
@ -220,7 +219,7 @@ static char* get_input(void)
|
|||
// Print a blank line
|
||||
printf("\n");
|
||||
|
||||
char* input;
|
||||
char *input;
|
||||
for (;;) {
|
||||
input = myreadline(input_prompt);
|
||||
|
||||
|
@ -248,12 +247,11 @@ static char* get_input(void)
|
|||
return (input);
|
||||
}
|
||||
|
||||
bool silent_yes_or_no(void)
|
||||
{
|
||||
bool silent_yes_or_no(void) {
|
||||
bool outcome = false;
|
||||
|
||||
for (;;) {
|
||||
char* reply = get_input();
|
||||
char *reply = get_input();
|
||||
if (reply == NULL) {
|
||||
// LCOV_EXCL_START
|
||||
// Should be unreachable. Reply should never be NULL
|
||||
|
@ -267,7 +265,7 @@ bool silent_yes_or_no(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
char* firstword = (char*) xcalloc(strlen(reply) + 1);
|
||||
char *firstword = (char *)xcalloc(strlen(reply) + 1);
|
||||
sscanf(reply, "%s", firstword);
|
||||
|
||||
free(reply);
|
||||
|
@ -294,17 +292,16 @@ bool silent_yes_or_no(void)
|
|||
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 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);
|
||||
|
||||
char* reply = get_input();
|
||||
char *reply = get_input();
|
||||
if (reply == NULL) {
|
||||
// LCOV_EXCL_START
|
||||
// Should be unreachable. Reply should never be NULL
|
||||
|
@ -319,13 +316,14 @@ bool yes_or_no(const char* question, const char* yes_response, const char* no_re
|
|||
continue;
|
||||
}
|
||||
|
||||
char* firstword = (char*) xcalloc(strlen(reply) + 1);
|
||||
char *firstword = (char *)xcalloc(strlen(reply) + 1);
|
||||
sscanf(reply, "%s", firstword);
|
||||
|
||||
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);
|
||||
|
@ -344,7 +342,6 @@ bool yes_or_no(const char* question, const char* yes_response, const char* no_re
|
|||
break;
|
||||
} else
|
||||
rspeak(PLEASE_ANSWER);
|
||||
|
||||
}
|
||||
|
||||
return (outcome);
|
||||
|
@ -352,12 +349,13 @@ bool yes_or_no(const char* question, const char* yes_response, const char* no_re
|
|||
|
||||
/* 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.
|
||||
{
|
||||
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 ||
|
||||
if (strncasecmp(word, motions[i].words.strs[j],
|
||||
TOKLEN) == 0 &&
|
||||
(strlen(word) > 1 ||
|
||||
strchr(ignore, word[0]) == NULL ||
|
||||
!settings.oldstyle))
|
||||
return (i);
|
||||
|
@ -367,12 +365,14 @@ static int get_motion_vocab_id(const char* 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
|
||||
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)
|
||||
if (strncasecmp(word, objects[i].words.strs[j],
|
||||
TOKLEN) == 0)
|
||||
return (i);
|
||||
}
|
||||
}
|
||||
|
@ -380,47 +380,51 @@ static int get_object_vocab_id(const char* 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.
|
||||
{
|
||||
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 ||
|
||||
if (strncasecmp(word, actions[i].words.strs[j],
|
||||
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.
|
||||
return (WORD_NOT_FOUND);
|
||||
}
|
||||
|
||||
static bool is_valid_int(const char *str)
|
||||
/* Returns true if the string passed in is represents a valid integer,
|
||||
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 == '-')
|
||||
if (*str == '-') {
|
||||
++str;
|
||||
}
|
||||
|
||||
// Handle empty string or just "-". Should never reach this
|
||||
// point, because this is only used with transitive verbs.
|
||||
if (!*str)
|
||||
if (!*str) {
|
||||
return false; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// Check for non-digit chars in the rest of the string.
|
||||
while (*str) {
|
||||
if (!isdigit(*str))
|
||||
if (!isdigit(*str)) {
|
||||
return false;
|
||||
else
|
||||
} else {
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type)
|
||||
{
|
||||
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;
|
||||
|
@ -431,7 +435,8 @@ static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type)
|
|||
vocab_t ref_num;
|
||||
|
||||
ref_num = get_motion_vocab_id(word);
|
||||
// Second conjunct is because the magic-word placeholder is a bit special
|
||||
// Second conjunct is because the magic-word placeholder is a bit
|
||||
// special
|
||||
if (ref_num != WORD_NOT_FOUND) {
|
||||
*id = ref_num;
|
||||
*type = MOTION;
|
||||
|
@ -471,8 +476,7 @@ static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type)
|
|||
return;
|
||||
}
|
||||
|
||||
static void tokenize(char* raw, command_t *cmd)
|
||||
{
|
||||
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.
|
||||
|
@ -500,24 +504,28 @@ 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';
|
||||
for (size_t i = 0; i < strlen(cmd->word[0].raw); i++)
|
||||
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++)
|
||||
}
|
||||
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));
|
||||
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 */
|
||||
{
|
||||
bool get_command_input(command_t *command) {
|
||||
/* Get user input on stdin, parse and map to command */
|
||||
char inputbuf[LINESIZE];
|
||||
char* input;
|
||||
char *input;
|
||||
|
||||
for (;;) {
|
||||
input = get_input();
|
||||
|
@ -528,8 +536,9 @@ bool get_command_input(command_t *command)
|
|||
free(input);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(input, "") != 0)
|
||||
if (strcmp(input, "") != 0) {
|
||||
break;
|
||||
}
|
||||
free(input);
|
||||
}
|
||||
|
||||
|
@ -540,14 +549,14 @@ bool get_command_input(command_t *command)
|
|||
|
||||
#ifdef GDEBUG
|
||||
/* Needs to stay synced with enum word_type_t */
|
||||
const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", "NUMERIC"};
|
||||
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],
|
||||
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
|
||||
|
||||
|
@ -555,9 +564,8 @@ bool get_command_input(command_t *command)
|
|||
return true;
|
||||
}
|
||||
|
||||
void clear_command(command_t *cmd)
|
||||
/* Resets the state of the command to 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;
|
||||
|
@ -565,10 +573,10 @@ void clear_command(command_t *cmd)
|
|||
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. */
|
||||
{
|
||||
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;
|
||||
|
@ -577,28 +585,29 @@ void juggle(obj_t object)
|
|||
move(object + NOBJECTS, j);
|
||||
}
|
||||
|
||||
void move(obj_t object, loc_t where)
|
||||
/* Place any object anywhere by picking it up and dropping it. May
|
||||
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)
|
||||
if (object > NOBJECTS) {
|
||||
from = game.objects[object - NOBJECTS].fixed;
|
||||
else
|
||||
} else {
|
||||
from = game.objects[object].place;
|
||||
/* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong... */
|
||||
if (from != LOC_NOWHERE && from != CARRIED)
|
||||
}
|
||||
/* (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. */
|
||||
{
|
||||
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.
|
||||
*/
|
||||
|
@ -608,16 +617,17 @@ void put(obj_t object, loc_t where, int pval)
|
|||
#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. */
|
||||
{
|
||||
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)
|
||||
if (game.objects[object].place == CARRIED) {
|
||||
return;
|
||||
}
|
||||
game.objects[object].place = CARRIED;
|
||||
|
||||
/*
|
||||
|
@ -641,68 +651,69 @@ void carry(obj_t object, loc_t where)
|
|||
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)
|
||||
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 {
|
||||
} 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.
|
||||
/* 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)
|
||||
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 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)
|
||||
if (game.dflag < 2) {
|
||||
return at;
|
||||
}
|
||||
at = -1;
|
||||
for (int i = 1; i <= NDWARVES - 1; i++) {
|
||||
if (game.dwarves[i].loc == where)
|
||||
if (game.dwarves[i].loc == where) {
|
||||
return i;
|
||||
if (game.dwarves[i].loc != 0)
|
||||
}
|
||||
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. */
|
||||
{
|
||||
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. */
|
||||
{
|
||||
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 */
|
||||
{
|
||||
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;
|
||||
|
@ -715,9 +726,8 @@ void set_seed(int32_t seedval)
|
|||
game.zzword[5] = '\0';
|
||||
}
|
||||
|
||||
static int32_t get_next_lcg_value(void)
|
||||
/* Return the LCG's current value, and then iterate it. */
|
||||
{
|
||||
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) {
|
||||
|
@ -726,23 +736,21 @@ static int32_t get_next_lcg_value(void)
|
|||
return old_x;
|
||||
}
|
||||
|
||||
int32_t randrange(int32_t range)
|
||||
/* Return a random integer from [0, range). */
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
112
saveresume.c
112
saveresume.c
|
@ -8,11 +8,11 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "advent.h"
|
||||
|
||||
|
@ -23,17 +23,21 @@
|
|||
|
||||
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. */
|
||||
{
|
||||
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)
|
||||
if (save.version == 0) {
|
||||
save.version = SAVE_VERSION;
|
||||
if (save.canary == 0)
|
||||
}
|
||||
if (save.canary == 0) {
|
||||
save.canary = ENDIAN_MAGIC;
|
||||
|
||||
}
|
||||
save.game = game;
|
||||
IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
|
||||
return (0);
|
||||
|
@ -41,17 +45,18 @@ int savefile(FILE *fp)
|
|||
|
||||
/* Suspend and resume */
|
||||
|
||||
static char *strip(char *name)
|
||||
{
|
||||
static char *strip(char *name) {
|
||||
// Trim leading whitespace
|
||||
while(isspace((unsigned char)*name))
|
||||
while (isspace((unsigned char)*name)) {
|
||||
name++; // LCOV_EXCL_LINE
|
||||
if(*name != '\0') {
|
||||
}
|
||||
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))
|
||||
while (end > name && isspace((unsigned char)*end)) {
|
||||
end--;
|
||||
}
|
||||
// Write new null terminator character
|
||||
end[1] = '\0';
|
||||
}
|
||||
|
@ -59,8 +64,7 @@ static char *strip(char *name)
|
|||
return name;
|
||||
}
|
||||
|
||||
int suspend(void)
|
||||
{
|
||||
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).
|
||||
|
@ -73,20 +77,26 @@ int suspend(void)
|
|||
FILE *fp = NULL;
|
||||
|
||||
rspeak(SUSPEND_WARNING);
|
||||
if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
|
||||
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)
|
||||
char *name = myreadline("\nFile name: ");
|
||||
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(strip(name), WRITE_MODE);
|
||||
if (fp == NULL)
|
||||
if (fp == NULL) {
|
||||
printf("Can't open file %s, try again.\n", name);
|
||||
}
|
||||
free(name);
|
||||
}
|
||||
|
||||
|
@ -96,8 +106,7 @@ int suspend(void)
|
|||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int resume(void)
|
||||
{
|
||||
int resume(void) {
|
||||
/* Resume. Read a suspended game back from a file.
|
||||
* If ADVENT_NOSAVE is defined, gripe instead. */
|
||||
|
||||
|
@ -109,28 +118,31 @@ int resume(void)
|
|||
|
||||
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]))
|
||||
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: ");
|
||||
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)
|
||||
if (fp == NULL) {
|
||||
printf("Can't open file %s, try again.\n", name);
|
||||
}
|
||||
free(name);
|
||||
}
|
||||
|
||||
return restore(fp);
|
||||
}
|
||||
|
||||
int restore(FILE* fp)
|
||||
{
|
||||
int restore(FILE *fp) {
|
||||
/* Read and restore game state from file, assuming
|
||||
* sane initial state.
|
||||
* If ADVENT_NOSAVE is defined, gripe instead. */
|
||||
|
@ -141,10 +153,12 @@ int restore(FILE* fp)
|
|||
|
||||
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)
|
||||
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 (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);
|
||||
|
@ -154,8 +168,7 @@ int restore(FILE* fp)
|
|||
return GO_TOP;
|
||||
}
|
||||
|
||||
bool is_valid(struct game_t valgame)
|
||||
{
|
||||
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
|
||||
|
@ -178,25 +191,29 @@ bool is_valid(struct game_t valgame)
|
|||
}
|
||||
|
||||
/* Bounds check for locations */
|
||||
if ( valgame.chloc < -1 || valgame.chloc > NLOCATIONS ||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -232,14 +249,17 @@ bool is_valid(struct game_t valgame)
|
|||
}
|
||||
}
|
||||
|
||||
/* Check that values in linked lists for objects in locations are inside bounds */
|
||||
/* 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) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
18
score.c
18
score.c
|
@ -4,15 +4,15 @@
|
|||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "advent.h"
|
||||
#include "dungeon.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
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 */
|
||||
/* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if
|
||||
* died or won */
|
||||
int score = 0;
|
||||
|
||||
/* The present scoring algorithm is as follows:
|
||||
|
@ -31,8 +31,8 @@ int score(enum termination mode) {
|
|||
* 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. */
|
||||
* 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. */
|
||||
|
@ -52,7 +52,8 @@ int score(enum termination mode) {
|
|||
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;
|
||||
|
@ -105,7 +106,8 @@ int score(enum termination mode) {
|
|||
score += 2;
|
||||
mxscor += 2;
|
||||
|
||||
/* Deduct for hints/turns/saves. Hints < 4 are special; see database desc. */
|
||||
/* 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;
|
||||
|
@ -128,7 +130,7 @@ int score(enum termination mode) {
|
|||
}
|
||||
|
||||
void terminate(enum termination mode) {
|
||||
/* End of game. Let's tell him all about it. */
|
||||
/* End of game. Let's tell him all about it. */
|
||||
int points = score(mode);
|
||||
#if defined ADVENT_AUTOSAVE
|
||||
autosave();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue