LCOV - code coverage report
Current view: top level - open-adventure - misc.c (source / functions) Hit Total Coverage
Test: advent.info Lines: 347 347 100.0 %
Date: 2017-09-17 15:40:10 Functions: 30 30 100.0 %

          Line data    Source code
       1             : #include <unistd.h>
       2             : #include <stdlib.h>
       3             : #include <stdio.h>
       4             : #include <string.h>
       5             : #include <stdarg.h>
       6             : #include <sys/time.h>
       7             : #include <ctype.h>
       8             : #include <editline/readline.h>
       9             : #include <inttypes.h>
      10             : 
      11             : #include "advent.h"
      12             : #include "dungeon.h"
      13             : 
      14       54083 : static void* xcalloc(size_t size)
      15             : {
      16       54083 :     void* ptr = calloc(size, 1);
      17       54083 :     if (ptr == NULL) {
      18             :         // LCOV_EXCL_START
      19             :         // exclude from coverage analysis because we can't simulate an out of memory error in testing
      20             :         fprintf(stderr, "Out of memory!\n");
      21             :         exit(EXIT_FAILURE);
      22             :         // LCOV_EXCL_STOP
      23             :     }
      24       54083 :     return (ptr);
      25             : }
      26             : 
      27             : /*  I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */
      28             : 
      29       36692 : static void vspeak(const char* msg, bool blank, va_list ap)
      30             : {
      31             :     // Do nothing if we got a null pointer.
      32       36692 :     if (msg == NULL)
      33         324 :         return;
      34             : 
      35             :     // Do nothing if we got an empty string.
      36       36368 :     if (strlen(msg) == 0)
      37         517 :         return;
      38             : 
      39       35851 :     if (blank == true)
      40       35416 :         printf("\n");
      41             : 
      42       35851 :     int msglen = strlen(msg);
      43             : 
      44             :     // Rendered string
      45       35851 :     ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */
      46       35851 :     char* rendered = xcalloc(size);
      47       35851 :     char* renderp = rendered;
      48             : 
      49             :     // Handle format specifiers (including the custom %S) by
      50             :     // adjusting the parameter accordingly, and replacing the
      51             :     // specifier with %s.
      52       35851 :     bool pluralize = false;
      53     1780880 :     for (int i = 0; i < msglen; i++) {
      54     1745029 :         if (msg[i] != '%') {
      55             :             /* Ugh.  Least obtrusive way to deal with artifacts "on the floor"
      56             :              * being dropped outside of both cave and building. */
      57     1744299 :             if (strncmp(msg + i, "floor", 5) == 0 && strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) {
      58           2 :                 strcpy(renderp, "ground");
      59           2 :                 renderp += 6;
      60           2 :                 i += 4;
      61           2 :                 size -= 5;
      62             :             } else {
      63     1744297 :                 *renderp++ = msg[i];
      64     1744297 :                 size--;
      65             :             }
      66             :         } else {
      67         730 :             i++;
      68             :             // Integer specifier.
      69         730 :             if (msg[i] == 'd') {
      70         507 :                 int32_t arg = va_arg(ap, int32_t);
      71         507 :                 int ret = snprintf(renderp, size, "%" PRId32, arg);
      72         507 :                 if (ret < size) {
      73         507 :                     renderp += ret;
      74         507 :                     size -= ret;
      75             :                 }
      76         507 :                 pluralize = (arg != 1);
      77             :             }
      78             : 
      79             :             // Unmodified string specifier.
      80         730 :             if (msg[i] == 's') {
      81          34 :                 char *arg = va_arg(ap, char *);
      82          34 :                 strncat(renderp, arg, size - 1);
      83          34 :                 size_t len = strlen(renderp);
      84          34 :                 renderp += len;
      85          34 :                 size -= len;
      86             :             }
      87             : 
      88             :             // Singular/plural specifier.
      89         730 :             if (msg[i] == 'S') {
      90             :                 // look at the *previous* numeric parameter
      91         189 :                 if (pluralize) {
      92         185 :                     *renderp++ = 's';
      93         185 :                     size--;
      94             :                 }
      95             :             }
      96             : 
      97             :             // LCOV_EXCL_START - doesn't occur in test suite.
      98             :             /* Version specifier */
      99             :             if (msg[i] == 'V') {
     100             :                 strcpy(renderp, VERSION);
     101             :                 size_t len = strlen(VERSION);
     102             :                 renderp += len;
     103             :                 size -= len;
     104             :             }
     105             :             // LCOV_EXCL_STOP
     106             :         }
     107             :     }
     108       35851 :     *renderp = 0;
     109             : 
     110             :     // Print the message.
     111       35851 :     printf("%s\n", rendered);
     112             : 
     113       35851 :     free(rendered);
     114             : }
     115             : 
     116       14449 : void speak(const char* msg, ...)
     117             : {
     118       14449 :     va_list ap;
     119       14449 :     va_start(ap, msg);
     120       14449 :     vspeak(msg, true, ap);
     121       14449 :     va_end(ap);
     122       14449 : }
     123             : 
     124          68 : void sspeak(const int msg, ...)
     125             : {
     126          68 :     va_list ap;
     127          68 :     va_start(ap, msg);
     128          68 :     fputc('\n', stdout);
     129          68 :     vprintf(arbitrary_messages[msg], ap);
     130          68 :     fputc('\n', stdout);
     131          68 :     va_end(ap);
     132          68 : }
     133             : 
     134       15098 : void pspeak(vocab_t msg, enum speaktype mode, int skip, bool blank, ...)
     135             : /* Find the skip+1st message from msg and print it.  Modes are:
     136             :  * feel = for inventory, what you can touch
     137             :  * look = the full description for the state the object is in
     138             :  * listen = the sound for the state the object is in
     139             :  * study = text on the object. */
     140             : {
     141       15098 :     va_list ap;
     142       15098 :     va_start(ap, blank);
     143       15098 :     switch (mode) {
     144         435 :     case touch:
     145         435 :         vspeak(objects[msg].inventory, blank, ap);
     146         435 :         break;
     147       13587 :     case look:
     148       13587 :         vspeak(objects[msg].descriptions[skip], blank, ap);
     149       13587 :         break;
     150          34 :     case hear:
     151          34 :         vspeak(objects[msg].sounds[skip], blank, ap);
     152          34 :         break;
     153           5 :     case study:
     154           5 :         vspeak(objects[msg].texts[skip], blank, ap);
     155           5 :         break;
     156        1037 :     case change:
     157        1037 :         vspeak(objects[msg].changes[skip], blank, ap);
     158        1037 :         break;
     159             :     }
     160       15098 :     va_end(ap);
     161       15098 : }
     162             : 
     163        7145 : void rspeak(vocab_t i, ...)
     164             : /* Print the i-th "random" message (section 6 of database). */
     165             : {
     166        7145 :     va_list ap;
     167        7145 :     va_start(ap, i);
     168        7145 :     vspeak(arbitrary_messages[i], true, ap);
     169        7145 :     va_end(ap);
     170        7145 : }
     171             : 
     172       17994 : void echo_input(FILE* destination, const char* input_prompt, const char* input)
     173             : {
     174       17994 :     size_t len = strlen(input_prompt) + strlen(input) + 1;
     175       17994 :     char* prompt_and_input = (char*) xcalloc(len);
     176       17994 :     strcpy(prompt_and_input, input_prompt);
     177       17994 :     strcat(prompt_and_input, input);
     178       17994 :     fprintf(destination, "%s\n", prompt_and_input);
     179       17994 :     free(prompt_and_input);
     180       17994 : }
     181             : 
     182       17739 : static int word_count(char* str)
     183             : {
     184       17739 :     char delims[] = " \t";
     185       17739 :     int count = 0;
     186       17739 :     int inblanks = true;
     187             : 
     188       85335 :     for (char *s = str; *s; s++)
     189       67596 :         if (inblanks) {
     190       22087 :             if (strchr(delims, *s) == 0) {
     191       22084 :                 ++count;
     192       22084 :                 inblanks = false;
     193             :             }
     194             :         } else {
     195       45509 :             if (strchr(delims, *s) != 0) {
     196        4371 :                 inblanks = true;
     197             :             }
     198             :         }
     199             : 
     200       17739 :     return (count);
     201             : }
     202             : 
     203       18050 : static char* get_input(void)
     204             : {
     205             :     // Set up the prompt
     206       18050 :     char input_prompt[] = "> ";
     207       18050 :     if (!settings.prompt)
     208           8 :         input_prompt[0] = '\0';
     209             : 
     210             :     // Print a blank line
     211       18050 :     printf("\n");
     212             : 
     213             :     char* input;
     214             :     while (true) {
     215       18480 :         input = readline(input_prompt);
     216             : 
     217       18265 :         if (input == NULL) // Got EOF; return with it.
     218          71 :             return (input);
     219       18194 :         if (input[0] == '#') { // Ignore comments.
     220         215 :             free(input);
     221         215 :             continue;
     222             :         }
     223             :         // We have a 'normal' line; leave the loop.
     224       17979 :         break;
     225             :     }
     226             : 
     227             :     // Strip trailing newlines from the input
     228       17979 :     input[strcspn(input, "\n")] = 0;
     229             : 
     230       17979 :     add_history(input);
     231             : 
     232       17979 :     if (!isatty(0))
     233       17979 :         echo_input(stdout, input_prompt, input);
     234             : 
     235       17979 :     if (settings.logfp)
     236          15 :         echo_input(settings.logfp, "", input);
     237             : 
     238       17979 :     return (input);
     239             : }
     240             : 
     241          52 : bool silent_yes(void)
     242             : {
     243          52 :     bool outcome = false;
     244             : 
     245           2 :     for (;;) {
     246          54 :         char* reply = get_input();
     247          54 :         if (reply == NULL) {
     248             :             // LCOV_EXCL_START
     249             :             // Should be unreachable. Reply should never be NULL
     250             :             free(reply);
     251             :             exit(EXIT_SUCCESS);
     252             :             // LCOV_EXCL_STOP
     253             :         }
     254          54 :         if (strlen(reply) == 0) {
     255           1 :             free(reply);
     256           1 :             rspeak(PLEASE_ANSWER);
     257           1 :             continue;
     258             :         }
     259             : 
     260          53 :         char* firstword = (char*) xcalloc(strlen(reply) + 1);
     261          53 :         sscanf(reply, "%s", firstword);
     262             : 
     263          53 :         free(reply);
     264             : 
     265         180 :         for (int i = 0; i < (int)strlen(firstword); ++i)
     266         127 :             firstword[i] = tolower(firstword[i]);
     267             : 
     268          53 :         int yes = strncmp("yes", firstword, sizeof("yes") - 1);
     269          53 :         int y = strncmp("y", firstword, sizeof("y") - 1);
     270          53 :         int no = strncmp("no", firstword, sizeof("no") - 1);
     271          53 :         int n = strncmp("n", firstword, sizeof("n") - 1);
     272             : 
     273          53 :         free(firstword);
     274             : 
     275          53 :         if (yes == 0 ||
     276             :             y == 0) {
     277          51 :             outcome = true;
     278          51 :             break;
     279           2 :         } else if (no == 0 ||
     280             :                    n == 0) {
     281           1 :             outcome = false;
     282           1 :             break;
     283             :         } else
     284           1 :             rspeak(PLEASE_ANSWER);
     285             :     }
     286          52 :     return (outcome);
     287             : }
     288             : 
     289             : 
     290         149 : bool yes(const char* question, const char* yes_response, const char* no_response)
     291             : /*  Print message X, wait for yes/no answer.  If yes, print Y and return true;
     292             :  *  if no, print Z and return false. */
     293             : {
     294         149 :     bool outcome = false;
     295             : 
     296          41 :     for (;;) {
     297         190 :         speak(question);
     298             : 
     299         190 :         char* reply = get_input();
     300         190 :         if (reply == NULL) {
     301             :             // LCOV_EXCL_START
     302             :             // Should be unreachable. Reply should never be NULL
     303             :             free(reply);
     304             :             exit(EXIT_SUCCESS);
     305             :             // LCOV_EXCL_STOP
     306             :         }
     307             : 
     308         186 :         if (strlen(reply) == 0) {
     309           1 :             free(reply);
     310           1 :             rspeak(PLEASE_ANSWER);
     311           1 :             continue;
     312             :         }
     313             : 
     314         185 :         char* firstword = (char*) xcalloc(strlen(reply) + 1);
     315         185 :         sscanf(reply, "%s", firstword);
     316             : 
     317         185 :         free(reply);
     318             : 
     319         446 :         for (int i = 0; i < (int)strlen(firstword); ++i)
     320         261 :             firstword[i] = tolower(firstword[i]);
     321             : 
     322         185 :         int yes = strncmp("yes", firstword, sizeof("yes") - 1);
     323         185 :         int y = strncmp("y", firstword, sizeof("y") - 1);
     324         185 :         int no = strncmp("no", firstword, sizeof("no") - 1);
     325         185 :         int n = strncmp("n", firstword, sizeof("n") - 1);
     326             : 
     327         185 :         free(firstword);
     328             : 
     329         185 :         if (yes == 0 ||
     330             :             y == 0) {
     331          46 :             speak(yes_response);
     332          46 :             outcome = true;
     333          46 :             break;
     334         139 :         } else if (no == 0 ||
     335             :                    n == 0) {
     336          99 :             speak(no_response);
     337          99 :             outcome = false;
     338          99 :             break;
     339             :         } else
     340          40 :             rspeak(PLEASE_ANSWER);
     341             : 
     342             :     }
     343             : 
     344         145 :     return (outcome);
     345             : }
     346             : 
     347             : /*  Data structure  routines */
     348             : 
     349       22081 : static int get_motion_vocab_id(const char* word)
     350             : // Return the first motion number that has 'word' as one of its words.
     351             : {
     352     1219084 :     for (int i = 0; i < NMOTIONS; ++i) {
     353     2895138 :         for (int j = 0; j < motions[i].words.n; ++j) {
     354     1705754 :             if (strncasecmp(word, motions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 ||
     355        7621 :                     strchr(ignore, word[0]) == NULL ||
     356           2 :                     !settings.oldstyle))
     357       12366 :                 return (i);
     358             :         }
     359             :     }
     360             :     // If execution reaches here, we didn't find the word.
     361        9715 :     return (WORD_NOT_FOUND);
     362             : }
     363             : 
     364        9715 : static int get_object_vocab_id(const char* word)
     365             : // Return the first object number that has 'word' as one of its words.
     366             : {
     367      530580 :     for (int i = 0; i < NOBJECTS + 1; ++i) { // FIXME: the + 1 should go when 1-indexing for objects is removed
     368     1285156 :         for (int j = 0; j < objects[i].words.n; ++j) {
     369      764291 :             if (strncasecmp(word, objects[i].words.strs[j], TOKLEN) == 0)
     370        4354 :                 return (i);
     371             :         }
     372             :     }
     373             :     // If execution reaches here, we didn't find the word.
     374        5361 :     return (WORD_NOT_FOUND);
     375             : }
     376             : 
     377        5361 : static int get_action_vocab_id(const char* word)
     378             : // Return the first motion number that has 'word' as one of its words.
     379             : {
     380       46916 :     for (int i = 0; i < NACTIONS; ++i) {
     381      169670 :         for (int j = 0; j < actions[i].words.n; ++j) {
     382      128283 :             if (strncasecmp(word, actions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 ||
     383         170 :                     strchr(ignore, word[0]) == NULL ||
     384           2 :                     !settings.oldstyle))
     385        5216 :                 return (i);
     386             :         }
     387             :     }
     388             :     // If execution reaches here, we didn't find the word.
     389         145 :     return (WORD_NOT_FOUND);
     390             : }
     391             : 
     392         101 : static bool is_valid_int(const char *str)
     393             : /* Returns true if the string passed in is represents a valid integer,
     394             :  * that could then be parsed by atoi() */
     395             : {
     396             :     // Handle negative number
     397         101 :     if (*str == '-')
     398           1 :         ++str;
     399             : 
     400             :     // Handle empty string or just "-". Should never reach this
     401             :     // point, because this is only used with transitive verbs.
     402         101 :     if (!*str)
     403             :         return false; // LCOV_EXCL_LINE
     404             : 
     405             :     // Check for non-digit chars in the rest of the stirng.
     406        1029 :     while (*str) {
     407         842 :         if (!isdigit(*str))
     408          15 :             return false;
     409             :         else
     410         827 :             ++str;
     411             :     }
     412             : 
     413          86 :     return true;
     414             : }
     415             : 
     416       35430 : static void get_vocab_metadata(command_word_t* word)
     417             : {
     418             :     /* Check for an empty string */
     419       35430 :     if (strncmp(word->raw, "", sizeof("")) == 0) {
     420       13349 :         word->id = WORD_EMPTY;
     421       13349 :         word->type = NO_WORD_TYPE;
     422       13349 :         return;
     423             :     }
     424             : 
     425             :     vocab_t ref_num;
     426             : 
     427       22081 :     ref_num = get_motion_vocab_id(word->raw);
     428       22081 :     if (ref_num != WORD_NOT_FOUND) {
     429       12366 :         word->id = ref_num;
     430       12366 :         word->type = MOTION;
     431       12366 :         return;
     432             :     }
     433             : 
     434        9715 :     ref_num = get_object_vocab_id(word->raw);
     435        9715 :     if (ref_num != WORD_NOT_FOUND) {
     436        4354 :         word->id = ref_num;
     437        4354 :         word->type = OBJECT;
     438        4354 :         return;
     439             :     }
     440             : 
     441        5361 :     ref_num = get_action_vocab_id(word->raw);
     442        5361 :     if (ref_num != WORD_NOT_FOUND) {
     443        5216 :         word->id = ref_num;
     444        5216 :         word->type = ACTION;
     445        5216 :         return;
     446             :     }
     447             : 
     448             :     // Check for the reservoir magic word.
     449         145 :     if (strcasecmp(word->raw, game.zzword) == 0) {
     450          44 :         word->id = PART;
     451          44 :         word->type = ACTION;
     452          44 :         return;
     453             :     }
     454             : 
     455             :     // Check words that are actually numbers.
     456         101 :     if (is_valid_int(word->raw)) {
     457          86 :         word->id = WORD_EMPTY;
     458          86 :         word->type = NUMERIC;
     459          86 :         return;
     460             :     }
     461             : 
     462          15 :     word->id = WORD_NOT_FOUND;
     463          15 :     word->type = NO_WORD_TYPE;
     464          15 :     return;
     465             : }
     466             : 
     467       17715 : static void tokenize(char* raw, command_t *cmd)
     468             : {
     469       17715 :     memset(cmd, '\0', sizeof(command_t));
     470             : 
     471             :     /* Bound prefix on the %s would be needed to prevent buffer
     472             :      * overflow.  but we shortstop this more simply by making each
     473             :      * raw-input buffer as long as the entire input buffer. */
     474       17715 :     sscanf(raw, "%s%s", cmd->word[0].raw, cmd->word[1].raw);
     475             : 
     476             :     /* (ESR) In oldstyle mode, simulate the uppercasing and truncating
     477             :      * effect on raw tokens of packing them into sixbit characters, 5
     478             :      * to a 32-bit word.  This is something the FORTRAN version did
     479             :      * becuse archaic FORTRAN had no string types.  Don Wood's
     480             :      * mechanical translation of 2.5 to C retained the packing and
     481             :      * thus this misfeature.
     482             :      *
     483             :      * It's philosophically questionable whether this is the right
     484             :      * thing to do even in oldstyle mode.  On one hand, the text
     485             :      * mangling was not authorial intent, but a result of limitations
     486             :      * in their tools. On the other, not simulating this misbehavior
     487             :      * goes against the goal of making oldstyle as accurate as
     488             :      * possible an emulation of the original UI.
     489             :      */
     490       17715 :     if (settings.oldstyle) {
     491           6 :         cmd->word[0].raw[TOKLEN + TOKLEN] = cmd->word[1].raw[TOKLEN + TOKLEN] = '\0';
     492          16 :         for (size_t i = 0; i < strlen(cmd->word[0].raw); i++)
     493          10 :             cmd->word[0].raw[i] = toupper(cmd->word[0].raw[i]);
     494          11 :         for (size_t i = 0; i < strlen(cmd->word[1].raw); i++)
     495           5 :             cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]);
     496             :     }
     497             : 
     498             :     /* populate command with parsed vocabulary metadata */
     499       17715 :     get_vocab_metadata(&(cmd->word[0]));
     500       17715 :     get_vocab_metadata(&(cmd->word[1]));
     501       17715 : }
     502             : 
     503       17782 : bool get_command_input(command_t *command)
     504             : /* Get user input on stdin, parse and map to command */
     505             : {
     506       17782 :     char inputbuf[LINESIZE];
     507             :     char* input;
     508             : 
     509             :     for (;;) {
     510       17830 :         input = get_input();
     511       17806 :         if (input == NULL)
     512          67 :             return false;
     513       17739 :         if (word_count(input) > 2) {
     514           1 :             rspeak(TWO_WORDS);
     515           1 :             free(input);
     516           1 :             continue;
     517             :         }
     518       17738 :         if (strcmp(input, "") != 0)
     519       17715 :             break;
     520          23 :         free(input);
     521             :     }
     522             : 
     523       17715 :     strncpy(inputbuf, input, LINESIZE - 1);
     524       17715 :     free(input);
     525             : 
     526       17715 :     tokenize(inputbuf, command);
     527             : 
     528       17715 :     return true;
     529             : }
     530             : 
     531          92 : void juggle(obj_t object)
     532             : /*  Juggle an object by picking it up and putting it down again, the purpose
     533             :  *  being to get the object to the front of the chain of things at its loc. */
     534             : {
     535             :     loc_t i, j;
     536             : 
     537          92 :     i = game.place[object];
     538          92 :     j = game.fixed[object];
     539          92 :     move(object, i);
     540          92 :     move(object + NOBJECTS, j);
     541          92 : }
     542             : 
     543        1210 : void move(obj_t object, loc_t where)
     544             : /*  Place any object anywhere by picking it up and dropping it.  May
     545             :  *  already be toting, in which case the carry is a no-op.  Mustn't
     546             :  *  pick up objects which are not at any loc, since carry wants to
     547             :  *  remove objects from game.atloc chains. */
     548             : {
     549             :     loc_t from;
     550             : 
     551        1210 :     if (object > NOBJECTS)
     552         385 :         from = game.fixed[object - NOBJECTS];
     553             :     else
     554         825 :         from = game.place[object];
     555             :     /* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong... */
     556        1210 :     if (from != LOC_NOWHERE && from != CARRIED)
     557         899 :         carry(object, from);
     558        1210 :     drop(object, where);
     559        1210 : }
     560             : 
     561         126 : loc_t put(obj_t object, loc_t where, long pval)
     562             : /*  put() is the same as move(), except it returns a value used to set up the
     563             :  *  negated game.prop values for the repository objects. */
     564             : {
     565         126 :     move(object, where);
     566         126 :     return STASHED(pval);
     567             : }
     568             : 
     569        2871 : void carry(obj_t object, loc_t where)
     570             : /*  Start toting an object, removing it from the list of things at its former
     571             :  *  location.  Incr holdng unless it was already being toted.  If object>NOBJECTS
     572             :  *  (moving "fixed" second loc), don't change game.place or game.holdng. */
     573             : {
     574             :     long temp;
     575             : 
     576        2871 :     if (object <= NOBJECTS) {
     577        2571 :         if (game.place[object] == CARRIED)
     578         143 :             return;
     579        2428 :         game.place[object] = CARRIED;
     580        2428 :         ++game.holdng;
     581             :     }
     582        2728 :     if (game.atloc[where] == object) {
     583        1712 :         game.atloc[where] = game.link[object];
     584        1712 :         return;
     585             :     }
     586        1016 :     temp = game.atloc[where];
     587        3666 :     while (game.link[temp] != object) {
     588        1634 :         temp = game.link[temp];
     589             :     }
     590        1016 :     game.link[temp] = game.link[object];
     591             : }
     592             : 
     593        9539 : void drop(obj_t object, loc_t where)
     594             : /*  Place an object at a given loc, prefixing it onto the game.atloc list.  Decr
     595             :  *  game.holdng if the object was being toted. */
     596             : {
     597        9539 :     if (object > NOBJECTS)
     598        1453 :         game.fixed[object - NOBJECTS] = where;
     599             :     else {
     600        8086 :         if (game.place[object] == CARRIED)
     601        2225 :             --game.holdng;
     602        8086 :         game.place[object] = where;
     603             :     }
     604        9539 :     if (where == LOC_NOWHERE ||
     605             :         where == CARRIED)
     606         602 :         return;
     607        8937 :     game.link[object] = game.atloc[where];
     608        8937 :     game.atloc[where] = object;
     609             : }
     610             : 
     611         359 : int atdwrf(loc_t where)
     612             : /*  Return the index of first dwarf at the given location, zero if no dwarf is
     613             :  *  there (or if dwarves not active yet), -1 if all dwarves are dead.  Ignore
     614             :  *  the pirate (6th dwarf). */
     615             : {
     616             :     int at;
     617             : 
     618         359 :     at = 0;
     619         359 :     if (game.dflag < 2)
     620          21 :         return at;
     621         338 :     at = -1;
     622        1064 :     for (long i = 1; i <= NDWARVES - 1; i++) {
     623        1020 :         if (game.dloc[i] == where)
     624         294 :             return i;
     625         726 :         if (game.dloc[i] != 0)
     626         320 :             at = 0;
     627             :     }
     628          44 :     return at;
     629             : }
     630             : 
     631             : /*  Utility routines (setbit, tstbit, set_seed, get_next_lcg_value,
     632             :  *  randrange) */
     633             : 
     634         104 : long setbit(int bit)
     635             : /*  Returns 2**bit for use in constructing bit-masks. */
     636             : {
     637         104 :     return (1L << bit);
     638             : }
     639             : 
     640      503623 : bool tstbit(long mask, int bit)
     641             : /*  Returns true if the specified bit is set in the mask. */
     642             : {
     643      503623 :     return (mask & (1 << bit)) != 0;
     644             : }
     645             : 
     646         189 : void set_seed(int32_t seedval)
     647             : /* Set the LCG seed */
     648             : {
     649         189 :     game.lcg_x = seedval % LCG_M;
     650         189 :     if (game.lcg_x < 0) {
     651           1 :         game.lcg_x = LCG_M + game.lcg_x;
     652             :     }
     653             :     // once seed is set, we need to generate the Z`ZZZ word
     654        1134 :     for (int i = 0; i < 5; ++i) {
     655         945 :         game.zzword[i] = 'A' + randrange(26);
     656             :     }
     657         189 :     game.zzword[1] = '\''; // force second char to apostrophe
     658         189 :     game.zzword[5] = '\0';
     659         189 : }
     660             : 
     661       27771 : static int32_t get_next_lcg_value(void)
     662             : /* Return the LCG's current value, and then iterate it. */
     663             : {
     664       27771 :     int32_t old_x = game.lcg_x;
     665       27771 :     game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M;
     666       27771 :     return old_x;
     667             : }
     668             : 
     669       27771 : int32_t randrange(int32_t range)
     670             : /* Return a random integer from [0, range). */
     671             : {
     672       27771 :     return range * get_next_lcg_value() / LCG_M;
     673             : }
     674             : 
     675             : // LCOV_EXCL_START
     676             : void bug(enum bugtype num, const char *error_string)
     677             : {
     678             :     fprintf(stderr, "Fatal error %d, %s.\n", num, error_string);
     679             :     exit(EXIT_FAILURE);
     680             : }
     681             : // LCOV_EXCL_STOP
     682             : 
     683             : /* end */
     684             : 
     685        1036 : void state_change(obj_t obj, int state)
     686             : /* Object must have a change-message list for this to be useful; only some do */
     687             : {
     688        1036 :     game.prop[obj] = state;
     689        1036 :     pspeak(obj, change, state, true);
     690        1036 : }
     691             : 
     692             : /* end */

Generated by: LCOV version 1.13