X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=main.c;h=514ae598c97e3f01f898076395c9e92754cb079a;hb=f7d6884dedabaa237056164ec6d90335de8025cb;hp=926835cc8dfd838230e9a8420e33043fc69e400f;hpb=5f4df123b40b8d9c01b88bc506ee5bb8b3c82d70;p=open-adventure.git diff --git a/main.c b/main.c index 926835c..514ae59 100644 --- a/main.c +++ b/main.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "advent.h" #include "dungeon.h" @@ -40,6 +42,7 @@ static void sig_handler(int signo) */ static bool do_command(void); +static bool do_move(void); int main(int argc, char *argv[]) { @@ -49,11 +52,11 @@ int main(int argc, char *argv[]) #ifndef ADVENT_NOSAVE const char* opts = "l:or:"; - const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename]\n"; + const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [script...]\n"; FILE *rfp = NULL; #else const char* opts = "l:o"; - const char* usage = "Usage: %s [-l logfilename] [-o]\n"; + const char* usage = "Usage: %s [-l logfilename] [-o] [script...]\n"; #endif while ((ch = getopt(argc, argv, opts)) != EOF) { switch (ch) { @@ -94,28 +97,38 @@ int main(int argc, char *argv[]) } } + /* copy inncation line part after switches */ + settings.argc = argc - optind; + settings.argv = argv + optind; + settings.optind = 0; + /* Initialize game variables */ - long seedval = initialise(); + int seedval = initialise(); #ifndef ADVENT_NOSAVE if (!rfp) { - game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); + game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); if (game.novice) game.limit = NOVICELIMIT; } else { restore(rfp); } #else - game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); + game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); if (game.novice) game.limit = NOVICELIMIT; #endif if (settings.logfp) - fprintf(settings.logfp, "seed %ld\n", seedval); + fprintf(settings.logfp, "seed %d\n", seedval); /* interpret commands until EOF or interrupt */ for (;;) { + // if we're supposed to move, move + if (!do_move()) + continue; + + // get command if (!do_command()) break; } @@ -123,7 +136,52 @@ int main(int argc, char *argv[]) terminate(quitgame); } -/* Check if this loc is eligible for any hints. If been here long +char *myreadline(const char *prompt) +{ + /* + * This function isbn't required for gameplay, readline() straight + * up would suffice for tat. It's where we interpret command-line + * logfiles for testing purposes. + */ + /* Normal case - no script arguments */ + if (settings.argc == 0) + return readline(prompt); + + char *buf = malloc(LINESIZE+1); + for (;;) { + if (settings.scriptfp == NULL || feof(settings.scriptfp)) { + if (settings.optind >= settings.argc) { + free(buf); + return NULL; + } + + char *next = settings.argv[settings.optind++]; + + if (settings.scriptfp != NULL && feof(settings.scriptfp)) + fclose(settings.scriptfp); + if (strcmp(next, "-") == 0) + settings.scriptfp = stdin; // LCOV_EXCL_LINE + else + settings.scriptfp = fopen(next, "r"); + } + + if (isatty(fileno(settings.scriptfp))) { + free(buf); // LCOV_EXCL_LINE + return readline(prompt); // LCOV_EXCL_LINE + } else { + char *ln = fgets(buf, LINESIZE, settings.scriptfp); + if (ln != NULL) { + fputs(PROMPT, stdout); + fputs(ln, stdout); + return ln; + } + } + } + + return NULL; +} + +/* Check if this loc is eligible for any hints. If been here int * enough, display. Ignore "HINTS" < 4 (special stuff, see database * notes). */ static void checkhints(void) @@ -135,7 +193,7 @@ static void checkhints(void) if (!CNDBIT(game.loc, hint + 1 + COND_HBASE)) game.hintlc[hint] = -1; ++game.hintlc[hint]; - /* Come here if he's been long enough at required loc(s) for some + /* Come here if he's been int enough at required loc(s) for some * unused hint. */ if (game.hintlc[hint] >= hints[hint].turns) { int i; @@ -197,22 +255,21 @@ static void checkhints(void) game.hintlc[hint] = 0; return; default: // LCOV_EXCL_LINE + // Should never hap[pen BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE } /* Fall through to hint display */ game.hintlc[hint] = 0; - if (!yes(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN])) + if (!yes_or_no(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN])) return; rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty); - game.hinted[hint] = yes(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]); + game.hinted[hint] = yes_or_no(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]); if (game.hinted[hint] && game.limit > WARNTIME) game.limit += WARNTIME * hints[hint].penalty; } } } - - } static bool spotted_by_pirate(int i) @@ -452,20 +509,25 @@ static bool dwarfmove(void) static void croak(void) /* Okay, he's dead. Let's get on with it. */ { - if (game.numdie < 0) - game.numdie = 0; // LCOV_EXCL_LINE const char* query = obituaries[game.numdie].query; const char* yes_response = obituaries[game.numdie].yes_response; + ++game.numdie; + if (game.closng) { /* He died during closing time. No resurrection. Tally up a * death and exit. */ rspeak(DEATH_CLOSING); terminate(endgame); - } else if ( !yes(query, yes_response, arbitrary_messages[OK_MAN]) - || game.numdie == NDEATHS) + } else if (!yes_or_no(query, yes_response, arbitrary_messages[OK_MAN]) + || game.numdie == NDEATHS) { + /* Player is asked if he wants to try again. If not, or if + * he's already used all of his lives, we end the game */ terminate(endgame); - else { + } else { + /* If player wishes to continue, we empty the liquids in the + * user's inventory, turn off the lamp, and drop all items + * where he died. */ game.place[WATER] = game.place[OIL] = LOC_NOWHERE; if (TOTING(LAMP)) game.prop[LAMP] = LAMP_DARK; @@ -480,8 +542,11 @@ static void croak(void) } } -static void describe_location(void) { +static void describe_location(void) +/* Describe the location to the user */ +{ const char* msg = locations[game.loc].description.small; + if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == NO_MESSAGE) msg = locations[game.loc].description.big; @@ -495,7 +560,7 @@ static void describe_location(void) { speak(msg); - if (game.loc == LOC_Y2 && PCT(25) && !game.closng) // FIXME: magic number + if (game.loc == LOC_Y2 && PCT(25) && !game.closng) rspeak(SAYS_PLUGH); } @@ -641,8 +706,8 @@ static void playermove(int motion) for (;;) { /* L12 loop */ for (;;) { enum condtype_t condtype = travel[travel_entry].condtype; - long condarg1 = travel[travel_entry].condarg1; - long condarg2 = travel[travel_entry].condarg2; + int condarg1 = travel[travel_entry].condarg1; + int condarg2 = travel[travel_entry].condarg2; if (condtype < cond_not) { /* YAML N and [pct N] conditionals */ if (condtype == cond_goto || condtype == cond_pct) { @@ -730,7 +795,7 @@ static void playermove(int motion) if (game.prop[TROLL] == TROLL_PAIDONCE) { pspeak(TROLL, look, true, TROLL_PAIDONCE); game.prop[TROLL] = TROLL_UNPAID; - move(TROLL2, LOC_NOWHERE); + DESTROY(TROLL2); move(TROLL2 + NOBJECTS, IS_FREE); move(TROLL, objects[TROLL].plac); move(TROLL + NOBJECTS, objects[TROLL].fixd); @@ -859,7 +924,7 @@ static bool closecheck(void) game.dseen[i] = false; game.dloc[i] = LOC_NOWHERE; } - move(TROLL, LOC_NOWHERE); + DESTROY(TROLL); move(TROLL + NOBJECTS, IS_FREE); move(TROLL2, objects[TROLL].plac); move(TROLL2 + NOBJECTS, objects[TROLL].fixd); @@ -980,16 +1045,7 @@ static void listobjects(void) } } -void clear_command(command_t *cmd) -{ - cmd->verb = ACT_NULL; - cmd->part = unknown; - game.oldobj = cmd->obj; - cmd->obj = NO_OBJECT; - cmd->state = EMPTY; -} - -bool preprocess_command(command_t *command) +static bool preprocess_command(command_t *command) /* Pre-processes a command input to see if we need to tease out a few specific cases: * - "enter water" or "enter stream": * wierd specific case that gets the user wet, and then kicks us back to get another command @@ -1032,7 +1088,7 @@ bool preprocess_command(command_t *command) if (game.loc == LOC_COBBLE || game.loc == LOC_DEBRIS || game.loc == LOC_AWKWARD || - game.loc == LOC_BIRD || + game.loc == LOC_BIRDCHAMBER || game.loc == LOC_PITTOP) { command->word[0].id = ENTRANCE; } @@ -1051,18 +1107,20 @@ bool preprocess_command(command_t *command) command->word[0].type = ACTION; } } + + /* If no word type is given for the first word, we assume it's a motion. */ + if(command->word[0].type == NO_WORD_TYPE) + command->word[0].type = MOTION; + command->state = PREPROCESSED; return true; } return false; } -static bool do_command() -/* Get and execute a command */ +static bool do_move(void) +/* Actually execute the move to the new location and dwarf movement */ { - static command_t command; - command.state = EMPTY; - /* Can't leave cave once it's closing (except by main office). */ if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) { rspeak(EXIT_CLOSED); @@ -1090,9 +1148,8 @@ static bool do_command() if (!dwarfmove()) croak(); - if (game.loc == LOC_NOWHERE) { + if (game.loc == LOC_NOWHERE) croak(); - } /* The easiest way to get killed is to fall into a pit in * pitch darkness. */ @@ -1100,9 +1157,18 @@ static bool do_command() rspeak(PIT_FALL); game.oldlc2 = game.loc; croak(); - return true; + return false; } + return true; +} + +static bool do_command() +/* Get and execute a command */ +{ + static command_t command; + clear_command(&command); + /* Describe the current location and (maybe) get next command. */ while (command.state != EXECUTED) { describe_location(); @@ -1178,7 +1244,6 @@ static bool do_command() } switch (command.word[0].type) { - case NO_WORD_TYPE: // FIXME: treating NO_WORD_TYPE as a motion word is confusing case MOTION: playermove(command.word[0].id); command.state = EXECUTED; @@ -1200,8 +1265,9 @@ static bool do_command() clear_command(&command); continue; } - break; + break;// LCOV_EXCL_LINE default: // LCOV_EXCL_LINE + case NO_WORD_TYPE: // LCOV_EXCL_LINE BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE } @@ -1232,6 +1298,9 @@ static bool do_command() /* object cleared; we need to go back to the preprocessing step */ command.state = GIVEN; break; + case GO_CHECKHINT: // FIXME: re-name to be more contextual; this was previously a label + command.state = GIVEN; + break; case GO_DWARFWAKE: /* Oh dear, he's disturbed the dwarves. */ rspeak(DWARVES_AWAKEN); @@ -1241,9 +1310,6 @@ static bool do_command() break; case GO_TOP: // FIXME: re-name to be more contextual; this was previously a label break; - case GO_CHECKHINT: // FIXME: re-name to be more contextual; this was previously a label - command.state = GIVEN; - break; default: // LCOV_EXCL_LINE BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE }