From edebb2bf9aa780ee2f7006c1d2be9168564d34df Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Sat, 2 Aug 2014 05:20:05 +0200
Subject: [PATCH] Add auto-mapping / map memory.

---
 SERVER_COMMANDS                |   4 +
 TODO                           |   6 ++
 src/client/cleanup.c           |   1 +
 src/client/draw_wins.c         |  36 +++++++--
 src/client/io.c                |  20 +++--
 src/client/main.c              |   3 +-
 src/client/world.h             |   3 +-
 src/server/ai.c                |   3 +-
 src/server/field_of_view.c     |  23 ++++++
 src/server/field_of_view.h     |   2 +-
 src/server/god_commands.c      |  56 ++++++++++++--
 src/server/god_commands.h      |   5 +-
 src/server/hardcoded_strings.c |   3 +-
 src/server/hardcoded_strings.h |   3 +-
 src/server/io.c                | 130 +++++++++++++++++++++++----------
 src/server/run.c               |  13 +++-
 src/server/thing_actions.c     |   8 ++
 src/server/things.c            |   1 +
 src/server/things.h            |   1 +
 19 files changed, 252 insertions(+), 69 deletions(-)

diff --git a/SERVER_COMMANDS b/SERVER_COMMANDS
index d574180..c229f63 100644
--- a/SERVER_COMMANDS
+++ b/SERVER_COMMANDS
@@ -157,6 +157,10 @@ T_CARRIES [0 to 255]
 Add thing of ID in argument to inventory of selected thing, if said thing is
 available for carrying and not the selected thing.
 
+T_MEMMAP [0 to 255] [string]
+Set part of selected thing's memory of the game map to string argument: the line
+of the argument's number.
+
 TT_ID [-32768 to 32767]
 Select thing type to manipulate by argument as ID. If argument is <0 or <255,
 change it to the lowest unused thing type ID. If thing type of ID does not exist
diff --git a/TODO b/TODO
index c37c5c2..c8b6681 100644
--- a/TODO
+++ b/TODO
@@ -4,6 +4,10 @@ IN GENERAL:
 
 - expand use of hardcoded_strings module(s)
 
+SERVER:
+
+- check whether saving every player turn slows down gameplay
+
 BOTH SERVER/CLIENT:
 
 - make server and client communicate by specific world state info requests 
@@ -12,3 +16,5 @@ BOTH SERVER/CLIENT:
 CLIENT:
 
 - re-work unnecessary complex command / keybinding / server message mapping
+
+- fix memory leakin draw_wins module
diff --git a/src/client/cleanup.c b/src/client/cleanup.c
index 3499246..f4b97bf 100644
--- a/src/client/cleanup.c
+++ b/src/client/cleanup.c
@@ -19,6 +19,7 @@ static uint32_t cleanup_flags = 0x0000;
 extern void cleanup()
 {
     free(world.map.cells);
+    free(world.mem_map);
     free(world.log);
     free(world.player_inventory);
     if (cleanup_flags & CLEANUP_INTERFACE)
diff --git a/src/client/draw_wins.c b/src/client/draw_wins.c
index bb86bb9..6736a1b 100644
--- a/src/client/draw_wins.c
+++ b/src/client/draw_wins.c
@@ -2,7 +2,7 @@
 
 #define _POSIX_C_SOURCE 200809L /* strdup() */
 #include "draw_wins.h"
-#include <ncurses.h> /* typedefs attr_t, chtype, define A_REVERSE */
+#include <ncurses.h> /* attr_t, chtype, init_pair(), A_REVERSE, COLOR_PAIR() */
 #include <stddef.h> /* NULL */
 #include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT16_MAX */
 #include <stdio.h> /* sprintf() */
@@ -339,14 +339,40 @@ extern void draw_win_log(struct Win * win)
 
 extern void draw_win_map(struct Win * win)
 {
+    init_pair(1, COLOR_WHITE, COLOR_BLUE);
+    init_pair(2, COLOR_BLUE, COLOR_BLACK);
+    attr_t attr_fov = 0;
+    attr_t attr_mem = COLOR_PAIR(2);
+    attr_t attr_sha = COLOR_PAIR(1);
     try_resize_winmap(win, world.map.length, world.map.length * 2);
-    uint16_t z = 0;
-    uint16_t x, y;
-    for (y = 0; y < world.map.length; y++)
+    uint16_t x, y, z;
+    for (y = 0, z = 0; y < world.map.length; y++)
     {
         for (x = 0; x < world.map.length; x++)
         {
-            set_ch_on_yx(win, y, x * 2 + (y % 2), world.map.cells[z]);
+            attr_t attr_c = ' ' == world.mem_map[z] ? attr_sha : attr_mem;
+            chtype c = world.mem_map[z] | attr_c;
+            set_ch_on_yx(win, y, x * 2 + (y % 2), c);
+            if (x + (y % 2) < world.map.length)
+            {
+                set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | attr_c);
+            }
+            z++;
+        }
+    }
+    for (y = 0, z = 0; y < world.map.length; y++)
+    {
+        for (x = 0; x < world.map.length; x++)
+        {
+            if (' ' != world.map.cells[z])
+            {
+                chtype c = world.map.cells[z] | attr_fov;
+                set_ch_on_yx(win, y, x * 2 + (y % 2), c);
+                if (x + (y % 2) < world.map.length)
+                {
+                    set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | attr_fov);
+                }
+            }
             z++;
         }
     }
diff --git a/src/client/io.c b/src/client/io.c
index d0b9dc0..c0cf977 100644
--- a/src/client/io.c
+++ b/src/client/io.c
@@ -30,11 +30,11 @@
  */
 static void read_inventory(char * read_buf, uint32_t linemax, FILE * file);
 
-/* Read the next characters in "file" into world.map.cells. In detail: Read
+/* Read the next characters in "file" into "map". In detail: Read
  * world.map.length times world.map.length characters, followed by one ignored
  * character (that we assume is a newline).
  */
-static void read_map_cells(FILE * file);
+static void read_map_cells(FILE * file, char ** map);
 
 /* Repeatedly use try_fgets() with given arguments to read the remaining lines
  * of "file" into the world.log string.
@@ -116,17 +116,22 @@ static void read_inventory(char * read_buf, uint32_t linemax, FILE * file)
 
 
 
-static void read_map_cells(FILE * file)
+static void read_map_cells(FILE * file, char ** map)
 {
-    free(world.map.cells);
-    world.map.cells = try_malloc(world.map.length * world.map.length, __func__);
+    if (*map)
+    {
+        free(*map);
+        *map = NULL;
+    }
+    *map = try_malloc(world.map.length * world.map.length, __func__);
+    char * map_cells = *map;
     uint16_t y, x;
     for (y = 0; y < world.map.length; y++)
     {
         for (x = 0; x < world.map.length; x++)
         {
             char c = try_fgetc(file, __func__);
-            world.map.cells[(y * world.map.length) + x] = c;
+            map_cells[y * world.map.length + x] = c;
         }
         try_fgetc(file, __func__);
     }
@@ -213,7 +218,8 @@ static uint8_t read_world()
         first_read = 0;
     }
     world.map.length = read_value_from_line(read_buf, linemax, file);
-    read_map_cells(file);
+    read_map_cells(file, &world.map.cells);
+    read_map_cells(file, &world.mem_map);
     read_log(read_buf, linemax, file);
     free(read_buf);
     try_fclose(file, __func__);
diff --git a/src/client/main.c b/src/client/main.c
index 9c6a72a..68841ca 100644
--- a/src/client/main.c
+++ b/src/client/main.c
@@ -2,7 +2,7 @@
 
 #define _POSIX_C_SOURCE 1 /* sigaction, sigaction() */
 #define _DARWIN_C_SOURCE 1 /* SIGWINCH on OS X */
-#include <ncurses.h> /* keypad() */
+#include <ncurses.h> /* keypad(), start_color() */
 #include <signal.h> /* SIGWINCH, sigaction, sigaction() */
 #include <stddef.h> /* NULL */
 #include <stdlib.h> /* exit() */
@@ -44,6 +44,7 @@ int main(int argc, char * argv[])
 
     /* Initialize the whole interface. */
     world.winDB.t_screen = initscr();
+    start_color();
     set_cleanup_flag(CLEANUP_NCURSES);
     noecho();
     curs_set(0);
diff --git a/src/client/world.h b/src/client/world.h
index 5d50173..bf4394a 100644
--- a/src/client/world.h
+++ b/src/client/world.h
@@ -26,12 +26,13 @@ struct World
     struct KeyBindingDB kb_global; /* globally availabe keybindings */
     struct KeyBindingDB kb_wingeom; /* Win geometry config view keybindings */
     struct KeyBindingDB kb_winkeys; /* Win keybindings config view keybindings*/
-    struct Map map; /* game map geometry and content */
+    struct Map map; /* game map geometry and content of player's map view */
     time_t last_update; /* used for comparison with worldstate file's mtime */
     char * log; /* log of player's activities */
     char * path_interface; /* path of interface configuration file */
     char * path_commands; /* path of commands config file */
     char * player_inventory; /* one-item-per-line string list of owned items */
+    char * mem_map; /* map cells of player's map memory */
     struct yx_uint8 player_pos; /* coordinates of player on map */
     uint16_t turn; /* world/game turn */
     uint8_t halfdelay; /* how long to wait for getch() input in io_loop() */
diff --git a/src/server/ai.c b/src/server/ai.c
index b761c6b..5be48f3 100644
--- a/src/server/ai.c
+++ b/src/server/ai.c
@@ -5,7 +5,6 @@
 #include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT16_MAX */
 #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 */
@@ -133,7 +132,7 @@ static char get_dir_to_nearest_enemy(struct Thing * t_origin)
     for (i = 0; i < map_size; i++)
     {
         score_map[i] = UINT16_MAX;
-        if (t_origin->fov_map[i] & VISIBLE && world.map.cells[i] == '.')
+        if ('.' == t_origin->mem_map[i])
         {
             score_map[i] = max_score;
         }
diff --git a/src/server/field_of_view.c b/src/server/field_of_view.c
index 440c84c..f14fffd 100644
--- a/src/server/field_of_view.c
+++ b/src/server/field_of_view.c
@@ -84,6 +84,9 @@ static void eval_position(uint16_t dist, uint16_t hex_i, uint8_t * fov_map,
                           struct yx_uint8 * test_pos,
                           struct shadow_angle ** shadows);
 
+/* Update "t"'s .mem_map memory with what's in its current field of view. */
+static void update_map_memory(struct Thing * t, uint32_t map_size);
+
 
 
 static void mv_yx_in_hex_dir(char d, struct yx_uint8 * yx)
@@ -327,6 +330,25 @@ static void eval_position(uint16_t dist, uint16_t hex_i, uint8_t * fov_map,
 
 
 
+static void update_map_memory(struct Thing * t, uint32_t map_size)
+{
+    if (!t->mem_map)
+    {
+        t->mem_map = try_malloc(map_size, __func__);
+        memset(t->mem_map, ' ', map_size);
+    }
+    uint32_t i;
+    for (i = 0; i < map_size; i++)
+    {
+        if (' ' == t->mem_map[i] && t->fov_map[i] & VISIBLE)
+        {
+            t->mem_map[i] = world.map.cells[i];
+        }
+    }
+}
+
+
+
 extern void build_fov_map(struct Thing * t)
 {
     uint32_t map_size = world.map.length * world.map.length;
@@ -363,4 +385,5 @@ extern void build_fov_map(struct Thing * t)
     }
     mv_yx_in_dir_wrap(0, NULL, 1);
     free_angles(shadows);
+    update_map_memory(t, map_size);
 }
diff --git a/src/server/field_of_view.h b/src/server/field_of_view.h
index 6920594..9060f1d 100644
--- a/src/server/field_of_view.h
+++ b/src/server/field_of_view.h
@@ -19,7 +19,7 @@ enum fov_cell_states {
     VISIBLE = 0x01
 };
 
-/* Build "t"'s field of view. */
+/* Build "t"'s field of view and update its map memory with the result. */
 extern void build_fov_map(struct Thing * t);
 
 
diff --git a/src/server/god_commands.c b/src/server/god_commands.c
index 6eec572..755acd5 100644
--- a/src/server/god_commands.c
+++ b/src/server/god_commands.c
@@ -4,10 +4,11 @@
 #include <stddef.h> /* NULL */
 #include <stdint.h> /* uint8_t */
 #include <stdlib.h> /* atoi(), free() */
-#include <string.h> /* strcmp() */
+#include <string.h> /* strcmp(), memset(), memcpy() */
 #include <unistd.h> /* F_OK, access(), unlink() */
 #include "../common/parse_file.h" /* err_line(), parse_val(), parsetest_int() */
 #include "../common/rexit.h" /* exit_trouble() */
+#include "../common/try_malloc.h" /* try_malloc() */
 #include "cleanup.h" /* unset_cleanup_flag() */
 #include "field_of_view.h" /* build_fov_map() */
 #include "hardcoded_strings.h" /* s */
@@ -48,7 +49,7 @@ static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
 
 /* Parse/apply god command in "tok0"/"tok1" to manipulate a Thing. */
-static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
+static uint8_t parse_thing_manipulation_1arg(char * tok0, char * tok1);
 
 /* Performs parse_world_active()'s world activation legality tests. */
 static uint8_t world_may_be_set_active();
@@ -70,9 +71,15 @@ static uint8_t set_map_length(char * tok0, char * tok1);
 
 
 
+/* Thing, ThingType or ThingAction selected to be manipulated. */
+static struct Thing * t = NULL;
+static struct ThingType * tt = NULL;
+static struct ThingAction * ta = NULL;
+
+
+
 static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1)
 {
-    static struct ThingType * tt = NULL;
     if (!tt &&
         (   !strcmp(tok0, s[S_CMD_TT_CONSUM]) || !strcmp(tok0, s[S_CMD_TT_SYMB])
          || !strcmp(tok0, s[S_CMD_TT_STARTN]) || !strcmp(tok0, s[S_CMD_TT_NAME])
@@ -128,7 +135,6 @@ static uint8_t try_func_name(struct ThingAction * ta, char * name,
 
 static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1)
 {
-    static struct ThingAction * ta = NULL;
     if (!ta &&
         (!strcmp(tok0, s[S_CMD_TA_EFFORT]) || !strcmp(tok0, s[S_CMD_TA_NAME])))
     {
@@ -272,9 +278,8 @@ static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
 
 
 
-static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
+static uint8_t parse_thing_manipulation_1arg(char * tok0, char * tok1)
 {
-    static struct Thing * t = NULL;
     if (!t &&
         (   !strcmp(tok0, s[S_CMD_T_PROGRESS]) || !strcmp(tok0, s[S_CMD_T_TYPE])
          || !strcmp(tok0, s[S_CMD_T_CARRIES]) || !strcmp(tok0, s[S_CMD_T_POSY])
@@ -410,7 +415,7 @@ extern uint8_t parse_god_command_1arg(char * tok0, char * tok1)
 {
     if (   parse_thingtype_manipulation(tok0, tok1)
         || parse_thingaction_manipulation(tok0, tok1)
-        || parse_thing_manipulation(tok0, tok1)
+        || parse_thing_manipulation_1arg(tok0, tok1)
         || set_map_length(tok0,tok1)
         || parse_val(tok0,tok1,s[S_CMD_SEED_RAND],'U', (char *)&world.seed)
         || parse_val(tok0,tok1,s[S_CMD_TURN],'u',(char *)&world.turn)
@@ -433,3 +438,40 @@ extern uint8_t parse_god_command_1arg(char * tok0, char * tok1)
     }
     return 1;
 }
+
+
+
+extern uint8_t parse_god_command_2arg(char * tok0, char * tok1, char * tok2)
+{
+    if (!t && !strcmp(tok0, s[S_CMD_T_MEMMAP]))
+    {
+        err_line(1, "No thing defined to manipulate yet.");
+        return 1;
+    }
+    if (!strcmp(tok0, s[S_CMD_T_MEMMAP]))
+    {
+        uint8_t y = atoi(tok1);
+        if (parsetest_int(tok1, '8') || y >= world.map.length)
+        {
+            err_line(1, "Illegal value for map line number.");
+            return 1;
+        }
+        if (strlen(tok2) != world.map.length)
+        {
+            err_line(1, "Map line length is unequal map width.");
+            return 1;
+        }
+        if (!t->mem_map)
+        {
+            uint32_t map_size = world.map.length * world.map.length;
+            t->mem_map = try_malloc(map_size, __func__);
+            memset(t->mem_map, ' ', map_size);
+        }
+        memcpy(t->mem_map + y * world.map.length, tok2, world.map.length);
+    }
+    else
+    {
+        return 0;
+    }
+    return 1;
+}
diff --git a/src/server/god_commands.h b/src/server/god_commands.h
index 87af27e..d9b1558 100644
--- a/src/server/god_commands.h
+++ b/src/server/god_commands.h
@@ -11,8 +11,9 @@
 #include <stdint.h> /* uint8_t */
 
 
-/* Parse/apply god command "tok0" with argument "tok1". */
-extern uint8_t parse_god_command_1arg(char * tok1, char * tok2);
+/* Parse/apply god command "tok0" with argument "tok1", "tok2" etc. . */
+extern uint8_t parse_god_command_1arg(char * tok0, char * tok1);
+extern uint8_t parse_god_command_2arg(char * tok0, char * tok1, char * tok2);
 
 
 
diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c
index e9578c7..4e801cf 100644
--- a/src/server/hardcoded_strings.c
+++ b/src/server/hardcoded_strings.c
@@ -4,7 +4,7 @@
 
 
 
-char * s[38];
+char * s[39];
 
 
 
@@ -36,6 +36,7 @@ extern void init_strings()
     s[S_CMD_T_PROGRESS] = "T_PROGRESS";
     s[S_CMD_T_HP] = "T_LIFEPOINTS";
     s[S_CMD_T_CARRIES] = "T_CARRIES";
+    s[S_CMD_T_MEMMAP] = "T_MEMMAP";
     s[S_CMD_WAIT] = "wait";
     s[S_CMD_MOVE] = "move";
     s[S_CMD_PICKUP] = "pick_up";
diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h
index 2783290..3c8dd81 100644
--- a/src/server/hardcoded_strings.h
+++ b/src/server/hardcoded_strings.h
@@ -36,6 +36,7 @@ enum string_num
     S_CMD_T_PROGRESS,
     S_CMD_T_HP,
     S_CMD_T_CARRIES,
+    S_CMD_T_MEMMAP,
     S_CMD_WAIT,
     S_CMD_MOVE,
     S_CMD_PICKUP,
@@ -52,7 +53,7 @@ enum string_num
 
 extern void init_strings();
 
-extern char * s[38];
+extern char * s[39];
 
 
 
diff --git a/src/server/io.c b/src/server/io.c
index f6d6928..70a0107 100644
--- a/src/server/io.c
+++ b/src/server/io.c
@@ -5,10 +5,10 @@
 #include <errno.h> /* global errno */
 #include <limits.h> /* PIPE_BUF */
 #include <stddef.h> /* size_t, NULL */
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t */
+#include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT8_MAX */
 #include <stdio.h> /* defines EOF, FILE, sprintf(), fprintf() */
 #include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), memcpy(), memset(), strchr() */
+#include <string.h> /* strlen(), snprintf(), memcpy(), memset(), strchr() */
 #include <sys/types.h> /* time_t */
 #include <time.h> /* time(), nanosleep() */
 #include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(),
@@ -26,9 +26,12 @@
 
 
 
-/* Write to "file" god commands (one per line) to recreate thing "t". */
-static void write_key_value(FILE * file, char * key, uint32_t value);
-static void write_key_string(FILE * file, char * key, char * string);
+/* Helpers to write lines of god commands to recreate thing "t". */
+static void write_key_space(FILE * file, char * key);
+static void write_value(FILE * file, uint32_t value);
+static void write_string(FILE * file, char * string);
+static void write_key_space_value(FILE * file, char * key, uint32_t value);
+static void write_key_space_string(FILE * file, char * key, char * string);
 
 /* Write to "file" \n-delimited line of "key" + space + "value" as string. */
 static void write_thing(FILE * file, struct Thing * t);
@@ -61,30 +64,34 @@ static void write_inventory(struct Thing * player, FILE * file);
  */
 static char * build_visible_map(struct Thing * player);
 
-/* Write to "file" game map as visible to "player", build_visible_map()-drawn.
- * Write one row per \n-delimited line.
+/* Write to "file" game map as visible to "player" right now, as drawn by
+ * build_visible_map(), and thereafter game map as memorized by player in its
+ * .mem_map. Write one row per \n-delimited line.
  */
 static void write_map(struct Thing * player, FILE * file);
 
 
 
-static void write_key_value(FILE * file, char * key, uint32_t value)
+static void write_key_space(FILE * file, char * key)
 {
     try_fwrite(key, strlen(key), 1, file, __func__);
     try_fputc(' ', file, __func__);
+}
+
+
+
+static void write_value(FILE * file, uint32_t value)
+{
     char * line = try_malloc(11, __func__);
     exit_trouble(-1 == sprintf(line, "%u", value), __func__, s[S_FCN_SPRINTF]);
     try_fwrite(line, strlen(line), 1, file, __func__);
     free(line);
-    try_fputc('\n', file, __func__);
 }
 
 
 
-static void write_key_string(FILE * file, char * key, char * string)
+static void write_string(FILE * file, char * string)
 {
-    try_fwrite(key, strlen(key), 1, file, __func__);
-    try_fputc(' ', file, __func__);
     uint8_t contains_space = NULL != strchr(string, ' ');
     if (contains_space)
     {
@@ -95,6 +102,23 @@ static void write_key_string(FILE * file, char * key, char * string)
     {
         try_fputc('\'', file, __func__);
     }
+}
+
+
+
+static void write_key_space_value(FILE * file, char * key, uint32_t value)
+{
+    write_key_space(file, key);
+    write_value(file, value);
+    try_fputc('\n', file, __func__);
+}
+
+
+
+static void write_key_space_string(FILE * file, char * key, char * string)
+{
+    write_key_space(file, key);
+    write_string(file, string);
     try_fputc('\n', file, __func__);
 }
 
@@ -107,17 +131,39 @@ static void write_thing(FILE * file, struct Thing * t)
     {
         write_thing(file, o);
     }
-    write_key_value(file, s[S_CMD_T_ID], t->id);
-    write_key_value(file, s[S_CMD_T_TYPE], t->type);
-    write_key_value(file, s[S_CMD_T_POSY], t->pos.y);
-    write_key_value(file, s[S_CMD_T_POSX], t->pos.x);
-    write_key_value(file, s[S_CMD_T_COMMAND], t->command);
-    write_key_value(file, s[S_CMD_T_ARGUMENT], t->arg);
-    write_key_value(file, s[S_CMD_T_PROGRESS], t->progress);
-    write_key_value(file, s[S_CMD_T_HP], t->lifepoints);
+    write_key_space_value(file, s[S_CMD_T_ID], t->id);
+    write_key_space_value(file, s[S_CMD_T_TYPE], t->type);
+    write_key_space_value(file, s[S_CMD_T_POSY], t->pos.y);
+    write_key_space_value(file, s[S_CMD_T_POSX], t->pos.x);
+    write_key_space_value(file, s[S_CMD_T_COMMAND], t->command);
+    write_key_space_value(file, s[S_CMD_T_ARGUMENT], t->arg);
+    write_key_space_value(file, s[S_CMD_T_PROGRESS], t->progress);
+    write_key_space_value(file, s[S_CMD_T_HP], t->lifepoints);
     for (o = t->owns; o; o = o->next)
     {
-        write_key_value(file, s[S_CMD_T_CARRIES], o->id);
+        write_key_space_value(file, s[S_CMD_T_CARRIES], o->id);
+    }
+    if (t->mem_map)
+    {
+        uint32_t map_size = world.map.length * world.map.length;/* snprintf() */
+        char * mem_map_copy = try_malloc(map_size + 1, __func__);/* reads one */
+        memcpy(mem_map_copy, t->mem_map, map_size);   /* byte beyond map_size */
+        mem_map_copy[map_size] = '\0';     /* if string is not \0-terminated. */
+        uint16_t y;
+        char string[UINT8_MAX + 1 + 1];
+        for (y = 0; y < world.map.length; y++)
+        {
+
+            int test = snprintf(string, world.map.length + 1, "%s",
+                                mem_map_copy + (y * world.map.length));
+            exit_trouble(test < 0, __func__, "snprintf()");
+            write_key_space(file, s[S_CMD_T_MEMMAP]);
+            write_value(file, y);
+            try_fputc(' ', file, __func__);
+            write_string(file, string);
+            try_fputc('\n', file, __func__);
+        }
+        free(mem_map_copy);
     }
     try_fputc('\n', file, __func__);
 }
@@ -301,11 +347,19 @@ static void write_map(struct Thing * player, FILE * file)
     {
         for (x = 0; x < world.map.length; x++)
         {
-            try_fputc(visible_map[(y * world.map.length) + x], file, __func__);
+            try_fputc(visible_map[y * world.map.length + x], file, __func__);
         }
         try_fputc('\n', file, __func__);
     }
     free(visible_map);
+    for (y = 0; y < world.map.length; y++)
+    {
+        for (x = 0; x < world.map.length; x++)
+        {
+            try_fputc(player->mem_map[y * world.map.length + x], file, __func__);
+        }
+        try_fputc('\n', file, __func__);
+    }
 }
 
 
@@ -340,44 +394,44 @@ extern void save_world()
 {
     char * path_tmp;
     FILE * file = atomic_write_start(s[S_PATH_SAVE], &path_tmp);
-    write_key_value(file, s[S_CMD_MAPLENGTH], world.map.length);
-    write_key_value(file, s[S_CMD_PLAYTYPE], world.player_type);
+    write_key_space_value(file, s[S_CMD_MAPLENGTH], world.map.length);
+    write_key_space_value(file, s[S_CMD_PLAYTYPE], world.player_type);
     try_fputc('\n', file, __func__);
     struct ThingAction * ta;
     for (ta = world.thing_actions; ta; ta = ta->next)
     {
-        write_key_value(file, s[S_CMD_TA_ID], ta->id);
-        write_key_value(file, s[S_CMD_TA_EFFORT], ta->effort);
-        write_key_string(file, s[S_CMD_TA_NAME], ta->name);
+        write_key_space_value(file, s[S_CMD_TA_ID], ta->id);
+        write_key_space_value(file, s[S_CMD_TA_EFFORT], ta->effort);
+        write_key_space_string(file, s[S_CMD_TA_NAME], ta->name);
         try_fputc('\n', file, __func__);
     }
     struct ThingType * tt;
     for (tt = world.thing_types; tt; tt = tt->next)
     {
-        write_key_value(file, s[S_CMD_TT_ID], tt->id);
-        write_key_value(file, s[S_CMD_TT_STARTN], tt->start_n);
-        write_key_value(file, s[S_CMD_TT_HP], tt->lifepoints);
+        write_key_space_value(file, s[S_CMD_TT_ID], tt->id);
+        write_key_space_value(file, s[S_CMD_TT_STARTN], tt->start_n);
+        write_key_space_value(file, s[S_CMD_TT_HP], tt->lifepoints);
         int test = fprintf(file, "%s %c\n", s[S_CMD_TT_SYMB], tt->char_on_map);
         exit_trouble(test < 0, __func__, "fprintf");
-        write_key_string(file, s[S_CMD_TT_NAME], tt->name);
-        write_key_value(file, s[S_CMD_TT_CONSUM], tt->consumable);
+        write_key_space_string(file, s[S_CMD_TT_NAME], tt->name);
+        write_key_space_value(file, s[S_CMD_TT_CONSUM], tt->consumable);
         try_fputc('\n', file, __func__);
     }
     for (tt = world.thing_types; tt; tt = tt->next)
     {
-        write_key_value(file, s[S_CMD_TT_ID], tt->id);
-        write_key_value(file, s[S_CMD_TT_CORPS], tt->corpse_id);
+        write_key_space_value(file, s[S_CMD_TT_ID], tt->id);
+        write_key_space_value(file, s[S_CMD_TT_CORPS], tt->corpse_id);
     }
     try_fputc('\n', file, __func__);
-    write_key_value(file, s[S_CMD_SEED_MAP], world.seed_map);
-    write_key_value(file, s[S_CMD_SEED_RAND], world.seed);
-    write_key_value(file, s[S_CMD_TURN], world.turn);
+    write_key_space_value(file, s[S_CMD_SEED_MAP], world.seed_map);
+    write_key_space_value(file, s[S_CMD_SEED_RAND], world.seed);
+    write_key_space_value(file, s[S_CMD_TURN], world.turn);
     try_fputc('\n', file, __func__);
     struct Thing * t;
     for (t = world.things; t; t = t->next)
     {
         write_thing(file, t);
     }
-    write_key_value(file, s[S_CMD_WORLD_ACTIVE], 1);
+    write_key_space_value(file, s[S_CMD_WORLD_ACTIVE], 1);
     atomic_write_finish(file, s[S_PATH_SAVE], path_tmp);
 }
diff --git a/src/server/run.c b/src/server/run.c
index 6cbbd58..ecfae2b 100644
--- a/src/server/run.c
+++ b/src/server/run.c
@@ -18,7 +18,7 @@
 #include "../common/try_malloc.h" /* try_malloc() */
 #include "ai.h" /* ai() */
 #include "cleanup.h" /* unset_cleanup_flag() */
-#include "god_commands.h" /* parse_god_command_1arg() */
+#include "god_commands.h" /* parse_god_command_1arg(),parse_god_command_2arg()*/
 #include "hardcoded_strings.h" /* s */
 #include "io.h" /* io_round(), save_world() */
 #include "things.h" /* Thing, get_thing_action_id_by_name(), get_player() */
@@ -39,7 +39,7 @@ static uint8_t parse_player_command_0arg(char * tok0);
 /* Parse player command "tok0" with one argument "tok1" to player action. */
 static uint8_t parse_player_command_1arg(char * tok0, char * tok1);
 
-/* Parse/apply command "tok0". */
+/* Parse/apply command "tok0" (read further tokens as necessary). */
 static uint8_t parse_command(char * tok0);
 
 /* Compares first line of server out file to world.server_test, aborts if they
@@ -149,6 +149,14 @@ static uint8_t parse_command(char * tok0)
         {
             return 1;
         }
+        else
+        {
+            char * tok2 = token_from_line(NULL);
+            if (tok2 && parse_god_command_2arg(tok0, tok1, tok2))
+            {
+                return 1;
+            }
+        }
     }
     return 0;
 }
@@ -199,7 +207,6 @@ static void turn_over()
                 ai(thing);
             }
             thing->progress++;
-
             struct ThingAction * ta = get_thing_action(thing->command);
             if (thing->progress == ta->effort)
             {
diff --git a/src/server/thing_actions.c b/src/server/thing_actions.c
index 8d6a802..40a44ba 100644
--- a/src/server/thing_actions.c
+++ b/src/server/thing_actions.c
@@ -142,8 +142,16 @@ static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted)
         if (player == hitted)
         {
             update_log(" You die.");
+            memset(hitted->fov_map, ' ', world.map.length * world.map.length);
             return;
         }
+        else
+        {
+            free(hitted->fov_map);
+            hitted->fov_map = NULL;
+            free(hitted->mem_map);
+            hitted->mem_map = NULL;
+        }
         update_log(" It dies.");
     }
 }
diff --git a/src/server/things.c b/src/server/things.c
index 4a5a490..e73a09a 100644
--- a/src/server/things.c
+++ b/src/server/things.c
@@ -156,6 +156,7 @@ extern void free_things(struct Thing * t)
     free_things(t->owns);
     free_things(t->next);
     free(t->fov_map);
+    free(t->mem_map);
     free(t);
     if (t == world.things)         /* So add_things()' NULL-delimited thing   */
     {                              /* iteration loop does not iterate over    */
diff --git a/src/server/things.h b/src/server/things.h
index d5ef508..4b9291c 100644
--- a/src/server/things.h
+++ b/src/server/things.h
@@ -19,6 +19,7 @@ struct Thing
     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 * mem_map;           /* map knowledge of thing by FOV and memory */
     uint8_t type;                /* ID of appropriate thing definition */
     uint8_t lifepoints;          /* 0: thing is inanimate; >0: hitpoints */
     uint8_t command;             /* thing's current action; 0 if none */
-- 
2.30.2