home · contact · privacy
Load last world state from save file, not from re-stepping record file.
authorChristian Heller <c.heller@plomlompom.de>
Fri, 4 Jul 2014 21:18:05 +0000 (23:18 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Fri, 4 Jul 2014 21:18:05 +0000 (23:18 +0200)
Re-wrote large chunks of code dealing with server message parsing and
file parsing in general. Introduced "god commands" (manipulating the
game state beyond player actor control) that direct the re-generation of
the game state when loading the save file. Introduced a module
"hardcoded_strings" to store strings re-used and expected among various
modules.

24 files changed:
README
TODO
src/client/command_db.c
src/client/interface_conf.c
src/common/parse_file.c
src/common/parse_file.h
src/server/ai.c
src/server/cleanup.c
src/server/configfile.c
src/server/configfile.h
src/server/hardcoded_strings.c [new file with mode: 0644]
src/server/hardcoded_strings.h [new file with mode: 0644]
src/server/init.c
src/server/init.h
src/server/io.c
src/server/io.h
src/server/main.c
src/server/map.c
src/server/map.h
src/server/run.c
src/server/run.h
src/server/things.c
src/server/things.h
src/server/world.h

diff --git a/README b/README
index 8b79cf28fda8dcc6fd898fb7894e46b9751dd550..9775dd8c8828de3329b6c23c62a7b2271905319b 100644 (file)
--- a/README
+++ b/README
@@ -15,10 +15,9 @@ Enemies' AI is very dumb so far: Each turn, they try to move towards their
 shortest-path-wise nearest enemy visible to them. If they see no enemy, they
 just wait.
 
-Once you start a new world, every move of yours is recorded in a file called
-"record". Once you re-start the game, all of your previous moves are replayed
-automatically up to the point wherere you left the game. To start over in a new
-world, simply delete this file.
+Every move of yours re-writes a file "savefile" that describes the new state of
+the world. Once you re-start the game, the game state is recreated from the
+"savefile" file. To start over in a new world, simply delete this file.
 
 System requirements / installation / running the game
 -----------------------------------------------------
@@ -67,12 +66,15 @@ in-client window management.
 Replay game recording
 ---------------------
 
-Run "./roguelike -s" to watch a recording of the current game from the
-beginning. Hit any player action key to increment turns (they will not trigger
-the actions usually mapped to them, only repeat the actions done at that point
-in the game as defined in the "record" file). Keys to manage windows, scroll on
-the map and quit the program do their usual thing. Append a number to the -s
-option (like "-s100") to start the recording at the respective turn number.
+Once you start a new world, every move of yours is recorded in a file called
+"record". It gets overwritten when a new game world is started after deletion
+of the "savefile" file. Run "./roguelike -s" to watch the current game's
+recording from the beginning. Hit any player action key to increment turns (they
+will not trigger the actions usually mapped to them, only repeat the actions
+done at that point in the game as defined in the "record" file). Keys to manage
+windows, scroll on the map and quit the program do their usual thing. Append a
+number to the -s option (like "-s100") to start the recording at the respective
+turn number.
 
 Hacking / server internals and configuration
 --------------------------------------------
diff --git a/TODO b/TODO
index c6623da4ddf03a19c52db310a12cb1e684a275a2..00b66b2ed702694a56b220423db14b4afab45476 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,22 +1,24 @@
 Next planned steps in plomrogue development:
 
-BOTH SERVER/CLIENT:
-
-- make server and client communicate by specific world state info requests 
-  in server/out, replacing server/worldstate
+IN GENERAL:
 
 - check for return values of *printf()
 
 - be more strict and humble when allocating memory from the stack
 
+- expand use of hardcoded_strings module(s)
+
+BOTH SERVER/CLIENT:
+
+- make server and client communicate by specific world state info requests 
+  in server/out, replacing server/worldstate
+
 SERVER:
 
 - optimize too-slow AI / FOV algorithms
 
-- is it actually useful to define map object action ids in the config file?
-
-- for game continuation, replace re-playing of whole record files with loading
-  game state snapshots / save files
+- save confserver/world data in record and save file, too; handle them like god
+  commands
 
 CLIENT:
 
index 29ef83dbbea5d59a8f15b656e60b4fa8aa6bfc3c..b9d10a6234c92560bbeb1994d4ec0de5ccb52f10 100644 (file)
@@ -9,7 +9,8 @@
 #include "../common/parse_file.h" /* EDIT_STARTED,parse_init_entry(),
                                    * parse_id_uniq(), parse_unknown_arg(),
                                    * parsetest_too_many_values(), parse_file(),
-                                   * parse_and_reduce_to_readyflag(),parse_val()
+                                   * parse_and_reduce_to_readyflag(),
+                                   * parse_flagval()
                                    */
 #include "array_append.h" /* array_append() */
 #include "world.h" /* global world */
@@ -66,12 +67,12 @@ static void tokens_into_entries(char * token0, char * token1)
             cmd->dsc_short = strdup(token1);
             parse_id_uniq(NULL != get_command(cmd->dsc_short));
         }
-        else if (!(   parse_val(token0, token1, "DESCRIPTION", &cmd_flags,
-                                DESC_SET, 's', (char *) &cmd->dsc_long)
-                   || parse_val(token0, token1, "SERVER_COMMAND", &cmd_flags,
-                                SERVERCMD_SET, 's', (char *) &cmd->server_msg)
-                   || parse_val(token0, token1, "SERVER_ARGUMENT", &cmd_flags,
-                                SERVERARG_SET, 'c', (char *) &cmd->arg)))
+        else if (!(   parse_flagval(token0, token1, "DESCRIPTION", &cmd_flags,
+                                    DESC_SET, 's', (char *) &cmd->dsc_long)
+                   || parse_flagval(token0, token1,"SERVER_COMMAND", &cmd_flags,
+                                    SERVERCMD_SET, 's',(char *)&cmd->server_msg)
+                   || parse_flagval(token0, token1,"SERVER_ARGUMENT",&cmd_flags,
+                                    SERVERARG_SET, 'c', (char *) &cmd->arg)))
         {
             parse_unknown_arg();
         }
index 6d4d65a953dd3aa0d5908798d7b63bbad89f0875..e3bfd022a5db6d3c61c1757d1819cb894935849a 100644 (file)
@@ -9,7 +9,7 @@
 #include <stdio.h> /* FILE, sprintf() */
 #include <string.h> /* strchr(), strcmp(), strdup(), strlen() */
 #include <unistd.h> /* optarg, getopt() */
-#include "../common/parse_file.h" /* EDIT_STARTED, parse_file(), parse_val(),
+#include "../common/parse_file.h" /* EDIT_STARTED, parse_file(),parse_flagval(),
                                    * token_from_line(), parsetest_singlechar(),
                                    * parse_and_reduce_to_readyflag(),
                                    * parsetest_defcontext(),parse_unknown_arg(),
@@ -311,19 +311,19 @@ static uint8_t set_members(char * token0, char * token1, uint8_t * win_flags,
                            uint8_t * ord_flags,uint8_t kbd_flags,char * str_key,
                            struct Win * win, struct KeyBindingDB * kbdb)
 {
-    if (   parse_val(token0, token1, "NAME", win_flags,
-                     NAME_SET, 's', (char *) &win->title)
-        || parse_val(token0, token1, "WIDTH", win_flags,
-                     WIDTH_SET, 'i', (char *) &win->target_width)
-        || parse_val(token0, token1, "HEIGHT", win_flags,
-                     HEIGHT_SET, 'i', (char *) &win->target_height));
-    else if (parse_val(token0, token1, "BREAK", win_flags,
-                       BREAK_SET, '8', (char *) &win->linebreak))
+    if (   parse_flagval(token0, token1, "NAME", win_flags,
+                         NAME_SET, 's', (char *) &win->title)
+        || parse_flagval(token0, token1, "WIDTH", win_flags,
+                         WIDTH_SET, 'i', (char *) &win->target_width)
+        || parse_flagval(token0, token1, "HEIGHT", win_flags,
+                         HEIGHT_SET, 'i', (char *) &win->target_height));
+    else if (parse_flagval(token0, token1, "BREAK", win_flags,
+                           BREAK_SET, '8', (char *) &win->linebreak))
     {
         err_line(2 < win->linebreak, "Value must be 0, 1 or 2.");
     }
-    else if (parse_val(token0, token1, "WIN_FOCUS", ord_flags,
-                       FOCUS_SET, 'c', &tmp_active))
+    else if (parse_flagval(token0, token1, "WIN_FOCUS", ord_flags,
+                           FOCUS_SET, 'c', &tmp_active))
     {
         char * err_null = "Value not empty as it should be.";
         char * err_outside = "ID not found in WIN_ORDER ID series.";
index 50fc30d04f8e467b61341dd2532519c920edc319..8ce20ad55db79c7369225134e73d2f09a5d2b42a 100644 (file)
 
 
 
-/* Set by parse_file(), used by err_line() for more informative messages. */
+/* Set by parse_file(), helps err_line() deciding what to do/output on error. */
 static uint32_t err_line_count = 0;
 static char * err_line_line = NULL;
 static char * err_line_intro = NULL;
+static uint8_t err_line_exit = 1;
 
 
 
@@ -54,23 +55,23 @@ extern void parse_file(char * path, void (* token_to_entry) (char *, char *))
     char * prefix = "Failed reading config file: \"";
     char * affix = "\". ";
     size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1;
-    err_line_intro = try_malloc(size, f_name);
-    int test = snprintf(err_line_intro, size, "%s%s%s", prefix, path, affix);
+    char * errline_intro = try_malloc(size, f_name);
+    int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix);
     exit_trouble(test < 0, f_name, "snprintf()");
-    exit_err(access(path, F_OK), err_line_intro);
+    exit_err(access(path, F_OK), errline_intro);
     FILE * file = try_fopen(path, "r", f_name);
     uint32_t linemax = textfile_width(file);
-    err_line_line = try_malloc(linemax + 1, f_name);
-    err_line_count = 0;
+    char * errline_line = try_malloc(linemax + 1, f_name);
+    set_err_line_options(errline_intro, errline_line, 0, 1);
     err_line(0 == linemax, "File is empty.");
     char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */
     char * token1 = NULL;
     char * err_val = "No value given.";
-    while (try_fgets(err_line_line, linemax + 1, file, f_name))
+    while (try_fgets(errline_line, linemax + 1, file, f_name))
     {
         err_line_count++;
         err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit.");
-        char * line_copy = strdup(err_line_line);
+        char * line_copy = strdup(errline_line);
         token0 = token_from_line(line_copy);
         if (token0)
         {
@@ -82,17 +83,28 @@ extern void parse_file(char * path, void (* token_to_entry) (char *, char *))
     }
     token_to_entry(token0, token1);
     try_fclose(file, f_name);
-    free(err_line_line);
-    free(err_line_intro);
+    free(errline_line);
+    free(errline_intro);
 }
 
 
 
-extern void err_line(uint8_t test, char * msg)
+extern void set_err_line_options(char * intro, char * line, uint32_t count,
+                                 uint8_t exit)
+{
+    err_line_count = count;
+    err_line_line = line;
+    err_line_intro = intro;
+    err_line_exit = exit;
+}
+
+
+
+extern uint8_t err_line(uint8_t test, char * msg)
 {
     if (!test)
     {
-        return;
+        return 0;
     }
     char * f_name = "err_line()";
     char * prefix = " Offending line ";
@@ -104,7 +116,14 @@ extern void err_line(uint8_t test, char * msg)
     int ret = snprintf(err, size, "%s%s%s%d%s%s", err_line_intro, msg, prefix,
                        err_line_count, affix, err_line_line);
     exit_trouble(ret < 0, f_name, "snprintf()");
-    exit_err(1, err);
+    if (err_line_exit)
+    {
+        exit_err(1, err);
+    }
+    exit_trouble(0 > printf("%s\n", err), f_name, "printf()");
+    exit_trouble(EOF == fflush(stdout), f_name, "fflush()");
+    free(err);
+    return 1;
 }
 
 
@@ -124,6 +143,10 @@ extern char * token_from_line(char * line)
             *(--final_char) = '\0';
         }
     }
+    if (final_char < start)
+    {
+        return NULL;
+    }
     uint8_t empty = 1;
     uint32_t i;
     for (i = 0; '\0' != start[i]; i++)
@@ -137,7 +160,7 @@ extern char * token_from_line(char * line)
     }
     if (empty)
     {
-        return start = NULL;
+        return NULL;
     }
     set_token_end(&start, &limit_char);
     return start;
@@ -145,32 +168,42 @@ extern char * token_from_line(char * line)
 
 
 
-extern void parsetest_int(char * string, char type)
+extern uint8_t parsetest_int(char * string, char type)
 {
-    char * err;
-    if ('8' == type)
-    {
-        err = "Value must be proper representation of unsigned 8 bit integer.";
-    }
-    if ('i' == type)
-    {
-        err = "Value must be proper representation of signed 16 bit integer.";
-    }
-    err_line(strlen(string) < 1, err);
+    char * err_8 = "Value must represent proper unsigned 8 bit integer.";
+    char * err_i = "Value must represent proper signed 16 bit integer.";
+    char * err_u = "Value must represent proper unsigned 16 bit integer.";
+    char * err_U = "Value must represent proper unsigned 32 bit integer.";
+    char * err = ('8' == type) ? err_8 : err_U;
+    err = ('i' == type) ? err_i : err;
+    err = ('u' == type) ? err_u : err;
+    uint8_t ret = err_line(strlen(string) < 1, err);
     uint8_t i;
     uint8_t test;
     for (i = 0; '\0' != string[i]; i++)
     {
         char * err_many = "Value of too many characters.";
-        err_line(string[i + 1] && UINT8_MAX == i, err_many);
+        ret = ret + err_line(string[i + 1] && UINT8_MAX == i, err_many);
         test = (   (0 == i && ('-' == string[i] || '+' == string[i]))
                 || ('0' <= string[i]  && string[i] <= '9'));
-        err_line(!test, err);
+        ret = ret + err_line(!test, err);
     }
-    err_line(strlen(string) < 2 && ('-' == string[i] || '+' == string[i]), err);
-    err_line('8'==type && (atoi(string) < 0 || atoi(string) > UINT8_MAX), err);
-    test = 'i'==type && (atoi(string) < INT16_MIN || atoi(string) > INT16_MAX);
-    err_line(test, err);
+    ret = ret + err_line(   strlen(string) < 2
+                         && ('-' == string[i] || '+' == string[i]), err);
+    test =     (   '8' == type
+                && (   strlen(string) > 4
+                    || atoi(string) < 0 || atoi(string) > UINT8_MAX))
+            || (   'i' == type
+                && (   strlen(string) > 6
+                    || atol(string) < INT16_MIN || atol(string) > INT16_MAX))
+            || (   'u' == type
+                && (   strlen(string) > 6
+                    || atoll(string) < 0 || atol(string) > UINT16_MAX))
+            || (   'U' == type
+                && (   strlen(string) > 11
+                    || atoll(string) < 0 || atoll(string) > UINT32_MAX));
+    ret = ret + err_line(test, err);
+    return ret;
 }
 
 
@@ -182,9 +215,9 @@ extern void parsetest_defcontext(uint8_t flags)
 
 
 
-extern void parsetest_singlechar(char * string)
+extern uint8_t parsetest_singlechar(char * string)
 {
-    err_line(1 != strlen(string), "Value must be single ASCII character.");
+    return err_line(1 !=strlen(string),"Value must be single ASCII character.");
 }
 
 
@@ -222,31 +255,36 @@ extern char * parse_init_entry(uint8_t * flags, size_t size)
 
 
 extern uint8_t parse_val(char * token0, char * token1, char * comparand,
-                         uint8_t * flags, uint8_t set_flag, char type,
-                         char * element)
+                         char type, char * element)
 {
     if (!strcmp(token0, comparand))
     {
-        parsetest_defcontext(*flags);
-        *flags = *flags | set_flag;
         if      ('s' == type)
         {
             * (char **) element = strdup(token1);
         }
-        else if ('c' == type)
+        else if ('c' == type && !parsetest_singlechar(token1))
         {
-            parsetest_singlechar(token1);
             *element = (token1)[0];
         }
-        else if ('8' == type)
-        {
-            parsetest_int(token1, '8');
-            * (uint8_t *) element = atoi(token1);
-        }
-        else if ('i' == type)
+        else if (!parsetest_int(token1, type))
         {
-            parsetest_int(token1, 'i');
-            * (int16_t *) element = atoi(token1);
+            if ('8' == type)
+            {
+                * (uint8_t *) element = atoi(token1);
+            }
+            else if ('i' == type)
+            {
+                * (int16_t *) element = atoi(token1);
+            }
+            else if ('u' == type)
+            {
+                * (uint16_t *) element = atol(token1);
+            }
+            else if ('U' == type)
+            {
+                * (uint32_t *) element = atoll(token1);
+            }
         }
         return 1;
     }
@@ -255,6 +293,21 @@ extern uint8_t parse_val(char * token0, char * token1, char * comparand,
 
 
 
+extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
+                             uint8_t * flags, uint8_t set_flag, char type,
+                             char * element)
+{
+    if (parse_val(token0, token1, comparand, type, element))
+    {
+        parsetest_defcontext(*flags);
+        *flags = *flags | set_flag;
+        return 1;
+    }
+    return 0;
+}
+
+
+
 extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag)
 {
     char * err_fin = "Last definition block not finished yet.";
index 29e7ae18ccec5c362438f7186ed43cf24099a362..4192dac046ee7190a8976e3253fe5b5b2f25f6fc 100644 (file)
@@ -19,17 +19,23 @@ enum parse_flags
 
 
 /* Parse file at "path" by passing each line's first two tokens to
- * "token_to_entry". Ignore empty line. Non-empty lines must feature at least
+ * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least
  * two tokens as delimited either be whitespace or single quotes (to allow for
  * tokens featuring whitespaces). When EOF is reached, token_to_entry() is
  * called a last time with a first token of NULL.
  */
 extern void parse_file(char * path, void ( *token_to_entry) (char *, char *));
 
-/* If "test" != 0, exit on output of "msg" and faulty line and line number as
- * parsed by parse_file(). (Ought to be called as offspring to parse_file().)
+/* Set err_line() options: "intro" message, char array used to store analyzed
+ * lines ("line"), line start "count", whether to "exit" on error message.
  */
-extern void err_line(uint8_t test, char * msg);
+extern void set_err_line_options(char * intro, char * line, uint32_t count,
+                                 uint8_t exit);
+
+/* If "test", output "msg", faulty line, its number and exit if so defined by
+ * set_err_line_options(), else return 1 on "test" and 0 if "test" is 0.
+ */
+extern uint8_t err_line(uint8_t test, char * msg);
 
 /* Return next token from "line" or NULL if none is found. Tokens either a)
  * start at the first non-whitespace character and end before the next
@@ -42,11 +48,13 @@ extern void err_line(uint8_t test, char * msg);
  * */
 extern char * token_from_line(char * line);
 
-/* Test for "string" to represent proper int16 (type: "i") or uint8 ("8"). */
-extern void parsetest_int(char * string, char type);
+/* Test for "string" to represent proper int16 (type: "i"), uint8 ("8"), uint16
+ * ("u") or uint32 ("U"). Returns 0 if proper value, else >0.
+ */
+extern uint8_t parsetest_int(char * string, char type);
 
-/* Test for "string" to be of length 1 (excluding "\0" terminator). */
-extern void parsetest_singlechar(char * string);
+/* Test for "string" to be of length 1 (excluding "\0"). Return 1 on failure. */
+extern uint8_t parsetest_singlechar(char * string);
 
 /* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */
 extern void parsetest_defcontext(uint8_t flags);
@@ -64,12 +72,16 @@ extern void parse_id_uniq(int test);
 extern char * parse_init_entry(uint8_t * flags, size_t size);
 
 /* If "token0" fits "comparand", set "element" to value read from "token1" as
- * string (type: "s"), char ("c") uint8 ("8") or int16 ("i"), set that element's
- * flag to "flags" and return 1; else return 0.
+ * string (type: "s"), char ("c") uint8 ("8"), uint16 ("u"), uint32 ("U") or
+ * int16 ("i"), and return 1; else 0.
  */
 extern uint8_t parse_val(char * token0, char * token1, char * comparand,
-                         uint8_t * flags, uint8_t set_flag, char type,
-                         char * element);
+                         char type, char * element);
+
+/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */
+extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
+                             uint8_t * flags, uint8_t set_flag, char type,
+                             char * element);
 
 /* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */
 extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag);
index 54db84babcf99dc2cebf60f8dd66c0ff152c877d..ff883dd7fef2bab1306fb5e7f3730b941a9e7e4a 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdlib.h> /* free() */
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "field_of_view.h" /* VISIBLE */
+#include "hardcoded_strings.h" /* s */
 #include "thing_actions.h" /* get_thing_action_id_by_name() */
 #include "things.h" /* struct Thing */
 #include "world.h" /* global world */
@@ -168,11 +169,11 @@ static char get_dir_to_nearest_enemy(struct Thing * t_origin)
 
 extern void ai(struct Thing * t)
 {
-    t->command = get_thing_action_id_by_name("wait");
-    char sel = get_dir_to_nearest_enemy(t);
-    if (0 != sel)
-    {
-        t->command = get_thing_action_id_by_name("move");
+    t->command = get_thing_action_id_by_name(s[CMD_WAIT]);
+    char sel = t->fov_map ? get_dir_to_nearest_enemy(t) : 0;/* t->fov_map may */
+    if (0 != sel)                                           /* be absent due  */
+    {                                                       /* to god command.*/
+        t->command = get_thing_action_id_by_name(s[CMD_MOVE]);
         t->arg = sel;
     }
 }
index 1959c1c831aedf979bd777b999a0bf09b5cff015..02f52b3d7d13b32d49cb2ff1e9a0c27af90ece95 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdlib.h> /* free() */
 #include <unistd.h> /* unlink() */
 #include "../common/readwrite.h" /* try_fclose() */
+#include "hardcoded_strings.h" /* s */
 #include "thing_actions.h" /* free_thing_actions() */
 #include "things.h" /* free_things(), free_thing_types() */
 #include "world.h" /* global world */
@@ -25,7 +26,7 @@ extern void cleanup()
     free(world.map.cells);
     if (cleanup_flags & CLEANUP_WORLDSTATE)
     {
-        unlink(world.path_worldstate);
+        unlink(s[PATH_WORLDSTATE]);
     }
     if (cleanup_flags & CLEANUP_THINGS)
     {
@@ -42,13 +43,13 @@ extern void cleanup()
     if (cleanup_flags & CLEANUP_IN)
     {
         try_fclose(world.file_in, f_name);
-        unlink(world.path_in);
+        unlink(s[PATH_IN]);
     }
     if (cleanup_flags & CLEANUP_OUT)
     {
         try_fclose(world.file_out, f_name);
         free(world.server_test);
-        unlink(world.path_out);
+        unlink(s[PATH_OUT]);
     }
 }
 
index b53b85e197b5ff9a4e49f88bb4f945bfa71b5f81..f6561aa53d08198ce122b88457d819f2a336063d 100644 (file)
@@ -8,13 +8,15 @@
 #include "../common/parse_file.h" /* EDIT_STARTED, parsetest_int(),parse_file(),
                                    * parsetest_too_many_values(),parse_id_uniq()
                                    * parse_unknown_arg(), parse_init_entry(),
-                                   * parse_and_reduce_to_readyflag(),parse_val()
+                                   * parse_and_reduce_to_readyflag(),
+                                   * parse_flagval()
                                    */
 #include "../common/rexit.h" /* exit_err(), exit_trouble() */
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "cleanup.h" /* set_cleanup_flag(), CLEANUP_THING_TYPES,
                       * CLEANUP_THING_ACTIONS
                       */
+#include "hardcoded_strings.h" /* s */
 #include "thing_actions.h" /* ThingAction */
 #include "things.h" /* Thing, ThingType */
 #include "world.h" /* world global */
@@ -252,33 +254,33 @@ static uint8_t set_members(char * token0, char * token1, uint8_t * thing_flags,
                            struct ThingType * tt, struct ThingAction * ta)
 {
     if (   *action_flags & EDIT_STARTED
-        && parse_val(token0, token1, "NAME", action_flags,
-                     NAME_SET, 's', (char *) &ta->name))
+        && parse_flagval(token0, token1, "NAME", action_flags,
+                         NAME_SET, 's', (char *) &ta->name))
     {
-        if (!(   try_func_name(ta, "move", actor_move)
-              || try_func_name(ta, "pick_up", actor_pick)
-              || try_func_name(ta, "drop", actor_drop)
-              || try_func_name(ta, "use", actor_use)))
+        if (!(   try_func_name(ta, s[CMD_MOVE], actor_move)
+              || try_func_name(ta, s[CMD_PICKUP], actor_pick)
+              || try_func_name(ta, s[CMD_DROP], actor_drop)
+              || try_func_name(ta, s[CMD_USE], actor_use)))
         {
             ta->func = actor_wait;
         }
         *action_flags = *action_flags | NAME_SET;
         return 1;
     }
-    else if (   parse_val(token0, token1, "NAME", thing_flags,
-                          NAME_SET, 's', (char *) &tt->name)
-             || parse_val(token0, token1, "SYMBOL", thing_flags,
-                          SYMBOL_SET, 'c', (char *) &tt->char_on_map)
-             || parse_val(token0, token1, "EFFORT", action_flags,
-                          EFFORT_SET, '8', (char *) &ta->effort)
-             || parse_val(token0, token1, "START_NUMBER", thing_flags,
-                          START_N_SET, '8', (char *) &tt->start_n)
-             || parse_val(token0, token1, "LIFEPOINTS", thing_flags,
-                          LIFEPOINTS_SET, '8', (char *) &tt->lifepoints)
-             || parse_val(token0, token1, "CONSUMABLE", thing_flags,
-                          CONSUMABLE_SET, '8', (char *) &tt->consumable)
-             || parse_val(token0, token1, "CORPSE_ID", thing_flags,
-                          CORPSE_ID_SET, '8', (char *) &tt->corpse_id))
+    else if (   parse_flagval(token0, token1, "NAME", thing_flags,
+                              NAME_SET, 's', (char *) &tt->name)
+             || parse_flagval(token0, token1, "SYMBOL", thing_flags,
+                              SYMBOL_SET, 'c', (char *) &tt->char_on_map)
+             || parse_flagval(token0, token1, "EFFORT", action_flags,
+                              EFFORT_SET, '8', (char *) &ta->effort)
+             || parse_flagval(token0, token1, "START_NUMBER", thing_flags,
+                              START_N_SET, '8', (char *) &tt->start_n)
+             || parse_flagval(token0, token1, "LIFEPOINTS", thing_flags,
+                              LIFEPOINTS_SET, '8', (char *) &tt->lifepoints)
+             || parse_flagval(token0, token1, "CONSUMABLE", thing_flags,
+                              CONSUMABLE_SET, '8', (char *) &tt->consumable)
+             || parse_flagval(token0, token1, "CORPSE_ID", thing_flags,
+                              CORPSE_ID_SET, '8', (char *) &tt->corpse_id))
     {
         return 1;
     }
@@ -302,7 +304,7 @@ static uint8_t try_func_name(struct ThingAction * ta, char * name,
 
 extern void read_config_file()
 {
-    parse_file(world.path_config, tokens_into_entries);
+    parse_file(s[PATH_CONFIG], tokens_into_entries);
     exit_err(!world.map.length, "Map size not defined in config file.");
     uint8_t player_type_is_valid = 0;
     struct ThingType * tt;
index 901834af96cc87c8cac982ad327dcd8fa66003c9..d80a2ba004e2bc3c1a2eaca78537a4934f33e916 100644 (file)
@@ -8,8 +8,8 @@
 
 
 
-/* Parse file at world.path_config into thing type and thing action definitions
- * at world.thing_types and world.thing_actions.
+/* Parse thing type / action definitons file nto thing type and thing action
+ * definitions at world.thing_types and world.thing_actions.
  */
 extern void read_config_file();
 
diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c
new file mode 100644 (file)
index 0000000..e007547
--- /dev/null
@@ -0,0 +1,39 @@
+/* hardcoded_strings.c */
+
+#include "hardcoded_strings.h"
+
+
+
+char * s[26];
+
+
+
+extern void init_strings()
+{
+    s[PATH_CONFIG] = "confserver/world";
+    s[PATH_WORLDSTATE] = "server/worldstate";
+    s[PATH_OUT] = "server/out";
+    s[PATH_IN] = "server/in";
+    s[PATH_RECORD] = "record";
+    s[PATH_SUFFIX_TMP] = "_tmp";
+    s[PATH_SAVE] = "savefile";
+    s[CMD_MAKE_WORLD] = "MAKE_WORLD";
+    s[CMD_DO_FOV] = "BUILD_FOVS";
+    s[CMD_SEED_MAP] = "SEED_MAP";
+    s[CMD_SEED_RAND] = "SEED_RANDOMNESS";
+    s[CMD_TURN] = "TURN";
+    s[CMD_THING] = "THING";
+    s[CMD_TYPE] = "TYPE";
+    s[CMD_POS_Y] = "POS_Y";
+    s[CMD_POS_X] = "POS_X";
+    s[CMD_COMMAND] =  "COMMAND";
+    s[CMD_ARGUMENT] = "ARGUMENT";
+    s[CMD_PROGRESS] = "PROGRESS";
+    s[CMD_LIFEPOINTS] = "LIFEPOINTS";
+    s[CMD_CARRIES] = "CARRIES";
+    s[CMD_WAIT] = "wait";
+    s[CMD_MOVE] = "move";
+    s[CMD_PICKUP] = "pick_up";
+    s[CMD_DROP] = "drop";
+    s[CMD_USE] = "use";
+}
diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h
new file mode 100644 (file)
index 0000000..3fda2ac
--- /dev/null
@@ -0,0 +1,47 @@
+/* hardcoded_strings.h
+ *
+ * For re-used hardcoded strings.
+ */
+
+#ifndef STRINGS_H
+#define STRINGS_H
+
+
+
+enum string_num
+{
+    PATH_CONFIG,
+    PATH_WORLDSTATE,
+    PATH_OUT,
+    PATH_IN,
+    PATH_RECORD,
+    PATH_SUFFIX_TMP,
+    PATH_SAVE,
+    CMD_MAKE_WORLD,
+    CMD_DO_FOV,
+    CMD_SEED_MAP,
+    CMD_SEED_RAND,
+    CMD_TURN,
+    CMD_THING,
+    CMD_TYPE,
+    CMD_POS_Y,
+    CMD_POS_X,
+    CMD_COMMAND,
+    CMD_ARGUMENT,
+    CMD_PROGRESS,
+    CMD_LIFEPOINTS,
+    CMD_CARRIES,
+    CMD_WAIT,
+    CMD_MOVE,
+    CMD_PICKUP,
+    CMD_DROP,
+    CMD_USE
+};
+
+extern void init_strings();
+
+extern char * s[26];
+
+
+
+#endif
index a3c1589b6992c1af654e327f9b193e464329d62e..219223ba5e0ba7dc39ef1573443836e4aea4cf3d 100644 (file)
@@ -19,7 +19,8 @@
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "cleanup.h" /* set_cleanup_flag() */
 #include "field_of_view.h" /* build_fov_map() */
-#include "map.h" /* init_map() */
+#include "hardcoded_strings.h" /* s */
+#include "map.h" /* remake_map() */
 #include "things.h" /* Thing, ThingType, free_things(), add_things(),
                      * get_player()
                      */
 
 
 
+
+/* Replay game from record file up to the turn named in world.replay, then turn
+ * over to manual replay via io_loop().
+ */
+static void replay_game();
+
+
+
+static void replay_game()
+{
+    char * f_name = "replay_game()";
+    exit_err(access(s[PATH_RECORD], F_OK), "No record found to replay.");
+    FILE * file = try_fopen(s[PATH_RECORD], "r", f_name);
+    uint32_t linemax = textfile_width(file);
+    char line[linemax + 1];
+    while (   world.turn < world.replay
+           && NULL != try_fgets(line, linemax + 1, file, f_name))
+    {
+        obey_msg(line, 0);
+    }
+    uint8_t end = 0;
+    while (!io_loop())
+    {
+        if (!end)
+        {
+            end = (NULL == try_fgets(line, linemax + 1, file, f_name));
+            if (!end)
+            {
+                obey_msg(line, 0);
+            }
+        }
+    }
+    try_fclose(file, f_name);
+}
+
+
+
 extern void obey_argv(int argc, char * argv[])
 {
     int opt;
@@ -59,36 +97,35 @@ extern void setup_server_io()
     char * f_name = "setup_server_io()";
     int test = mkdir("server", 0700);
     exit_trouble(test && EEXIST != errno, f_name, "mkdir()");
-    world.file_out = try_fopen(world.path_out, "w", f_name);
+    world.file_out = try_fopen(s[PATH_OUT], "w", f_name);
     world.server_test = try_malloc(10 + 1 + 10 + 1 + 1, f_name);
     sprintf(world.server_test, "%d %d\n", getpid(), (int) time(0));
     try_fwrite(world.server_test, strlen(world.server_test), 1,
                world.file_out, f_name);
     fflush(world.file_out);
     set_cleanup_flag(CLEANUP_OUT);
-    if (!access(world.path_in, F_OK))  /* This keeps out input from old input */
+    char * path_in = s[PATH_IN];
+    if (!access(path_in, F_OK))        /* This keeps out input from old input */
     {                                  /* file streams of clients             */
-        unlink(world.path_in);         /* communicating with server processes */
+        unlink(path_in)   ;            /* communicating with server processes */
     }                                  /* superseded by this current one.     */
-    world.file_in = try_fopen(world.path_in, "w", f_name);
+    world.file_in = try_fopen(path_in, "w", f_name);
     try_fclose(world.file_in, f_name);
-    world.file_in = try_fopen(world.path_in, "r", f_name);
+    world.file_in = try_fopen(path_in, "r", f_name);
     set_cleanup_flag(CLEANUP_IN);
 }
 
 
 
-extern void remake_world(uint32_t seed)
+extern void remake_world()
 {
     char * f_name = "remake_world()";
     free(world.log);
-    world.log = NULL;  /* thing_actions.c's update_log() checks for this. */
-    world.seed = seed;
-    world.thing_count = 0;
-    free(world.map.cells);
+    world.log = NULL;      /* thing_actions.c's update_log() checks for this. */
+    world.seed_map = world.seed;
     free_things(world.things);
     world.last_update_turn = 0;
-    init_map();
+    remake_map();
     struct ThingType * tt;
     for (tt = world.thing_types; NULL != tt; tt = tt->next)
     {
@@ -111,9 +148,9 @@ extern void remake_world(uint32_t seed)
     {
         t->fov_map = t->lifepoints ? build_fov_map(t) : NULL;
     }
-    if (world.turn)
+    if (!access(s[PATH_RECORD], F_OK))
     {
-        exit_trouble(unlink(world.path_record), f_name, "unlink()");
+        exit_trouble(unlink(s[PATH_RECORD]), f_name, "unlink()");
     }
     world.turn = 1;
 }
@@ -123,41 +160,32 @@ extern void remake_world(uint32_t seed)
 extern void run_game()
 {
     char * f_name = "run_game()";
-    if (!access(world.path_record, F_OK))
+    if (world.replay)
     {
-        FILE * file = try_fopen(world.path_record, "r", f_name);
+        replay_game();
+        return;
+    }
+    char * path_savefile = s[PATH_SAVE];
+    if (!access(path_savefile, F_OK))
+    {
+        FILE * file = try_fopen(path_savefile, "r", f_name);
         uint32_t linemax = textfile_width(file);
         char line[linemax + 1];
-        while (   (!world.replay || (world.turn < world.replay))
-               && NULL != try_fgets(line, linemax + 1, file, f_name))
-        {
-            obey_msg(line, 0);
-        }
-        if (!world.replay)
-        {
-            try_fclose(file, f_name);
-            io_loop();
-            return;
-        }
-        uint8_t end = 0;
-        while (!io_loop())
+        while (NULL != try_fgets(line, linemax + 1, file, f_name))
         {
-            if (!end)
+            if (strlen(line) && strcmp("\n", line))
             {
-                end = (NULL == try_fgets(line, linemax + 1, file, f_name));
-                if (!end)
-                {
-                    obey_msg(line, 0);
-                }
+                obey_msg(line, 0);
             }
         }
         try_fclose(file, f_name);
-        return;
     }
-    exit_err(world.replay, "No record file found to replay.");
-    char * command = "seed";
-    char msg[strlen(command) + 1 + 11 + 1];
-    sprintf(msg, "%s %d", command, (int) time(NULL));
-    obey_msg(msg, 1);
+    else
+    {
+        char * command = s[CMD_MAKE_WORLD];
+        char msg[strlen(command) + 1 + 11 + 1];
+        sprintf(msg, "%s %d", command, (int) time(NULL));
+        obey_msg(msg, 1);
+    }
     io_loop();
 }
index 0fbc79b51c20c42b2f17b5497dda33ea8e34bf01..e56337414bd8f1f96243433b0d73424832f742ba 100644 (file)
@@ -16,24 +16,24 @@ extern void obey_argv(int argc, char * argv[]);
 /* Start server in file and out file, latter with server process test string. */
 extern void setup_server_io();
 
-/* Dissolves old game world if it exists, and generates a new one from "seed".
- * Unlinks a pre-existing file at world.path_record if called on a world.turn>0,
- * i.e. if called after iterating through an already established game world.
+/* Dissolves old game world if it exists, generates a new one from world.seed.
+ * Unlinks any pre-existing record file.
  *
  * Thing (action) definitions read in from server config directory are not
  * affected. The map is populated accordingly. world.last_update_turn is set to
  * 0 and world.turn to 1, so that io_round()'s criteria for updating the output
  * file are triggered even when this function is called during a round 1.
  */
-extern void remake_world(uint32_t seed);
+extern void remake_world();
 
-/* Create a game state from which to play or replay, then enter io_loop().
+/* Create a game world state, then enter play or replay mode.
  *
- * If no record file exists at world.path_record, generate new world (by a
- * "seed" command calling remake_world()) in play mode, or error-exit in replay
- * mode. If a record file exists, in play mode auto-replay it up to the last
- * game state before turning over to the player; in replay mode, auto-replay it
- * up to the turn named in world.replay and then turn over to manual replay.
+ * If replay mode is called for, try for the record file and follow its commands
+ + up to the turn specified by the user, then enter manual replay. Otherwise,
+ * start into play mode after having either recreated a game world state from
+ * the savefile, or, if none exists, having created a new world with the
+ * MAKE_WORLD command. Manual replay as well as play mode take place inside
+ * io_loop().
  */
 extern void run_game();
 
index 8b09ec0f8bdf4c8b898d1e7f15947d8390442f13..b2d8005a7a75b22788081e9cf9639ffeed8ceba1 100644 (file)
 #include "../common/readwrite.h" /* try_fopen(), try_fclose_unlink_rename(),
                                   * try_fwrite(), try_fputc(), try_fgetc()
                                   */
+#include "../common/rexit.h" /* exit_trouble() */
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "cleanup.h" /* set_cleanup_flag() */
 #include "field_of_view.h" /* VISIBLE */
+#include "hardcoded_strings.h" /* s */
 #include "map.h" /* yx_to_map_pos() */
 #include "things.h" /* Thing, ThingType, get_thing_type(), get_player() */
 #include "world.h" /* global world  */
 
 
 
+/* Write to "file" god commands (one per line) to recreate thing "t". */
+static void write_key_value(FILE * file, char * key, uint32_t value);
+
+/* Write to "file" \n-delimited line of "key" + space + "value" as string. */
+static void write_thing(FILE * file, struct Thing * t);
+
 /* Cut out and return first \0-terminated string from world.queue and
  * appropriately reduce world.queue_size. Return NULL if queue is empty.
  * Superfluous \0 bytes after the string are also cut out. Should the queue
@@ -58,6 +66,45 @@ static void write_map(struct Thing * player, FILE * file);
 
 
 
+static void write_key_value(FILE * file, char * key, uint32_t value)
+{
+    char * f_name = "write_key_value()";
+    try_fwrite(key, strlen(key), 1, file, f_name);
+    try_fputc(' ', file, f_name);
+    char * line = try_malloc(11, f_name);
+    exit_trouble(-1 == sprintf(line, "%u", value), f_name, "sprintf()");
+    try_fwrite(line, strlen(line), 1, file, f_name);
+    free(line);
+    try_fputc('\n', file, f_name);
+}
+
+
+
+static void write_thing(FILE * file, struct Thing * t)
+{
+    char * f_name = "write_thing()";
+    struct Thing * o;
+    for (o = t->owns; o; o = o->next)
+    {
+        write_thing(file, o);
+    }
+    write_key_value(file, s[CMD_THING], t->id);
+    write_key_value(file, s[CMD_TYPE], t->type);
+    write_key_value(file, s[CMD_POS_Y], t->pos.y);
+    write_key_value(file, s[CMD_POS_X], t->pos.x);
+    write_key_value(file, s[CMD_COMMAND], t->command);
+    write_key_value(file, s[CMD_ARGUMENT], t->arg);
+    write_key_value(file, s[CMD_PROGRESS], t->progress);
+    write_key_value(file, s[CMD_LIFEPOINTS], t->lifepoints);
+    for (o = t->owns; o; o = o->next)
+    {
+        write_key_value(file, s[CMD_CARRIES], o->id);
+    }
+    try_fputc('\n', file, f_name);
+}
+
+
+
 static char * get_message_from_queue()
 {
     char * f_name = "get_message_from_queue()";
@@ -133,8 +180,8 @@ static void read_file_into_queue()
 static void update_worldstate_file()
 {
     char * f_name = "update_worldstate_file()";
-    char path_tmp[strlen(world.path_worldstate) + strlen(world.tmp_suffix) + 1];
-    sprintf(path_tmp, "%s%s", world.path_worldstate, world.tmp_suffix);
+    char path_tmp[strlen(s[PATH_WORLDSTATE]) + strlen(s[PATH_SUFFIX_TMP]) + 1];
+    sprintf(path_tmp, "%s%s", s[PATH_WORLDSTATE], s[PATH_SUFFIX_TMP]);
     FILE * file = try_fopen(path_tmp, "w", f_name);
     struct Thing * player = get_player();
     write_value_as_line(world.turn, file);
@@ -148,7 +195,7 @@ static void update_worldstate_file()
     {
         try_fwrite(world.log, strlen(world.log), 1, file, f_name);
     }
-    try_fclose_unlink_rename(file, path_tmp, world.path_worldstate, f_name);
+    try_fclose_unlink_rename(file, path_tmp, s[PATH_WORLDSTATE], f_name);
     set_cleanup_flag(CLEANUP_WORLDSTATE);
     char * dot = ".\n";;
     try_fwrite(dot, strlen(dot), 1, world.file_out, f_name);
@@ -199,29 +246,32 @@ static char * build_visible_map(struct Thing * player)
     uint32_t map_size = world.map.length * world.map.length;
     char * visible_map = try_malloc(map_size, f_name);
     memset(visible_map, ' ', map_size);
-    uint16_t pos_i;
-    for (pos_i = 0; pos_i < map_size; pos_i++)
-    {
-        if (player->fov_map[pos_i] & VISIBLE)
+    if (player->fov_map) /* May fail if player thing was created / positioned */
+    {                    /* by god command after turning off FOV building.    */
+        uint16_t pos_i;
+        for (pos_i = 0; pos_i < map_size; pos_i++)
         {
-            visible_map[pos_i] = world.map.cells[pos_i];
+            if (player->fov_map[pos_i] & VISIBLE)
+            {
+                visible_map[pos_i] = world.map.cells[pos_i];
+            }
         }
-    }
-    struct Thing * t;
-    struct ThingType * tt;
-    char c;
-    uint8_t i;
-    for (i = 0; i < 2; i++)
-    {
-        for (t = world.things; t != 0; t = t->next)
+        struct Thing * t;
+        struct ThingType * tt;
+        char c;
+        uint8_t i;
+        for (i = 0; i < 2; i++)
         {
-            if (   player->fov_map[yx_to_map_pos(&t->pos)] & VISIBLE
-                && (   (0 == i && 0 == t->lifepoints)
-                    || (1 == i && 0 < t->lifepoints)))
+            for (t = world.things; t != 0; t = t->next)
             {
-                tt = get_thing_type(t->type);
-                c = tt->char_on_map;
-                visible_map[yx_to_map_pos(&t->pos)] = c;
+                if (   player->fov_map[yx_to_map_pos(&t->pos)] & VISIBLE
+                    && (   (0 == i && 0 == t->lifepoints)
+                        || (1 == i && 0 < t->lifepoints)))
+                {
+                    tt = get_thing_type(t->type);
+                    c = tt->char_on_map;
+                    visible_map[yx_to_map_pos(&t->pos)] = c;
+                }
             }
         }
     }
@@ -272,3 +322,25 @@ extern char * io_round()
     }
     return get_message_from_queue();
 }
+
+
+
+extern void save_world()
+{
+    char * f_name = "save_world()";
+    char * path = s[PATH_SAVE];
+    FILE * file = try_fopen(path, "w", f_name);
+    write_key_value(file, s[CMD_DO_FOV], 0);
+    try_fputc('\n', file, f_name);
+    write_key_value(file, s[CMD_SEED_MAP], world.seed_map);
+    write_key_value(file, s[CMD_SEED_RAND], world.seed);
+    write_key_value(file, s[CMD_TURN], world.turn);
+    try_fputc('\n', file, f_name);
+    struct Thing * t;
+    for (t = world.things; t; t = t->next)
+    {
+        write_thing(file, t);
+    }
+    write_key_value(file, s[CMD_DO_FOV], 1);
+    try_fclose(file, f_name);
+}
index 02f8e9017d2dadc7c2bc6b6581f6b8a3f0d4193c..0e04d80edc44db702b83a24afcdaaf8b2fe1f1b6 100644 (file)
@@ -1,4 +1,4 @@
-/* io.h:
+/* io.h
  *
  * Communication of the server with the outside world and its client via input,
  * output and world state files.
 
 /* Return single \0-terminated string read from input queue (world.queue); or,
  * if queue is empty and world.turn is unequal world.last_update_turn, update
- * world state file at world.path_worldstate (and update world.last_update_turn
- * and write a single dot line to output file at world.path_out), then read file
- * at world.path_in for the next load of bytes to put onto the input queue.
+ * world state file (and world.last_update_turn) and write a single dot line to
+ * server out file, then read server in file for the next load of bytes to put
+ * onto the input queue.
  *
- * Reading the file at world.path_in may put many \0-terminated strings on the
- * queue at once. Successive calls of io_round() will make these available one
- * by one. Each such call cuts off bytes from the beginning of world.queue, up
- * to and including the last \0 byte that is followed by a non-\0 byte or ends
- * the queue. If the queue starts with a \0 byte, it and its \0 followers are
- * cut and a NULL pointer is returned. Reading from the input file stops only
- * when one or more byte were read and the next read returns 0 bytes. If the
+ * Reading the server in file may put many \0-terminated strings on the queue at
+ * once. Successive calls of io_round() will make these available one by one.
+ * Each such call cuts off bytes from the beginning of world.queue, up to and
+ * including the last \0 byte that is followed by a non-\0 byte or ends the
+ * queue. If the queue starts with a \0 byte, it and its \0 followers are cut
+ * and a NULL pointer is returned. Reading from the input file stops only when
+ * one or more byte were read and the next read returns 0 bytes. If the
  * re-filled queue does not end in a \0 byte, a \0 byte is appended to it.
  */
 extern char * io_round();
 
+/* Write to savefile god commands (one per line) to rebuild the current world
+ * state.
+ */
+extern void save_world();
+
 
 
 #endif
index 366fb17b8864abdb58e539098bc683f49bac5df5..33e539102beded5802016eadea339ea40f2cc77e 100644 (file)
@@ -5,6 +5,7 @@
 #include "../common/rexit.h" /* exit_err, set_cleanup_func() */
 #include "cleanup.h" /* set_cleanup_flag(), cleanup() */
 #include "configfile.h" /* read_config_file() */
+#include "hardcoded_strings.h" /* s */
 #include "init.h" /* run_game(), obey_argv(), obey_argv(), setup_server_io() */
 #include "world.h" /* struct World */
 
@@ -20,6 +21,7 @@ int main(int argc, char ** argv)
     set_cleanup_func(cleanup);
 
     /* Init settings from command line / hard-coded values. Print start info. */
+    init_strings();
     obey_argv(argc, argv);
     if (world.is_verbose)
     {
@@ -33,12 +35,6 @@ int main(int argc, char ** argv)
             exit_err(-1 == test, printf_err);
         }
     }
-    world.path_config       = "confserver/world";
-    world.path_worldstate   = "server/worldstate";
-    world.path_out          = "server/out";
-    world.path_in           = "server/in";
-    world.path_record       = "record";
-    world.tmp_suffix        = "_tmp";
 
     /* Init config file and server i/o files. */
     read_config_file();
index 1c9a2f1881fdbac990dcd48ffe3bc9232aaa479d..059a7133134cea14f0c4343241f05fba373daf84 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "map.h"
 #include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT16_MAX */
+#include <stdlib.h> /* free() */
 #include "../common/rexit.h" /* exit_err() */
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "../common/yx_uint8.h" /* struct yx_uint8 */
@@ -136,13 +137,17 @@ static void make_trees()
 
 
 
-extern void init_map()
+extern void remake_map()
 {
     char * f_name = "init_map()";
+    free(world.map.cells);
     world.map.cells = try_malloc(world.map.length * world.map.length, f_name);
+    uint32_t store_seed = world.seed;
+    world.seed = world.seed_map;
     make_sea();
     make_island();
     make_trees();
+    world.seed = store_seed;
 }
 
 
index f74d85df18bd7b6305b1733949df74e52ca7a60d..4ff6f68faa123348023c550909a3a9bc18875021 100644 (file)
 
 
 
-/* Initialize island map "~" cells representing water and "." cells representing
- * land. The island shape is built randomly by starting with a sea of one land
- * cell in the middle, then going into a cycle of repeatedly selecting a random
- * seal cell and transforming it into land if it is neighbor to land; the cycle
- * ends when a land cell is due to be created right at the border of the map.
- * Lots of 'X' cells representing trees are put on the island, too.
+/* (Re-)make island map "~" cells representing water and "." cells representing
+ * land. The island shape is built randomly from world.seed_map by starting with
+ * a sea of one land cell in the middle, then going into a cycle of repeatedly
+ * selecting a random sea cell and transforming it into land if it is neighbor
+ * to land; the cycle ends when a land cell is due to be created right at the
+ * border of the map. Lots of 'X' cells representing trees are put on the island.
  */
-extern void init_map();
+extern void remake_map();
 
 /* Check if coordinate "pos" on (or beyond) world.map is accessible to thing
  * movement.
index 952cebb566d317d46bc62df7a1a8a3233d02fd73..ec018c594e1fbd5659863f84fa0171c04070e503 100644 (file)
 /* src/server/run.c */
 
+#define _POSIX_C_SOURCE 200809L
 #include "run.h"
 #include <stddef.h> /* NULL */
 #include <stdint.h> /* uint8_t, uint16_t, uint32_t */
 #include <stdio.h> /* FILE, sprintf(), fflush() */
-#include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), strcmp() strncmp(), atoi() */
+#include <stdlib.h> /* free(), atoi() */
+#include <string.h> /* strlen(), strcmp() strncmp(), strdup() */
 #include <unistd.h> /* access() */
+#include "../common/parse_file.h" /* set_err_line_options(), token_from_line(),
+                                   * err_line()
+                                   */
 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
                                   * try_fgets(), try_fclose_unlink_rename(),
                                   * textfile_width(), try_fputc()
                                   */
 #include "../common/rexit.h" /* exit_trouble(), exit_err() */
 #include "ai.h" /* ai() */
-#include "cleanup.h" /* unset_cleanup_flag() */
+#include "cleanup.h" /* set_cleanup_flag(), unset_cleanup_flag() */
+#include "field_of_view.h" /* build_fov_map() */
+#include "hardcoded_strings.h" /* s */
 #include "init.h" /* remake_world() */
-#include "io.h" /* io_round() */
-#include "thing_actions.h" /* get_thing_action_id_by_name() */
-#include "things.h" /* Thing, get_player() */
+#include "io.h" /* io_round(), save_world() */
+#include "map.h" /* remake_map() */
+#include "thing_actions.h" /* ThingAction */
+#include "things.h" /* Thing, get_thing(), own_thing(), add_thing(),
+                     * get_thing_action_id_by_name(), get_player()
+                     */
 #include "world.h" /* global world */
 
 
 
+/* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */
+static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god commansd in "tok0"/"tok1" on positioning a thing "t". */
+static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */
+static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */
+static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command on enabling/disabling generation of fields of view on
+ * god commands that may affect them, via static global "do_fov". On enabling,
+ * (re-)generate all animate things' fields of view.
+ */
+static uint8_t parse_do_fov(char * tok0, char * tok1);
+
+/* Parse/apply god command in "tok0"/"tok1" manipulating a thing's state. */
+static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
+
+/* Parse player command in "tok0"/"tok1" to action in player thing. */
+static uint8_t parse_player_command(char * tok0, char * tok1);
+
+/* Compares first line of server out file to world.server_test, aborts if they
+ * don't match, but not before unsetting the flags deleting files in the server
+ * directory, for in that case those must be assumed to belong to another server
+ * process.
+ */
+static void server_test();
+
 /* Run the game world and its inhabitants (and their actions) until the player
  * avatar is free to receive new commands (or is dead).
  */
 static void turn_over();
 
-/* If "msg"'s first part matches "command_name", set player's Thing's .command
- * to the command's id and its .arg to a numerical value following in the latter
- * part of "msg" (if no digits are found, use 0); then finish player's turn and
- * turn game over to the NPCs via turn_over(); then return 1. Else, return 0.
- */
-static uint8_t apply_player_command(char * msg, char * command_name);
 
-/* Compares first line of file at world.path_out to world.server_test, aborts if
- * they don't match, but not before unsetting the flags deleting files in the
- * server directory, for in that case those must be assumed to belong to another
- * server process.
- */
-static void server_test();
+
+/* Do god commands to create / position things generate their fields of view? */
+static uint8_t do_fov = 0;
+
+
+
+static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
+{
+    uint8_t id;
+    if (parse_val(tok0, tok1, s[CMD_CARRIES], '8', (char *) &id))
+    {
+        if (!err_line(id == t->id, "Thing cannot carry itself."))
+        {
+            struct Thing * o = get_thing(world.things, id, 0);
+            if (!err_line(!o, "Thing cannot carry thing that does not exist."))
+            {
+                own_thing(&(t->owns), &world.things, id);
+                o->pos = t->pos;
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+
+static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
+{
+    char axis = 0;
+    if      (!strcmp(tok0, s[CMD_POS_Y]))
+    {
+        axis = 'y';
+    }
+    else if (!strcmp(tok0, s[CMD_POS_X]))
+    {
+        axis = 'x';
+    }
+    if (axis && !parsetest_int(tok1, '8'))
+    {
+        uint8_t length = atoi(tok1);
+        char * err = "Position is outside of map.";
+        if (!err_line(length >= world.map.length, err))
+        {
+            if      ('y' == axis)
+            {
+                t->pos.y = length;
+            }
+            else if ('x' == axis)
+            {
+                t->pos.x = length;
+            }
+            free(t->fov_map);
+            t->fov_map = do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+
+static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
+{
+    uint8_t type;
+    if (parse_val(tok0, tok1, s[CMD_TYPE], '8', (char *) &type))
+    {
+        struct ThingType * tt = world.thing_types;
+        for (; NULL != tt && type != tt->id; tt = tt->next);
+        if (!err_line(!tt, "Thing type does not exist."))
+        {
+            t->type = type;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+
+static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
+{
+    uint8_t command;
+    if (parse_val(tok0, tok1, s[CMD_COMMAND], '8', (char *) &command))
+    {
+        if (!command)
+        {
+            t->command = command;
+            return 1;
+        }
+        struct ThingAction * ta = world.thing_actions;
+        for (; NULL != ta && command != ta->id; ta = ta->next);
+        if (!err_line(!ta, "Thing action does not exist."))
+        {
+            t->command = command;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+
+static uint8_t parse_do_fov(char * tok0, char * tok1)
+{
+    if (parse_val(tok0, tok1, s[CMD_DO_FOV], '8', (char *) &do_fov))
+    {
+        if (do_fov)
+        {
+            struct Thing * ti;
+            for (ti = world.things; ti; ti = ti->next)
+            {
+                ti->fov_map = ti->lifepoints ? build_fov_map(ti) : ti->fov_map;
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+
+
+static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
+{
+    uint8_t id;
+    static struct Thing * t = NULL;
+    if (t && (   parse_thing_type(tok0, tok1, t)
+              || parse_thing_command(tok0, tok1, t)
+              || parse_val(tok0, tok1, s[CMD_ARGUMENT], '8', (char *) &t->arg)
+              || parse_val(tok0, tok1, s[CMD_PROGRESS], '8', (char *) &t->progress)
+
+              || parse_val(tok0, tok1, s[CMD_LIFEPOINTS],'8',(char *) &t->lifepoints)
+              || parse_position(tok0, tok1, t)
+              || parse_carry(tok0, tok1, t)));
+    else if (parse_val(tok0, tok1, s[CMD_THING], '8', (char *) &id))
+    {
+        t = get_thing(world.things, id, 1);
+        if (!t)
+        {
+            t = add_thing(id, 0, 0);
+            set_cleanup_flag(CLEANUP_THINGS);
+            t->fov_map = do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
+        }
+    }
+    else
+    {
+        return 0;
+    }
+    return 1;
+}
+
+
+
+static uint8_t parse_player_command(char * tok0, char * tok1)
+{
+    struct Thing * player = get_player();
+    if (   parse_val(tok0, tok1, s[CMD_WAIT], '8', (char *) &player->arg)
+        || parse_val(tok0, tok1, s[CMD_MOVE], '8', (char *) &player->arg)
+        || parse_val(tok0, tok1, s[CMD_PICKUP], '8', (char *) &player->arg)
+        || parse_val(tok0, tok1, s[CMD_DROP], '8', (char *) &player->arg)
+        || parse_val(tok0, tok1, s[CMD_USE], '8', (char *) &player->arg))
+    {
+        player->command = get_thing_action_id_by_name(tok0);
+        turn_over();
+    }
+    else
+    {
+        return 0;
+    }
+    return 1;
+}
+
+
+
+static void server_test()
+{
+    char * f_name = "server_test()";
+    char test[10 + 1 + 10 + 1 + 1];
+    FILE * file = try_fopen(s[PATH_OUT], "r", f_name);
+    try_fgets(test, 10 + 10 + 1 + 1, file, f_name);
+    try_fclose(file, f_name);
+    if (strcmp(test, world.server_test))
+    {
+        unset_cleanup_flag(CLEANUP_WORLDSTATE);
+        unset_cleanup_flag(CLEANUP_OUT);
+        unset_cleanup_flag(CLEANUP_IN);
+        char * msg = "Server test string in server output file does not match. "
+                     "This indicates that the current server process has been "
+                     "superseded by another one.";
+        exit_err(1, msg);
+    }
+}
 
 
 
@@ -85,77 +305,67 @@ static void turn_over()
 
 
 
-static uint8_t apply_player_command(char * msg, char * command_name)
+static void record_msg(char * msg)
 {
-    if (!strncmp(msg, command_name, strlen(command_name)))
+    char * f_name = "record_msg()";
+    char path_tmp[strlen(s[PATH_RECORD]) + strlen(s[PATH_SUFFIX_TMP]) + 1];
+    sprintf(path_tmp, "%s%s", s[PATH_RECORD], s[PATH_SUFFIX_TMP]);
+    FILE * file_tmp  = try_fopen(path_tmp, "w", f_name);
+    if (!access(s[PATH_RECORD], F_OK))
     {
-        struct Thing * player = get_player();
-        player->arg = atoi(&(msg[strlen(command_name)]));
-        player->command = get_thing_action_id_by_name(command_name);
-        turn_over();
-        return 1;
+        FILE * file_read = try_fopen(s[PATH_RECORD], "r", f_name);
+        uint32_t linemax = textfile_width(file_read);
+        char line[linemax + 1];
+        while (try_fgets(line, linemax + 1, file_read, f_name))
+        {
+            try_fwrite(line, strlen(line), 1, file_tmp, f_name);
+        }
+        try_fclose(file_read, f_name);
     }
-    return 0;
+    try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
+    try_fputc('\n', file_tmp, f_name);
+    try_fclose_unlink_rename(file_tmp, path_tmp, s[PATH_RECORD], f_name);
 }
 
 
 
-static void server_test()
+extern void obey_msg(char * msg, uint8_t do_record)
 {
-    char * f_name = "server_test()";
-    char test[10 + 1 + 10 + 1 + 1];
-    FILE * file = try_fopen(world.path_out, "r", f_name);
-    try_fgets(test, 10 + 10 + 1 + 1, file, f_name);
-    try_fclose(file, f_name);
-    if (strcmp(test, world.server_test))
+    set_err_line_options("Trouble with message: ", msg, 0, 0);
+    char * msg_copy = strdup(msg);
+    char * tok0 = token_from_line(msg_copy);
+    char * tok1 = token_from_line(NULL);
+    char * tok2 = token_from_line(NULL);
+    if (err_line(!(tok0 && tok1) || tok2, "Bad number of tokens."))
     {
-        unset_cleanup_flag(CLEANUP_WORLDSTATE);
-        unset_cleanup_flag(CLEANUP_OUT);
-        unset_cleanup_flag(CLEANUP_IN);
-        char * msg = "Server test string in server output file does not match. "
-                     "This indicates that the current server process has been "
-                     "superseded by another one.";
-        exit_err(1, msg);
+        return;
     }
-}
+    if (   parse_thing_manipulation(tok0, tok1)
+        || parse_player_command(tok0, tok1)
+        || parse_val(tok0, tok1, s[CMD_SEED_RAND], 'U', (char *) &world.seed)
+        || parse_val(tok0, tok1, s[CMD_TURN], 'u', (char *) &world.turn)
+        || parse_do_fov(tok0, tok1));
+    else if (parse_val(tok0, tok1, s[CMD_SEED_MAP], 'U', (char *) &world.seed_map))
 
-
-
-extern void obey_msg(char * msg, uint8_t do_record)
-{
-    char * f_name = "obey_msg()";
-    if (   apply_player_command(msg, "wait")   /* TODO: Check for non-error   */
-        || apply_player_command(msg, "move")   /* return value of a modified  */
-        || apply_player_command(msg, "pick_up")/*get_thing_action_id_by_name()*/
-        || apply_player_command(msg, "drop")   /* and if id found, execute on */
-        || apply_player_command(msg, "use"));  /* it what's in                */
-    else                                       /* apply_player_command().     */
-    {
-        char * seed_command = "seed";
-        if (!strncmp(msg, seed_command, strlen(seed_command)))
-        {
-            remake_world(atoi(&(msg[strlen(seed_command)])));
-        }
+    {
+        remake_map();
+    }
+    else if (parse_val(tok0, tok1, s[CMD_MAKE_WORLD], 'U', (char *) &world.seed))
+    {
+        remake_world();
+    }
+    else
+    {
+        err_line(1, "Unknown command.");
+        free(msg_copy);
+        return;
     }
+    world.last_update_turn = 0;
+    free(msg_copy);
     if (do_record)
     {
-        char path_tmp[strlen(world.path_record) + strlen(world.tmp_suffix) + 1];
-        sprintf(path_tmp, "%s%s", world.path_record, world.tmp_suffix);
-        FILE * file_tmp  = try_fopen(path_tmp, "w", f_name);
-        if (!access(world.path_record, F_OK))
-        {
-            FILE * file_read = try_fopen(world.path_record, "r", f_name);
-            uint32_t linemax = textfile_width(file_read);
-            char line[linemax + 1];
-            while (try_fgets(line, linemax + 1, file_read, f_name))
-            {
-                try_fwrite(line, strlen(line), 1, file_tmp, f_name);
-            }
-            try_fclose(file_read, f_name);
-        }
-        try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
-        try_fputc('\n', file_tmp, f_name);
-        try_fclose_unlink_rename(file_tmp, path_tmp, world.path_record, f_name);
+        save_world();
+        record_msg(msg);
     }
 }
 
index 9fa526ff0121f2fea4e1352cec82f1f7b588e15e..07ca9029f24655a0b66a87466dc1ac512da0f4c3 100644 (file)
@@ -10,8 +10,8 @@
 
 
 
-/* Try parsing "msg" into a server or player command to run. Player commands are
- * are recorded into the record file at world.path_record if "do_record" is set.
+/* Try parsing "msg" into a command to apply, and apply it. Record commands to
+ * the file at world.path_record if "do_record" is set.
  */
 extern void obey_msg(char * msg, uint8_t do_record);
 
@@ -19,8 +19,8 @@ extern void obey_msg(char * msg, uint8_t do_record);
  * on "QUIT" command. In replay mode, exits with 0 on each non-"QUIT" command.
  * Writes a "PONG" line to server output file on "PING" command. In play mode,
  * processes further incomming commands via obey_msg(). Compares the first line
- * of the file at world.path_out with world.server_test to ensure that the
- * current server process has not been superseded by a new one.
+ * of the server out file with world.server_test to ensure that the current
+ * server process has not been superseded by a new one.
  */
 extern uint8_t io_loop();
 
index d5aff96d93b70e29f52a69420950462782aaea5b..e0aa674851fcb2527fe74e70df448c9eca52167c 100644 (file)
@@ -2,12 +2,12 @@
 
 #include "things.h"
 #include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, UINT16_MAX */
+#include <stdint.h> /* uint8_t, uint16_t, UINT8_MAX, UINT16_MAX */
 #include <stdlib.h> /* free() */
 #include <string.h> /* memset(), strlen() */
 #include "../common/rexit.h" /* exit_err() */
 #include "../common/try_malloc.h" /* try_malloc() */
-#include "../common/yx_uint8.h" /* yx_uint8 struct */
+#include "../common/yx_uint8.h" /* yx_uint8 */
 #include "map.h" /* is_passable() */
 #include "rrand.h" /* rrand() */
 #include "world.h" /* global world */
 
 
 
-/* Return pointer to thing of "id" in chain starting at "ptr". */
-static struct Thing * get_thing(struct Thing * ptr, uint8_t id);
 
-/* Add thing of "type" to map on passable position. Don't put actor on actor. */
-static void add_thing(uint8_t type);
+/* Return lowest unused id for new thing. */
+static uint8_t get_lowest_unused_id();
 
 
 
-static struct Thing * get_thing(struct Thing * ptr, uint8_t id)
+static uint8_t get_lowest_unused_id()
+{
+    uint8_t i = 0;
+    while (1)
+    {
+        if (!get_thing(world.things, i, 1))
+        {
+            return i;
+        }
+        exit_err(i == UINT8_MAX, "No unused ID available to add new thing.");
+        i++;
+    }
+}
+
+
+
+extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep)
 {
     while (1)
     {
@@ -31,10 +45,13 @@ static struct Thing * get_thing(struct Thing * ptr, uint8_t id)
         {
             return ptr;
         }
-        struct Thing * owned_thing = get_thing(ptr->owns, id);
-        if (NULL != owned_thing)
+        if (deep)
         {
-            return ptr;
+            struct Thing * owned_thing = get_thing(ptr->owns, id, 1);
+            if (NULL != owned_thing)
+            {
+                return ptr;
+            }
         }
         ptr = ptr->next;
     }
@@ -42,18 +59,32 @@ static struct Thing * get_thing(struct Thing * ptr, uint8_t id)
 
 
 
-static void add_thing(uint8_t type)
+extern void free_thing_types(struct ThingType * tt_start)
+{
+    if (NULL == tt_start)
+    {
+        return;
+    }
+    free_thing_types(tt_start->next);
+    free(tt_start->name);
+    free(tt_start);
+}
+
+
+
+extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos)
 {
     char * f_name = "add_thing()";
     struct ThingType * tt = get_thing_type(type);
     struct Thing *     t  = try_malloc(sizeof(struct Thing), f_name);
     memset(t, 0, sizeof(struct Thing));
-    t->id         = world.thing_count++;
+    t->id = (0 <= id && id <= UINT8_MAX) ? id : get_lowest_unused_id();
     t->type       = tt->id;
     t->lifepoints = tt->lifepoints;
     char * err = "Space to put thing on too hard to find. Map too small?";
     uint16_t i = 0;
-    while (1)
+    memset(&(t->pos), 0, sizeof(struct yx_uint8));
+    while (find_pos)
     {
         struct yx_uint8 pos;
         for (pos.y = pos.x = 0; 0 == is_passable(pos); i++)
@@ -81,19 +112,7 @@ static void add_thing(uint8_t type)
     struct Thing ** t_ptr_ptr = &world.things;
     for (; NULL != * t_ptr_ptr; t_ptr_ptr = &(*t_ptr_ptr)->next);
     * t_ptr_ptr = t;
-}
-
-
-
-extern void free_thing_types(struct ThingType * tt_start)
-{
-    if (NULL == tt_start)
-    {
-        return;
-    }
-    free_thing_types(tt_start->next);
-    free(tt_start->name);
-    free(tt_start);
+    return t;
 }
 
 
@@ -103,7 +122,7 @@ extern void add_things(uint8_t type, uint8_t n)
     uint8_t i;
     for (i = 0; i < n; i++)
     {
-        add_thing(type);
+        add_thing(-1, type, 1);
     }
 }
 
@@ -160,7 +179,7 @@ extern void own_thing(struct Thing ** target, struct Thing ** source,
 
 extern struct Thing * get_player()
 {
-    return get_thing(world.things, 0);
+    return get_thing(world.things, 0, 1);
 }
 
 
index 3040d04b193bf021390390d998d35d9d566dac8b..889374a80d5e3fabc36647459f145902f66fdbf2 100644 (file)
@@ -14,8 +14,8 @@
 
 struct Thing
 {
-    struct Thing * next;        /* pointer to next one in things chain */
-    struct Thing * owns;        /* chain of things owned / in inventory */
+    struct Thing * next;         /* pointer to next one in things chain */
+    struct Thing * owns;         /* chain of things owned / in inventory */
     struct yx_uint8 pos;         /* coordinate on map */
     uint8_t * fov_map;           /* map of the thing's field of view */
     uint8_t id;                  /* individual thing's unique identifier */
@@ -40,9 +40,18 @@ struct ThingType
 
 
 
+/* Return thing of "id" in chain at "ptr", search inventories too if "deep". */
+extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep);
+
 /* Free thing types chain starting at "tt_start". */
 extern void free_thing_types(struct ThingType * tt_start);
 
+/* Add thing of "id" and "type" to map on random passable position (positions
+ * which contain an actor are not deemed passable) if "find_pos", else on y=0,
+ * x=0. If "id" is >= 0 and <= UINT8_MAX, use lowest unused id. Return thing.
+ */
+extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos);
+
 /* Add thing(s) ("n": how many?) of "type" to map on random position(s). New
  * animate things are never placed in the same square with other animate ones.
  */
index 91a32a7cbf0d2e117f38a4121768b5c0a636eeec..4318562ef437068cde27d908f35614420e8c0673 100644 (file)
@@ -19,27 +19,21 @@ struct World
 {
     FILE * file_in; /* Input stream on file at .path_in. */
     FILE * file_out; /* Output stream on file at .path_out. */
-    struct Map map;
+    struct Map map; /* Game map. */
     struct ThingType * thing_types; /* Thing type definitions. */
     struct ThingAction * thing_actions; /* Thing action definitions. */
     struct Thing * things; /* All physical things of the game world. */
     char * log; /* Logs the game events from the player's view. */
     char * server_test; /* String uniquely identifying server process. */
-    char * path_in; /* File to write client messages into. */
-    char * path_out; /* File to write server messages into. */
-    char * path_worldstate; /* File to represent world state  to clients.*/
-    char * path_record; /* Record file from which to read the game history. */
-    char * path_config; /* Path for thing type / action definitions file. */
-    char * tmp_suffix; /* Appended to paths of files for their tmp versions. */
     char * queue; /* Stores un-processed messages read from the input file. */
     uint32_t queue_size;/* Length of .queue sequence of \0-terminated strings.*/
     uint32_t seed; /* Randomness seed. */
+    uint32_t seed_map; /* Map seed. */
     uint16_t replay; /* Turn up to which to replay game. No replay if zero. */
     uint16_t turn; /* Current game turn. */
     uint16_t last_update_turn; /* Last turn the .path_out file was updated. */
     uint8_t player_type; /* Thing type that player will start as. */
     uint8_t is_verbose; /* Should server send debugging info to stdout? */
-    uint8_t thing_count; /* Counts things generated so far. */
     uint8_t enemy_fov; /* != 0 if non-player actors only see field of view. */
 };