home · contact · privacy
Re-insert map existence check in command_worldactive().
[plomrogue] / roguelike-server
index db5e93bb61c5282f649c239f4d03a1d1bc1093f9..e723339a46bdad1e0c121cdb1b41e69537c9f77d 100755 (executable)
@@ -39,6 +39,12 @@ def prep_library():
     return libpr
 
 
+def c_pointer_to_bytearray(ba):
+    """Return C char * pointer to ba."""
+    type = ctypes.c_char * len(ba)
+    return type.from_buffer(ba)
+
+
 def strong_write(file, string):
     """Apply write(string), then flush()."""
     file.write(string)
@@ -58,7 +64,7 @@ def setup_server_io():
         path_tmp = path + tmp_suffix
         msg = "Found file '" + path_tmp + "' that may be a leftover from an " \
               "aborted previous attempt to write '" + path + "'. Aborting " \
-             "until matter is resolved by removing it from its current path."
+              "until matter is resolved by removing it from its current path."
         if os.access(path_tmp, os.F_OK):
             raise SystemExit(msg)
     io_db["teststring"] = str(os.getpid()) + " " + str(time.time())
@@ -133,7 +139,8 @@ def obey(command, prefix, replay=False, do_record=False):
                 if time.time() > io_db["save_wait"] + 15:
                     atomic_write(io_db["path_record"], io_db["record_chunk"],
                                  do_append=True)
-                    save_world()
+                    if world_db["WORLD_ACTIVE"]:
+                        save_world()
                     io_db["record_chunk"] = ""
                     io_db["save_wait"] = time.time()
             io_db["worldstate_updateable"] = world_db["WORLD_ACTIVE"]
@@ -173,7 +180,7 @@ def save_world():
                 for i in range(length):
                     line = map[i * length:(i * length) + length].decode()
                     string = string + key + " " + str(i) + " " + quote(line) \
-                             + "\n"
+                        + "\n"
             return string
         return helper
 
@@ -181,7 +188,7 @@ def save_world():
         string = ""
         for memthing in world_db["Things"][id]["T_MEMTHING"]:
             string = string + "T_MEMTHING " + str(memthing[0]) + " " + \
-                     str(memthing[1]) + " " + str(memthing[2]) + "\n"
+                str(memthing[1]) + " " + str(memthing[2]) + "\n"
         return string
 
     def helper(category, id_string, special_keys={}):
@@ -199,7 +206,7 @@ def save_world():
 
     string = ""
     for key in sorted(world_db.keys()):
-        if dict != type(world_db[key]) and key != "MAP" and \
+        if (not isinstance(world_db[key], dict)) and key != "MAP" and \
            key != "WORLD_ACTIVE" and key != "SEED_MAP":
             string = string + key + " " + str(world_db[key]) + "\n"
     string = string + "SEED_MAP " + str(world_db["SEED_MAP"]) + "\n"
@@ -207,7 +214,7 @@ def save_world():
     string = string + helper("ThingTypes", "TT_ID", {"TT_CORPSE_ID": False})
     for id in sorted(world_db["ThingTypes"].keys()):
         string = string + "TT_ID " + str(id) + "\n" + "TT_CORPSE_ID " + \
-                 str(world_db["ThingTypes"][id]["TT_CORPSE_ID"]) + "\n"
+            str(world_db["ThingTypes"][id]["TT_CORPSE_ID"]) + "\n"
     string = string + helper("Things", "T_ID",
                              {"T_CARRIES": False, "carried": False,
                               "T_MEMMAP": mapsetter("T_MEMMAP"),
@@ -216,10 +223,10 @@ def save_world():
     for id in sorted(world_db["Things"].keys()):
         if [] != world_db["Things"][id]["T_CARRIES"]:
             string = string + "T_ID " + str(id) + "\n"
-            for carried in sorted(world_db["Things"][id]["T_CARRIES"].keys()):
+            for carried in sorted(world_db["Things"][id]["T_CARRIES"]):
                 string = string + "T_CARRIES " + str(carried) + "\n"
     string = string + "SEED_RANDOMNESS " + str(rand.seed) + "\n" + \
-             "WORLD_ACTIVE " + str(world_db["WORLD_ACTIVE"])
+        "WORLD_ACTIVE " + str(world_db["WORLD_ACTIVE"])
     atomic_write(io_db["path_save"], string)
 
 
@@ -325,12 +332,12 @@ def try_worldstate_update():
                 name = world_db["ThingTypes"][type_id]["TT_NAME"]
                 inventory = inventory + name + "\n"
         string = str(world_db["TURN"]) + "\n" + \
-                 str(world_db["Things"][0]["T_LIFEPOINTS"]) + "\n" + \
-                 str(world_db["Things"][0]["T_SATIATION"]) + "\n" + \
-                 inventory + "%\n" + \
-                 str(world_db["Things"][0]["T_POSY"]) + "\n" + \
-                 str(world_db["Things"][0]["T_POSX"]) + "\n" + \
-                 str(world_db["MAP_LENGTH"]) + "\n"
+            str(world_db["Things"][0]["T_LIFEPOINTS"]) + "\n" + \
+            str(world_db["Things"][0]["T_SATIATION"]) + "\n" + \
+            inventory + "%\n" + \
+            str(world_db["Things"][0]["T_POSY"]) + "\n" + \
+            str(world_db["Things"][0]["T_POSX"]) + "\n" + \
+            str(world_db["MAP_LENGTH"]) + "\n"
         length = world_db["MAP_LENGTH"]
         fov = bytearray(b' ' * (length ** 2))
         for pos in range(length ** 2):
@@ -456,7 +463,7 @@ def remake_map():
         x = rand.next() % length
         pos = (y * length) + x
         if "." == chr(world_db["MAP"][pos]) \
-          and ((not single_allowed) or is_neighbor((y, x), "X")):
+                and ((not single_allowed) or is_neighbor((y, x), "X")):
             world_db["MAP"][pos] = ord("X")
             i_trees += 1
     rand.seed = store_seed
@@ -465,6 +472,21 @@ def remake_map():
 
 def update_map_memory(t, age_map=True):
     """Update t's T_MEMMAP with what's in its FOV now,age its T_MEMMEPTHMAP."""
+    def age_some_memdepthmap_on_nonfov_cells():
+        # OUTSOURCED FOR PERFORMANCE REASONS TO libplomrogue.so:
+        # ord_v = ord("v")
+        # ord_0 = ord("0")
+        # ord_9 = ord("9")
+        # for pos in [pos for pos in range(world_db["MAP_LENGTH"] ** 2)
+        #             if not ord_v == t["fovmap"][pos]
+        #             if ord_0 <= t["T_MEMDEPTHMAP"][pos]
+        #             if ord_9 > t["T_MEMDEPTHMAP"][pos]
+        #             if not rand.next() % (2 **
+        #                                   (t["T_MEMDEPTHMAP"][pos] - 48))]:
+        #     t["T_MEMDEPTHMAP"][pos] += 1
+        memdepthmap = c_pointer_to_bytearray(t["T_MEMDEPTHMAP"])
+        fovmap = c_pointer_to_bytearray(t["fovmap"])
+        libpr.age_some_memdepthmap_on_nonfov_cells(memdepthmap, fovmap)
     if not t["T_MEMMAP"]:
         t["T_MEMMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
     if not t["T_MEMDEPTHMAP"]:
@@ -478,21 +500,17 @@ def update_map_memory(t, age_map=True):
         if ord_space == t["T_MEMMAP"][pos]:
             t["T_MEMMAP"][pos] = world_db["MAP"][pos]
     if age_map:
-        maptype = ctypes.c_char * len(t["T_MEMDEPTHMAP"])
-        memdepthmap = maptype.from_buffer(t["T_MEMDEPTHMAP"])
-        fovmap = maptype.from_buffer(t["fovmap"])
-        libpr.age_some_memdepthmap_on_nonfov_cells(memdepthmap, fovmap)
-    for mt in [mt for mt in t["T_MEMTHING"]
-               if "v" == chr(t["fovmap"][(mt[1] * world_db["MAP_LENGTH"])
-                                         + mt[2]])]:
-         t["T_MEMTHING"].remove(mt)
+        age_some_memdepthmap_on_nonfov_cells()
+    t["T_MEMTHING"] = [mt for mt in t["T_MEMTHING"]
+                       if ord_v != t["fovmap"][(mt[1] * world_db["MAP_LENGTH"])
+                                               + mt[2]]]
     for id in [id for id in world_db["Things"]
                if not world_db["Things"][id]["carried"]]:
         type = world_db["Things"][id]["T_TYPE"]
         if not world_db["ThingTypes"][type]["TT_LIFEPOINTS"]:
             y = world_db["Things"][id]["T_POSY"]
             x = world_db["Things"][id]["T_POSX"]
-            if "v" == chr(t["fovmap"][(y * world_db["MAP_LENGTH"]) + x]):
+            if ord_v == t["fovmap"][(y * world_db["MAP_LENGTH"]) + x]:
                 t["T_MEMTHING"].append((type, y, x))
 
 
@@ -508,12 +526,12 @@ def integer_test(val_string, min, max=None):
     """Return val_string if integer >= min & (if max set) <= max, else None."""
     try:
         val = int(val_string)
-        if val < min or (max != None and val > max):
+        if val < min or (max is not None and val > max):
             raise ValueError
         return val
     except ValueError:
         msg = "Ignoring: Please use integer >= " + str(min)
-        if max != None:
+        if max is not None:
             msg += " and <= " + str(max)
         msg += "."
         print(msg)
@@ -549,11 +567,9 @@ def setter(category, key, min, max=None):
 def build_fov_map(t):
     """Build Thing's FOV map."""
     t["fovmap"] = bytearray(b'v' * (world_db["MAP_LENGTH"] ** 2))
-    maptype = ctypes.c_char * len(world_db["MAP"])
-    test = libpr.build_fov_map(t["T_POSY"], t["T_POSX"],
-                               maptype.from_buffer(t["fovmap"]),
-                               maptype.from_buffer(world_db["MAP"]))
-    if test:
+    fovmap = c_pointer_to_bytearray(t["fovmap"])
+    map = c_pointer_to_bytearray(world_db["MAP"])
+    if libpr.build_fov_map(t["T_POSY"], t["T_POSX"], fovmap, map):
         raise RuntimeError("Malloc error in build_fov_Map().")
 
 
@@ -562,9 +578,15 @@ def decrement_lifepoints(t):
 
     If t is the player avatar, only blank its fovmap, so that the client may
     still display memory data. On non-player things, erase fovmap and memory.
+    Dying actors drop all their things.
     """
     t["T_LIFEPOINTS"] -= 1
     if 0 == t["T_LIFEPOINTS"]:
+        for id in t["T_CARRIES"]:
+            t["T_CARRIES"].remove(id)
+            world_db["Things"][id]["T_POSY"] = t["T_POSY"]
+            world_db["Things"][id]["T_POSX"] = t["T_POSX"]
+            world_db["Things"][id]["carried"] = False
         t["T_TYPE"] = world_db["ThingTypes"][t["T_TYPE"]]["TT_CORPSE_ID"]
         if world_db["Things"][0] == t:
             t["fovmap"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
@@ -574,7 +596,6 @@ def decrement_lifepoints(t):
             t["T_MEMMAP"] = False
             t["T_MEMDEPTHMAP"] = False
             t["T_MEMTHING"] = []
-            strong_write(io_db["file_out"], "LOG It dies.\n")
 
 
 def mv_yx_in_dir_legal(dir, y, x):
@@ -607,14 +628,15 @@ def actor_move(t):
                   if world_db["Things"][id]["T_POSX"] == move_result[2]]
         if len(hitted):
             hit_id = hitted[0]
-            hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
-            hitter = "You" if t == world_db["Things"][0] else hitter_name
-            hitted_type = world_db["Things"][hit_id]["T_TYPE"]
-            hitted_name = world_db["ThingTypes"][hitted_type]["TT_NAME"]
-            hitted = "you" if hit_id == 0 else hitted_name
-            verb = " wound " if hitter == "You" else " wounds "
-            strong_write(io_db["file_out"], "LOG " + hitter + verb + hitted +
-                                            ".\n")
+            if t == world_db["Things"][0]:
+                hitted_type = world_db["Things"][hit_id]["T_TYPE"]
+                hitted_name = world_db["ThingTypes"][hitted_type]["TT_NAME"]
+                strong_write(io_db["file_out"], "LOG You wound "
+                                                + hitted_name + ".\n")
+            elif 0 == hit_id:
+                hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
+                strong_write(io_db["file_out"], "LOG " + hitter_name +
+                                                " wounds you.\n")
             decrement_lifepoints(world_db["Things"][hit_id])
             return
     dir = [dir for dir in directions_db
@@ -634,15 +656,18 @@ def actor_move(t):
 
 def actor_pick_up(t):
     """Make t pick up (topmost?) Thing from ground into inventory."""
-    # Topmostness is actually not defined so far. Picks Thing with highest ID.
+    # Topmostness is actually not defined so far. Picks most nutritious Thing.
     ids = [id for id in world_db["Things"] if world_db["Things"][id] != t
            if not world_db["Things"][id]["carried"]
            if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
            if world_db["Things"][id]["T_POSX"] == t["T_POSX"]]
     if len(ids):
-        highest_id = 0
+        highest_id = ids[0]
+        nutritious = 0
         for id in ids:
-            if id > highest_id:
+            type = world_db["Things"][id]["T_TYPE"]
+            if world_db["ThingTypes"][type]["TT_CONSUMABLE"] > nutritious:
+                nutritious = world_db["ThingTypes"][type]["TT_CONSUMABLE"]
                 highest_id = id
         world_db["Things"][highest_id]["carried"] = True
         t["T_CARRIES"].append(highest_id)
@@ -688,31 +713,21 @@ def actor_use(t):
                      "LOG You try to use an object, but you own none.\n")
 
 
-def thingproliferation(t):
-    """To chance of 1/TT_PROLIFERATE, create t offspring in neighbor cell.
+def thingproliferation(t, prol_map):
+    """To chance of 1/TT_PROLIFERATE, create t offspring in open neighbor cell.
 
-    Naturally only works with TT_PROLIFERATE > 0. The neighbor cell must be
-    passable and not be inhabited by a Thing of the same type, or, if Thing is
-    animate, any other animate Thing. If there are several map cell candidates,
-    one is selected randomly.
+    Naturally only works with TT_PROLIFERATE > 0. The neighbor cell must be be
+    marked '.' in prol_map. If there are several map cell candidates, one is
+    selected randomly.
     """
-    def test_cell(t, y, x):
-        if "." == chr(world_db["MAP"][(y * world_db["MAP_LENGTH"]) + x]):
-            for id in [id for id in world_db["Things"]
-                       if y == world_db["Things"][id]["T_POSY"]
-                       if x == world_db["Things"][id]["T_POSX"]
-                       if (t["T_TYPE"] == world_db["Things"][id]["T_TYPE"])
-                       or (t["T_LIFEPOINTS"] and
-                           world_db["Things"][id]["T_LIFEPOINTS"])]:
-                return False
-            return True
-        return False
     prolscore = world_db["ThingTypes"][t["T_TYPE"]]["TT_PROLIFERATE"]
     if prolscore and (1 == prolscore or 1 == (rand.next() % prolscore)):
         candidates = []
         for dir in [directions_db[key] for key in directions_db]:
             mv_result = mv_yx_in_dir_legal(dir, t["T_POSY"], t["T_POSX"])
-            if mv_result[0] and test_cell(t, mv_result[1], mv_result[2]):
+            if mv_result[0] and  ord('.') == prol_map[mv_result[1]
+                                                      * world_db["MAP_LENGTH"]
+                                                      + mv_result[2]]:
                 candidates.append((mv_result[1], mv_result[2]))
         if len(candidates):
             i = rand.next() % len(candidates)
@@ -728,18 +743,15 @@ def try_healing(t):
     """
     if t["T_SATIATION"] > 0 \
        and t["T_LIFEPOINTS"] < \
-           world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"] \
+        world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"] \
        and 0 == (rand.next() % 31) \
        and t["T_COMMAND"] == [id for id in world_db["ThingActions"]
                               if world_db["ThingActions"][id]["TA_NAME"] ==
-                                 "wait"][0]:
+                              "wait"][0]:
         t["T_LIFEPOINTS"] += 1
         t["T_SATIATION"] -= 32
         if t == world_db["Things"][0]:
             strong_write(io_db["file_out"], "LOG You heal.\n")
-        else:
-            name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
-            strong_write(io_db["file_out"], "LOG " + name + "heals.\n")
 
 
 def hunger(t):
@@ -753,10 +765,6 @@ def hunger(t):
     if int(int(testbase / stomach) / ((rand.next() % stomach) + 1)):
         if t == world_db["Things"][0]:
             strong_write(io_db["file_out"], "LOG You suffer from hunger.\n")
-        else:
-            name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
-            strong_write(io_db["file_out"], "LOG " + name +
-                                            " suffers from hunger.\n")
         decrement_lifepoints(t)
 
 
@@ -780,10 +788,12 @@ def get_dir_to_target(t, filter):
     """
 
     def zero_score_map_where_char_on_memdepthmap(c):
-        maptype = ctypes.c_char * len(t["T_MEMDEPTHMAP"])
-        map = maptype.from_buffer(t["T_MEMDEPTHMAP"])
-        test = libpr.zero_score_map_where_char_on_memdepthmap(c, map)
-        if test:
+        # OUTSOURCED FOR PERFORMANCE REASONS TO libplomrogue.so:
+        # for i in [i for i in range(world_db["MAP_LENGTH"] ** 2)
+        #           if t["T_MEMDEPTHMAP"][i] == mem_depth_c[0]]:
+        #     set_map_score(i, 0)
+        map = c_pointer_to_bytearray(t["T_MEMDEPTHMAP"])
+        if libpr.zero_score_map_where_char_on_memdepthmap(c, map):
             raise RuntimeError("No score map allocated for "
                                "zero_score_map_where_char_on_memdepthmap().")
 
@@ -809,33 +819,42 @@ def get_dir_to_target(t, filter):
                                           + Thing["T_POSX"]]):
                     ThingType = world_db["ThingTypes"][Thing["T_TYPE"]]
                     if ("f" == filter and ThingType["TT_LIFEPOINTS"] >=
-                                          t["T_LIFEPOINTS"]) \
+                        t["T_LIFEPOINTS"]) \
                        or ("a" == filter and ThingType["TT_LIFEPOINTS"] <
-                                             t["T_LIFEPOINTS"]):
+                            t["T_LIFEPOINTS"]):
                         return True
         elif t["T_MEMMAP"] and "c" == filter:
             for mt in t["T_MEMTHING"]:
                 if ' ' != chr(t["T_MEMMAP"][(mt[1] * world_db["MAP_LENGTH"])
-                                         + mt[2]]) \
+                                            + mt[2]]) \
                    and world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]:
                     return True
         return False
 
+    def set_cells_passable_on_memmap_to_65534_on_scoremap():
+        # OUTSOURCED FOR PERFORMANCE REASONS TO libplomrogue.so:
+        # ord_dot = ord(".")
+        # memmap = t["T_MEMMAP"]
+        # for i in [i for i in range(world_db["MAP_LENGTH"] ** 2)
+        #            if ord_dot == memmap[i]]:
+        #     set_map_score(i, 65534) # i.e. 65535-1
+        map = c_pointer_to_bytearray(t["T_MEMMAP"])
+        if libpr.set_cells_passable_on_memmap_to_65534_on_scoremap(map):
+            raise RuntimeError("No score map allocated for set_cells_passable"
+                               "_on_memmap_to_65534_on_scoremap().")
+
     def init_score_map():
         test = libpr.init_score_map()
         if test:
             raise RuntimeError("Malloc error in init_score_map().")
-        ord_dot = ord(".")
         ord_v = ord("v")
         ord_blank = ord(" ")
-        for i in [i for i in range(world_db["MAP_LENGTH"] ** 2)
-                  if ord_dot == t["T_MEMMAP"][i]]:
-            set_map_score(i, 65535 - 1)
+        set_cells_passable_on_memmap_to_65534_on_scoremap()
         if "a" == filter:
             for id in world_db["Things"]:
                 Thing = world_db["Things"][id]
                 pos = Thing["T_POSY"] * world_db["MAP_LENGTH"] \
-                      + Thing["T_POSX"]
+                    + Thing["T_POSX"]
                 if t != Thing and Thing["T_LIFEPOINTS"] and \
                    t["T_TYPE"] != Thing["T_TYPE"] and \
                    ord_v == t["fovmap"][pos] and \
@@ -849,7 +868,7 @@ def get_dir_to_target(t, filter):
                        if world_db["Things"][id]["T_LIFEPOINTS"]]:
                 Thing = world_db["Things"][id]
                 pos = Thing["T_POSY"] * world_db["MAP_LENGTH"] \
-                      + Thing["T_POSX"]
+                    + Thing["T_POSX"]
                 if t["T_TYPE"] != Thing["T_TYPE"] and \
                    ord_v == t["fovmap"][pos] and \
                    t["T_LIFEPOINTS"] <= \
@@ -858,8 +877,8 @@ def get_dir_to_target(t, filter):
         elif "c" == filter:
             for mt in [mt for mt in t["T_MEMTHING"]
                        if ord_blank != t["T_MEMMAP"][mt[1]
-                                                    * world_db["MAP_LENGTH"]
-                                                    + mt[2]]
+                                                     * world_db["MAP_LENGTH"]
+                                                     + mt[2]]
                        if world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]]:
                 set_map_score(mt[1] * world_db["MAP_LENGTH"] + mt[2], 0)
         elif "s" == filter:
@@ -897,7 +916,7 @@ def get_dir_to_target(t, filter):
             for i in range(len(dirs)):
                 mv_yx_in_dir_legal(dirs[i], t["T_POSY"], t["T_POSX"])
                 pos_cmp = libpr.result_y() * world_db["MAP_LENGTH"] \
-                          + libpr.result_x()
+                    + libpr.result_x()
                 for pos in [pos for pos in inhabited if pos == pos_cmp]:
                     neighbors[i] = 65535
                     break
@@ -918,7 +937,7 @@ def get_dir_to_target(t, filter):
                     t["T_COMMAND"] = [id for id in world_db["ThingActions"]
                                       if
                                       world_db["ThingActions"][id]["TA_NAME"]
-                                         == "wait"][0]
+                                      == "wait"][0]
                     return 1
             elif dir_to_target and 3 < get_map_score(eye_pos):
                 dir_to_target = 0
@@ -933,7 +952,7 @@ def get_dir_to_target(t, filter):
         run_i -= 1
         init_score_map()
         mem_depth_c = b'9' if b' ' == mem_depth_c \
-                           else bytes([mem_depth_c[0] - 1])
+            else bytes([mem_depth_c[0] - 1])
         if libpr.dijkstra_map():
             raise RuntimeError("No score map allocated for dijkstra_map().")
         dir_to_target = get_dir_from_neighbors()
@@ -941,7 +960,7 @@ def get_dir_to_target(t, filter):
         if dir_to_target and str == type(dir_to_target):
             t["T_COMMAND"] = [id for id in world_db["ThingActions"]
                               if world_db["ThingActions"][id]["TA_NAME"]
-                                 == "move"][0]
+                              == "move"][0]
             t["T_ARGUMENT"] = ord(dir_to_target)
     return dir_to_target
 
@@ -989,12 +1008,12 @@ def ai(t):
         if -1 != sel:
             t["T_COMMAND"] = [id for id in world_db["ThingActions"]
                               if world_db["ThingActions"][id]["TA_NAME"]
-                                 == "use"][0]
+                              == "use"][0]
             t["T_ARGUMENT"] = sel
         elif standing_on_consumable(t):
             t["T_COMMAND"] = [id for id in world_db["ThingActions"]
                               if world_db["ThingActions"][id]["TA_NAME"]
-                                 == "pick_up"][0]
+                              == "pick_up"][0]
         elif (not get_dir_to_target(t, "c")) and \
              (not get_dir_to_target(t, "a")):
             get_dir_to_target(t, "s")
@@ -1005,6 +1024,12 @@ def turn_over():
     id = 0
     whilebreaker = False
     while world_db["Things"][0]["T_LIFEPOINTS"]:
+        proliferable_map = world_db["MAP"][:]
+        for id in [id for id in world_db["Things"]
+                   if not world_db["Things"][id]["carried"]]:
+            y = world_db["Things"][id]["T_POSY"]
+            x = world_db["Things"][id]["T_POSX"]
+            proliferable_map[y * world_db["MAP_LENGTH"] + x] = ord('X')
         for id in [id for id in world_db["Things"]]:  # Only what's from start!
             if not id in world_db["Things"] or \
                world_db["Things"][id]["carried"]:   # May have been consumed or
@@ -1020,14 +1045,14 @@ def turn_over():
                 try_healing(Thing)
                 Thing["T_PROGRESS"] += 1
                 taid = [a for a in world_db["ThingActions"]
-                          if a == Thing["T_COMMAND"]][0]
+                        if a == Thing["T_COMMAND"]][0]
                 ThingAction = world_db["ThingActions"][taid]
                 if Thing["T_PROGRESS"] == ThingAction["TA_EFFORT"]:
                     eval("actor_" + ThingAction["TA_NAME"])(Thing)
                     Thing["T_COMMAND"] = 0
                     Thing["T_PROGRESS"] = 0
                 hunger(Thing)
-            thingproliferation(Thing)
+            thingproliferation(Thing, proliferable_map)
         if whilebreaker:
             break
         world_db["TURN"] += 1
@@ -1091,8 +1116,10 @@ def command_ping():
 
 def command_quit():
     """Abort server process."""
-    save_world()
-    atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True)
+    if None == opts.replay:
+        if world_db["WORLD_ACTIVE"]:
+            save_world()
+        atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True)
     raise SystemExit("received QUIT command")
 
 
@@ -1133,7 +1160,7 @@ def play_commander(action, args=False):
 
     def set_command():
         id = [x for x in world_db["ThingActions"]
-                if world_db["ThingActions"][x]["TA_NAME"] == action][0]
+              if world_db["ThingActions"][x]["TA_NAME"] == action][0]
         world_db["Things"][0]["T_COMMAND"] = id
         turn_over()
 
@@ -1258,10 +1285,9 @@ def command_worldactive(worldactive_string):
     """Toggle world_db["WORLD_ACTIVE"] if possible.
 
     An active world can always be set inactive. An inactive world can only be
-    set active with a "wait" ThingAction, and a player Thing (of ID 0). On
-    activation, rebuild all Things' FOVs, and the player's map memory.
+    set active with a "wait" ThingAction, and a player Thing (of ID 0), and a
+    map. On activation, rebuild all Things' FOVs, and the player's map memory.
     """
-    # In original version, map existence was also tested (unnecessarily?).
     val = integer_test(worldactive_string, 0, 1)
     if val:
         if 0 != world_db["WORLD_ACTIVE"]:
@@ -1280,7 +1306,7 @@ def command_worldactive(worldactive_string):
                 if 0 == Thing:
                     player_exists = True
                     break
-            if wait_exists and player_exists:
+            if wait_exists and player_exists and "MAP" in world_db:
                 for id in world_db["Things"]:
                     if world_db["Things"][id]["T_LIFEPOINTS"]:
                         build_fov_map(world_db["Things"][id])
@@ -1353,7 +1379,7 @@ def command_tcarries(str_int):
         if val == command_tid.id:
             print("Ignoring: Thing cannot carry itself.")
         elif val in world_db["Things"] \
-             and not world_db["Things"][val]["carried"]:
+                and not world_db["Things"][val]["carried"]:
             world_db["Things"][command_tid.id]["T_CARRIES"].append(val)
             world_db["Things"][val]["carried"] = True
         else: