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
|
check: advent cheat
|
||||||
cd tests; $(MAKE) --quiet
|
cd tests; $(MAKE) --quiet
|
||||||
|
|
||||||
|
reflow:
|
||||||
|
@clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]")
|
||||||
|
|
||||||
# Requires gcov, lcov, libasan6, and libubsan1
|
# Requires gcov, lcov, libasan6, and libubsan1
|
||||||
# The last two are Ubuntu names, might vary on other distributions.
|
# The last two are Ubuntu names, might vary on other distributions.
|
||||||
# After this, run your browser on coverage/open-adventure/index.html
|
# After this, run your browser on coverage/open-adventure/index.html
|
||||||
|
|
314
actions.c
314
actions.c
|
@ -5,10 +5,10 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "advent.h"
|
#include "advent.h"
|
||||||
|
|
||||||
|
@ -59,8 +59,8 @@ static phase_codes_t attack(command_t command) {
|
||||||
obj = VEND;
|
obj = VEND;
|
||||||
++changes;
|
++changes;
|
||||||
}
|
}
|
||||||
/* Clam and oyster both treated as clam for intransitive case;
|
/* Clam and oyster both treated as clam for intransitive
|
||||||
* no harm done. */
|
* case; no harm done. */
|
||||||
if (HERE(CLAM) || HERE(OYSTER)) {
|
if (HERE(CLAM) || HERE(OYSTER)) {
|
||||||
obj = CLAM;
|
obj = CLAM;
|
||||||
++changes;
|
++changes;
|
||||||
|
@ -80,8 +80,9 @@ static phase_codes_t attack(command_t command) {
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
if (obj == VEND) {
|
if (obj == VEND) {
|
||||||
state_change(VEND,
|
state_change(VEND, game.objects[VEND].prop == VEND_BLOCKS
|
||||||
game.objects[VEND].prop == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
|
? VEND_UNBLOCKS
|
||||||
|
: VEND_BLOCKS);
|
||||||
|
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
@ -115,10 +116,10 @@ static phase_codes_t attack(command_t command) {
|
||||||
}
|
}
|
||||||
state_change(DRAGON, DRAGON_DEAD);
|
state_change(DRAGON, DRAGON_DEAD);
|
||||||
game.objects[RUG].prop = RUG_FLOOR;
|
game.objects[RUG].prop = RUG_FLOOR;
|
||||||
/* Hardcoding LOC_SECRET5 as the dragon's death location is ugly.
|
/* Hardcoding LOC_SECRET5 as the dragon's death location is
|
||||||
* The way it was computed before was worse; it depended on the
|
* ugly. The way it was computed before was worse; it depended
|
||||||
* two dragon locations being LOC_SECRET4 and LOC_SECRET6 and
|
* on the two dragon locations being LOC_SECRET4 and LOC_SECRET6
|
||||||
* LOC_SECRET5 being right between them.
|
* and LOC_SECRET5 being right between them.
|
||||||
*/
|
*/
|
||||||
move(DRAGON + NOBJECTS, IS_FIXED);
|
move(DRAGON + NOBJECTS, IS_FIXED);
|
||||||
move(RUG + NOBJECTS, IS_FREE);
|
move(RUG + NOBJECTS, IS_FREE);
|
||||||
|
@ -149,9 +150,7 @@ static phase_codes_t attack(command_t command) {
|
||||||
game.dwarves[i].seen = false;
|
game.dwarves[i].seen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rspeak((dwarves > 1) ?
|
rspeak((dwarves > 1) ? OGRE_PANIC1 : OGRE_PANIC2);
|
||||||
OGRE_PANIC1 :
|
|
||||||
OGRE_PANIC2);
|
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,21 +184,21 @@ static phase_codes_t attack(command_t command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/* Only called on FEE FIE FOE FOO (AND FUM). Advance to next state if
|
||||||
* in proper order. Look up foo in special section of vocab to determine which
|
* given in proper order. Look up foo in special section of vocab to
|
||||||
* word we've got. Last word zips the eggs back to the giant room (unless
|
* determine which word we've got. Last word zips the eggs back to the
|
||||||
* already there). */
|
* giant room (unless already there). */
|
||||||
int foobar = abs(game.foobar);
|
int foobar = abs(game.foobar);
|
||||||
|
|
||||||
/* Only FEE can start a magic-word sequence. */
|
/* 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);
|
rspeak(NOTHING_HAPPENS);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((foobar == WORD_EMPTY && id == FEE) ||
|
if ((foobar == WORD_EMPTY && id == FEE) ||
|
||||||
(foobar == FEE && id == FIE) ||
|
(foobar == FEE && id == FIE) || (foobar == FIE && id == FOE) ||
|
||||||
(foobar == FIE && id == FOE) ||
|
|
||||||
(foobar == FOE && id == FOO)) {
|
(foobar == FOE && id == FOO)) {
|
||||||
game.foobar = id;
|
game.foobar = id;
|
||||||
if (id != FOO) {
|
if (id != FOO) {
|
||||||
|
@ -212,10 +211,11 @@ static phase_codes_t bigwords(vocab_t id) {
|
||||||
rspeak(NOTHING_HAPPENS);
|
rspeak(NOTHING_HAPPENS);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
} else {
|
} else {
|
||||||
/* Bring back troll if we steal the eggs back from him before
|
/* Bring back troll if we steal the eggs back from him
|
||||||
* crossing. */
|
* before crossing. */
|
||||||
if (game.objects[EGGS].place == LOC_NOWHERE && game.objects[TROLL].place == LOC_NOWHERE
|
if (game.objects[EGGS].place == LOC_NOWHERE &&
|
||||||
&& game.objects[TROLL].prop == TROLL_UNPAID)
|
game.objects[TROLL].place == LOC_NOWHERE &&
|
||||||
|
game.objects[TROLL].prop == TROLL_UNPAID)
|
||||||
game.objects[TROLL].prop = TROLL_PAIDONCE;
|
game.objects[TROLL].prop = TROLL_PAIDONCE;
|
||||||
if (HERE(EGGS)) {
|
if (HERE(EGGS)) {
|
||||||
pspeak(EGGS, look, true, EGGS_VANISHED);
|
pspeak(EGGS, look, true, EGGS_VANISHED);
|
||||||
|
@ -241,7 +241,8 @@ static phase_codes_t bigwords(vocab_t id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blast(void) {
|
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)
|
if (PROP_IS_NOTFOUND(ROD2) || !game.closed)
|
||||||
rspeak(REQUIRES_DYNAMITE);
|
rspeak(REQUIRES_DYNAMITE);
|
||||||
else {
|
else {
|
||||||
|
@ -260,7 +261,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. */
|
/* Break. Only works for mirror in repository and, of course, the
|
||||||
|
* vase. */
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case MIRROR:
|
case MIRROR:
|
||||||
if (game.closed) {
|
if (game.closed) {
|
||||||
|
@ -286,7 +288,8 @@ static phase_codes_t vbreak(verb_t verb, obj_t obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static phase_codes_t brief(void) {
|
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.abbnum = 10000;
|
||||||
game.detail = 3;
|
game.detail = 3;
|
||||||
rspeak(BRIEF_CONFIRM);
|
rspeak(BRIEF_CONFIRM);
|
||||||
|
@ -294,11 +297,12 @@ static phase_codes_t brief(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/* Carry an object. Special cases for bird and cage (if bird in cage,
|
||||||
* take one without the other). Liquids also special, since they depend on
|
* can't take one without the other). Liquids also special, since they
|
||||||
* status of bottle. Also various side effects, etc. */
|
* depend on status of bottle. Also various side effects, etc. */
|
||||||
if (obj == INTRANSITIVE) {
|
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 ||
|
if (game.locs[game.loc].atloc == NO_OBJECT ||
|
||||||
game.link[game.locs[game.loc].atloc] != 0 ||
|
game.link[game.locs[game.loc].atloc] != 0 ||
|
||||||
atdwrf(game.loc) > 0) {
|
atdwrf(game.loc) > 0) {
|
||||||
|
@ -322,16 +326,24 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case PLANT:
|
case PLANT:
|
||||||
/* Next guard tests whether plant is tiny or stashed */
|
/* 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;
|
break;
|
||||||
case BEAR:
|
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;
|
break;
|
||||||
case CHAIN:
|
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;
|
break;
|
||||||
case RUG:
|
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;
|
break;
|
||||||
case URN:
|
case URN:
|
||||||
rspeak(URN_NOBUDGE);
|
rspeak(URN_NOBUDGE);
|
||||||
|
@ -370,10 +382,10 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
||||||
if (game.holdng >= INVLIMIT) {
|
if (game.holdng >= INVLIMIT) {
|
||||||
rspeak(CARRY_LIMIT);
|
rspeak(CARRY_LIMIT);
|
||||||
return GO_CLEAROBJ;
|
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) {
|
if (game.objects[BIRD].prop == BIRD_FOREST_UNCAGED) {
|
||||||
DESTROY(BIRD);
|
DESTROY(BIRD);
|
||||||
rspeak(BIRD_CRAP);
|
rspeak(BIRD_CRAP);
|
||||||
|
@ -390,7 +402,8 @@ static phase_codes_t vcarry(verb_t verb, obj_t obj) {
|
||||||
game.objects[BIRD].prop = BIRD_CAGED;
|
game.objects[BIRD].prop = BIRD_CAGED;
|
||||||
}
|
}
|
||||||
if ((obj == BIRD || obj == CAGE) &&
|
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 */
|
/* expression maps BIRD to CAGE and CAGE to BIRD */
|
||||||
carry(BIRD + CAGE - obj, game.loc);
|
carry(BIRD + CAGE - obj, game.loc);
|
||||||
}
|
}
|
||||||
|
@ -428,9 +441,9 @@ static int chain(verb_t verb) {
|
||||||
switch (game.objects[BEAR].prop) {
|
switch (game.objects[BEAR].prop) {
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case BEAR_DEAD:
|
case BEAR_DEAD:
|
||||||
/* Can't be reached until the bear can die in some way other
|
/* Can't be reached until the bear can die in some way
|
||||||
* than a bridge collapse. Leave in in case this changes, but
|
* other than a bridge collapse. Leave in in case this
|
||||||
* exclude from coverage testing. */
|
* changes, but exclude from coverage testing. */
|
||||||
game.objects[BEAR].fixed = IS_FIXED;
|
game.objects[BEAR].fixed = IS_FIXED;
|
||||||
break;
|
break;
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
@ -461,9 +474,9 @@ static int chain(verb_t verb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/* Discard object. "Throw" also comes here for most objects. Special
|
||||||
* bird (might attack snake or dragon) and cage (might contain bird) and vase.
|
* cases for bird (might attack snake or dragon) and cage (might contain
|
||||||
* Drop coins at vending machine for extra batteries. */
|
* bird) and vase. Drop coins at vending machine for extra batteries. */
|
||||||
if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
|
if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
|
||||||
obj = ROD2;
|
obj = ROD2;
|
||||||
}
|
}
|
||||||
|
@ -473,11 +486,13 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
||||||
return GO_CLEAROBJ;
|
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);
|
rspeak(GEM_FITS);
|
||||||
game.objects[obj].prop = STATE_IN_CAVITY;
|
game.objects[obj].prop = STATE_IN_CAVITY;
|
||||||
game.objects[CAVITY].prop = CAVITY_FULL;
|
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))) {
|
(obj == RUBY && game.objects[RUG].prop == RUG_HOVER))) {
|
||||||
if (obj == RUBY) {
|
if (obj == RUBY) {
|
||||||
rspeak(RUG_SETTLES);
|
rspeak(RUG_SETTLES);
|
||||||
|
@ -487,7 +502,9 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
||||||
rspeak(RUG_RISES);
|
rspeak(RUG_RISES);
|
||||||
}
|
}
|
||||||
if (!TOTING(RUG) || obj == RUBY) {
|
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;
|
game.objects[RUG].prop = k;
|
||||||
if (k == RUG_HOVER) {
|
if (k == RUG_HOVER) {
|
||||||
k = objects[SAPPH].plac;
|
k = objects[SAPPH].plac;
|
||||||
|
@ -526,9 +543,8 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
||||||
|
|
||||||
if (obj == VASE) {
|
if (obj == VASE) {
|
||||||
if (game.loc != objects[PILLOW].plac) {
|
if (game.loc != objects[PILLOW].plac) {
|
||||||
state_change(VASE, AT(PILLOW)
|
state_change(VASE,
|
||||||
? VASE_WHOLE
|
AT(PILLOW) ? VASE_WHOLE : VASE_DROPPED);
|
||||||
: VASE_DROPPED);
|
|
||||||
if (game.objects[VASE].prop != VASE_WHOLE) {
|
if (game.objects[VASE].prop != VASE_WHOLE) {
|
||||||
game.objects[VASE].fixed = IS_FIXED;
|
game.objects[VASE].fixed = IS_FIXED;
|
||||||
}
|
}
|
||||||
|
@ -559,7 +575,8 @@ static phase_codes_t discard(verb_t verb, obj_t obj) {
|
||||||
rspeak(OK_MAN);
|
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);
|
drop(obj, game.loc);
|
||||||
return GO_CLEAROBJ;
|
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) {
|
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
|
/* Drink. If no object, assume water and look for it here. If water
|
||||||
* the bottle, drink that, else must be at a water loc, so drink stream. */
|
* is in the bottle, drink that, else must be at a water loc, so drink
|
||||||
|
* stream. */
|
||||||
if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
|
if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
|
||||||
(LIQUID() != WATER || !HERE(BOTTLE))) {
|
(LIQUID() != WATER || !HERE(BOTTLE))) {
|
||||||
return GO_UNKNOWN;
|
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) {
|
static phase_codes_t eat(verb_t verb, obj_t obj) {
|
||||||
/* Eat. Intransitive: assume food if present, else ask what. Transitive: food
|
/* Eat. Intransitive: assume food if present, else ask what.
|
||||||
* ok, some things lose appetite, rest are ridiculous. */
|
* Transitive: food ok, some things lose appetite, rest are ridiculous.
|
||||||
|
*/
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case INTRANSITIVE:
|
case INTRANSITIVE:
|
||||||
if (!HERE(FOOD))
|
if (!HERE(FOOD))
|
||||||
|
@ -651,9 +670,7 @@ static phase_codes_t extinguish(verb_t verb, obj_t obj) {
|
||||||
break;
|
break;
|
||||||
case LAMP:
|
case LAMP:
|
||||||
state_change(LAMP, LAMP_DARK);
|
state_change(LAMP, LAMP_DARK);
|
||||||
rspeak(DARK(game.loc) ?
|
rspeak(DARK(game.loc) ? PITCH_DARK : NO_MESSAGE);
|
||||||
PITCH_DARK :
|
|
||||||
NO_MESSAGE);
|
|
||||||
break;
|
break;
|
||||||
case DRAGON:
|
case DRAGON:
|
||||||
case VOLCANO:
|
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) {
|
static phase_codes_t feed(verb_t verb, obj_t obj) {
|
||||||
/* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf, make him
|
/* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf,
|
||||||
* mad. Bear, special. */
|
* make him mad. Bear, special. */
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case BIRD:
|
case BIRD:
|
||||||
rspeak(BIRD_PINING);
|
rspeak(BIRD_PINING);
|
||||||
|
@ -796,9 +813,8 @@ phase_codes_t fill(verb_t verb, obj_t obj) {
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
state_change(BOTTLE, (LIQLOC(game.loc) == OIL)
|
state_change(BOTTLE,
|
||||||
? OIL_BOTTLE
|
(LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE);
|
||||||
: WATER_BOTTLE);
|
|
||||||
if (TOTING(BOTTLE)) {
|
if (TOTING(BOTTLE)) {
|
||||||
game.objects[LIQUID()].place = CARRIED;
|
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) {
|
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)) {
|
if (TOTING(obj)) {
|
||||||
rspeak(ALREADY_CARRYING);
|
rspeak(ALREADY_CARRYING);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
|
@ -823,7 +840,6 @@ static phase_codes_t find(verb_t verb, obj_t obj) {
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
speak(actions[verb].message);
|
speak(actions[verb].message);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
@ -871,7 +887,8 @@ static phase_codes_t fly(verb_t verb, obj_t obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static phase_codes_t inven(void) {
|
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;
|
bool empty = true;
|
||||||
for (obj_t i = 1; i <= NOBJECTS; i++) {
|
for (obj_t i = 1; i <= NOBJECTS; i++) {
|
||||||
if (i == BEAR || !TOTING(i))
|
if (i == BEAR || !TOTING(i))
|
||||||
|
@ -893,7 +910,8 @@ 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) {
|
if (obj == INTRANSITIVE) {
|
||||||
int selects = 0;
|
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;
|
obj = LAMP;
|
||||||
selects++;
|
selects++;
|
||||||
}
|
}
|
||||||
|
@ -907,9 +925,9 @@ static phase_codes_t light(verb_t verb, obj_t obj) {
|
||||||
|
|
||||||
switch (obj) {
|
switch (obj) {
|
||||||
case URN:
|
case URN:
|
||||||
state_change(URN, game.objects[URN].prop == URN_EMPTY ?
|
state_change(URN, game.objects[URN].prop == URN_EMPTY
|
||||||
URN_EMPTY :
|
? URN_EMPTY
|
||||||
URN_LIT);
|
: URN_LIT);
|
||||||
break;
|
break;
|
||||||
case LAMP:
|
case LAMP:
|
||||||
if (game.limit < 0) {
|
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) {
|
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;
|
bool soundlatch = false;
|
||||||
vocab_t sound = locations[game.loc].sound;
|
vocab_t sound = locations[game.loc].sound;
|
||||||
if (sound != SILENT) {
|
if (sound != SILENT) {
|
||||||
|
@ -939,7 +958,8 @@ static phase_codes_t listen(void) {
|
||||||
soundlatch = true;
|
soundlatch = true;
|
||||||
}
|
}
|
||||||
for (obj_t i = 1; i <= NOBJECTS; i++) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
int mi = game.objects[i].prop;
|
int mi = game.objects[i].prop;
|
||||||
|
@ -1008,9 +1028,9 @@ static phase_codes_t lock(verb_t verb, obj_t obj) {
|
||||||
}
|
}
|
||||||
game.panic = true;
|
game.panic = true;
|
||||||
} else {
|
} else {
|
||||||
state_change(GRATE, (verb == LOCK) ?
|
state_change(GRATE, (verb == LOCK)
|
||||||
GRATE_CLOSED :
|
? GRATE_CLOSED
|
||||||
GRATE_OPEN);
|
: GRATE_OPEN);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rspeak(NO_KEYS);
|
rspeak(NO_KEYS);
|
||||||
|
@ -1042,7 +1062,8 @@ static phase_codes_t lock(verb_t verb, obj_t obj) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DOOR:
|
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;
|
break;
|
||||||
case CAGE:
|
case CAGE:
|
||||||
rspeak(NO_LOCK);
|
rspeak(NO_LOCK);
|
||||||
|
@ -1087,7 +1108,8 @@ static phase_codes_t pour(verb_t verb, obj_t obj) {
|
||||||
if (!AT(DOOR)) {
|
if (!AT(DOOR)) {
|
||||||
if (obj == WATER) {
|
if (obj == WATER) {
|
||||||
/* cycle through the three plant states */
|
/* 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;
|
game.objects[PLANT2].prop = game.objects[PLANT].prop;
|
||||||
return GO_MOVE;
|
return GO_MOVE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1095,16 +1117,16 @@ static phase_codes_t pour(verb_t verb, obj_t obj) {
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state_change(DOOR, (obj == OIL) ?
|
state_change(DOOR, (obj == OIL) ? DOOR_UNRUSTED : DOOR_RUSTED);
|
||||||
DOOR_UNRUSTED :
|
|
||||||
DOOR_RUSTED);
|
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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. */
|
/* Quit. Intransitive only. Verify intent and exit if that's what he
|
||||||
if (yes_or_no(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) {
|
* wants. */
|
||||||
|
if (yes_or_no(arbitrary_messages[REALLY_QUIT],
|
||||||
|
arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) {
|
||||||
terminate(quitgame);
|
terminate(quitgame);
|
||||||
}
|
}
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
|
@ -1116,11 +1138,13 @@ static phase_codes_t read(command_t command)
|
||||||
if (command.obj == INTRANSITIVE) {
|
if (command.obj == INTRANSITIVE) {
|
||||||
command.obj = NO_OBJECT;
|
command.obj = NO_OBJECT;
|
||||||
for (int i = 1; i <= NOBJECTS; i++) {
|
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;
|
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;
|
return GO_UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1131,14 +1155,19 @@ static phase_codes_t read(command_t command)
|
||||||
if (!TOTING(OYSTER) || !game.closed) {
|
if (!TOTING(OYSTER) || !game.closed) {
|
||||||
rspeak(DONT_UNDERSTAND);
|
rspeak(DONT_UNDERSTAND);
|
||||||
} else if (!game.clshnt) {
|
} 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 {
|
} 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);
|
speak(actions[command.verb].message);
|
||||||
} else {
|
} else {
|
||||||
pspeak(command.obj, study, true, game.objects[command.obj].prop);
|
pspeak(command.obj, study, true,
|
||||||
|
game.objects[command.obj].prop);
|
||||||
}
|
}
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
@ -1149,8 +1178,9 @@ static phase_codes_t reservoir(void) {
|
||||||
rspeak(NOTHING_HAPPENS);
|
rspeak(NOTHING_HAPPENS);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
} else {
|
} else {
|
||||||
state_change(RESER,
|
state_change(RESER, game.objects[RESER].prop == WATERS_PARTED
|
||||||
game.objects[RESER].prop == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED);
|
? WATERS_UNPARTED
|
||||||
|
: WATERS_PARTED);
|
||||||
if (AT(RESER))
|
if (AT(RESER))
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
else {
|
else {
|
||||||
|
@ -1182,8 +1212,7 @@ static phase_codes_t rub(verb_t verb, obj_t obj) {
|
||||||
static phase_codes_t say(command_t command) {
|
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 &&
|
if (command.word[1].type == MOTION &&
|
||||||
(command.word[1].id == XYZZY ||
|
(command.word[1].id == XYZZY || command.word[1].id == PLUGH ||
|
||||||
command.word[1].id == PLUGH ||
|
|
||||||
command.word[1].id == PLOVER)) {
|
command.word[1].id == PLOVER)) {
|
||||||
return GO_WORD2;
|
return GO_WORD2;
|
||||||
}
|
}
|
||||||
|
@ -1192,20 +1221,16 @@ static phase_codes_t say(command_t command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.word[1].type == ACTION &&
|
if (command.word[1].type == ACTION &&
|
||||||
(command.word[1].id == FEE ||
|
(command.word[1].id == FEE || command.word[1].id == FIE ||
|
||||||
command.word[1].id == FIE ||
|
command.word[1].id == FOE || command.word[1].id == FOO ||
|
||||||
command.word[1].id == FOE ||
|
command.word[1].id == FUM || command.word[1].id == PART)) {
|
||||||
command.word[1].id == FOO ||
|
|
||||||
command.word[1].id == FUM ||
|
|
||||||
command.word[1].id == PART)) {
|
|
||||||
return bigwords(command.word[1].id);
|
return bigwords(command.word[1].id);
|
||||||
}
|
}
|
||||||
sspeak(OKEY_DOKEY, command.word[1].raw);
|
sspeak(OKEY_DOKEY, command.word[1].raw);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
static phase_codes_t throw_support(vocab_t spk)
|
static phase_codes_t throw_support(vocab_t spk) {
|
||||||
{
|
|
||||||
rspeak(spk);
|
rspeak(spk);
|
||||||
drop(AXE, game.loc);
|
drop(AXE, game.loc);
|
||||||
return GO_MOVE;
|
return GO_MOVE;
|
||||||
|
@ -1240,7 +1265,8 @@ static phase_codes_t throwit(command_t command) {
|
||||||
return (discard(command.verb, command.obj));
|
return (discard(command.verb, command.obj));
|
||||||
} else {
|
} else {
|
||||||
if (atdwrf(game.loc) <= 0) {
|
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);
|
return throw_support(DRAGON_SCALES);
|
||||||
if (AT(TROLL)) {
|
if (AT(TROLL)) {
|
||||||
return throw_support(TROLL_RETURNS);
|
return throw_support(TROLL_RETURNS);
|
||||||
|
@ -1248,8 +1274,10 @@ static phase_codes_t throwit(command_t command) {
|
||||||
if (AT(OGRE)) {
|
if (AT(OGRE)) {
|
||||||
return throw_support(OGRE_DODGE);
|
return throw_support(OGRE_DODGE);
|
||||||
}
|
}
|
||||||
if (HERE(BEAR) && game.objects[BEAR].prop == UNTAMED_BEAR) {
|
if (HERE(BEAR) &&
|
||||||
/* This'll teach him to throw the axe at the bear! */
|
game.objects[BEAR].prop == UNTAMED_BEAR) {
|
||||||
|
/* This'll teach him to throw the axe at the
|
||||||
|
* bear! */
|
||||||
drop(AXE, game.loc);
|
drop(AXE, game.loc);
|
||||||
game.objects[AXE].fixed = IS_FIXED;
|
game.objects[AXE].fixed = IS_FIXED;
|
||||||
juggle(BEAR);
|
juggle(BEAR);
|
||||||
|
@ -1266,9 +1294,8 @@ static phase_codes_t throwit(command_t command) {
|
||||||
int i = atdwrf(game.loc);
|
int i = atdwrf(game.loc);
|
||||||
game.dwarves[i].seen = false;
|
game.dwarves[i].seen = false;
|
||||||
game.dwarves[i].loc = LOC_NOWHERE;
|
game.dwarves[i].loc = LOC_NOWHERE;
|
||||||
return throw_support((++game.dkill == 1) ?
|
return throw_support(
|
||||||
DWARF_SMOKE :
|
(++game.dkill == 1) ? DWARF_SMOKE : KILLED_DWARF);
|
||||||
KILLED_DWARF);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1302,16 +1329,16 @@ static phase_codes_t waste(verb_t verb, turn_t turns) {
|
||||||
|
|
||||||
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. */
|
/* Wave. No effect unless waving rod at fissure or at bird. */
|
||||||
if (obj != ROD || !TOTING(obj) || (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
|
if (obj != ROD || !TOTING(obj) ||
|
||||||
speak(((!TOTING(obj)) && (obj != ROD ||
|
(!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
|
||||||
!TOTING(ROD2))) ?
|
speak(((!TOTING(obj)) && (obj != ROD || !TOTING(ROD2)))
|
||||||
arbitrary_messages[ARENT_CARRYING] :
|
? arbitrary_messages[ARENT_CARRYING]
|
||||||
actions[verb].message);
|
: actions[verb].message);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.objects[BIRD].prop == BIRD_UNCAGED && game.loc == game.objects[STEPS].place
|
if (game.objects[BIRD].prop == BIRD_UNCAGED &&
|
||||||
&& PROP_IS_NOTFOUND(JADE)) {
|
game.loc == game.objects[STEPS].place && PROP_IS_NOTFOUND(JADE)) {
|
||||||
drop(JADE, game.loc);
|
drop(JADE, game.loc);
|
||||||
PROP_SET_FOUND(JADE);
|
PROP_SET_FOUND(JADE);
|
||||||
--game.tally;
|
--game.tally;
|
||||||
|
@ -1319,35 +1346,37 @@ static phase_codes_t wave(verb_t verb, obj_t obj) {
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
} else {
|
} else {
|
||||||
if (game.closed) {
|
if (game.closed) {
|
||||||
rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
|
rspeak((game.objects[BIRD].prop == BIRD_CAGED)
|
||||||
CAGE_FLY :
|
? CAGE_FLY
|
||||||
FREE_FLY);
|
: FREE_FLY);
|
||||||
return GO_DWARFWAKE;
|
return GO_DWARFWAKE;
|
||||||
}
|
}
|
||||||
if (game.closng || !AT(FISSURE)) {
|
if (game.closng || !AT(FISSURE)) {
|
||||||
rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
|
rspeak((game.objects[BIRD].prop == BIRD_CAGED)
|
||||||
CAGE_FLY :
|
? CAGE_FLY
|
||||||
FREE_FLY);
|
: FREE_FLY);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
if (HERE(BIRD))
|
if (HERE(BIRD))
|
||||||
rspeak((game.objects[BIRD].prop == BIRD_CAGED) ?
|
rspeak((game.objects[BIRD].prop == BIRD_CAGED)
|
||||||
CAGE_FLY :
|
? CAGE_FLY
|
||||||
FREE_FLY);
|
: FREE_FLY);
|
||||||
|
|
||||||
state_change(FISSURE,
|
state_change(FISSURE, game.objects[FISSURE].prop == BRIDGED
|
||||||
game.objects[FISSURE].prop == BRIDGED ? UNBRIDGED : BRIDGED);
|
? UNBRIDGED
|
||||||
|
: BRIDGED);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/* Analyse a verb. Remember what it was, go back for object if second
|
||||||
* unless verb is "say", which snarfs arbitrary second word.
|
* word unless verb is "say", which snarfs arbitrary second word.
|
||||||
*/
|
*/
|
||||||
/* Previously, actions that result in a message, but don't do anything
|
/* Previously, actions that result in a message, but don't do anything
|
||||||
* further were called "specials". Now they're handled here as normal
|
* 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) {
|
if (actions[command.verb].noaction) {
|
||||||
speak(actions[command.verb].message);
|
speak(actions[command.verb].message);
|
||||||
return GO_CLEAROBJ;
|
return GO_CLEAROBJ;
|
||||||
|
@ -1365,13 +1394,16 @@ phase_codes_t action(command_t command) {
|
||||||
/* FALL THROUGH */;
|
/* FALL THROUGH */;
|
||||||
} else if (command.obj == DWARF && atdwrf(game.loc) > 0) {
|
} else if (command.obj == DWARF && atdwrf(game.loc) > 0) {
|
||||||
/* FALL THROUGH */;
|
/* 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))) {
|
command.obj == LIQLOC(game.loc))) {
|
||||||
/* FALL THROUGH */;
|
/* 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;
|
command.obj = URN;
|
||||||
/* FALL THROUGH */;
|
/* 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;
|
command.obj = PLANT2;
|
||||||
/* FALL THROUGH */;
|
/* FALL THROUGH */;
|
||||||
} else if (command.obj == KNIFE && game.knfloc == game.loc) {
|
} else if (command.obj == KNIFE && game.knfloc == game.loc) {
|
||||||
|
@ -1382,7 +1414,9 @@ phase_codes_t action(command_t command) {
|
||||||
command.obj = ROD2;
|
command.obj = ROD2;
|
||||||
/* FALL THROUGH */;
|
/* FALL THROUGH */;
|
||||||
} else if ((command.verb == FIND ||
|
} 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 */;
|
/* FALL THROUGH */;
|
||||||
} else {
|
} else {
|
||||||
sspeak(NO_SEE, command.word[0].raw);
|
sspeak(NO_SEE, command.word[0].raw);
|
||||||
|
@ -1400,13 +1434,16 @@ phase_codes_t action(command_t command) {
|
||||||
return GO_WORD2;
|
return GO_WORD2;
|
||||||
}
|
}
|
||||||
if (command.verb == SAY) {
|
if (command.verb == SAY) {
|
||||||
/* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE
|
/* KEYS is not special, anything not NO_OBJECT or
|
||||||
* will do here. We're preventing interpretation as an intransitive
|
* INTRANSITIVE will do here. We're preventing
|
||||||
* verb when the word is unknown. */
|
* interpretation as an intransitive verb when the word
|
||||||
command.obj = command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
|
* is unknown. */
|
||||||
|
command.obj =
|
||||||
|
command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
|
||||||
}
|
}
|
||||||
if (command.obj == NO_OBJECT || command.obj == INTRANSITIVE) {
|
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) {
|
switch (command.verb) {
|
||||||
case CARRY:
|
case CARRY:
|
||||||
return vcarry(command.verb, INTRANSITIVE);
|
return vcarry(command.verb, INTRANSITIVE);
|
||||||
|
@ -1592,7 +1629,8 @@ phase_codes_t action(command_t command) {
|
||||||
case SEED:
|
case SEED:
|
||||||
return seed(command.verb, command.word[1].raw);
|
return seed(command.verb, command.word[1].raw);
|
||||||
case WASTE:
|
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
|
default: // LCOV_EXCL_LINE
|
||||||
BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
|
BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
73
advent.h
73
advent.h
|
@ -4,11 +4,11 @@
|
||||||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "dungeon.h"
|
#include "dungeon.h"
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@
|
||||||
#define FLASHTIME 50 // turns from first warning till blinding flash
|
#define FLASHTIME 50 // turns from first warning till blinding flash
|
||||||
#define PANICTIME 15 // time left after closing
|
#define PANICTIME 15 // time left after closing
|
||||||
#define BATTERYLIFE 2500 // turn limit increment from batteries
|
#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 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 PIT_KILL_PROB 35 // Percentage probability of dying from fall in pit.
|
||||||
#define CARRIED -1 // Player is toting it
|
#define CARRIED -1 // Player is toting it
|
||||||
|
@ -89,9 +90,15 @@
|
||||||
#define PROP_STASHIFY(n) (-(n))
|
#define PROP_STASHIFY(n) (-(n))
|
||||||
#define PROP_IS_STASHED(obj) (game.objects[obj].prop < 0)
|
#define PROP_IS_STASHED(obj) (game.objects[obj].prop < 0)
|
||||||
#define PROP_IS_NOTFOUND(obj) (!game.objects[obj].found)
|
#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_FOUND(obj) \
|
||||||
#define PROP_IS_STASHED_OR_UNSEEN(obj) (!game.objects[obj].found || game.objects[obj].prop < 0)
|
(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_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_SET_NOT_FOUND(obj) game.objects[obj].found = false
|
||||||
#define PROP_IS_NOTFOUND2(g, o) (!g.objects[o].found)
|
#define PROP_IS_NOTFOUND2(g, o) (!g.objects[o].found)
|
||||||
#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE)
|
#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE)
|
||||||
|
@ -116,22 +123,32 @@
|
||||||
* GSTONE(OBJ) = true if OBJ is a gemstone
|
* GSTONE(OBJ) = true if OBJ is a gemstone
|
||||||
* FOREST(LOC) = true if LOC is part of the forest
|
* FOREST(LOC) = true if LOC is part of the forest
|
||||||
* OUTSID(LOC) = true if location not in the cave
|
* 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
|
* INSIDE(LOC) = true if location is in the cave or the building at the
|
||||||
* INDEEP(LOC) = true if location is in the Hall of Mists or deeper
|
* beginning of the game INDEEP(LOC) = true if location is in the Hall of Mists
|
||||||
* BUG(X) = report bug and exit
|
* or deeper BUG(X) = report bug and exit
|
||||||
*/
|
*/
|
||||||
#define DESTROY(N) move(N, LOC_NOWHERE)
|
#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 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 HERE(OBJ) (AT(OBJ) || TOTING(OBJ))
|
||||||
#define CNDBIT(L, N) (tstbit(conditions[L], N))
|
#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 LIQUID() \
|
||||||
#define LIQLOC(LOC) (CNDBIT((LOC),COND_FLUID)? CNDBIT((LOC),COND_OILY) ? OIL : WATER : NO_OBJECT)
|
(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 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 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 FOREST(LOC) CNDBIT(LOC, COND_FOREST)
|
||||||
#define OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC))
|
#define OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC))
|
||||||
#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING)
|
#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING)
|
||||||
|
@ -204,9 +221,9 @@ struct game_t {
|
||||||
/* dflag controls the level of activation of dwarves:
|
/* dflag controls the level of activation of dwarves:
|
||||||
* 0 No dwarf stuff yet (wait until reaches Hall Of Mists)
|
* 0 No dwarf stuff yet (wait until reaches Hall Of Mists)
|
||||||
* 1 Reached Hall Of Mists, but hasn't met first dwarf
|
* 1 Reached Hall Of Mists, but hasn't met first dwarf
|
||||||
* 2 Met first dwarf, others start moving, no knives thrown yet
|
* 2 Met first dwarf, others start moving, no knives thrown
|
||||||
* 3 A knife has been thrown (first set always misses)
|
*yet 3 A knife has been thrown (first set always misses) 3+
|
||||||
* 3+ Dwarves are mad (increases their accuracy) */
|
*Dwarves are mad (increases their accuracy) */
|
||||||
int32_t dflag;
|
int32_t dflag;
|
||||||
|
|
||||||
int32_t dkill; // dwarves killed
|
int32_t dkill; // dwarves killed
|
||||||
|
@ -275,7 +292,15 @@ typedef struct {
|
||||||
word_type_t type;
|
word_type_t type;
|
||||||
} command_word_t;
|
} 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 {
|
typedef struct {
|
||||||
enum speechpart part;
|
enum speechpart part;
|
||||||
|
@ -288,9 +313,9 @@ typedef struct {
|
||||||
/*
|
/*
|
||||||
* Bump on save format change.
|
* Bump on save format change.
|
||||||
*
|
*
|
||||||
* Note: Verify that the tests run clean before bumping this, then rebuild the check
|
* Note: Verify that the tests run clean before bumping this, then rebuild the
|
||||||
* files afterwards. Otherwise you will get a spurious failure due to the old version
|
* check files afterwards. Otherwise you will get a spurious failure due to the
|
||||||
* having been generated into a check file.
|
* old version having been generated into a check file.
|
||||||
*/
|
*/
|
||||||
#define SAVE_VERSION 31
|
#define SAVE_VERSION 31
|
||||||
|
|
||||||
|
@ -301,8 +326,8 @@ typedef struct {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you change the first three members, the resume function may not properly
|
* 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
|
* reject saves from older versions. Later members can change, but bump the
|
||||||
* when you do that.
|
* version when you do that.
|
||||||
*/
|
*/
|
||||||
struct save_t {
|
struct save_t {
|
||||||
char magic[sizeof(ADVENT_MAGIC)];
|
char magic[sizeof(ADVENT_MAGIC)];
|
||||||
|
|
36
cheat.c
36
cheat.c
|
@ -7,15 +7,14 @@
|
||||||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* 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 "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;
|
int ch;
|
||||||
char *savefilename = NULL;
|
char *savefilename = NULL;
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
@ -30,7 +29,9 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
/* Options. */
|
/* Options. */
|
||||||
const char *opts = "d:l:s:t:v:o:";
|
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 *usage =
|
||||||
|
"Usage: %s [-d numdie] [-s numsaves] [-v version] -o savefilename "
|
||||||
|
"\n"
|
||||||
" -d number of deaths. Signed integer.\n"
|
" -d number of deaths. Signed integer.\n"
|
||||||
" -l lifetime of lamp in turns. Signed integer.\n"
|
" -l lifetime of lamp in turns. Signed integer.\n"
|
||||||
" -s number of saves. Signed integer.\n"
|
" -s number of saves. Signed integer.\n"
|
||||||
|
@ -64,8 +65,7 @@ int main(int argc, char *argv[])
|
||||||
savefilename = optarg;
|
savefilename = optarg;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr,
|
fprintf(stderr, usage, argv[0]);
|
||||||
usage, argv[0]);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -73,17 +73,14 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
// Save filename required; the point of cheat is to generate save file
|
// Save filename required; the point of cheat is to generate save file
|
||||||
if (savefilename == NULL) {
|
if (savefilename == NULL) {
|
||||||
fprintf(stderr,
|
fprintf(stderr, usage, argv[0]);
|
||||||
usage, argv[0]);
|
fprintf(stderr, "ERROR: filename required\n");
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: filename required\n");
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = fopen(savefilename, WRITE_MODE);
|
fp = fopen(savefilename, WRITE_MODE);
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
fprintf(stderr,
|
fprintf(stderr, "Can't open file %s. Exiting.\n", savefilename);
|
||||||
"Can't open file %s. Exiting.\n", savefilename);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,12 +99,7 @@ int main(int argc, char *argv[])
|
||||||
* See the actually useful version of this in main.c
|
* See the actually useful version of this in main.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *myreadline(const char *prompt)
|
char *myreadline(const char *prompt) { return readline(prompt); }
|
||||||
{
|
|
||||||
return readline(prompt);
|
|
||||||
}
|
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
/* end */
|
/* end */
|
||||||
|
|
||||||
|
|
||||||
|
|
31
init.c
31
init.c
|
@ -5,39 +5,28 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* 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 <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "advent.h"
|
#include "advent.h"
|
||||||
|
|
||||||
struct settings_t settings = {
|
struct settings_t settings = {.logfp = NULL, .oldstyle = false, .prompt = true};
|
||||||
.logfp = NULL,
|
|
||||||
.oldstyle = false,
|
|
||||||
.prompt = true
|
|
||||||
};
|
|
||||||
|
|
||||||
struct game_t game = {
|
struct game_t game = {
|
||||||
/* Last dwarf is special (the pirate). He always starts at his
|
/* Last dwarf is special (the pirate). He always starts at his
|
||||||
* chest's eventual location inside the maze. This loc is saved
|
* chest's eventual location inside the maze. This loc is saved
|
||||||
* in chloc for ref. The dead end in the other maze has its
|
* in chloc for ref. The dead end in the other maze has its
|
||||||
* loc stored in chloc2. */
|
* loc stored in chloc2. */
|
||||||
.chloc = LOC_MAZEEND12,
|
.chloc = LOC_MAZEEND12, .chloc2 = LOC_DEADEND13, .abbnum = 5,
|
||||||
.chloc2 = LOC_DEADEND13,
|
.clock1 = WARNTIME, .clock2 = FLASHTIME, .newloc = LOC_START,
|
||||||
.abbnum = 5,
|
.loc = LOC_START, .limit = GAMELIMIT, .foobar = WORD_EMPTY,
|
||||||
.clock1 = WARNTIME,
|
|
||||||
.clock2 = FLASHTIME,
|
|
||||||
.newloc = LOC_START,
|
|
||||||
.loc = LOC_START,
|
|
||||||
.limit = GAMELIMIT,
|
|
||||||
.foobar = WORD_EMPTY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int initialise(void)
|
int initialise(void) {
|
||||||
{
|
|
||||||
if (settings.oldstyle)
|
if (settings.oldstyle)
|
||||||
printf("Initialising...\n");
|
printf("Initialising...\n");
|
||||||
|
|
||||||
|
|
276
misc.c
276
misc.c
|
@ -5,25 +5,25 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* 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 <ctype.h>
|
||||||
#include <editline/readline.h>
|
#include <editline/readline.h>
|
||||||
#include <inttypes.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 "advent.h"
|
||||||
#include "dungeon.h"
|
#include "dungeon.h"
|
||||||
|
|
||||||
static void* xcalloc(size_t size)
|
static void *xcalloc(size_t size) {
|
||||||
{
|
|
||||||
void *ptr = calloc(size, 1);
|
void *ptr = calloc(size, 1);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
// LCOV_EXCL_START
|
// 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");
|
fprintf(stderr, "Out of memory!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
@ -33,19 +33,21 @@ static void* xcalloc(size_t size)
|
||||||
|
|
||||||
/* I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */
|
/* I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */
|
||||||
|
|
||||||
static void vspeak(const char* msg, bool blank, va_list ap)
|
static void vspeak(const char *msg, bool blank, va_list ap) {
|
||||||
/* Engine for various speak functions */
|
/* Engine for various speak functions */
|
||||||
{
|
|
||||||
// Do nothing if we got a null pointer.
|
// Do nothing if we got a null pointer.
|
||||||
if (msg == NULL)
|
if (msg == NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Do nothing if we got an empty string.
|
// Do nothing if we got an empty string.
|
||||||
if (strlen(msg) == 0)
|
if (strlen(msg) == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (blank == true)
|
if (blank == true) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
int msglen = strlen(msg);
|
int msglen = strlen(msg);
|
||||||
|
|
||||||
|
@ -60,9 +62,11 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
||||||
bool pluralize = false;
|
bool pluralize = false;
|
||||||
for (int i = 0; i < msglen; i++) {
|
for (int i = 0; i < msglen; i++) {
|
||||||
if (msg[i] != '%') {
|
if (msg[i] != '%') {
|
||||||
/* Ugh. Least obtrusive way to deal with artifacts "on the floor"
|
/* Ugh. Least obtrusive way to deal with artifacts "on
|
||||||
* being dropped outside of both cave and building. */
|
* the floor" being dropped outside of both cave and
|
||||||
if (strncmp(msg + i, "floor", 5) == 0 && strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) {
|
* building. */
|
||||||
|
if (strncmp(msg + i, "floor", 5) == 0 &&
|
||||||
|
strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) {
|
||||||
strcpy(renderp, "ground");
|
strcpy(renderp, "ground");
|
||||||
renderp += 6;
|
renderp += 6;
|
||||||
i += 4;
|
i += 4;
|
||||||
|
@ -76,7 +80,8 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
||||||
// Integer specifier.
|
// Integer specifier.
|
||||||
if (msg[i] == 'd') {
|
if (msg[i] == 'd') {
|
||||||
int32_t arg = va_arg(ap, int32_t);
|
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) {
|
if (ret < size) {
|
||||||
renderp += ret;
|
renderp += ret;
|
||||||
size -= ret;
|
size -= ret;
|
||||||
|
@ -121,18 +126,16 @@ static void vspeak(const char* msg, bool blank, va_list ap)
|
||||||
free(rendered);
|
free(rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
void speak(const char* msg, ...)
|
void speak(const char *msg, ...) {
|
||||||
/* speak a specified string */
|
/* speak a specified string */
|
||||||
{
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
vspeak(msg, true, ap);
|
vspeak(msg, true, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sspeak(const int msg, ...)
|
void sspeak(const int msg, ...) {
|
||||||
/* Speak a message from the arbitrary-messages list */
|
/* Speak a message from the arbitrary-messages list */
|
||||||
{
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
|
@ -141,13 +144,12 @@ void sspeak(const int msg, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...)
|
void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...) {
|
||||||
/* Find the skip+1st message from msg and print it. Modes are:
|
/* Find the skip+1st message from msg and print it. Modes are:
|
||||||
* feel = for inventory, what you can touch
|
* feel = for inventory, what you can touch
|
||||||
* look = the full description for the state the object is in
|
* look = the full description for the state the object is in
|
||||||
* listen = the sound for the state the object is in
|
* listen = the sound for the state the object is in
|
||||||
* study = text on the object. */
|
* study = text on the object. */
|
||||||
{
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, skip);
|
va_start(ap, skip);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
@ -170,17 +172,16 @@ void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rspeak(vocab_t i, ...)
|
void rspeak(vocab_t i, ...) {
|
||||||
/* Print the i-th "random" message (section 6 of database). */
|
/* Print the i-th "random" message (section 6 of database). */
|
||||||
{
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, i);
|
va_start(ap, i);
|
||||||
vspeak(arbitrary_messages[i], true, ap);
|
vspeak(arbitrary_messages[i], true, ap);
|
||||||
va_end(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;
|
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);
|
strcpy(prompt_and_input, input_prompt);
|
||||||
|
@ -189,8 +190,7 @@ void echo_input(FILE* destination, const char* input_prompt, const char* input)
|
||||||
free(prompt_and_input);
|
free(prompt_and_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int word_count(char* str)
|
static int word_count(char *str) {
|
||||||
{
|
|
||||||
char delims[] = " \t";
|
char delims[] = " \t";
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int inblanks = true;
|
int inblanks = true;
|
||||||
|
@ -210,8 +210,7 @@ static int word_count(char* str)
|
||||||
return (count);
|
return (count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* get_input(void)
|
static char *get_input(void) {
|
||||||
{
|
|
||||||
// Set up the prompt
|
// Set up the prompt
|
||||||
char input_prompt[] = PROMPT;
|
char input_prompt[] = PROMPT;
|
||||||
if (!settings.prompt)
|
if (!settings.prompt)
|
||||||
|
@ -248,8 +247,7 @@ static char* get_input(void)
|
||||||
return (input);
|
return (input);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool silent_yes_or_no(void)
|
bool silent_yes_or_no(void) {
|
||||||
{
|
|
||||||
bool outcome = false;
|
bool outcome = false;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -294,11 +292,10 @@ bool silent_yes_or_no(void)
|
||||||
return (outcome);
|
return (outcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool yes_or_no(const char *question, const char *yes_response,
|
||||||
bool yes_or_no(const char* question, const char* yes_response, const char* no_response)
|
const char *no_response) {
|
||||||
/* Print message X, wait for yes/no answer. If yes, print Y and return true;
|
/* Print message X, wait for yes/no answer. If yes, print Y and return
|
||||||
* if no, print Z and return false. */
|
* true; if no, print Z and return false. */
|
||||||
{
|
|
||||||
bool outcome = false;
|
bool outcome = false;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -324,8 +321,9 @@ bool yes_or_no(const char* question, const char* yes_response, const char* no_re
|
||||||
|
|
||||||
free(reply);
|
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]);
|
firstword[i] = tolower(firstword[i]);
|
||||||
|
}
|
||||||
|
|
||||||
int yes = strncmp("yes", firstword, sizeof("yes") - 1);
|
int yes = strncmp("yes", firstword, sizeof("yes") - 1);
|
||||||
int y = strncmp("y", firstword, sizeof("y") - 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;
|
break;
|
||||||
} else
|
} else
|
||||||
rspeak(PLEASE_ANSWER);
|
rspeak(PLEASE_ANSWER);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (outcome);
|
return (outcome);
|
||||||
|
@ -352,12 +349,13 @@ bool yes_or_no(const char* question, const char* yes_response, const char* no_re
|
||||||
|
|
||||||
/* Data structure routines */
|
/* Data structure routines */
|
||||||
|
|
||||||
static int get_motion_vocab_id(const char* word)
|
static int get_motion_vocab_id(const char *word) {
|
||||||
// Return the first motion number that has 'word' as one of its words.
|
// Return the first motion number that has 'word' as one of its words.
|
||||||
{
|
|
||||||
for (int i = 0; i < NMOTIONS; ++i) {
|
for (int i = 0; i < NMOTIONS; ++i) {
|
||||||
for (int j = 0; j < motions[i].words.n; ++j) {
|
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 ||
|
strchr(ignore, word[0]) == NULL ||
|
||||||
!settings.oldstyle))
|
!settings.oldstyle))
|
||||||
return (i);
|
return (i);
|
||||||
|
@ -367,12 +365,14 @@ static int get_motion_vocab_id(const char* word)
|
||||||
return (WORD_NOT_FOUND);
|
return (WORD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_object_vocab_id(const char* word)
|
static int get_object_vocab_id(const char *word) {
|
||||||
// Return the first object number that has 'word' as one of its words.
|
// Return the first object number that has 'word' as one of its words.
|
||||||
{
|
for (int i = 0; i < NOBJECTS + 1;
|
||||||
for (int i = 0; i < NOBJECTS + 1; ++i) { // FIXME: the + 1 should go when 1-indexing for objects is removed
|
++i) { // FIXME: the + 1 should go when 1-indexing for objects is
|
||||||
|
// removed
|
||||||
for (int j = 0; j < objects[i].words.n; ++j) {
|
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);
|
return (i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,47 +380,51 @@ static int get_object_vocab_id(const char* word)
|
||||||
return (WORD_NOT_FOUND);
|
return (WORD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_action_vocab_id(const char* word)
|
static int get_action_vocab_id(const char *word) {
|
||||||
// Return the first motion number that has 'word' as one of its words.
|
// Return the first motion number that has 'word' as one of its words.
|
||||||
{
|
|
||||||
for (int i = 0; i < NACTIONS; ++i) {
|
for (int i = 0; i < NACTIONS; ++i) {
|
||||||
for (int j = 0; j < actions[i].words.n; ++j) {
|
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 ||
|
strchr(ignore, word[0]) == NULL ||
|
||||||
!settings.oldstyle))
|
!settings.oldstyle)) {
|
||||||
return (i);
|
return (i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If execution reaches here, we didn't find the word.
|
// If execution reaches here, we didn't find the word.
|
||||||
return (WORD_NOT_FOUND);
|
return (WORD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_valid_int(const char *str)
|
static bool is_valid_int(const char *str) {
|
||||||
/* Returns true if the string passed in is represents a valid integer,
|
/* Returns true if the string passed in is represents a valid integer,
|
||||||
* that could then be parsed by atoi() */
|
* that could then be parsed by atoi() */
|
||||||
{
|
|
||||||
// Handle negative number
|
// Handle negative number
|
||||||
if (*str == '-')
|
if (*str == '-') {
|
||||||
++str;
|
++str;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle empty string or just "-". Should never reach this
|
// Handle empty string or just "-". Should never reach this
|
||||||
// point, because this is only used with transitive verbs.
|
// point, because this is only used with transitive verbs.
|
||||||
if (!*str)
|
if (!*str) {
|
||||||
return false; // LCOV_EXCL_LINE
|
return false; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
// Check for non-digit chars in the rest of the string.
|
// Check for non-digit chars in the rest of the string.
|
||||||
while (*str) {
|
while (*str) {
|
||||||
if (!isdigit(*str))
|
if (!isdigit(*str)) {
|
||||||
return false;
|
return false;
|
||||||
else
|
} else {
|
||||||
++str;
|
++str;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
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 */
|
/* Check for an empty string */
|
||||||
if (strncmp(word, "", sizeof("")) == 0) {
|
if (strncmp(word, "", sizeof("")) == 0) {
|
||||||
*id = WORD_EMPTY;
|
*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;
|
vocab_t ref_num;
|
||||||
|
|
||||||
ref_num = get_motion_vocab_id(word);
|
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) {
|
if (ref_num != WORD_NOT_FOUND) {
|
||||||
*id = ref_num;
|
*id = ref_num;
|
||||||
*type = MOTION;
|
*type = MOTION;
|
||||||
|
@ -471,8 +476,7 @@ static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type)
|
||||||
return;
|
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
|
* Be careful about modifying this. We do not want to nuke the
|
||||||
* the speech part or ID from the previous turn.
|
* the speech part or ID from the previous turn.
|
||||||
|
@ -500,22 +504,26 @@ static void tokenize(char* raw, command_t *cmd)
|
||||||
* possible an emulation of the original UI.
|
* possible an emulation of the original UI.
|
||||||
*/
|
*/
|
||||||
if (settings.oldstyle) {
|
if (settings.oldstyle) {
|
||||||
cmd->word[0].raw[TOKLEN + TOKLEN] = cmd->word[1].raw[TOKLEN + TOKLEN] = '\0';
|
cmd->word[0].raw[TOKLEN + TOKLEN] =
|
||||||
for (size_t i = 0; i < strlen(cmd->word[0].raw); i++)
|
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]);
|
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]);
|
cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* populate command with parsed vocabulary metadata */
|
/* 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[0].raw, &(cmd->word[0].id),
|
||||||
get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id), &(cmd->word[1].type));
|
&(cmd->word[0].type));
|
||||||
|
get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id),
|
||||||
|
&(cmd->word[1].type));
|
||||||
cmd->state = TOKENIZED;
|
cmd->state = TOKENIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_command_input(command_t *command)
|
bool get_command_input(command_t *command) {
|
||||||
/* Get user input on stdin, parse and map to command */
|
/* Get user input on stdin, parse and map to command */
|
||||||
{
|
|
||||||
char inputbuf[LINESIZE];
|
char inputbuf[LINESIZE];
|
||||||
char *input;
|
char *input;
|
||||||
|
|
||||||
|
@ -528,8 +536,9 @@ bool get_command_input(command_t *command)
|
||||||
free(input);
|
free(input);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strcmp(input, "") != 0)
|
if (strcmp(input, "") != 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
free(input);
|
free(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,14 +549,14 @@ bool get_command_input(command_t *command)
|
||||||
|
|
||||||
#ifdef GDEBUG
|
#ifdef GDEBUG
|
||||||
/* Needs to stay synced with enum word_type_t */
|
/* 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 */
|
/* needs to stay synced with enum speechpart */
|
||||||
const char *roles[] = {"unknown", "intransitive", "transitive"};
|
const char *roles[] = {"unknown", "intransitive", "transitive"};
|
||||||
printf("Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n",
|
printf(
|
||||||
roles[command->part],
|
"Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n",
|
||||||
types[command->word[0].type],
|
roles[command->part], types[command->word[0].type],
|
||||||
command->word[0].id,
|
command->word[0].id, types[command->word[1].type],
|
||||||
types[command->word[1].type],
|
|
||||||
command->word[1].id);
|
command->word[1].id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -555,9 +564,8 @@ bool get_command_input(command_t *command)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_command(command_t *cmd)
|
void clear_command(command_t *cmd) {
|
||||||
/* Resets the state of the command to empty */
|
/* Resets the state of the command to empty */
|
||||||
{
|
|
||||||
cmd->verb = ACT_NULL;
|
cmd->verb = ACT_NULL;
|
||||||
cmd->part = unknown;
|
cmd->part = unknown;
|
||||||
game.oldobj = cmd->obj;
|
game.oldobj = cmd->obj;
|
||||||
|
@ -565,10 +573,10 @@ void clear_command(command_t *cmd)
|
||||||
cmd->state = EMPTY;
|
cmd->state = EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void juggle(obj_t object)
|
void juggle(obj_t object) {
|
||||||
/* Juggle an object by picking it up and putting it down again, the purpose
|
/* Juggle an object by picking it up and putting it down again, the
|
||||||
* being to get the object to the front of the chain of things at its loc. */
|
* purpose being to get the object to the front of the chain of things
|
||||||
{
|
* at its loc. */
|
||||||
loc_t i, j;
|
loc_t i, j;
|
||||||
|
|
||||||
i = game.objects[object].place;
|
i = game.objects[object].place;
|
||||||
|
@ -577,28 +585,29 @@ void juggle(obj_t object)
|
||||||
move(object + NOBJECTS, j);
|
move(object + NOBJECTS, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
void move(obj_t object, loc_t where)
|
void move(obj_t object, loc_t where) {
|
||||||
/* Place any object anywhere by picking it up and dropping it. May
|
/* 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
|
* 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
|
* pick up objects which are not at any loc, since carry wants to
|
||||||
* remove objects from game atloc chains. */
|
* remove objects from game atloc chains. */
|
||||||
{
|
|
||||||
loc_t from;
|
loc_t from;
|
||||||
|
|
||||||
if (object > NOBJECTS)
|
if (object > NOBJECTS) {
|
||||||
from = game.objects[object - NOBJECTS].fixed;
|
from = game.objects[object - NOBJECTS].fixed;
|
||||||
else
|
} else {
|
||||||
from = game.objects[object].place;
|
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);
|
carry(object, from);
|
||||||
|
}
|
||||||
drop(object, where);
|
drop(object, where);
|
||||||
}
|
}
|
||||||
|
|
||||||
void put(obj_t object, loc_t where, int pval)
|
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
|
/* put() is the same as move(), except it returns a value used to set
|
||||||
* negated game.prop values for the repository objects. */
|
* up the negated game.prop values for the repository objects. */
|
||||||
{
|
|
||||||
move(object, where);
|
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 advebt.h.
|
||||||
*/
|
*/
|
||||||
|
@ -608,16 +617,17 @@ void put(obj_t object, loc_t where, int pval)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void carry(obj_t object, loc_t where)
|
void carry(obj_t object, loc_t where) {
|
||||||
/* Start toting an object, removing it from the list of things at its former
|
/* Start toting an object, removing it from the list of things at its
|
||||||
* location. Incr holdng unless it was already being toted. If object>NOBJECTS
|
* former location. Incr holdng unless it was already being toted. If
|
||||||
* (moving "fixed" second loc), don't change game.place or game.holdng. */
|
* object>NOBJECTS (moving "fixed" second loc), don't change game.place
|
||||||
{
|
* or game.holdng. */
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
if (object <= NOBJECTS) {
|
if (object <= NOBJECTS) {
|
||||||
if (game.objects[object].place == CARRIED)
|
if (game.objects[object].place == CARRIED) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
game.objects[object].place = CARRIED;
|
game.objects[object].place = CARRIED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -641,68 +651,69 @@ void carry(obj_t object, loc_t where)
|
||||||
game.link[temp] = game.link[object];
|
game.link[temp] = game.link[object];
|
||||||
}
|
}
|
||||||
|
|
||||||
void drop(obj_t object, loc_t where)
|
void drop(obj_t object, loc_t where) {
|
||||||
/* Place an object at a given loc, prefixing it onto the game atloc list. Decr
|
/* Place an object at a given loc, prefixing it onto the game atloc
|
||||||
* game.holdng if the object was being toted. No state change on the object. */
|
* list. Decr game.holdng if the object was being toted. No state
|
||||||
{
|
* change on the object. */
|
||||||
if (object > NOBJECTS)
|
if (object > NOBJECTS) {
|
||||||
game.objects[object - NOBJECTS].fixed = where;
|
game.objects[object - NOBJECTS].fixed = where;
|
||||||
else {
|
} else {
|
||||||
if (game.objects[object].place == CARRIED)
|
if (game.objects[object].place == CARRIED)
|
||||||
if (object != BIRD)
|
if (object != BIRD)
|
||||||
/* The bird has to be weightless. This ugly hack (and the
|
/* The bird has to be weightless. This ugly
|
||||||
* corresponding code in the carry function) brought to you
|
* hack (and the corresponding code in the carry
|
||||||
* by the fact that when the bird is caged, we need to be able
|
* function) brought to you by the fact that
|
||||||
* to either 'take bird' or 'take cage' and have the right thing
|
* when the bird is caged, we need to be able to
|
||||||
* happen.
|
* either 'take bird' or 'take cage' and have
|
||||||
|
* the right thing happen.
|
||||||
*/
|
*/
|
||||||
--game.holdng;
|
--game.holdng;
|
||||||
game.objects[object].place = where;
|
game.objects[object].place = where;
|
||||||
}
|
}
|
||||||
if (where == LOC_NOWHERE || where == CARRIED)
|
if (where == LOC_NOWHERE || where == CARRIED) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
game.link[object] = game.locs[where].atloc;
|
game.link[object] = game.locs[where].atloc;
|
||||||
game.locs[where].atloc = object;
|
game.locs[where].atloc = object;
|
||||||
}
|
}
|
||||||
|
|
||||||
int atdwrf(loc_t where)
|
int atdwrf(loc_t where) {
|
||||||
/* Return the index of first dwarf at the given location, zero if no dwarf is
|
/* Return the index of first dwarf at the given location, zero if no
|
||||||
* there (or if dwarves not active yet), -1 if all dwarves are dead. Ignore
|
* dwarf is there (or if dwarves not active yet), -1 if all dwarves are
|
||||||
* the pirate (6th dwarf). */
|
* dead. Ignore the pirate (6th dwarf). */
|
||||||
{
|
|
||||||
int at;
|
int at;
|
||||||
|
|
||||||
at = 0;
|
at = 0;
|
||||||
if (game.dflag < 2)
|
if (game.dflag < 2) {
|
||||||
return at;
|
return at;
|
||||||
|
}
|
||||||
at = -1;
|
at = -1;
|
||||||
for (int i = 1; i <= NDWARVES - 1; i++) {
|
for (int i = 1; i <= NDWARVES - 1; i++) {
|
||||||
if (game.dwarves[i].loc == where)
|
if (game.dwarves[i].loc == where) {
|
||||||
return i;
|
return i;
|
||||||
if (game.dwarves[i].loc != 0)
|
}
|
||||||
|
if (game.dwarves[i].loc != 0) {
|
||||||
at = 0;
|
at = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return at;
|
return at;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Utility routines (setbit, tstbit, set_seed, get_next_lcg_value,
|
/* Utility routines (setbit, tstbit, set_seed, get_next_lcg_value,
|
||||||
* randrange) */
|
* randrange) */
|
||||||
|
|
||||||
int setbit(int bit)
|
int setbit(int bit) {
|
||||||
/* Returns 2**bit for use in constructing bit-masks. */
|
/* Returns 2**bit for use in constructing bit-masks. */
|
||||||
{
|
|
||||||
return (1L << bit);
|
return (1L << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tstbit(int mask, int bit)
|
bool tstbit(int mask, int bit) {
|
||||||
/* Returns true if the specified bit is set in the mask. */
|
/* Returns true if the specified bit is set in the mask. */
|
||||||
{
|
|
||||||
return (mask & (1 << bit)) != 0;
|
return (mask & (1 << bit)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_seed(int32_t seedval)
|
void set_seed(int32_t seedval) {
|
||||||
/* Set the LCG1 seed */
|
/* Set the LCG1 seed */
|
||||||
{
|
|
||||||
game.lcg_x = seedval % LCG_M;
|
game.lcg_x = seedval % LCG_M;
|
||||||
if (game.lcg_x < 0) {
|
if (game.lcg_x < 0) {
|
||||||
game.lcg_x = LCG_M + game.lcg_x;
|
game.lcg_x = LCG_M + game.lcg_x;
|
||||||
|
@ -715,9 +726,8 @@ void set_seed(int32_t seedval)
|
||||||
game.zzword[5] = '\0';
|
game.zzword[5] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t get_next_lcg_value(void)
|
static int32_t get_next_lcg_value(void) {
|
||||||
/* Return the LCG's current value, and then iterate it. */
|
/* Return the LCG's current value, and then iterate it. */
|
||||||
{
|
|
||||||
int32_t old_x = game.lcg_x;
|
int32_t old_x = game.lcg_x;
|
||||||
game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M;
|
game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M;
|
||||||
if (settings.debug) {
|
if (settings.debug) {
|
||||||
|
@ -726,23 +736,21 @@ static int32_t get_next_lcg_value(void)
|
||||||
return old_x;
|
return old_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t randrange(int32_t range)
|
int32_t randrange(int32_t range) {
|
||||||
/* Return a random integer from [0, range). */
|
/* Return a random integer from [0, range). */
|
||||||
{
|
|
||||||
return range * get_next_lcg_value() / LCG_M;
|
return range * get_next_lcg_value() / LCG_M;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// 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);
|
fprintf(stderr, "Fatal error %d, %s.\n", num, error_string);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
void state_change(obj_t obj, int state)
|
void state_change(obj_t obj, int state) {
|
||||||
/* Object must have a change-message list for this to be useful; only some do */
|
/* Object must have a change-message list for this to be useful; only
|
||||||
{
|
* some do */
|
||||||
game.objects[obj].prop = state;
|
game.objects[obj].prop = state;
|
||||||
pspeak(obj, change, true, state);
|
pspeak(obj, change, true, state);
|
||||||
}
|
}
|
||||||
|
|
100
saveresume.c
100
saveresume.c
|
@ -8,11 +8,11 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "advent.h"
|
#include "advent.h"
|
||||||
|
|
||||||
|
@ -23,17 +23,21 @@
|
||||||
|
|
||||||
struct save_t save;
|
struct save_t save;
|
||||||
|
|
||||||
#define IGNORE(r) do{if (r){}}while(0)
|
#define IGNORE(r) \
|
||||||
|
do { \
|
||||||
|
if (r) { \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
int savefile(FILE *fp)
|
int savefile(FILE *fp) {
|
||||||
/* Save game to file. No input or output from user. */
|
/* Save game to file. No input or output from user. */
|
||||||
{
|
|
||||||
memcpy(&save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC));
|
memcpy(&save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC));
|
||||||
if (save.version == 0)
|
if (save.version == 0) {
|
||||||
save.version = SAVE_VERSION;
|
save.version = SAVE_VERSION;
|
||||||
if (save.canary == 0)
|
}
|
||||||
|
if (save.canary == 0) {
|
||||||
save.canary = ENDIAN_MAGIC;
|
save.canary = ENDIAN_MAGIC;
|
||||||
|
}
|
||||||
save.game = game;
|
save.game = game;
|
||||||
IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
|
IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
|
||||||
return (0);
|
return (0);
|
||||||
|
@ -41,17 +45,18 @@ int savefile(FILE *fp)
|
||||||
|
|
||||||
/* Suspend and resume */
|
/* Suspend and resume */
|
||||||
|
|
||||||
static char *strip(char *name)
|
static char *strip(char *name) {
|
||||||
{
|
|
||||||
// Trim leading whitespace
|
// Trim leading whitespace
|
||||||
while(isspace((unsigned char)*name))
|
while (isspace((unsigned char)*name)) {
|
||||||
name++; // LCOV_EXCL_LINE
|
name++; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
if (*name != '\0') {
|
if (*name != '\0') {
|
||||||
// Trim trailing whitespace;
|
// Trim trailing whitespace;
|
||||||
// might be left there by autocomplete
|
// might be left there by autocomplete
|
||||||
char *end = name + strlen(name) - 1;
|
char *end = name + strlen(name) - 1;
|
||||||
while(end > name && isspace((unsigned char)*end))
|
while (end > name && isspace((unsigned char)*end)) {
|
||||||
end--;
|
end--;
|
||||||
|
}
|
||||||
// Write new null terminator character
|
// Write new null terminator character
|
||||||
end[1] = '\0';
|
end[1] = '\0';
|
||||||
}
|
}
|
||||||
|
@ -59,8 +64,7 @@ static char *strip(char *name)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int suspend(void)
|
int suspend(void) {
|
||||||
{
|
|
||||||
/* Suspend. Offer to save things in a file, but charging
|
/* Suspend. Offer to save things in a file, but charging
|
||||||
* some points (so can't win by using saved games to retry
|
* some points (so can't win by using saved games to retry
|
||||||
* battles or to start over after learning zzword).
|
* battles or to start over after learning zzword).
|
||||||
|
@ -73,20 +77,26 @@ int suspend(void)
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
|
||||||
rspeak(SUSPEND_WARNING);
|
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;
|
return GO_CLEAROBJ;
|
||||||
|
}
|
||||||
game.saved = game.saved + 5;
|
game.saved = game.saved + 5;
|
||||||
|
|
||||||
while (fp == NULL) {
|
while (fp == NULL) {
|
||||||
char *name = myreadline("\nFile name: ");
|
char *name = myreadline("\nFile name: ");
|
||||||
if (name == NULL)
|
if (name == NULL) {
|
||||||
return GO_TOP;
|
return GO_TOP;
|
||||||
|
}
|
||||||
name = strip(name);
|
name = strip(name);
|
||||||
if (strlen(name) == 0)
|
if (strlen(name) == 0) {
|
||||||
return GO_TOP; // LCOV_EXCL_LINE
|
return GO_TOP; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
fp = fopen(strip(name), WRITE_MODE);
|
fp = fopen(strip(name), WRITE_MODE);
|
||||||
if (fp == NULL)
|
if (fp == NULL) {
|
||||||
printf("Can't open file %s, try again.\n", name);
|
printf("Can't open file %s, try again.\n", name);
|
||||||
|
}
|
||||||
free(name);
|
free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +106,7 @@ int suspend(void)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int resume(void)
|
int resume(void) {
|
||||||
{
|
|
||||||
/* Resume. Read a suspended game back from a file.
|
/* Resume. Read a suspended game back from a file.
|
||||||
* If ADVENT_NOSAVE is defined, gripe instead. */
|
* If ADVENT_NOSAVE is defined, gripe instead. */
|
||||||
|
|
||||||
|
@ -109,9 +118,12 @@ int resume(void)
|
||||||
|
|
||||||
if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) {
|
if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) {
|
||||||
rspeak(RESUME_ABANDON);
|
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;
|
return GO_CLEAROBJ;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (fp == NULL) {
|
while (fp == NULL) {
|
||||||
char *name = myreadline("\nFile name: ");
|
char *name = myreadline("\nFile name: ");
|
||||||
|
@ -121,16 +133,16 @@ int resume(void)
|
||||||
if (strlen(name) == 0)
|
if (strlen(name) == 0)
|
||||||
return GO_TOP; // LCOV_EXCL_LINE
|
return GO_TOP; // LCOV_EXCL_LINE
|
||||||
fp = fopen(name, READ_MODE);
|
fp = fopen(name, READ_MODE);
|
||||||
if (fp == NULL)
|
if (fp == NULL) {
|
||||||
printf("Can't open file %s, try again.\n", name);
|
printf("Can't open file %s, try again.\n", name);
|
||||||
|
}
|
||||||
free(name);
|
free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return restore(fp);
|
return restore(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int restore(FILE* fp)
|
int restore(FILE *fp) {
|
||||||
{
|
|
||||||
/* Read and restore game state from file, assuming
|
/* Read and restore game state from file, assuming
|
||||||
* sane initial state.
|
* sane initial state.
|
||||||
* If ADVENT_NOSAVE is defined, gripe instead. */
|
* If ADVENT_NOSAVE is defined, gripe instead. */
|
||||||
|
@ -141,10 +153,12 @@ int restore(FILE* fp)
|
||||||
|
|
||||||
IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
|
IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
|
||||||
fclose(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);
|
rspeak(BAD_SAVE);
|
||||||
else if (save.version != SAVE_VERSION) {
|
} else if (save.version != SAVE_VERSION) {
|
||||||
rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10), SAVE_VERSION / 10, MOD(SAVE_VERSION, 10));
|
rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10),
|
||||||
|
SAVE_VERSION / 10, MOD(SAVE_VERSION, 10));
|
||||||
} else if (!is_valid(save.game)) {
|
} else if (!is_valid(save.game)) {
|
||||||
rspeak(SAVE_TAMPERING);
|
rspeak(SAVE_TAMPERING);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
@ -154,8 +168,7 @@ int restore(FILE* fp)
|
||||||
return GO_TOP;
|
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:
|
/* Save files can be roughly grouped into three groups:
|
||||||
* With valid, reachable state, with valid, but unreachable
|
* With valid, reachable state, with valid, but unreachable
|
||||||
* state and with invalid state. We check that state is
|
* state and with invalid state. We check that state is
|
||||||
|
@ -180,23 +193,27 @@ bool is_valid(struct game_t valgame)
|
||||||
/* Bounds check for locations */
|
/* 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.chloc2 < -1 || valgame.chloc2 > NLOCATIONS ||
|
||||||
valgame.loc < 0 || valgame.loc > NLOCATIONS ||
|
valgame.loc < 0 || valgame.loc > NLOCATIONS || valgame.newloc < 0 ||
|
||||||
valgame.newloc < 0 || valgame.newloc > NLOCATIONS ||
|
valgame.newloc > NLOCATIONS || valgame.oldloc < 0 ||
|
||||||
valgame.oldloc < 0 || valgame.oldloc > NLOCATIONS ||
|
valgame.oldloc > NLOCATIONS || valgame.oldlc2 < 0 ||
|
||||||
valgame.oldlc2 < 0 || valgame.oldlc2 > NLOCATIONS) {
|
valgame.oldlc2 > NLOCATIONS) {
|
||||||
return false; // LCOV_EXCL_LINE
|
return false; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
/* Bounds check for location arrays */
|
/* Bounds check for location arrays */
|
||||||
for (int i = 0; i <= NDWARVES; i++) {
|
for (int i = 0; i <= NDWARVES; i++) {
|
||||||
if (valgame.dwarves[i].loc < -1 || valgame.dwarves[i].loc > NLOCATIONS ||
|
if (valgame.dwarves[i].loc < -1 ||
|
||||||
valgame.dwarves[i].oldloc < -1 || valgame.dwarves[i].oldloc > NLOCATIONS) {
|
valgame.dwarves[i].loc > NLOCATIONS ||
|
||||||
|
valgame.dwarves[i].oldloc < -1 ||
|
||||||
|
valgame.dwarves[i].oldloc > NLOCATIONS) {
|
||||||
return false; // LCOV_EXCL_LINE
|
return false; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i <= NOBJECTS; i++) {
|
for (int i = 0; i <= NOBJECTS; i++) {
|
||||||
if (valgame.objects[i].place < -1 || valgame.objects[i].place > NLOCATIONS ||
|
if (valgame.objects[i].place < -1 ||
|
||||||
valgame.objects[i].fixed < -1 || valgame.objects[i].fixed > NLOCATIONS) {
|
valgame.objects[i].place > NLOCATIONS ||
|
||||||
|
valgame.objects[i].fixed < -1 ||
|
||||||
|
valgame.objects[i].fixed > NLOCATIONS) {
|
||||||
return false; // LCOV_EXCL_LINE
|
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++) {
|
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
|
return false; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++) {
|
for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++) {
|
||||||
if (valgame.link[obj] < NO_OBJECT || valgame.link[obj] > NOBJECTS * 2) {
|
if (valgame.link[obj] < NO_OBJECT ||
|
||||||
|
valgame.link[obj] > NOBJECTS * 2) {
|
||||||
return false; // LCOV_EXCL_LINE
|
return false; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
score.c
16
score.c
|
@ -4,15 +4,15 @@
|
||||||
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
* SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
|
||||||
#include "advent.h"
|
#include "advent.h"
|
||||||
#include "dungeon.h"
|
#include "dungeon.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
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) {
|
int score(enum termination mode) {
|
||||||
/* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if died
|
/* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if
|
||||||
* or won */
|
* died or won */
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
|
||||||
/* The present scoring algorithm is as follows:
|
/* The present scoring algorithm is as follows:
|
||||||
|
@ -31,8 +31,8 @@ int score(enum termination mode) {
|
||||||
* Came to Witt's End 1 1
|
* Came to Witt's End 1 1
|
||||||
* Round out the total 2 2
|
* Round out the total 2 2
|
||||||
* TOTAL: 430
|
* TOTAL: 430
|
||||||
* Points can also be deducted for using hints or too many turns, or for
|
* Points can also be deducted for using hints or too many turns, or
|
||||||
* saving intermediate positions. */
|
* for saving intermediate positions. */
|
||||||
|
|
||||||
/* First tally up the treasures. Must be in building and not broken.
|
/* First tally up the treasures. Must be in building and not broken.
|
||||||
* Give the poor guy 2 points just for finding each treasure. */
|
* 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)) {
|
if (!PROP_IS_STASHED(i) && !PROP_IS_NOTFOUND(i)) {
|
||||||
score += 2;
|
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;
|
score += k - 2;
|
||||||
}
|
}
|
||||||
mxscor += k;
|
mxscor += k;
|
||||||
|
@ -105,7 +106,8 @@ int score(enum termination mode) {
|
||||||
score += 2;
|
score += 2;
|
||||||
mxscor += 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++) {
|
for (int i = 0; i < NHINTS; i++) {
|
||||||
if (game.hints[i].used) {
|
if (game.hints[i].used) {
|
||||||
score = score - hints[i].penalty;
|
score = score - hints[i].penalty;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue