home · contact · privacy
Remove C variant of server, redefine build system to match this change.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 25 Aug 2015 03:08:00 +0000 (05:08 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 25 Aug 2015 03:08:00 +0000 (05:08 +0200)
35 files changed:
all.do
build/compiler_flags
compile-server.sh [deleted file]
plomrogue-server.py [deleted file]
redo
roguelike
roguelike-server [new file with mode: 0755]
roguelike-server.do [deleted file]
roguelike_python [deleted file]
src/server/ai.c [deleted file]
src/server/ai.h [deleted file]
src/server/cleanup.c [deleted file]
src/server/cleanup.h [deleted file]
src/server/field_of_view.c [deleted file]
src/server/field_of_view.h [deleted file]
src/server/god_commands.c [deleted file]
src/server/god_commands.h [deleted file]
src/server/hardcoded_strings.c [deleted file]
src/server/hardcoded_strings.h [deleted file]
src/server/init.c [deleted file]
src/server/init.h [deleted file]
src/server/io.c [deleted file]
src/server/io.h [deleted file]
src/server/main.c [deleted file]
src/server/map.c [deleted file]
src/server/map.h [deleted file]
src/server/rrand.c [deleted file]
src/server/rrand.h [deleted file]
src/server/run.c [deleted file]
src/server/run.h [deleted file]
src/server/thing_actions.c [deleted file]
src/server/thing_actions.h [deleted file]
src/server/things.c [deleted file]
src/server/things.h [deleted file]
src/server/world.h [deleted file]

diff --git a/all.do b/all.do
index 96e85362454dac918b26b5e58b6f66b5402399cd..337d4c22f0bed113a5d0b765bf1fc616610f8d8b 100644 (file)
--- a/all.do
+++ b/all.do
@@ -1,8 +1,8 @@
-# redo build file to build executables "roguelike-server", "roguelike-client".
+# redo build file to build executables "roguelike-server", "libplomrogue.so".
 
 # This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
 # or any later version. For details on its copyright, license, and warranties,
 # see the file NOTICE in the root directory of the PlomRogue source package.
 
 
 # This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
 # or any later version. For details on its copyright, license, and warranties,
 # see the file NOTICE in the root directory of the PlomRogue source package.
 
-redo-ifchange roguelike-server
+redo-ifchange roguelike-server 
 redo-ifchange roguelike-client
 redo-ifchange roguelike-client
index 59fcb18a27c8b08c7cb5ffc2c2605b7fd4589166..198da870d53130604998cdd8768dea0f65e7f89a 100644 (file)
@@ -2,4 +2,4 @@
 # or any later version. For details on its copyright, license, and warranties,
 # see the file NOTICE in the root directory of the PlomRogue source package.
 
 # or any later version. For details on its copyright, license, and warranties,
 # see the file NOTICE in the root directory of the PlomRogue source package.
 
-CFLAGS='-std=c11 -pedantic-errors -Wall -Werror -Wextra -Wformat-security -g'
+CFLAGS='-std=c11 -pedantic-errors -Wall -Werror -Wextra -Wformat-security -O3'
diff --git a/compile-server.sh b/compile-server.sh
deleted file mode 100755 (executable)
index 5fdc7b2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-gcc -shared -fPIC -std=c11 -pedantic-errors -Wall -Werror -Wextra -Wformat-security -O3 -o libplomrogue.so libplomrogue.c -lm
diff --git a/plomrogue-server.py b/plomrogue-server.py
deleted file mode 100755 (executable)
index ef3fdbc..0000000
+++ /dev/null
@@ -1,1629 +0,0 @@
-#!/usr/bin/python3
-
-# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
-# or any later version. For details on its copyright, license, and warranties,
-# see the file NOTICE in the root directory of the PlomRogue source package.
-
-
-import argparse
-import errno
-import os
-import shlex
-import shutil
-import time
-import ctypes
-
-
-class RandomnessIO:
-    """"Interface to libplomrogue's pseudo-randomness generator."""
-
-    def set_seed(self, seed):
-        libpr.seed_rrand(1, seed)
-
-    def get_seed(self):
-        return libpr.seed_rrand(0, 0)
-
-    def next(self):
-        return libpr.rrand()
-
-    seed = property(get_seed, set_seed)
-
-
-def prep_library():
-    """Prepare ctypes library at ./libplomrogue.so"""
-    libpath = ("./libplomrogue.so")
-    if not os.access(libpath, os.F_OK):
-        raise SystemExit("No library " + libpath +
-                         ", run ./compile-server.sh first?")
-    libpr = ctypes.cdll.LoadLibrary(libpath)
-    libpr.seed_rrand.restype = ctypes.c_uint32
-    return libpr
-
-
-def strong_write(file, string):
-    """Apply write(string), then flush()."""
-    file.write(string)
-    file.flush()
-
-
-def setup_server_io():
-    """Fill IO files DB with proper file( path)s. Write process IO test string.
-
-    Ensure IO files directory at server/. Remove any old input file if found.
-    Set up new input file for reading, and new output file for writing. Start
-    output file with process hash line of format PID + " " + floated UNIX time
-    (io_db["teststring"]). Raise SystemExit if file is found at path of either
-    record or save file plus io_db["tmp_suffix"].
-    """
-    def detect_atomic_leftover(path, tmp_suffix):
-        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."
-        if os.access(path_tmp, os.F_OK):
-            raise SystemExit(msg)
-    io_db["teststring"] = str(os.getpid()) + " " + str(time.time())
-    io_db["save_wait"] = 0
-    io_db["verbose"] = False
-    io_db["record_chunk"] = ""
-    os.makedirs(io_db["path_server"], exist_ok=True)
-    io_db["file_out"] = open(io_db["path_out"], "w")
-    strong_write(io_db["file_out"], io_db["teststring"] + "\n")
-    if os.access(io_db["path_in"], os.F_OK):
-        os.remove(io_db["path_in"])
-    io_db["file_in"] = open(io_db["path_in"], "w")
-    io_db["file_in"].close()
-    io_db["file_in"] = open(io_db["path_in"], "r")
-    detect_atomic_leftover(io_db["path_save"], io_db["tmp_suffix"])
-    detect_atomic_leftover(io_db["path_record"], io_db["tmp_suffix"])
-
-
-def cleanup_server_io():
-    """Close and (if io_db["kicked_by_rival"] false) remove files in io_db."""
-    def helper(file_key, path_key):
-        if file_key in io_db:
-            io_db[file_key].close()
-        if not io_db["kicked_by_rival"] \
-           and os.access(io_db[path_key], os.F_OK):
-            os.remove(io_db[path_key])
-    helper("file_in", "path_in")
-    helper("file_out", "path_out")
-    helper("file_worldstate", "path_worldstate")
-    if "file_record" in io_db:
-        io_db["file_record"].close()
-
-
-def obey(command, prefix, replay=False, do_record=False):
-    """Call function from commands_db mapped to command's first token.
-
-    Tokenize command string with shlex.split(comments=True). If replay is set,
-    a non-meta command from the commands_db merely triggers obey() on the next
-    command from the records file. If not, non-meta commands set
-    io_db["worldstate_updateable"] to world_db["WORLD_ACTIVE"], and, if
-    do_record is set, are recorded to io_db["record_chunk"], and save_world()
-    is called (and io_db["record_chunk"] written) if 15 seconds have passed
-    since the last time it was called. The prefix string is inserted into the
-    server's input message between its beginning 'input ' and ':'. All activity
-    is preceded by a server_test() call.
-    """
-    server_test()
-    if io_db["verbose"]:
-        print("input " + prefix + ": " + command)
-    try:
-        tokens = shlex.split(command, comments=True)
-    except ValueError as err:
-        print("Can't tokenize command string: " + str(err) + ".")
-        return
-    if len(tokens) > 0 and tokens[0] in commands_db \
-       and len(tokens) == commands_db[tokens[0]][0] + 1:
-        if commands_db[tokens[0]][1]:
-            commands_db[tokens[0]][2](*tokens[1:])
-        elif replay:
-            print("Due to replay mode, reading command as 'go on in record'.")
-            line = io_db["file_record"].readline()
-            if len(line) > 0:
-                obey(line.rstrip(), io_db["file_record"].prefix
-                     + str(io_db["file_record"].line_n))
-                io_db["file_record"].line_n = io_db["file_record"].line_n + 1
-            else:
-                print("Reached end of record file.")
-        else:
-            commands_db[tokens[0]][2](*tokens[1:])
-            if do_record:
-                io_db["record_chunk"] += command + "\n"
-                if time.time() > io_db["save_wait"] + 15:
-                    atomic_write(io_db["path_record"], io_db["record_chunk"],
-                                 do_append=True)
-                    save_world()
-                    io_db["record_chunk"] = ""
-                    io_db["save_wait"] = time.time()
-            io_db["worldstate_updateable"] = world_db["WORLD_ACTIVE"]
-    elif 0 != len(tokens):
-        print("Invalid command/argument, or bad number of tokens.")
-
-
-def atomic_write(path, text, do_append=False, delete=True):
-    """Atomic write of text to file at path, appended if do_append is set."""
-    path_tmp = path + io_db["tmp_suffix"]
-    mode = "w"
-    if do_append:
-        mode = "a"
-        if os.access(path, os.F_OK):
-            shutil.copyfile(path, path_tmp)
-    file = open(path_tmp, mode)
-    strong_write(file, text)
-    file.close()
-    if delete and os.access(path, os.F_OK):
-        os.remove(path)
-    os.rename(path_tmp, path)
-
-
-def save_world():
-    """Save all commands needed to reconstruct current world state."""
-
-    def quote(string):
-        string = string.replace("\u005C", '\u005C\u005C')
-        return '"' + string.replace('"', '\u005C"') + '"'
-
-    def mapsetter(key):
-        def helper(id):
-            string = ""
-            if world_db["Things"][id][key]:
-                map = world_db["Things"][id][key]
-                length = world_db["MAP_LENGTH"]
-                for i in range(length):
-                    line = map[i * length:(i * length) + length].decode()
-                    string = string + key + " " + str(i) + " " + quote(line) \
-                             + "\n"
-            return string
-        return helper
-
-    def memthing(id):
-        string = ""
-        for memthing in world_db["Things"][id]["T_MEMTHING"]:
-            string = string + "T_MEMTHING " + str(memthing[0]) + " " + \
-                     str(memthing[1]) + " " + str(memthing[2]) + "\n"
-        return string
-
-    def helper(category, id_string, special_keys={}):
-        string = ""
-        for id in sorted(world_db[category].keys()):
-            string = string + id_string + " " + str(id) + "\n"
-            for key in sorted(world_db[category][id].keys()):
-                if not key in special_keys:
-                    x = world_db[category][id][key]
-                    argument = quote(x) if str == type(x) else str(x)
-                    string = string + key + " " + argument + "\n"
-                elif special_keys[key]:
-                    string = string + special_keys[key](id)
-        return string
-
-    string = ""
-    for key in sorted(world_db.keys()):
-        if dict != type(world_db[key]) 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"
-    string = string + helper("ThingActions", "TA_ID")
-    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"
-    string = string + helper("Things", "T_ID",
-                             {"T_CARRIES": False, "carried": False,
-                              "T_MEMMAP": mapsetter("T_MEMMAP"),
-                              "T_MEMTHING": memthing, "fovmap": False,
-                              "T_MEMDEPTHMAP": mapsetter("T_MEMDEPTHMAP")})
-    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()):
-                string = string + "T_CARRIES " + str(carried) + "\n"
-    string = string + "SEED_RANDOMNESS " + str(rand.seed) + "\n" + \
-             "WORLD_ACTIVE " + str(world_db["WORLD_ACTIVE"])
-    atomic_write(io_db["path_save"], string)
-
-
-def obey_lines_in_file(path, name, do_record=False):
-    """Call obey() on each line of path's file, use name in input prefix."""
-    file = open(path, "r")
-    line_n = 1
-    for line in file.readlines():
-        obey(line.rstrip(), name + "file line " + str(line_n),
-             do_record=do_record)
-        line_n = line_n + 1
-    file.close()
-
-
-def parse_command_line_arguments():
-    """Return settings values read from command line arguments."""
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-s', nargs='?', type=int, dest='replay', const=1,
-                        action='store')
-    parser.add_argument('-l', nargs="?", const="save", dest='savefile',
-                        action="store")
-    parser.add_argument('-v', dest='verbose', action='store_true')
-    opts, unknown = parser.parse_known_args()
-    return opts
-
-
-def server_test():
-    """Ensure valid server out file belonging to current process.
-
-    This is done by comparing io_db["teststring"] to what's found at the start
-    of the current file at io_db["path_out"]. On failure, set
-    io_db["kicked_by_rival"] and raise SystemExit.
-    """
-    if not os.access(io_db["path_out"], os.F_OK):
-        raise SystemExit("Server output file has disappeared.")
-    file = open(io_db["path_out"], "r")
-    test = file.readline().rstrip("\n")
-    file.close()
-    if test != io_db["teststring"]:
-        io_db["kicked_by_rival"] = True
-        msg = "Server test string in server output file does not match. This" \
-              " indicates that the current server process has been " \
-              "superseded by another one."
-        raise SystemExit(msg)
-
-
-def read_command():
-    """Return next newline-delimited command from server in file.
-
-    Keep building return string until a newline is encountered. Pause between
-    unsuccessful reads, and after too much waiting, run server_test().
-    """
-    wait_on_fail = 0.03333
-    max_wait = 5
-    now = time.time()
-    command = ""
-    while True:
-        add = io_db["file_in"].readline()
-        if len(add) > 0:
-            command = command + add
-            if len(command) > 0 and "\n" == command[-1]:
-                command = command[:-1]
-                break
-        else:
-            time.sleep(wait_on_fail)
-            if now + max_wait < time.time():
-                server_test()
-                now = time.time()
-    return command
-
-
-def try_worldstate_update():
-    """Write worldstate file if io_db["worldstate_updateable"] is set."""
-    if io_db["worldstate_updateable"]:
-
-        def draw_visible_Things(map, run):
-            for id in world_db["Things"]:
-                type = world_db["Things"][id]["T_TYPE"]
-                consumable = world_db["ThingTypes"][type]["TT_CONSUMABLE"]
-                alive = world_db["ThingTypes"][type]["TT_LIFEPOINTS"]
-                if (0 == run and not consumable and not alive) \
-                   or (1 == run and consumable and not alive) \
-                   or (2 == run and alive):
-                    y = world_db["Things"][id]["T_POSY"]
-                    x = world_db["Things"][id]["T_POSX"]
-                    fovflag = world_db["Things"][0]["fovmap"][(y * length) + x]
-                    if 'v' == chr(fovflag):
-                        c = world_db["ThingTypes"][type]["TT_SYMBOL"]
-                        map[(y * length) + x] = ord(c)
-
-        def write_map(string, map):
-            for i in range(length):
-                line = map[i * length:(i * length) + length].decode()
-                string = string + line + "\n"
-            return string
-
-        inventory = ""
-        if [] == world_db["Things"][0]["T_CARRIES"]:
-            inventory = "(none)\n"
-        else:
-            for id in world_db["Things"][0]["T_CARRIES"]:
-                type_id = world_db["Things"][id]["T_TYPE"]
-                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"
-        length = world_db["MAP_LENGTH"]
-        fov = bytearray(b' ' * (length ** 2))
-        for pos in range(length ** 2):
-            if 'v' == chr(world_db["Things"][0]["fovmap"][pos]):
-                fov[pos] = world_db["MAP"][pos]
-        for i in range(3):
-            draw_visible_Things(fov, i)
-        string = write_map(string, fov)
-        mem = world_db["Things"][0]["T_MEMMAP"][:]
-        for i in range(2):
-            for mt in world_db["Things"][0]["T_MEMTHING"]:
-                consumable = world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]
-                if (i == 0 and not consumable) or (i == 1 and consumable):
-                    c = world_db["ThingTypes"][mt[0]]["TT_SYMBOL"]
-                    mem[(mt[1] * length) + mt[2]] = ord(c)
-        string = write_map(string, mem)
-        atomic_write(io_db["path_worldstate"], string, delete=False)
-        strong_write(io_db["file_out"], "WORLD_UPDATED\n")
-        io_db["worldstate_updateable"] = False
-
-
-def replay_game():
-    """Replay game from record file.
-
-    Use opts.replay as breakpoint turn to which to replay automatically before
-    switching to manual input by non-meta commands in server input file
-    triggering further reads of record file. Ensure opts.replay is at least 1.
-    Run try_worldstate_update() before each interactive obey()/read_command().
-    """
-    if opts.replay < 1:
-        opts.replay = 1
-    print("Replay mode. Auto-replaying up to turn " + str(opts.replay) +
-          " (if so late a turn is to be found).")
-    if not os.access(io_db["path_record"], os.F_OK):
-        raise SystemExit("No record file found to replay.")
-    io_db["file_record"] = open(io_db["path_record"], "r")
-    io_db["file_record"].prefix = "record file line "
-    io_db["file_record"].line_n = 1
-    while world_db["TURN"] < opts.replay:
-        line = io_db["file_record"].readline()
-        if "" == line:
-            break
-        obey(line.rstrip(), io_db["file_record"].prefix
-             + str(io_db["file_record"].line_n))
-        io_db["file_record"].line_n = io_db["file_record"].line_n + 1
-    while True:
-        try_worldstate_update()
-        obey(read_command(), "in file", replay=True)
-
-
-def play_game():
-    """Play game by server input file commands. Before, load save file found.
-
-    If no save file is found, a new world is generated from the commands in the
-    world config plus a 'MAKE WORLD [current Unix timestamp]'. Record this
-    command and all that follow via the server input file. Run
-    try_worldstate_update() before each interactive obey()/read_command().
-    """
-    if os.access(io_db["path_save"], os.F_OK):
-        obey_lines_in_file(io_db["path_save"], "save")
-    else:
-        if not os.access(io_db["path_worldconf"], os.F_OK):
-            msg = "No world config file from which to start a new world."
-            raise SystemExit(msg)
-        obey_lines_in_file(io_db["path_worldconf"], "world config ",
-                           do_record=True)
-        obey("MAKE_WORLD " + str(int(time.time())), "in file", do_record=True)
-    while True:
-        try_worldstate_update()
-        obey(read_command(), "in file", do_record=True)
-
-
-def remake_map():
-    """(Re-)make island map.
-
-    Let "~" represent water, "." land, "X" trees: Build island shape randomly,
-    start with one land cell in the middle, then go into 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 at the map's
-    border. Then put some trees on the map (TODO: more precise algorithm desc).
-    """
-    def is_neighbor(coordinates, type):
-        y = coordinates[0]
-        x = coordinates[1]
-        length = world_db["MAP_LENGTH"]
-        ind = y % 2
-        diag_west = x + (ind > 0)
-        diag_east = x + (ind < (length - 1))
-        pos = (y * length) + x
-        if (y > 0 and diag_east
-            and type == chr(world_db["MAP"][pos - length + ind])) \
-           or (x < (length - 1)
-               and type == chr(world_db["MAP"][pos + 1])) \
-           or (y < (length - 1) and diag_east
-               and type == chr(world_db["MAP"][pos + length + ind])) \
-           or (y > 0 and diag_west
-               and type == chr(world_db["MAP"][pos - length - (not ind)])) \
-           or (x > 0
-               and type == chr(world_db["MAP"][pos - 1])) \
-           or (y < (length - 1) and diag_west
-               and type == chr(world_db["MAP"][pos + length - (not ind)])):
-            return True
-        return False
-    store_seed = rand.seed
-    rand.seed = world_db["SEED_MAP"]
-    world_db["MAP"] = bytearray(b'~' * (world_db["MAP_LENGTH"] ** 2))
-    length = world_db["MAP_LENGTH"]
-    add_half_width = (not (length % 2)) * int(length / 2)
-    world_db["MAP"][int((length ** 2) / 2) + add_half_width] = ord(".")
-    while (1):
-        y = rand.next() % length
-        x = rand.next() % length
-        pos = (y * length) + x
-        if "~" == chr(world_db["MAP"][pos]) and is_neighbor((y, x), "."):
-            if y == 0 or y == (length - 1) or x == 0 or x == (length - 1):
-                break
-            world_db["MAP"][pos] = ord(".")
-    n_trees = int((length ** 2) / 16)
-    i_trees = 0
-    while (i_trees <= n_trees):
-        single_allowed = rand.next() % 32
-        y = rand.next() % length
-        x = rand.next() % length
-        pos = (y * length) + x
-        if "." == chr(world_db["MAP"][pos]) \
-          and ((not single_allowed) or is_neighbor((y, x), "X")):
-            world_db["MAP"][pos] = ord("X")
-            i_trees += 1
-    rand.seed = store_seed
-    # This all-too-precise replica of the original C code misses iter_limit().
-
-
-def update_map_memory(t, age_map=True):
-    """Update t's T_MEMMAP with what's in its FOV now,age its T_MEMMEPTHMAP."""
-    if not t["T_MEMMAP"]:
-        t["T_MEMMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
-    if not t["T_MEMDEPTHMAP"]:
-        t["T_MEMDEPTHMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
-    ord_v = ord("v")
-    ord_0 = ord("0")
-    ord_space = ord(" ")
-    for pos in [pos for pos in range(world_db["MAP_LENGTH"] ** 2)
-                if ord_v == t["fovmap"][pos]]:
-        t["T_MEMDEPTHMAP"][pos] = ord_0
-        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)
-    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]):
-                t["T_MEMTHING"].append((type, y, x))
-
-
-def set_world_inactive():
-    """Set world_db["WORLD_ACTIVE"] to 0 and remove worldstate file."""
-    server_test()
-    if os.access(io_db["path_worldstate"], os.F_OK):
-        os.remove(io_db["path_worldstate"])
-    world_db["WORLD_ACTIVE"] = 0
-
-
-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):
-            raise ValueError
-        return val
-    except ValueError:
-        msg = "Ignoring: Please use integer >= " + str(min)
-        if max != None:
-            msg += " and <= " + str(max)
-        msg += "."
-        print(msg)
-        return None
-
-
-def setter(category, key, min, max=None):
-    """Build setter for world_db([category + "s"][id])[key] to >=min/<=max."""
-    if category is None:
-        def f(val_string):
-            val = integer_test(val_string, min, max)
-            if None != val:
-                world_db[key] = val
-    else:
-        if category == "Thing":
-            id_store = command_tid
-            decorator = test_Thing_id
-        elif category == "ThingType":
-            id_store = command_ttid
-            decorator = test_ThingType_id
-        elif category == "ThingAction":
-            id_store = command_taid
-            decorator = test_ThingAction_id
-
-        @decorator
-        def f(val_string):
-            val = integer_test(val_string, min, max)
-            if None != val:
-                world_db[category + "s"][id_store.id][key] = val
-    return f
-
-
-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:
-        raise RuntimeError("Malloc error in build_fov_Map().")
-
-
-def decrement_lifepoints(t):
-    """Decrement t's lifepoints by 1, and if to zero, corpse it.
-
-    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.
-    """
-    t["T_LIFEPOINTS"] -= 1
-    if 0 == t["T_LIFEPOINTS"]:
-        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))
-            strong_write(io_db["file_out"], "LOG You die.\n")
-        else:
-            t["fovmap"] = False
-            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):
-    """Wrapper around libpr.mv_yx_in_dir_legal to simplify its use."""
-    dir_c = dir.encode("ascii")[0]
-    test = libpr.mv_yx_in_dir_legal_wrap(dir_c, y, x)
-    if -1 == test:
-        raise RuntimeError("Too much wrapping in mv_yx_in_dir_legal_wrap()!")
-    return (test, libpr.result_y(), libpr.result_x())
-
-
-def actor_wait(t):
-    """Make t do nothing (but loudly, if player avatar)."""
-    if t == world_db["Things"][0]:
-        strong_write(io_db["file_out"], "LOG You wait.\n")
-
-
-def actor_move(t):
-    """If passable, move/collide(=attack) thing into T_ARGUMENT's direction."""
-    passable = False
-    move_result = mv_yx_in_dir_legal(chr(t["T_ARGUMENT"]),
-                                     t["T_POSY"], t["T_POSX"])
-    if 1 == move_result[0]:
-        pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
-        passable = "." == chr(world_db["MAP"][pos])
-        hitted = [id for id in world_db["Things"]
-                  if world_db["Things"][id] != t
-                  if world_db["Things"][id]["T_LIFEPOINTS"]
-                  if world_db["Things"][id]["T_POSY"] == move_result[1]
-                  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")
-            decrement_lifepoints(world_db["Things"][hit_id])
-            return
-    dir = [dir for dir in directions_db
-           if directions_db[dir] == chr(t["T_ARGUMENT"])][0]
-    if passable:
-        t["T_POSY"] = move_result[1]
-        t["T_POSX"] = move_result[2]
-        for id in t["T_CARRIES"]:
-            world_db["Things"][id]["T_POSY"] = move_result[1]
-            world_db["Things"][id]["T_POSX"] = move_result[2]
-        build_fov_map(t)
-        if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You move " + dir + ".\n")
-    elif t == world_db["Things"][0]:
-        strong_write(io_db["file_out"], "LOG You fail to move " + dir + ".\n")
-
-
-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.
-    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
-        for id in ids:
-            if id > highest_id:
-                highest_id = id
-        world_db["Things"][highest_id]["carried"] = True
-        t["T_CARRIES"].append(highest_id)
-        if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You pick up an object.\n")
-    elif t == world_db["Things"][0]:
-        err = "You try to pick up an object, but there is none."
-        strong_write(io_db["file_out"], "LOG " + err + "\n")
-
-
-def actor_drop(t):
-    """Make t rop Thing from inventory to ground indexed by T_ARGUMENT."""
-    # TODO: Handle case where T_ARGUMENT matches nothing.
-    if len(t["T_CARRIES"]):
-        id = t["T_CARRIES"][t["T_ARGUMENT"]]
-        t["T_CARRIES"].remove(id)
-        world_db["Things"][id]["carried"] = False
-        if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You drop an object.\n")
-    elif t == world_db["Things"][0]:
-        err = "You try to drop an object, but you own none."
-        strong_write(io_db["file_out"], "LOG " + err + "\n")
-
-
-def actor_use(t):
-    """Make t use (for now: consume) T_ARGUMENT-indexed Thing in inventory."""
-    # TODO: Handle case where T_ARGUMENT matches nothing.
-    if len(t["T_CARRIES"]):
-        id = t["T_CARRIES"][t["T_ARGUMENT"]]
-        type = world_db["Things"][id]["T_TYPE"]
-        if world_db["ThingTypes"][type]["TT_CONSUMABLE"]:
-            t["T_CARRIES"].remove(id)
-            del world_db["Things"][id]
-            t["T_SATIATION"] += world_db["ThingTypes"][type]["TT_CONSUMABLE"]
-            if t == world_db["Things"][0]:
-                strong_write(io_db["file_out"],
-                             "LOG You consume this object.\n")
-        elif t == world_db["Things"][0]:
-            strong_write(io_db["file_out"],
-                         "LOG You try to use this object, but fail.\n")
-    elif t == world_db["Things"][0]:
-        strong_write(io_db["file_out"],
-                     "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.
-
-    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.
-    """
-    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]):
-                candidates.append((mv_result[1], mv_result[2]))
-        if len(candidates):
-            i = rand.next() % len(candidates)
-            id = id_setter(-1, "Things")
-            newT = new_Thing(t["T_TYPE"], (candidates[i][0], candidates[i][1]))
-            world_db["Things"][id] = newT
-
-
-def try_healing(t):
-    """Grow t's HP to a 1/32 chance if < HP max, satiation > 0, and waiting.
-
-    On success, decrease satiation score by 32.
-    """
-    if t["T_SATIATION"] > 0 \
-       and t["T_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]:
-        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):
-    """Decrement t's satiation,dependent on it trigger lifepoint dec chance."""
-    if t["T_SATIATION"] > -32768:
-        t["T_SATIATION"] -= 1
-    testbase = t["T_SATIATION"] if t["T_SATIATION"] >= 0 else -t["T_SATIATION"]
-    if not world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"]:
-        raise RuntimeError("A thing that should not hunger is hungering.")
-    stomach = int(32767 / world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"])
-    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)
-
-
-def get_dir_to_target(t, filter):
-    """Try to set T_COMMAND/T_ARGUMENT for move to "filter"-determined target.
-
-    The path-wise nearest target is chosen, via the shortest available path.
-    Target must not be t. On succcess, return positive value, else False.
-    Filters:
-    "a": Thing in FOV is below a certain distance, animate, but of ThingType
-         that is not t's, and starts out weaker than t is; build path as
-         avoiding things of t's ThingType
-    "f": neighbor cell (not inhabited by any animate Thing) further away from
-         animate Thing not further than x steps away and in FOV and of a
-         ThingType that is not t's, and starts out stronger or as strong as t
-         is currently; or (cornered), if no such flight cell, but Thing of
-         above criteria is too near,1 a cell closer to it, or, if less near,
-         just wait
-    "c": Thing in memorized map is consumable
-    "s": memory map cell with greatest-reachable degree of unexploredness
-    """
-
-    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:
-            raise RuntimeError("No score map allocated for "
-                               "zero_score_map_where_char_on_memdepthmap().")
-
-    def set_map_score(pos, score):
-        test = libpr.set_map_score(pos, score)
-        if test:
-            raise RuntimeError("No score map allocated for set_map_score().")
-
-    def get_map_score(pos):
-        result = libpr.get_map_score(pos)
-        if result < 0:
-            raise RuntimeError("No score map allocated for get_map_score().")
-        return result
-
-    def seeing_thing():
-        if t["fovmap"] and ("a" == filter or "f" == filter):
-            for id in world_db["Things"]:
-                Thing = world_db["Things"][id]
-                if Thing != t and Thing["T_LIFEPOINTS"] and \
-                   t["T_TYPE"] != Thing["T_TYPE"] and \
-                   'v' == chr(t["fovmap"][(Thing["T_POSY"]
-                                          * world_db["MAP_LENGTH"])
-                                          + Thing["T_POSX"]]):
-                    ThingType = world_db["ThingTypes"][Thing["T_TYPE"]]
-                    if ("f" == filter and ThingType["TT_LIFEPOINTS"] >=
-                                          t["T_LIFEPOINTS"]) \
-                       or ("a" == filter and ThingType["TT_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]]) \
-                   and world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]:
-                    return True
-        return False
-
-    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)
-        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"]
-                if t != Thing and Thing["T_LIFEPOINTS"] and \
-                   t["T_TYPE"] != Thing["T_TYPE"] and \
-                   ord_v == t["fovmap"][pos] and \
-                   t["T_LIFEPOINTS"] > \
-                   world_db["ThingTypes"][Thing["T_TYPE"]]["TT_LIFEPOINTS"]:
-                    set_map_score(pos, 0)
-                elif t["T_TYPE"] == Thing["T_TYPE"]:
-                    set_map_score(pos, 65535)
-        elif "f" == filter:
-            for id in [id for id in world_db["Things"]
-                       if world_db["Things"][id]["T_LIFEPOINTS"]]:
-                Thing = world_db["Things"][id]
-                pos = Thing["T_POSY"] * world_db["MAP_LENGTH"] \
-                      + Thing["T_POSX"]
-                if t["T_TYPE"] != Thing["T_TYPE"] and \
-                   ord_v == t["fovmap"][pos] and \
-                   t["T_LIFEPOINTS"] <= \
-                   world_db["ThingTypes"][Thing["T_TYPE"]]["TT_LIFEPOINTS"]:
-                    set_map_score(pos, 0)
-        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]]
-                       if world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]]:
-                set_map_score(mt[1] * world_db["MAP_LENGTH"] + mt[2], 0)
-        elif "s" == filter:
-            zero_score_map_where_char_on_memdepthmap(mem_depth_c[0])
-
-    def rand_target_dir(neighbors, cmp, dirs):
-        candidates = []
-        n_candidates = 0
-        for i in range(len(dirs)):
-            if cmp == neighbors[i]:
-                candidates.append(dirs[i])
-                n_candidates += 1
-        return candidates[rand.next() % n_candidates] if n_candidates else 0
-
-    def get_neighbor_scores(dirs, eye_pos):
-        scores = []
-        if libpr.ready_neighbor_scores(eye_pos):
-            raise RuntimeError("No score map allocated for " +
-                               "ready_neighbor_scores.()")
-        for i in range(len(dirs)):
-            scores.append(libpr.get_neighbor_score(i))
-        return scores
-
-    def get_dir_from_neighbors():
-        dir_to_target = False
-        dirs = "edcxsw"
-        eye_pos = t["T_POSY"] * world_db["MAP_LENGTH"] + t["T_POSX"]
-        neighbors = get_neighbor_scores(dirs, eye_pos)
-        if "f" == filter:
-            inhabited = [world_db["Things"][id]["T_POSY"]
-                         * world_db["MAP_LENGTH"]
-                         + world_db["Things"][id]["T_POSX"]
-                         for id in world_db["Things"]
-                         if world_db["Things"][id]["T_LIFEPOINTS"]]
-            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()
-                for pos in [pos for pos in inhabited if pos == pos_cmp]:
-                    neighbors[i] = 65535
-                    break
-        minmax_start = 0 if "f" == filter else 65535 - 1
-        minmax_neighbor = minmax_start
-        for i in range(len(dirs)):
-            if ("f" == filter and get_map_score(eye_pos) < neighbors[i] and
-                minmax_neighbor < neighbors[i] and 65535 != neighbors[i]) \
-               or ("f" != filter and minmax_neighbor > neighbors[i]):
-                minmax_neighbor = neighbors[i]
-        if minmax_neighbor != minmax_start:
-            dir_to_target = rand_target_dir(neighbors, minmax_neighbor, dirs)
-        if "f" == filter:
-            if not dir_to_target:
-                if 1 == get_map_score(eye_pos):
-                    dir_to_target = rand_target_dir(neighbors, 0, dirs)
-                elif 3 >= get_map_score(eye_pos):
-                    t["T_COMMAND"] = [id for id in world_db["ThingActions"]
-                                      if
-                                      world_db["ThingActions"][id]["TA_NAME"]
-                                         == "wait"][0]
-                    return 1
-            elif dir_to_target and 3 < get_map_score(eye_pos):
-                dir_to_target = 0
-        elif "a" == filter and 10 <= get_map_score(eye_pos):
-            dir_to_target = 0
-        return dir_to_target
-
-    dir_to_target = False
-    mem_depth_c = b' '
-    run_i = 9 + 1 if "s" == filter else 1
-    while run_i and not dir_to_target and ("s" == filter or seeing_thing()):
-        run_i -= 1
-        init_score_map()
-        mem_depth_c = b'9' if b' ' == mem_depth_c \
-                           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()
-        libpr.free_score_map()
-        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]
-            t["T_ARGUMENT"] = ord(dir_to_target)
-    return dir_to_target
-
-
-def standing_on_consumable(t):
-    """Return True/False whether t is standing on a consumable."""
-    for id in [id for id in world_db["Things"] if world_db["Things"][id] != t
-               if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
-               if world_db["Things"][id]["T_POSX"] == t["T_POSX"]
-               if world_db["ThingTypes"][world_db["Things"][id]["T_TYPE"]]
-                          ["TT_CONSUMABLE"]]:
-        return True
-    return False
-
-
-def get_inventory_slot_to_consume(t):
-    """Return slot Id of strongest consumable in t's inventory, else -1."""
-    cmp_consumability = 0
-    selection = -1
-    i = 0
-    for id in t["T_CARRIES"]:
-        type = world_db["Things"][id]["T_TYPE"]
-        if world_db["ThingTypes"][type]["TT_CONSUMABLE"] > cmp_consumability:
-            cmp_consumability = world_db["ThingTypes"][type]["TT_CONSUMABLE"]
-            selection = i
-        i += 1
-    return selection
-
-
-def ai(t):
-    """Determine next command/argment for actor t via AI algorithms.
-
-    AI will look for, and move towards, enemies (animate Things not of their
-    own ThingType); if they see none, they will consume consumables in their
-    inventory; if there are none, they will pick up what they stand on if they
-    stand on consumables; if they stand on none, they will move towards the
-    next consumable they see or remember on the map; if they see or remember
-    none, they will explore parts of the map unseen since ever or for at least
-    one turn; if there is nothing to explore, they will simply wait.
-    """
-    t["T_COMMAND"] = [id for id in world_db["ThingActions"]
-                      if world_db["ThingActions"][id]["TA_NAME"] == "wait"][0]
-    if not get_dir_to_target(t, "f"):
-        sel = get_inventory_slot_to_consume(t)
-        if -1 != sel:
-            t["T_COMMAND"] = [id for id in world_db["ThingActions"]
-                              if world_db["ThingActions"][id]["TA_NAME"]
-                                 == "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]
-        elif (not get_dir_to_target(t, "c")) and \
-             (not get_dir_to_target(t, "a")):
-            get_dir_to_target(t, "s")
-
-
-def turn_over():
-    """Run game world and its inhabitants until new player input expected."""
-    id = 0
-    whilebreaker = False
-    while world_db["Things"][0]["T_LIFEPOINTS"]:
-        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
-                continue                            # picked up during turn …
-            Thing = world_db["Things"][id]
-            if Thing["T_LIFEPOINTS"]:
-                if not Thing["T_COMMAND"]:
-                    update_map_memory(Thing)
-                    if 0 == id:
-                        whilebreaker = True
-                        break
-                    ai(Thing)
-                try_healing(Thing)
-                Thing["T_PROGRESS"] += 1
-                taid = [a for a in world_db["ThingActions"]
-                          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)
-        if whilebreaker:
-            break
-        world_db["TURN"] += 1
-
-
-def new_Thing(type, pos=(0, 0)):
-    """Return Thing of type T_TYPE, with fovmap if alive and world active."""
-    thing = {
-        "T_LIFEPOINTS": world_db["ThingTypes"][type]["TT_LIFEPOINTS"],
-        "T_ARGUMENT": 0,
-        "T_PROGRESS": 0,
-        "T_SATIATION": 0,
-        "T_COMMAND": 0,
-        "T_TYPE": type,
-        "T_POSY": pos[0],
-        "T_POSX": pos[1],
-        "T_CARRIES": [],
-        "carried": False,
-        "T_MEMTHING": [],
-        "T_MEMMAP": False,
-        "T_MEMDEPTHMAP": False,
-        "fovmap": False
-    }
-    if world_db["WORLD_ACTIVE"] and thing["T_LIFEPOINTS"]:
-        build_fov_map(thing)
-    return thing
-
-
-def id_setter(id, category, id_store=False, start_at_1=False):
-    """Set ID of object of category to manipulate ID unused? Create new one.
-    The ID is stored as id_store.id (if id_store is set). If the integer of the
-    input is valid (if start_at_1, >= 0, else >= -1), but <0 or (if start_at_1)
-    <1, calculate new ID: lowest unused ID >=0 or (if start_at_1) >= 1. None is
-    always returned when no new object is created, else the new object's ID.
-    """
-    min = 0 if start_at_1 else -1
-    if str == type(id):
-        id = integer_test(id, min)
-    if None != id:
-        if id in world_db[category]:
-            if id_store:
-                id_store.id = id
-            return None
-        else:
-            if (start_at_1 and 0 == id) \
-               or ((not start_at_1) and (id < 0)):
-                id = 0 if start_at_1 else -1
-                while 1:
-                    id = id + 1
-                    if id not in world_db[category]:
-                        break
-                    return None
-            if id_store:
-                id_store.id = id
-    return id
-
-
-def command_ping():
-    """Send PONG line to server output file."""
-    strong_write(io_db["file_out"], "PONG\n")
-
-
-def command_quit():
-    """Abort server process."""
-    save_world()
-    atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True)
-    raise SystemExit("received QUIT command")
-
-
-def command_thingshere(str_y, str_x):
-    """Write to out file list of Things known to player at coordinate y, x."""
-    if world_db["WORLD_ACTIVE"]:
-        y = integer_test(str_y, 0, 255)
-        x = integer_test(str_x, 0, 255)
-        length = world_db["MAP_LENGTH"]
-        if None != y and None != x and y < length and x < length:
-            pos = (y * world_db["MAP_LENGTH"]) + x
-            strong_write(io_db["file_out"], "THINGS_HERE START\n")
-            if "v" == chr(world_db["Things"][0]["fovmap"][pos]):
-                for id in world_db["Things"]:
-                    if y == world_db["Things"][id]["T_POSY"] \
-                       and x == world_db["Things"][id]["T_POSX"] \
-                       and not world_db["Things"][id]["carried"]:
-                        type = world_db["Things"][id]["T_TYPE"]
-                        name = world_db["ThingTypes"][type]["TT_NAME"]
-                        strong_write(io_db["file_out"], name + "\n")
-            else:
-                for mt in world_db["Things"][0]["T_MEMTHING"]:
-                    if y == mt[1] and x == mt[2]:
-                        name = world_db["ThingTypes"][mt[0]]["TT_NAME"]
-                        strong_write(io_db["file_out"], name + "\n")
-            strong_write(io_db["file_out"], "THINGS_HERE END\n")
-        else:
-            print("Ignoring: Invalid map coordinates.")
-    else:
-        print("Ignoring: Command only works on existing worlds.")
-
-
-def play_commander(action, args=False):
-    """Setter for player's T_COMMAND and T_ARGUMENT, then calling turn_over().
-
-    T_ARGUMENT is set to direction char if action=="wait",or 8-bit int if args.
-    """
-
-    def set_command():
-        id = [x for x in world_db["ThingActions"]
-                if world_db["ThingActions"][x]["TA_NAME"] == action][0]
-        world_db["Things"][0]["T_COMMAND"] = id
-        turn_over()
-
-    def set_command_and_argument_int(str_arg):
-        val = integer_test(str_arg, 0, 255)
-        if None != val:
-            world_db["Things"][0]["T_ARGUMENT"] = val
-            set_command()
-
-    def set_command_and_argument_movestring(str_arg):
-        if str_arg in directions_db:
-            world_db["Things"][0]["T_ARGUMENT"] = ord(directions_db[str_arg])
-            set_command()
-        else:
-            print("Ignoring: Argument must be valid direction string.")
-
-    if action == "move":
-        return set_command_and_argument_movestring
-    elif args:
-        return set_command_and_argument_int
-    else:
-        return set_command
-
-
-def command_seedrandomness(seed_string):
-    """Set rand seed to int(seed_string)."""
-    val = integer_test(seed_string, 0, 4294967295)
-    if None != val:
-        rand.seed = val
-
-
-def command_seedmap(seed_string):
-    """Set world_db["SEED_MAP"] to int(seed_string), then (re-)make map."""
-    setter(None, "SEED_MAP", 0, 4294967295)(seed_string)
-    remake_map()
-
-
-def command_makeworld(seed_string):
-    """(Re-)build game world, i.e. map, things, to a new turn 1 from seed.
-
-    Seed rand with seed, fill it into world_db["SEED_MAP"]. Do more only with a
-    "wait" ThingAction and world["PLAYER_TYPE"] matching ThingType of
-    TT_START_NUMBER > 0. Then, world_db["Things"] emptied, call remake_map()
-    and set world_db["WORLD_ACTIVE"], world_db["TURN"] to 1. Build new Things
-    according to ThingTypes' TT_START_NUMBERS, with Thing of ID 0 to ThingType
-    of ID = world["PLAYER_TYPE"]. Place Things randomly, and actors not on each
-    other. Init player's memory map. Write "NEW_WORLD" line to out file.
-    """
-
-    def free_pos():
-        i = 0
-        while 1:
-            err = "Space to put thing on too hard to find. Map too small?"
-            while 1:
-                y = rand.next() % world_db["MAP_LENGTH"]
-                x = rand.next() % world_db["MAP_LENGTH"]
-                if "." == chr(world_db["MAP"][y * world_db["MAP_LENGTH"] + x]):
-                    break
-                i += 1
-                if i == 65535:
-                    raise SystemExit(err)
-            # Replica of C code, wrongly ignores animatedness of new Thing.
-            pos_clear = (0 == len([id for id in world_db["Things"]
-                                   if world_db["Things"][id]["T_LIFEPOINTS"]
-                                   if world_db["Things"][id]["T_POSY"] == y
-                                   if world_db["Things"][id]["T_POSX"] == x]))
-            if pos_clear:
-                break
-        return (y, x)
-
-    val = integer_test(seed_string, 0, 4294967295)
-    if None == val:
-        return
-    rand.seed = val
-    world_db["SEED_MAP"] = val
-    player_will_be_generated = False
-    playertype = world_db["PLAYER_TYPE"]
-    for ThingType in world_db["ThingTypes"]:
-        if playertype == ThingType:
-            if 0 < world_db["ThingTypes"][ThingType]["TT_START_NUMBER"]:
-                player_will_be_generated = True
-            break
-    if not player_will_be_generated:
-        print("Ignoring beyond SEED_MAP: " +
-              "No player type with start number >0 defined.")
-        return
-    wait_action = False
-    for ThingAction in world_db["ThingActions"]:
-        if "wait" == world_db["ThingActions"][ThingAction]["TA_NAME"]:
-            wait_action = True
-    if not wait_action:
-        print("Ignoring beyond SEED_MAP: " +
-              "No thing action with name 'wait' defined.")
-        return
-    world_db["Things"] = {}
-    remake_map()
-    world_db["WORLD_ACTIVE"] = 1
-    world_db["TURN"] = 1
-    for i in range(world_db["ThingTypes"][playertype]["TT_START_NUMBER"]):
-        id = id_setter(-1, "Things")
-        world_db["Things"][id] = new_Thing(playertype, free_pos())
-    update_map_memory(world_db["Things"][0])
-    for type in world_db["ThingTypes"]:
-        for i in range(world_db["ThingTypes"][type]["TT_START_NUMBER"]):
-            if type != playertype:
-                id = id_setter(-1, "Things")
-                world_db["Things"][id] = new_Thing(type, free_pos())
-    strong_write(io_db["file_out"], "NEW_WORLD\n")
-
-
-def command_maplength(maplength_string):
-    """Redefine map length. Invalidate map, therefore lose all things on it."""
-    val = integer_test(maplength_string, 1, 256)
-    if None != val:
-        world_db["MAP_LENGTH"] = val
-        set_world_inactive()
-        world_db["Things"] = {}
-        libpr.set_maplength(val)
-
-
-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.
-    """
-    # In original version, map existence was also tested (unnecessarily?).
-    val = integer_test(worldactive_string, 0, 1)
-    if val:
-        if 0 != world_db["WORLD_ACTIVE"]:
-            if 0 == val:
-                set_world_inactive()
-            else:
-                print("World already active.")
-        elif 0 == world_db["WORLD_ACTIVE"]:
-            wait_exists = False
-            for ThingAction in world_db["ThingActions"]:
-                if "wait" == world_db["ThingActions"][ThingAction]["TA_NAME"]:
-                    wait_exists = True
-                    break
-            player_exists = False
-            for Thing in world_db["Things"]:
-                if 0 == Thing:
-                    player_exists = True
-                    break
-            if wait_exists and player_exists:
-                for id in world_db["Things"]:
-                    if world_db["Things"][id]["T_LIFEPOINTS"]:
-                        build_fov_map(world_db["Things"][id])
-                        if 0 == id:
-                            update_map_memory(world_db["Things"][id], False)
-                world_db["WORLD_ACTIVE"] = 1
-
-
-def test_for_id_maker(object, category):
-    """Return decorator testing for object having "id" attribute."""
-    def decorator(f):
-        def helper(*args):
-            if hasattr(object, "id"):
-                f(*args)
-            else:
-                print("Ignoring: No " + category +
-                      " defined to manipulate yet.")
-        return helper
-    return decorator
-
-
-def command_tid(id_string):
-    """Set ID of Thing to manipulate. ID unused? Create new one.
-
-    Default new Thing's type to the first available ThingType, others: zero.
-    """
-    id = id_setter(id_string, "Things", command_tid)
-    if None != id:
-        if world_db["ThingTypes"] == {}:
-            print("Ignoring: No ThingType to settle new Thing in.")
-            return
-        type = list(world_db["ThingTypes"].keys())[0]
-        world_db["Things"][id] = new_Thing(type)
-
-
-test_Thing_id = test_for_id_maker(command_tid, "Thing")
-
-
-@test_Thing_id
-def command_tcommand(str_int):
-    """Set T_COMMAND of selected Thing."""
-    val = integer_test(str_int, 0)
-    if None != val:
-        if 0 == val or val in world_db["ThingActions"]:
-            world_db["Things"][command_tid.id]["T_COMMAND"] = val
-        else:
-            print("Ignoring: ThingAction ID belongs to no known ThingAction.")
-
-
-@test_Thing_id
-def command_ttype(str_int):
-    """Set T_TYPE of selected Thing."""
-    val = integer_test(str_int, 0)
-    if None != val:
-        if val in world_db["ThingTypes"]:
-            world_db["Things"][command_tid.id]["T_TYPE"] = val
-        else:
-            print("Ignoring: ThingType ID belongs to no known ThingType.")
-
-
-@test_Thing_id
-def command_tcarries(str_int):
-    """Append int(str_int) to T_CARRIES of selected Thing.
-
-    The ID int(str_int) must not be of the selected Thing, and must belong to a
-    Thing with unset "carried" flag. Its "carried" flag will be set on owning.
-    """
-    val = integer_test(str_int, 0)
-    if None != val:
-        if val == command_tid.id:
-            print("Ignoring: Thing cannot carry itself.")
-        elif val in world_db["Things"] \
-             and not world_db["Things"][val]["carried"]:
-            world_db["Things"][command_tid.id]["T_CARRIES"].append(val)
-            world_db["Things"][val]["carried"] = True
-        else:
-            print("Ignoring: Thing not available for carrying.")
-    # Note that the whole carrying structure is different from the C version:
-    # Carried-ness is marked by a "carried" flag, not by Things containing
-    # Things internally.
-
-
-@test_Thing_id
-def command_tmemthing(str_t, str_y, str_x):
-    """Add (int(str_t), int(str_y), int(str_x)) to selected Thing's T_MEMTHING.
-
-    The type must fit to an existing ThingType, and the position into the map.
-    """
-    type = integer_test(str_t, 0)
-    posy = integer_test(str_y, 0, 255)
-    posx = integer_test(str_x, 0, 255)
-    if None != type and None != posy and None != posx:
-        if type not in world_db["ThingTypes"] \
-           or posy >= world_db["MAP_LENGTH"] or posx >= world_db["MAP_LENGTH"]:
-            print("Ignoring: Illegal value for thing type or position.")
-        else:
-            memthing = (type, posy, posx)
-            world_db["Things"][command_tid.id]["T_MEMTHING"].append(memthing)
-
-
-def setter_map(maptype):
-    """Set selected Thing's map of maptype's int(str_int)-th line to mapline.
-
-    If Thing has no map of maptype yet, initialize it with ' ' bytes first.
-    """
-    @test_Thing_id
-    def helper(str_int, mapline):
-        val = integer_test(str_int, 0, 255)
-        if None != val:
-            if val >= world_db["MAP_LENGTH"]:
-                print("Illegal value for map line number.")
-            elif len(mapline) != world_db["MAP_LENGTH"]:
-                print("Map line length is unequal map width.")
-            else:
-                length = world_db["MAP_LENGTH"]
-                map = None
-                if not world_db["Things"][command_tid.id][maptype]:
-                    map = bytearray(b' ' * (length ** 2))
-                else:
-                    map = world_db["Things"][command_tid.id][maptype]
-                map[val * length:(val * length) + length] = mapline.encode()
-                world_db["Things"][command_tid.id][maptype] = map
-    return helper
-
-
-def setter_tpos(axis):
-    """Generate setter for T_POSX or  T_POSY of selected Thing.
-
-    If world is active, rebuilds animate things' fovmap, player's memory map.
-    """
-    @test_Thing_id
-    def helper(str_int):
-        val = integer_test(str_int, 0, 255)
-        if None != val:
-            if val < world_db["MAP_LENGTH"]:
-                world_db["Things"][command_tid.id]["T_POS" + axis] = val
-                if world_db["WORLD_ACTIVE"] \
-                   and world_db["Things"][command_tid.id]["T_LIFEPOINTS"]:
-                    build_fov_map(world_db["Things"][command_tid.id])
-                    if 0 == command_tid.id:
-                        update_map_memory(world_db["Things"][command_tid.id])
-            else:
-                print("Ignoring: Position is outside of map.")
-    return helper
-
-
-def command_ttid(id_string):
-    """Set ID of ThingType to manipulate. ID unused? Create new one.
-
-    Default new ThingType's TT_SYMBOL to "?", TT_CORPSE_ID to self, others: 0.
-    """
-    id = id_setter(id_string, "ThingTypes", command_ttid)
-    if None != id:
-        world_db["ThingTypes"][id] = {
-            "TT_NAME": "(none)",
-            "TT_CONSUMABLE": 0,
-            "TT_LIFEPOINTS": 0,
-            "TT_PROLIFERATE": 0,
-            "TT_START_NUMBER": 0,
-            "TT_SYMBOL": "?",
-            "TT_CORPSE_ID": id
-        }
-
-
-test_ThingType_id = test_for_id_maker(command_ttid, "ThingType")
-
-
-@test_ThingType_id
-def command_ttname(name):
-    """Set TT_NAME of selected ThingType."""
-    world_db["ThingTypes"][command_ttid.id]["TT_NAME"] = name
-
-
-@test_ThingType_id
-def command_ttsymbol(char):
-    """Set TT_SYMBOL of selected ThingType. """
-    if 1 == len(char):
-        world_db["ThingTypes"][command_ttid.id]["TT_SYMBOL"] = char
-    else:
-        print("Ignoring: Argument must be single character.")
-
-
-@test_ThingType_id
-def command_ttcorpseid(str_int):
-    """Set TT_CORPSE_ID of selected ThingType."""
-    val = integer_test(str_int, 0)
-    if None != val:
-        if val in world_db["ThingTypes"]:
-            world_db["ThingTypes"][command_ttid.id]["TT_CORPSE_ID"] = val
-        else:
-            print("Ignoring: Corpse ID belongs to no known ThignType.")
-
-
-def command_taid(id_string):
-    """Set ID of ThingAction to manipulate. ID unused? Create new one.
-
-    Default new ThingAction's TA_EFFORT to 1, its TA_NAME to "wait".
-    """
-    id = id_setter(id_string, "ThingActions", command_taid, True)
-    if None != id:
-        world_db["ThingActions"][id] = {
-            "TA_EFFORT": 1,
-            "TA_NAME": "wait"
-        }
-
-
-test_ThingAction_id = test_for_id_maker(command_taid, "ThingAction")
-
-
-@test_ThingAction_id
-def command_taname(name):
-    """Set TA_NAME of selected ThingAction.
-
-    The name must match a valid thing action function. If after the name
-    setting no ThingAction with name "wait" remains, call set_world_inactive().
-    """
-    if name == "wait" or name == "move" or name == "use" or name == "drop" \
-       or name == "pick_up":
-        world_db["ThingActions"][command_taid.id]["TA_NAME"] = name
-        if 1 == world_db["WORLD_ACTIVE"]:
-            wait_defined = False
-            for id in world_db["ThingActions"]:
-                if "wait" == world_db["ThingActions"][id]["TA_NAME"]:
-                    wait_defined = True
-                    break
-            if not wait_defined:
-                set_world_inactive()
-    else:
-        print("Ignoring: Invalid action name.")
-    # In contrast to the original,naming won't map a function to a ThingAction.
-
-
-def command_ai():
-    """Call ai() on player Thing, then turn_over()."""
-    ai(world_db["Things"][0])
-    turn_over()
-
-
-"""Commands database.
-
-Map command start tokens to ([0]) number of expected command arguments, ([1])
-the command's meta-ness (i.e. is it to be written to the record file, is it to
-be ignored in replay mode if read from server input file), and ([2]) a function
-to be called on it.
-"""
-commands_db = {
-    "QUIT": (0, True, command_quit),
-    "PING": (0, True, command_ping),
-    "THINGS_HERE": (2, True, command_thingshere),
-    "MAKE_WORLD": (1, False, command_makeworld),
-    "SEED_MAP": (1, False, command_seedmap),
-    "SEED_RANDOMNESS": (1, False, command_seedrandomness),
-    "TURN": (1, False, setter(None, "TURN", 0, 65535)),
-    "PLAYER_TYPE": (1, False, setter(None, "PLAYER_TYPE", 0)),
-    "MAP_LENGTH": (1, False, command_maplength),
-    "WORLD_ACTIVE": (1, False, command_worldactive),
-    "TA_ID": (1, False, command_taid),
-    "TA_EFFORT": (1, False, setter("ThingAction", "TA_EFFORT", 0, 255)),
-    "TA_NAME": (1, False, command_taname),
-    "TT_ID": (1, False, command_ttid),
-    "TT_NAME": (1, False, command_ttname),
-    "TT_SYMBOL": (1, False, command_ttsymbol),
-    "TT_CORPSE_ID": (1, False, command_ttcorpseid),
-    "TT_CONSUMABLE": (1, False, setter("ThingType", "TT_CONSUMABLE",
-                                       0, 65535)),
-    "TT_START_NUMBER": (1, False, setter("ThingType", "TT_START_NUMBER",
-                                         0, 255)),
-    "TT_PROLIFERATE": (1, False, setter("ThingType", "TT_PROLIFERATE",
-                                        0, 255)),
-    "TT_LIFEPOINTS": (1, False, setter("ThingType", "TT_LIFEPOINTS", 0, 255)),
-    "T_ID": (1, False, command_tid),
-    "T_ARGUMENT": (1, False, setter("Thing", "T_ARGUMENT", 0, 255)),
-    "T_PROGRESS": (1, False, setter("Thing", "T_PROGRESS", 0, 255)),
-    "T_LIFEPOINTS": (1, False, setter("Thing", "T_LIFEPOINTS", 0, 255)),
-    "T_SATIATION": (1, False, setter("Thing", "T_SATIATION", -32768, 32767)),
-    "T_COMMAND": (1, False, command_tcommand),
-    "T_TYPE": (1, False, command_ttype),
-    "T_CARRIES": (1, False, command_tcarries),
-    "T_MEMMAP": (2, False, setter_map("T_MEMMAP")),
-    "T_MEMDEPTHMAP": (2, False, setter_map("T_MEMDEPTHMAP")),
-    "T_MEMTHING": (3, False, command_tmemthing),
-    "T_POSY": (1, False, setter_tpos("Y")),
-    "T_POSX": (1, False, setter_tpos("X")),
-    "wait": (0, False, play_commander("wait")),
-    "move": (1, False, play_commander("move")),
-    "pick_up": (0, False, play_commander("pick_up")),
-    "drop": (1, False, play_commander("drop", True)),
-    "use": (1, False, play_commander("use", True)),
-    "ai": (0, False, command_ai)
-}
-
-
-"""World state database. With sane default values. (Randomness is in rand.)"""
-world_db = {
-    "TURN": 0,
-    "MAP_LENGTH": 64,
-    "SEED_MAP": 0,
-    "PLAYER_TYPE": 0,
-    "WORLD_ACTIVE": 0,
-    "ThingActions": {},
-    "ThingTypes": {},
-    "Things": {}
-}
-
-"""Mapping of direction names to internal direction chars."""
-directions_db = {"east": "d", "south-east": "c", "south-west": "x",
-                 "west": "s", "north-west": "w", "north-east": "e"}
-
-"""File IO database."""
-io_db = {
-    "path_save": "save",
-    "path_record": "record_save",
-    "path_worldconf": "confserver/world",
-    "path_server": "server/",
-    "path_in": "server/in",
-    "path_out": "server/out",
-    "path_worldstate": "server/worldstate",
-    "tmp_suffix": "_tmp",
-    "kicked_by_rival": False,
-    "worldstate_updateable": False
-}
-
-
-try:
-    libpr = prep_library()
-    rand = RandomnessIO()
-    opts = parse_command_line_arguments()
-    if opts.savefile:
-        io_db["path_save"] = opts.savefile
-        io_db["path_record"] = "record_" + opts.savefile
-    setup_server_io()
-    if opts.verbose:
-        io_db["verbose"] = True
-    if None != opts.replay:
-        replay_game()
-    else:
-        play_game()
-except SystemExit as exit:
-    print("ABORTING: " + exit.args[0])
-except:
-    print("SOMETHING WENT WRONG IN UNEXPECTED WAYS")
-    raise
-finally:
-    cleanup_server_io()
diff --git a/redo b/redo
index 8f459fbdd6e2ac9f85e9d567c77975165a1bcb47..4081117268dee04d1df9a08acd01f043d71bf0fd 100755 (executable)
--- a/redo
+++ b/redo
@@ -18,9 +18,5 @@
 # <https://github.com/plomlompom/plomrogue/issues/2#issuecomment-50972436> for a
 # workaround.
 
 # <https://github.com/plomlompom/plomrogue/issues/2#issuecomment-50972436> for a
 # workaround.
 
-echo "non-redo stuff (preparing for future Python port sans redo needs):"
-echo "Building library for server's Python variant with mere shell one-liner."
-./compile-server.sh
-
 export PATH=$PATH:$PWD/build/redo_scripts
 redo "$@"
 export PATH=$PATH:$PWD/build/redo_scripts
 redo "$@"
index a0be8cc77f2e27f15d4e98b28ff8f8a9b9f502fe..b00272187288eed4be40d1bb8df26872623ae831 100755 (executable)
--- a/roguelike
+++ b/roguelike
@@ -2,7 +2,10 @@
 
 # Wrapper to the script so that its suppressed server messages get read on exit.
 ./start_server_client_union.sh "$@"
 
 # Wrapper to the script so that its suppressed server messages get read on exit.
 ./start_server_client_union.sh "$@"
+
+# For some reason, mere sync won't ensure a log is written out, so wait a while.
 sync
 sync
+sleep 0.5
 if [ -e ./log ]
 then
     cat log
 if [ -e ./log ]
 then
     cat log
diff --git a/roguelike-server b/roguelike-server
new file mode 100755 (executable)
index 0000000..ef3fdbc
--- /dev/null
@@ -0,0 +1,1629 @@
+#!/usr/bin/python3
+
+# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
+# or any later version. For details on its copyright, license, and warranties,
+# see the file NOTICE in the root directory of the PlomRogue source package.
+
+
+import argparse
+import errno
+import os
+import shlex
+import shutil
+import time
+import ctypes
+
+
+class RandomnessIO:
+    """"Interface to libplomrogue's pseudo-randomness generator."""
+
+    def set_seed(self, seed):
+        libpr.seed_rrand(1, seed)
+
+    def get_seed(self):
+        return libpr.seed_rrand(0, 0)
+
+    def next(self):
+        return libpr.rrand()
+
+    seed = property(get_seed, set_seed)
+
+
+def prep_library():
+    """Prepare ctypes library at ./libplomrogue.so"""
+    libpath = ("./libplomrogue.so")
+    if not os.access(libpath, os.F_OK):
+        raise SystemExit("No library " + libpath +
+                         ", run ./compile-server.sh first?")
+    libpr = ctypes.cdll.LoadLibrary(libpath)
+    libpr.seed_rrand.restype = ctypes.c_uint32
+    return libpr
+
+
+def strong_write(file, string):
+    """Apply write(string), then flush()."""
+    file.write(string)
+    file.flush()
+
+
+def setup_server_io():
+    """Fill IO files DB with proper file( path)s. Write process IO test string.
+
+    Ensure IO files directory at server/. Remove any old input file if found.
+    Set up new input file for reading, and new output file for writing. Start
+    output file with process hash line of format PID + " " + floated UNIX time
+    (io_db["teststring"]). Raise SystemExit if file is found at path of either
+    record or save file plus io_db["tmp_suffix"].
+    """
+    def detect_atomic_leftover(path, tmp_suffix):
+        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."
+        if os.access(path_tmp, os.F_OK):
+            raise SystemExit(msg)
+    io_db["teststring"] = str(os.getpid()) + " " + str(time.time())
+    io_db["save_wait"] = 0
+    io_db["verbose"] = False
+    io_db["record_chunk"] = ""
+    os.makedirs(io_db["path_server"], exist_ok=True)
+    io_db["file_out"] = open(io_db["path_out"], "w")
+    strong_write(io_db["file_out"], io_db["teststring"] + "\n")
+    if os.access(io_db["path_in"], os.F_OK):
+        os.remove(io_db["path_in"])
+    io_db["file_in"] = open(io_db["path_in"], "w")
+    io_db["file_in"].close()
+    io_db["file_in"] = open(io_db["path_in"], "r")
+    detect_atomic_leftover(io_db["path_save"], io_db["tmp_suffix"])
+    detect_atomic_leftover(io_db["path_record"], io_db["tmp_suffix"])
+
+
+def cleanup_server_io():
+    """Close and (if io_db["kicked_by_rival"] false) remove files in io_db."""
+    def helper(file_key, path_key):
+        if file_key in io_db:
+            io_db[file_key].close()
+        if not io_db["kicked_by_rival"] \
+           and os.access(io_db[path_key], os.F_OK):
+            os.remove(io_db[path_key])
+    helper("file_in", "path_in")
+    helper("file_out", "path_out")
+    helper("file_worldstate", "path_worldstate")
+    if "file_record" in io_db:
+        io_db["file_record"].close()
+
+
+def obey(command, prefix, replay=False, do_record=False):
+    """Call function from commands_db mapped to command's first token.
+
+    Tokenize command string with shlex.split(comments=True). If replay is set,
+    a non-meta command from the commands_db merely triggers obey() on the next
+    command from the records file. If not, non-meta commands set
+    io_db["worldstate_updateable"] to world_db["WORLD_ACTIVE"], and, if
+    do_record is set, are recorded to io_db["record_chunk"], and save_world()
+    is called (and io_db["record_chunk"] written) if 15 seconds have passed
+    since the last time it was called. The prefix string is inserted into the
+    server's input message between its beginning 'input ' and ':'. All activity
+    is preceded by a server_test() call.
+    """
+    server_test()
+    if io_db["verbose"]:
+        print("input " + prefix + ": " + command)
+    try:
+        tokens = shlex.split(command, comments=True)
+    except ValueError as err:
+        print("Can't tokenize command string: " + str(err) + ".")
+        return
+    if len(tokens) > 0 and tokens[0] in commands_db \
+       and len(tokens) == commands_db[tokens[0]][0] + 1:
+        if commands_db[tokens[0]][1]:
+            commands_db[tokens[0]][2](*tokens[1:])
+        elif replay:
+            print("Due to replay mode, reading command as 'go on in record'.")
+            line = io_db["file_record"].readline()
+            if len(line) > 0:
+                obey(line.rstrip(), io_db["file_record"].prefix
+                     + str(io_db["file_record"].line_n))
+                io_db["file_record"].line_n = io_db["file_record"].line_n + 1
+            else:
+                print("Reached end of record file.")
+        else:
+            commands_db[tokens[0]][2](*tokens[1:])
+            if do_record:
+                io_db["record_chunk"] += command + "\n"
+                if time.time() > io_db["save_wait"] + 15:
+                    atomic_write(io_db["path_record"], io_db["record_chunk"],
+                                 do_append=True)
+                    save_world()
+                    io_db["record_chunk"] = ""
+                    io_db["save_wait"] = time.time()
+            io_db["worldstate_updateable"] = world_db["WORLD_ACTIVE"]
+    elif 0 != len(tokens):
+        print("Invalid command/argument, or bad number of tokens.")
+
+
+def atomic_write(path, text, do_append=False, delete=True):
+    """Atomic write of text to file at path, appended if do_append is set."""
+    path_tmp = path + io_db["tmp_suffix"]
+    mode = "w"
+    if do_append:
+        mode = "a"
+        if os.access(path, os.F_OK):
+            shutil.copyfile(path, path_tmp)
+    file = open(path_tmp, mode)
+    strong_write(file, text)
+    file.close()
+    if delete and os.access(path, os.F_OK):
+        os.remove(path)
+    os.rename(path_tmp, path)
+
+
+def save_world():
+    """Save all commands needed to reconstruct current world state."""
+
+    def quote(string):
+        string = string.replace("\u005C", '\u005C\u005C')
+        return '"' + string.replace('"', '\u005C"') + '"'
+
+    def mapsetter(key):
+        def helper(id):
+            string = ""
+            if world_db["Things"][id][key]:
+                map = world_db["Things"][id][key]
+                length = world_db["MAP_LENGTH"]
+                for i in range(length):
+                    line = map[i * length:(i * length) + length].decode()
+                    string = string + key + " " + str(i) + " " + quote(line) \
+                             + "\n"
+            return string
+        return helper
+
+    def memthing(id):
+        string = ""
+        for memthing in world_db["Things"][id]["T_MEMTHING"]:
+            string = string + "T_MEMTHING " + str(memthing[0]) + " " + \
+                     str(memthing[1]) + " " + str(memthing[2]) + "\n"
+        return string
+
+    def helper(category, id_string, special_keys={}):
+        string = ""
+        for id in sorted(world_db[category].keys()):
+            string = string + id_string + " " + str(id) + "\n"
+            for key in sorted(world_db[category][id].keys()):
+                if not key in special_keys:
+                    x = world_db[category][id][key]
+                    argument = quote(x) if str == type(x) else str(x)
+                    string = string + key + " " + argument + "\n"
+                elif special_keys[key]:
+                    string = string + special_keys[key](id)
+        return string
+
+    string = ""
+    for key in sorted(world_db.keys()):
+        if dict != type(world_db[key]) 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"
+    string = string + helper("ThingActions", "TA_ID")
+    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"
+    string = string + helper("Things", "T_ID",
+                             {"T_CARRIES": False, "carried": False,
+                              "T_MEMMAP": mapsetter("T_MEMMAP"),
+                              "T_MEMTHING": memthing, "fovmap": False,
+                              "T_MEMDEPTHMAP": mapsetter("T_MEMDEPTHMAP")})
+    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()):
+                string = string + "T_CARRIES " + str(carried) + "\n"
+    string = string + "SEED_RANDOMNESS " + str(rand.seed) + "\n" + \
+             "WORLD_ACTIVE " + str(world_db["WORLD_ACTIVE"])
+    atomic_write(io_db["path_save"], string)
+
+
+def obey_lines_in_file(path, name, do_record=False):
+    """Call obey() on each line of path's file, use name in input prefix."""
+    file = open(path, "r")
+    line_n = 1
+    for line in file.readlines():
+        obey(line.rstrip(), name + "file line " + str(line_n),
+             do_record=do_record)
+        line_n = line_n + 1
+    file.close()
+
+
+def parse_command_line_arguments():
+    """Return settings values read from command line arguments."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-s', nargs='?', type=int, dest='replay', const=1,
+                        action='store')
+    parser.add_argument('-l', nargs="?", const="save", dest='savefile',
+                        action="store")
+    parser.add_argument('-v', dest='verbose', action='store_true')
+    opts, unknown = parser.parse_known_args()
+    return opts
+
+
+def server_test():
+    """Ensure valid server out file belonging to current process.
+
+    This is done by comparing io_db["teststring"] to what's found at the start
+    of the current file at io_db["path_out"]. On failure, set
+    io_db["kicked_by_rival"] and raise SystemExit.
+    """
+    if not os.access(io_db["path_out"], os.F_OK):
+        raise SystemExit("Server output file has disappeared.")
+    file = open(io_db["path_out"], "r")
+    test = file.readline().rstrip("\n")
+    file.close()
+    if test != io_db["teststring"]:
+        io_db["kicked_by_rival"] = True
+        msg = "Server test string in server output file does not match. This" \
+              " indicates that the current server process has been " \
+              "superseded by another one."
+        raise SystemExit(msg)
+
+
+def read_command():
+    """Return next newline-delimited command from server in file.
+
+    Keep building return string until a newline is encountered. Pause between
+    unsuccessful reads, and after too much waiting, run server_test().
+    """
+    wait_on_fail = 0.03333
+    max_wait = 5
+    now = time.time()
+    command = ""
+    while True:
+        add = io_db["file_in"].readline()
+        if len(add) > 0:
+            command = command + add
+            if len(command) > 0 and "\n" == command[-1]:
+                command = command[:-1]
+                break
+        else:
+            time.sleep(wait_on_fail)
+            if now + max_wait < time.time():
+                server_test()
+                now = time.time()
+    return command
+
+
+def try_worldstate_update():
+    """Write worldstate file if io_db["worldstate_updateable"] is set."""
+    if io_db["worldstate_updateable"]:
+
+        def draw_visible_Things(map, run):
+            for id in world_db["Things"]:
+                type = world_db["Things"][id]["T_TYPE"]
+                consumable = world_db["ThingTypes"][type]["TT_CONSUMABLE"]
+                alive = world_db["ThingTypes"][type]["TT_LIFEPOINTS"]
+                if (0 == run and not consumable and not alive) \
+                   or (1 == run and consumable and not alive) \
+                   or (2 == run and alive):
+                    y = world_db["Things"][id]["T_POSY"]
+                    x = world_db["Things"][id]["T_POSX"]
+                    fovflag = world_db["Things"][0]["fovmap"][(y * length) + x]
+                    if 'v' == chr(fovflag):
+                        c = world_db["ThingTypes"][type]["TT_SYMBOL"]
+                        map[(y * length) + x] = ord(c)
+
+        def write_map(string, map):
+            for i in range(length):
+                line = map[i * length:(i * length) + length].decode()
+                string = string + line + "\n"
+            return string
+
+        inventory = ""
+        if [] == world_db["Things"][0]["T_CARRIES"]:
+            inventory = "(none)\n"
+        else:
+            for id in world_db["Things"][0]["T_CARRIES"]:
+                type_id = world_db["Things"][id]["T_TYPE"]
+                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"
+        length = world_db["MAP_LENGTH"]
+        fov = bytearray(b' ' * (length ** 2))
+        for pos in range(length ** 2):
+            if 'v' == chr(world_db["Things"][0]["fovmap"][pos]):
+                fov[pos] = world_db["MAP"][pos]
+        for i in range(3):
+            draw_visible_Things(fov, i)
+        string = write_map(string, fov)
+        mem = world_db["Things"][0]["T_MEMMAP"][:]
+        for i in range(2):
+            for mt in world_db["Things"][0]["T_MEMTHING"]:
+                consumable = world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]
+                if (i == 0 and not consumable) or (i == 1 and consumable):
+                    c = world_db["ThingTypes"][mt[0]]["TT_SYMBOL"]
+                    mem[(mt[1] * length) + mt[2]] = ord(c)
+        string = write_map(string, mem)
+        atomic_write(io_db["path_worldstate"], string, delete=False)
+        strong_write(io_db["file_out"], "WORLD_UPDATED\n")
+        io_db["worldstate_updateable"] = False
+
+
+def replay_game():
+    """Replay game from record file.
+
+    Use opts.replay as breakpoint turn to which to replay automatically before
+    switching to manual input by non-meta commands in server input file
+    triggering further reads of record file. Ensure opts.replay is at least 1.
+    Run try_worldstate_update() before each interactive obey()/read_command().
+    """
+    if opts.replay < 1:
+        opts.replay = 1
+    print("Replay mode. Auto-replaying up to turn " + str(opts.replay) +
+          " (if so late a turn is to be found).")
+    if not os.access(io_db["path_record"], os.F_OK):
+        raise SystemExit("No record file found to replay.")
+    io_db["file_record"] = open(io_db["path_record"], "r")
+    io_db["file_record"].prefix = "record file line "
+    io_db["file_record"].line_n = 1
+    while world_db["TURN"] < opts.replay:
+        line = io_db["file_record"].readline()
+        if "" == line:
+            break
+        obey(line.rstrip(), io_db["file_record"].prefix
+             + str(io_db["file_record"].line_n))
+        io_db["file_record"].line_n = io_db["file_record"].line_n + 1
+    while True:
+        try_worldstate_update()
+        obey(read_command(), "in file", replay=True)
+
+
+def play_game():
+    """Play game by server input file commands. Before, load save file found.
+
+    If no save file is found, a new world is generated from the commands in the
+    world config plus a 'MAKE WORLD [current Unix timestamp]'. Record this
+    command and all that follow via the server input file. Run
+    try_worldstate_update() before each interactive obey()/read_command().
+    """
+    if os.access(io_db["path_save"], os.F_OK):
+        obey_lines_in_file(io_db["path_save"], "save")
+    else:
+        if not os.access(io_db["path_worldconf"], os.F_OK):
+            msg = "No world config file from which to start a new world."
+            raise SystemExit(msg)
+        obey_lines_in_file(io_db["path_worldconf"], "world config ",
+                           do_record=True)
+        obey("MAKE_WORLD " + str(int(time.time())), "in file", do_record=True)
+    while True:
+        try_worldstate_update()
+        obey(read_command(), "in file", do_record=True)
+
+
+def remake_map():
+    """(Re-)make island map.
+
+    Let "~" represent water, "." land, "X" trees: Build island shape randomly,
+    start with one land cell in the middle, then go into 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 at the map's
+    border. Then put some trees on the map (TODO: more precise algorithm desc).
+    """
+    def is_neighbor(coordinates, type):
+        y = coordinates[0]
+        x = coordinates[1]
+        length = world_db["MAP_LENGTH"]
+        ind = y % 2
+        diag_west = x + (ind > 0)
+        diag_east = x + (ind < (length - 1))
+        pos = (y * length) + x
+        if (y > 0 and diag_east
+            and type == chr(world_db["MAP"][pos - length + ind])) \
+           or (x < (length - 1)
+               and type == chr(world_db["MAP"][pos + 1])) \
+           or (y < (length - 1) and diag_east
+               and type == chr(world_db["MAP"][pos + length + ind])) \
+           or (y > 0 and diag_west
+               and type == chr(world_db["MAP"][pos - length - (not ind)])) \
+           or (x > 0
+               and type == chr(world_db["MAP"][pos - 1])) \
+           or (y < (length - 1) and diag_west
+               and type == chr(world_db["MAP"][pos + length - (not ind)])):
+            return True
+        return False
+    store_seed = rand.seed
+    rand.seed = world_db["SEED_MAP"]
+    world_db["MAP"] = bytearray(b'~' * (world_db["MAP_LENGTH"] ** 2))
+    length = world_db["MAP_LENGTH"]
+    add_half_width = (not (length % 2)) * int(length / 2)
+    world_db["MAP"][int((length ** 2) / 2) + add_half_width] = ord(".")
+    while (1):
+        y = rand.next() % length
+        x = rand.next() % length
+        pos = (y * length) + x
+        if "~" == chr(world_db["MAP"][pos]) and is_neighbor((y, x), "."):
+            if y == 0 or y == (length - 1) or x == 0 or x == (length - 1):
+                break
+            world_db["MAP"][pos] = ord(".")
+    n_trees = int((length ** 2) / 16)
+    i_trees = 0
+    while (i_trees <= n_trees):
+        single_allowed = rand.next() % 32
+        y = rand.next() % length
+        x = rand.next() % length
+        pos = (y * length) + x
+        if "." == chr(world_db["MAP"][pos]) \
+          and ((not single_allowed) or is_neighbor((y, x), "X")):
+            world_db["MAP"][pos] = ord("X")
+            i_trees += 1
+    rand.seed = store_seed
+    # This all-too-precise replica of the original C code misses iter_limit().
+
+
+def update_map_memory(t, age_map=True):
+    """Update t's T_MEMMAP with what's in its FOV now,age its T_MEMMEPTHMAP."""
+    if not t["T_MEMMAP"]:
+        t["T_MEMMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
+    if not t["T_MEMDEPTHMAP"]:
+        t["T_MEMDEPTHMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
+    ord_v = ord("v")
+    ord_0 = ord("0")
+    ord_space = ord(" ")
+    for pos in [pos for pos in range(world_db["MAP_LENGTH"] ** 2)
+                if ord_v == t["fovmap"][pos]]:
+        t["T_MEMDEPTHMAP"][pos] = ord_0
+        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)
+    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]):
+                t["T_MEMTHING"].append((type, y, x))
+
+
+def set_world_inactive():
+    """Set world_db["WORLD_ACTIVE"] to 0 and remove worldstate file."""
+    server_test()
+    if os.access(io_db["path_worldstate"], os.F_OK):
+        os.remove(io_db["path_worldstate"])
+    world_db["WORLD_ACTIVE"] = 0
+
+
+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):
+            raise ValueError
+        return val
+    except ValueError:
+        msg = "Ignoring: Please use integer >= " + str(min)
+        if max != None:
+            msg += " and <= " + str(max)
+        msg += "."
+        print(msg)
+        return None
+
+
+def setter(category, key, min, max=None):
+    """Build setter for world_db([category + "s"][id])[key] to >=min/<=max."""
+    if category is None:
+        def f(val_string):
+            val = integer_test(val_string, min, max)
+            if None != val:
+                world_db[key] = val
+    else:
+        if category == "Thing":
+            id_store = command_tid
+            decorator = test_Thing_id
+        elif category == "ThingType":
+            id_store = command_ttid
+            decorator = test_ThingType_id
+        elif category == "ThingAction":
+            id_store = command_taid
+            decorator = test_ThingAction_id
+
+        @decorator
+        def f(val_string):
+            val = integer_test(val_string, min, max)
+            if None != val:
+                world_db[category + "s"][id_store.id][key] = val
+    return f
+
+
+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:
+        raise RuntimeError("Malloc error in build_fov_Map().")
+
+
+def decrement_lifepoints(t):
+    """Decrement t's lifepoints by 1, and if to zero, corpse it.
+
+    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.
+    """
+    t["T_LIFEPOINTS"] -= 1
+    if 0 == t["T_LIFEPOINTS"]:
+        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))
+            strong_write(io_db["file_out"], "LOG You die.\n")
+        else:
+            t["fovmap"] = False
+            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):
+    """Wrapper around libpr.mv_yx_in_dir_legal to simplify its use."""
+    dir_c = dir.encode("ascii")[0]
+    test = libpr.mv_yx_in_dir_legal_wrap(dir_c, y, x)
+    if -1 == test:
+        raise RuntimeError("Too much wrapping in mv_yx_in_dir_legal_wrap()!")
+    return (test, libpr.result_y(), libpr.result_x())
+
+
+def actor_wait(t):
+    """Make t do nothing (but loudly, if player avatar)."""
+    if t == world_db["Things"][0]:
+        strong_write(io_db["file_out"], "LOG You wait.\n")
+
+
+def actor_move(t):
+    """If passable, move/collide(=attack) thing into T_ARGUMENT's direction."""
+    passable = False
+    move_result = mv_yx_in_dir_legal(chr(t["T_ARGUMENT"]),
+                                     t["T_POSY"], t["T_POSX"])
+    if 1 == move_result[0]:
+        pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
+        passable = "." == chr(world_db["MAP"][pos])
+        hitted = [id for id in world_db["Things"]
+                  if world_db["Things"][id] != t
+                  if world_db["Things"][id]["T_LIFEPOINTS"]
+                  if world_db["Things"][id]["T_POSY"] == move_result[1]
+                  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")
+            decrement_lifepoints(world_db["Things"][hit_id])
+            return
+    dir = [dir for dir in directions_db
+           if directions_db[dir] == chr(t["T_ARGUMENT"])][0]
+    if passable:
+        t["T_POSY"] = move_result[1]
+        t["T_POSX"] = move_result[2]
+        for id in t["T_CARRIES"]:
+            world_db["Things"][id]["T_POSY"] = move_result[1]
+            world_db["Things"][id]["T_POSX"] = move_result[2]
+        build_fov_map(t)
+        if t == world_db["Things"][0]:
+            strong_write(io_db["file_out"], "LOG You move " + dir + ".\n")
+    elif t == world_db["Things"][0]:
+        strong_write(io_db["file_out"], "LOG You fail to move " + dir + ".\n")
+
+
+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.
+    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
+        for id in ids:
+            if id > highest_id:
+                highest_id = id
+        world_db["Things"][highest_id]["carried"] = True
+        t["T_CARRIES"].append(highest_id)
+        if t == world_db["Things"][0]:
+            strong_write(io_db["file_out"], "LOG You pick up an object.\n")
+    elif t == world_db["Things"][0]:
+        err = "You try to pick up an object, but there is none."
+        strong_write(io_db["file_out"], "LOG " + err + "\n")
+
+
+def actor_drop(t):
+    """Make t rop Thing from inventory to ground indexed by T_ARGUMENT."""
+    # TODO: Handle case where T_ARGUMENT matches nothing.
+    if len(t["T_CARRIES"]):
+        id = t["T_CARRIES"][t["T_ARGUMENT"]]
+        t["T_CARRIES"].remove(id)
+        world_db["Things"][id]["carried"] = False
+        if t == world_db["Things"][0]:
+            strong_write(io_db["file_out"], "LOG You drop an object.\n")
+    elif t == world_db["Things"][0]:
+        err = "You try to drop an object, but you own none."
+        strong_write(io_db["file_out"], "LOG " + err + "\n")
+
+
+def actor_use(t):
+    """Make t use (for now: consume) T_ARGUMENT-indexed Thing in inventory."""
+    # TODO: Handle case where T_ARGUMENT matches nothing.
+    if len(t["T_CARRIES"]):
+        id = t["T_CARRIES"][t["T_ARGUMENT"]]
+        type = world_db["Things"][id]["T_TYPE"]
+        if world_db["ThingTypes"][type]["TT_CONSUMABLE"]:
+            t["T_CARRIES"].remove(id)
+            del world_db["Things"][id]
+            t["T_SATIATION"] += world_db["ThingTypes"][type]["TT_CONSUMABLE"]
+            if t == world_db["Things"][0]:
+                strong_write(io_db["file_out"],
+                             "LOG You consume this object.\n")
+        elif t == world_db["Things"][0]:
+            strong_write(io_db["file_out"],
+                         "LOG You try to use this object, but fail.\n")
+    elif t == world_db["Things"][0]:
+        strong_write(io_db["file_out"],
+                     "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.
+
+    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.
+    """
+    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]):
+                candidates.append((mv_result[1], mv_result[2]))
+        if len(candidates):
+            i = rand.next() % len(candidates)
+            id = id_setter(-1, "Things")
+            newT = new_Thing(t["T_TYPE"], (candidates[i][0], candidates[i][1]))
+            world_db["Things"][id] = newT
+
+
+def try_healing(t):
+    """Grow t's HP to a 1/32 chance if < HP max, satiation > 0, and waiting.
+
+    On success, decrease satiation score by 32.
+    """
+    if t["T_SATIATION"] > 0 \
+       and t["T_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]:
+        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):
+    """Decrement t's satiation,dependent on it trigger lifepoint dec chance."""
+    if t["T_SATIATION"] > -32768:
+        t["T_SATIATION"] -= 1
+    testbase = t["T_SATIATION"] if t["T_SATIATION"] >= 0 else -t["T_SATIATION"]
+    if not world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"]:
+        raise RuntimeError("A thing that should not hunger is hungering.")
+    stomach = int(32767 / world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"])
+    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)
+
+
+def get_dir_to_target(t, filter):
+    """Try to set T_COMMAND/T_ARGUMENT for move to "filter"-determined target.
+
+    The path-wise nearest target is chosen, via the shortest available path.
+    Target must not be t. On succcess, return positive value, else False.
+    Filters:
+    "a": Thing in FOV is below a certain distance, animate, but of ThingType
+         that is not t's, and starts out weaker than t is; build path as
+         avoiding things of t's ThingType
+    "f": neighbor cell (not inhabited by any animate Thing) further away from
+         animate Thing not further than x steps away and in FOV and of a
+         ThingType that is not t's, and starts out stronger or as strong as t
+         is currently; or (cornered), if no such flight cell, but Thing of
+         above criteria is too near,1 a cell closer to it, or, if less near,
+         just wait
+    "c": Thing in memorized map is consumable
+    "s": memory map cell with greatest-reachable degree of unexploredness
+    """
+
+    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:
+            raise RuntimeError("No score map allocated for "
+                               "zero_score_map_where_char_on_memdepthmap().")
+
+    def set_map_score(pos, score):
+        test = libpr.set_map_score(pos, score)
+        if test:
+            raise RuntimeError("No score map allocated for set_map_score().")
+
+    def get_map_score(pos):
+        result = libpr.get_map_score(pos)
+        if result < 0:
+            raise RuntimeError("No score map allocated for get_map_score().")
+        return result
+
+    def seeing_thing():
+        if t["fovmap"] and ("a" == filter or "f" == filter):
+            for id in world_db["Things"]:
+                Thing = world_db["Things"][id]
+                if Thing != t and Thing["T_LIFEPOINTS"] and \
+                   t["T_TYPE"] != Thing["T_TYPE"] and \
+                   'v' == chr(t["fovmap"][(Thing["T_POSY"]
+                                          * world_db["MAP_LENGTH"])
+                                          + Thing["T_POSX"]]):
+                    ThingType = world_db["ThingTypes"][Thing["T_TYPE"]]
+                    if ("f" == filter and ThingType["TT_LIFEPOINTS"] >=
+                                          t["T_LIFEPOINTS"]) \
+                       or ("a" == filter and ThingType["TT_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]]) \
+                   and world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]:
+                    return True
+        return False
+
+    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)
+        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"]
+                if t != Thing and Thing["T_LIFEPOINTS"] and \
+                   t["T_TYPE"] != Thing["T_TYPE"] and \
+                   ord_v == t["fovmap"][pos] and \
+                   t["T_LIFEPOINTS"] > \
+                   world_db["ThingTypes"][Thing["T_TYPE"]]["TT_LIFEPOINTS"]:
+                    set_map_score(pos, 0)
+                elif t["T_TYPE"] == Thing["T_TYPE"]:
+                    set_map_score(pos, 65535)
+        elif "f" == filter:
+            for id in [id for id in world_db["Things"]
+                       if world_db["Things"][id]["T_LIFEPOINTS"]]:
+                Thing = world_db["Things"][id]
+                pos = Thing["T_POSY"] * world_db["MAP_LENGTH"] \
+                      + Thing["T_POSX"]
+                if t["T_TYPE"] != Thing["T_TYPE"] and \
+                   ord_v == t["fovmap"][pos] and \
+                   t["T_LIFEPOINTS"] <= \
+                   world_db["ThingTypes"][Thing["T_TYPE"]]["TT_LIFEPOINTS"]:
+                    set_map_score(pos, 0)
+        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]]
+                       if world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]]:
+                set_map_score(mt[1] * world_db["MAP_LENGTH"] + mt[2], 0)
+        elif "s" == filter:
+            zero_score_map_where_char_on_memdepthmap(mem_depth_c[0])
+
+    def rand_target_dir(neighbors, cmp, dirs):
+        candidates = []
+        n_candidates = 0
+        for i in range(len(dirs)):
+            if cmp == neighbors[i]:
+                candidates.append(dirs[i])
+                n_candidates += 1
+        return candidates[rand.next() % n_candidates] if n_candidates else 0
+
+    def get_neighbor_scores(dirs, eye_pos):
+        scores = []
+        if libpr.ready_neighbor_scores(eye_pos):
+            raise RuntimeError("No score map allocated for " +
+                               "ready_neighbor_scores.()")
+        for i in range(len(dirs)):
+            scores.append(libpr.get_neighbor_score(i))
+        return scores
+
+    def get_dir_from_neighbors():
+        dir_to_target = False
+        dirs = "edcxsw"
+        eye_pos = t["T_POSY"] * world_db["MAP_LENGTH"] + t["T_POSX"]
+        neighbors = get_neighbor_scores(dirs, eye_pos)
+        if "f" == filter:
+            inhabited = [world_db["Things"][id]["T_POSY"]
+                         * world_db["MAP_LENGTH"]
+                         + world_db["Things"][id]["T_POSX"]
+                         for id in world_db["Things"]
+                         if world_db["Things"][id]["T_LIFEPOINTS"]]
+            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()
+                for pos in [pos for pos in inhabited if pos == pos_cmp]:
+                    neighbors[i] = 65535
+                    break
+        minmax_start = 0 if "f" == filter else 65535 - 1
+        minmax_neighbor = minmax_start
+        for i in range(len(dirs)):
+            if ("f" == filter and get_map_score(eye_pos) < neighbors[i] and
+                minmax_neighbor < neighbors[i] and 65535 != neighbors[i]) \
+               or ("f" != filter and minmax_neighbor > neighbors[i]):
+                minmax_neighbor = neighbors[i]
+        if minmax_neighbor != minmax_start:
+            dir_to_target = rand_target_dir(neighbors, minmax_neighbor, dirs)
+        if "f" == filter:
+            if not dir_to_target:
+                if 1 == get_map_score(eye_pos):
+                    dir_to_target = rand_target_dir(neighbors, 0, dirs)
+                elif 3 >= get_map_score(eye_pos):
+                    t["T_COMMAND"] = [id for id in world_db["ThingActions"]
+                                      if
+                                      world_db["ThingActions"][id]["TA_NAME"]
+                                         == "wait"][0]
+                    return 1
+            elif dir_to_target and 3 < get_map_score(eye_pos):
+                dir_to_target = 0
+        elif "a" == filter and 10 <= get_map_score(eye_pos):
+            dir_to_target = 0
+        return dir_to_target
+
+    dir_to_target = False
+    mem_depth_c = b' '
+    run_i = 9 + 1 if "s" == filter else 1
+    while run_i and not dir_to_target and ("s" == filter or seeing_thing()):
+        run_i -= 1
+        init_score_map()
+        mem_depth_c = b'9' if b' ' == mem_depth_c \
+                           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()
+        libpr.free_score_map()
+        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]
+            t["T_ARGUMENT"] = ord(dir_to_target)
+    return dir_to_target
+
+
+def standing_on_consumable(t):
+    """Return True/False whether t is standing on a consumable."""
+    for id in [id for id in world_db["Things"] if world_db["Things"][id] != t
+               if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
+               if world_db["Things"][id]["T_POSX"] == t["T_POSX"]
+               if world_db["ThingTypes"][world_db["Things"][id]["T_TYPE"]]
+                          ["TT_CONSUMABLE"]]:
+        return True
+    return False
+
+
+def get_inventory_slot_to_consume(t):
+    """Return slot Id of strongest consumable in t's inventory, else -1."""
+    cmp_consumability = 0
+    selection = -1
+    i = 0
+    for id in t["T_CARRIES"]:
+        type = world_db["Things"][id]["T_TYPE"]
+        if world_db["ThingTypes"][type]["TT_CONSUMABLE"] > cmp_consumability:
+            cmp_consumability = world_db["ThingTypes"][type]["TT_CONSUMABLE"]
+            selection = i
+        i += 1
+    return selection
+
+
+def ai(t):
+    """Determine next command/argment for actor t via AI algorithms.
+
+    AI will look for, and move towards, enemies (animate Things not of their
+    own ThingType); if they see none, they will consume consumables in their
+    inventory; if there are none, they will pick up what they stand on if they
+    stand on consumables; if they stand on none, they will move towards the
+    next consumable they see or remember on the map; if they see or remember
+    none, they will explore parts of the map unseen since ever or for at least
+    one turn; if there is nothing to explore, they will simply wait.
+    """
+    t["T_COMMAND"] = [id for id in world_db["ThingActions"]
+                      if world_db["ThingActions"][id]["TA_NAME"] == "wait"][0]
+    if not get_dir_to_target(t, "f"):
+        sel = get_inventory_slot_to_consume(t)
+        if -1 != sel:
+            t["T_COMMAND"] = [id for id in world_db["ThingActions"]
+                              if world_db["ThingActions"][id]["TA_NAME"]
+                                 == "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]
+        elif (not get_dir_to_target(t, "c")) and \
+             (not get_dir_to_target(t, "a")):
+            get_dir_to_target(t, "s")
+
+
+def turn_over():
+    """Run game world and its inhabitants until new player input expected."""
+    id = 0
+    whilebreaker = False
+    while world_db["Things"][0]["T_LIFEPOINTS"]:
+        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
+                continue                            # picked up during turn …
+            Thing = world_db["Things"][id]
+            if Thing["T_LIFEPOINTS"]:
+                if not Thing["T_COMMAND"]:
+                    update_map_memory(Thing)
+                    if 0 == id:
+                        whilebreaker = True
+                        break
+                    ai(Thing)
+                try_healing(Thing)
+                Thing["T_PROGRESS"] += 1
+                taid = [a for a in world_db["ThingActions"]
+                          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)
+        if whilebreaker:
+            break
+        world_db["TURN"] += 1
+
+
+def new_Thing(type, pos=(0, 0)):
+    """Return Thing of type T_TYPE, with fovmap if alive and world active."""
+    thing = {
+        "T_LIFEPOINTS": world_db["ThingTypes"][type]["TT_LIFEPOINTS"],
+        "T_ARGUMENT": 0,
+        "T_PROGRESS": 0,
+        "T_SATIATION": 0,
+        "T_COMMAND": 0,
+        "T_TYPE": type,
+        "T_POSY": pos[0],
+        "T_POSX": pos[1],
+        "T_CARRIES": [],
+        "carried": False,
+        "T_MEMTHING": [],
+        "T_MEMMAP": False,
+        "T_MEMDEPTHMAP": False,
+        "fovmap": False
+    }
+    if world_db["WORLD_ACTIVE"] and thing["T_LIFEPOINTS"]:
+        build_fov_map(thing)
+    return thing
+
+
+def id_setter(id, category, id_store=False, start_at_1=False):
+    """Set ID of object of category to manipulate ID unused? Create new one.
+    The ID is stored as id_store.id (if id_store is set). If the integer of the
+    input is valid (if start_at_1, >= 0, else >= -1), but <0 or (if start_at_1)
+    <1, calculate new ID: lowest unused ID >=0 or (if start_at_1) >= 1. None is
+    always returned when no new object is created, else the new object's ID.
+    """
+    min = 0 if start_at_1 else -1
+    if str == type(id):
+        id = integer_test(id, min)
+    if None != id:
+        if id in world_db[category]:
+            if id_store:
+                id_store.id = id
+            return None
+        else:
+            if (start_at_1 and 0 == id) \
+               or ((not start_at_1) and (id < 0)):
+                id = 0 if start_at_1 else -1
+                while 1:
+                    id = id + 1
+                    if id not in world_db[category]:
+                        break
+                    return None
+            if id_store:
+                id_store.id = id
+    return id
+
+
+def command_ping():
+    """Send PONG line to server output file."""
+    strong_write(io_db["file_out"], "PONG\n")
+
+
+def command_quit():
+    """Abort server process."""
+    save_world()
+    atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True)
+    raise SystemExit("received QUIT command")
+
+
+def command_thingshere(str_y, str_x):
+    """Write to out file list of Things known to player at coordinate y, x."""
+    if world_db["WORLD_ACTIVE"]:
+        y = integer_test(str_y, 0, 255)
+        x = integer_test(str_x, 0, 255)
+        length = world_db["MAP_LENGTH"]
+        if None != y and None != x and y < length and x < length:
+            pos = (y * world_db["MAP_LENGTH"]) + x
+            strong_write(io_db["file_out"], "THINGS_HERE START\n")
+            if "v" == chr(world_db["Things"][0]["fovmap"][pos]):
+                for id in world_db["Things"]:
+                    if y == world_db["Things"][id]["T_POSY"] \
+                       and x == world_db["Things"][id]["T_POSX"] \
+                       and not world_db["Things"][id]["carried"]:
+                        type = world_db["Things"][id]["T_TYPE"]
+                        name = world_db["ThingTypes"][type]["TT_NAME"]
+                        strong_write(io_db["file_out"], name + "\n")
+            else:
+                for mt in world_db["Things"][0]["T_MEMTHING"]:
+                    if y == mt[1] and x == mt[2]:
+                        name = world_db["ThingTypes"][mt[0]]["TT_NAME"]
+                        strong_write(io_db["file_out"], name + "\n")
+            strong_write(io_db["file_out"], "THINGS_HERE END\n")
+        else:
+            print("Ignoring: Invalid map coordinates.")
+    else:
+        print("Ignoring: Command only works on existing worlds.")
+
+
+def play_commander(action, args=False):
+    """Setter for player's T_COMMAND and T_ARGUMENT, then calling turn_over().
+
+    T_ARGUMENT is set to direction char if action=="wait",or 8-bit int if args.
+    """
+
+    def set_command():
+        id = [x for x in world_db["ThingActions"]
+                if world_db["ThingActions"][x]["TA_NAME"] == action][0]
+        world_db["Things"][0]["T_COMMAND"] = id
+        turn_over()
+
+    def set_command_and_argument_int(str_arg):
+        val = integer_test(str_arg, 0, 255)
+        if None != val:
+            world_db["Things"][0]["T_ARGUMENT"] = val
+            set_command()
+
+    def set_command_and_argument_movestring(str_arg):
+        if str_arg in directions_db:
+            world_db["Things"][0]["T_ARGUMENT"] = ord(directions_db[str_arg])
+            set_command()
+        else:
+            print("Ignoring: Argument must be valid direction string.")
+
+    if action == "move":
+        return set_command_and_argument_movestring
+    elif args:
+        return set_command_and_argument_int
+    else:
+        return set_command
+
+
+def command_seedrandomness(seed_string):
+    """Set rand seed to int(seed_string)."""
+    val = integer_test(seed_string, 0, 4294967295)
+    if None != val:
+        rand.seed = val
+
+
+def command_seedmap(seed_string):
+    """Set world_db["SEED_MAP"] to int(seed_string), then (re-)make map."""
+    setter(None, "SEED_MAP", 0, 4294967295)(seed_string)
+    remake_map()
+
+
+def command_makeworld(seed_string):
+    """(Re-)build game world, i.e. map, things, to a new turn 1 from seed.
+
+    Seed rand with seed, fill it into world_db["SEED_MAP"]. Do more only with a
+    "wait" ThingAction and world["PLAYER_TYPE"] matching ThingType of
+    TT_START_NUMBER > 0. Then, world_db["Things"] emptied, call remake_map()
+    and set world_db["WORLD_ACTIVE"], world_db["TURN"] to 1. Build new Things
+    according to ThingTypes' TT_START_NUMBERS, with Thing of ID 0 to ThingType
+    of ID = world["PLAYER_TYPE"]. Place Things randomly, and actors not on each
+    other. Init player's memory map. Write "NEW_WORLD" line to out file.
+    """
+
+    def free_pos():
+        i = 0
+        while 1:
+            err = "Space to put thing on too hard to find. Map too small?"
+            while 1:
+                y = rand.next() % world_db["MAP_LENGTH"]
+                x = rand.next() % world_db["MAP_LENGTH"]
+                if "." == chr(world_db["MAP"][y * world_db["MAP_LENGTH"] + x]):
+                    break
+                i += 1
+                if i == 65535:
+                    raise SystemExit(err)
+            # Replica of C code, wrongly ignores animatedness of new Thing.
+            pos_clear = (0 == len([id for id in world_db["Things"]
+                                   if world_db["Things"][id]["T_LIFEPOINTS"]
+                                   if world_db["Things"][id]["T_POSY"] == y
+                                   if world_db["Things"][id]["T_POSX"] == x]))
+            if pos_clear:
+                break
+        return (y, x)
+
+    val = integer_test(seed_string, 0, 4294967295)
+    if None == val:
+        return
+    rand.seed = val
+    world_db["SEED_MAP"] = val
+    player_will_be_generated = False
+    playertype = world_db["PLAYER_TYPE"]
+    for ThingType in world_db["ThingTypes"]:
+        if playertype == ThingType:
+            if 0 < world_db["ThingTypes"][ThingType]["TT_START_NUMBER"]:
+                player_will_be_generated = True
+            break
+    if not player_will_be_generated:
+        print("Ignoring beyond SEED_MAP: " +
+              "No player type with start number >0 defined.")
+        return
+    wait_action = False
+    for ThingAction in world_db["ThingActions"]:
+        if "wait" == world_db["ThingActions"][ThingAction]["TA_NAME"]:
+            wait_action = True
+    if not wait_action:
+        print("Ignoring beyond SEED_MAP: " +
+              "No thing action with name 'wait' defined.")
+        return
+    world_db["Things"] = {}
+    remake_map()
+    world_db["WORLD_ACTIVE"] = 1
+    world_db["TURN"] = 1
+    for i in range(world_db["ThingTypes"][playertype]["TT_START_NUMBER"]):
+        id = id_setter(-1, "Things")
+        world_db["Things"][id] = new_Thing(playertype, free_pos())
+    update_map_memory(world_db["Things"][0])
+    for type in world_db["ThingTypes"]:
+        for i in range(world_db["ThingTypes"][type]["TT_START_NUMBER"]):
+            if type != playertype:
+                id = id_setter(-1, "Things")
+                world_db["Things"][id] = new_Thing(type, free_pos())
+    strong_write(io_db["file_out"], "NEW_WORLD\n")
+
+
+def command_maplength(maplength_string):
+    """Redefine map length. Invalidate map, therefore lose all things on it."""
+    val = integer_test(maplength_string, 1, 256)
+    if None != val:
+        world_db["MAP_LENGTH"] = val
+        set_world_inactive()
+        world_db["Things"] = {}
+        libpr.set_maplength(val)
+
+
+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.
+    """
+    # In original version, map existence was also tested (unnecessarily?).
+    val = integer_test(worldactive_string, 0, 1)
+    if val:
+        if 0 != world_db["WORLD_ACTIVE"]:
+            if 0 == val:
+                set_world_inactive()
+            else:
+                print("World already active.")
+        elif 0 == world_db["WORLD_ACTIVE"]:
+            wait_exists = False
+            for ThingAction in world_db["ThingActions"]:
+                if "wait" == world_db["ThingActions"][ThingAction]["TA_NAME"]:
+                    wait_exists = True
+                    break
+            player_exists = False
+            for Thing in world_db["Things"]:
+                if 0 == Thing:
+                    player_exists = True
+                    break
+            if wait_exists and player_exists:
+                for id in world_db["Things"]:
+                    if world_db["Things"][id]["T_LIFEPOINTS"]:
+                        build_fov_map(world_db["Things"][id])
+                        if 0 == id:
+                            update_map_memory(world_db["Things"][id], False)
+                world_db["WORLD_ACTIVE"] = 1
+
+
+def test_for_id_maker(object, category):
+    """Return decorator testing for object having "id" attribute."""
+    def decorator(f):
+        def helper(*args):
+            if hasattr(object, "id"):
+                f(*args)
+            else:
+                print("Ignoring: No " + category +
+                      " defined to manipulate yet.")
+        return helper
+    return decorator
+
+
+def command_tid(id_string):
+    """Set ID of Thing to manipulate. ID unused? Create new one.
+
+    Default new Thing's type to the first available ThingType, others: zero.
+    """
+    id = id_setter(id_string, "Things", command_tid)
+    if None != id:
+        if world_db["ThingTypes"] == {}:
+            print("Ignoring: No ThingType to settle new Thing in.")
+            return
+        type = list(world_db["ThingTypes"].keys())[0]
+        world_db["Things"][id] = new_Thing(type)
+
+
+test_Thing_id = test_for_id_maker(command_tid, "Thing")
+
+
+@test_Thing_id
+def command_tcommand(str_int):
+    """Set T_COMMAND of selected Thing."""
+    val = integer_test(str_int, 0)
+    if None != val:
+        if 0 == val or val in world_db["ThingActions"]:
+            world_db["Things"][command_tid.id]["T_COMMAND"] = val
+        else:
+            print("Ignoring: ThingAction ID belongs to no known ThingAction.")
+
+
+@test_Thing_id
+def command_ttype(str_int):
+    """Set T_TYPE of selected Thing."""
+    val = integer_test(str_int, 0)
+    if None != val:
+        if val in world_db["ThingTypes"]:
+            world_db["Things"][command_tid.id]["T_TYPE"] = val
+        else:
+            print("Ignoring: ThingType ID belongs to no known ThingType.")
+
+
+@test_Thing_id
+def command_tcarries(str_int):
+    """Append int(str_int) to T_CARRIES of selected Thing.
+
+    The ID int(str_int) must not be of the selected Thing, and must belong to a
+    Thing with unset "carried" flag. Its "carried" flag will be set on owning.
+    """
+    val = integer_test(str_int, 0)
+    if None != val:
+        if val == command_tid.id:
+            print("Ignoring: Thing cannot carry itself.")
+        elif val in world_db["Things"] \
+             and not world_db["Things"][val]["carried"]:
+            world_db["Things"][command_tid.id]["T_CARRIES"].append(val)
+            world_db["Things"][val]["carried"] = True
+        else:
+            print("Ignoring: Thing not available for carrying.")
+    # Note that the whole carrying structure is different from the C version:
+    # Carried-ness is marked by a "carried" flag, not by Things containing
+    # Things internally.
+
+
+@test_Thing_id
+def command_tmemthing(str_t, str_y, str_x):
+    """Add (int(str_t), int(str_y), int(str_x)) to selected Thing's T_MEMTHING.
+
+    The type must fit to an existing ThingType, and the position into the map.
+    """
+    type = integer_test(str_t, 0)
+    posy = integer_test(str_y, 0, 255)
+    posx = integer_test(str_x, 0, 255)
+    if None != type and None != posy and None != posx:
+        if type not in world_db["ThingTypes"] \
+           or posy >= world_db["MAP_LENGTH"] or posx >= world_db["MAP_LENGTH"]:
+            print("Ignoring: Illegal value for thing type or position.")
+        else:
+            memthing = (type, posy, posx)
+            world_db["Things"][command_tid.id]["T_MEMTHING"].append(memthing)
+
+
+def setter_map(maptype):
+    """Set selected Thing's map of maptype's int(str_int)-th line to mapline.
+
+    If Thing has no map of maptype yet, initialize it with ' ' bytes first.
+    """
+    @test_Thing_id
+    def helper(str_int, mapline):
+        val = integer_test(str_int, 0, 255)
+        if None != val:
+            if val >= world_db["MAP_LENGTH"]:
+                print("Illegal value for map line number.")
+            elif len(mapline) != world_db["MAP_LENGTH"]:
+                print("Map line length is unequal map width.")
+            else:
+                length = world_db["MAP_LENGTH"]
+                map = None
+                if not world_db["Things"][command_tid.id][maptype]:
+                    map = bytearray(b' ' * (length ** 2))
+                else:
+                    map = world_db["Things"][command_tid.id][maptype]
+                map[val * length:(val * length) + length] = mapline.encode()
+                world_db["Things"][command_tid.id][maptype] = map
+    return helper
+
+
+def setter_tpos(axis):
+    """Generate setter for T_POSX or  T_POSY of selected Thing.
+
+    If world is active, rebuilds animate things' fovmap, player's memory map.
+    """
+    @test_Thing_id
+    def helper(str_int):
+        val = integer_test(str_int, 0, 255)
+        if None != val:
+            if val < world_db["MAP_LENGTH"]:
+                world_db["Things"][command_tid.id]["T_POS" + axis] = val
+                if world_db["WORLD_ACTIVE"] \
+                   and world_db["Things"][command_tid.id]["T_LIFEPOINTS"]:
+                    build_fov_map(world_db["Things"][command_tid.id])
+                    if 0 == command_tid.id:
+                        update_map_memory(world_db["Things"][command_tid.id])
+            else:
+                print("Ignoring: Position is outside of map.")
+    return helper
+
+
+def command_ttid(id_string):
+    """Set ID of ThingType to manipulate. ID unused? Create new one.
+
+    Default new ThingType's TT_SYMBOL to "?", TT_CORPSE_ID to self, others: 0.
+    """
+    id = id_setter(id_string, "ThingTypes", command_ttid)
+    if None != id:
+        world_db["ThingTypes"][id] = {
+            "TT_NAME": "(none)",
+            "TT_CONSUMABLE": 0,
+            "TT_LIFEPOINTS": 0,
+            "TT_PROLIFERATE": 0,
+            "TT_START_NUMBER": 0,
+            "TT_SYMBOL": "?",
+            "TT_CORPSE_ID": id
+        }
+
+
+test_ThingType_id = test_for_id_maker(command_ttid, "ThingType")
+
+
+@test_ThingType_id
+def command_ttname(name):
+    """Set TT_NAME of selected ThingType."""
+    world_db["ThingTypes"][command_ttid.id]["TT_NAME"] = name
+
+
+@test_ThingType_id
+def command_ttsymbol(char):
+    """Set TT_SYMBOL of selected ThingType. """
+    if 1 == len(char):
+        world_db["ThingTypes"][command_ttid.id]["TT_SYMBOL"] = char
+    else:
+        print("Ignoring: Argument must be single character.")
+
+
+@test_ThingType_id
+def command_ttcorpseid(str_int):
+    """Set TT_CORPSE_ID of selected ThingType."""
+    val = integer_test(str_int, 0)
+    if None != val:
+        if val in world_db["ThingTypes"]:
+            world_db["ThingTypes"][command_ttid.id]["TT_CORPSE_ID"] = val
+        else:
+            print("Ignoring: Corpse ID belongs to no known ThignType.")
+
+
+def command_taid(id_string):
+    """Set ID of ThingAction to manipulate. ID unused? Create new one.
+
+    Default new ThingAction's TA_EFFORT to 1, its TA_NAME to "wait".
+    """
+    id = id_setter(id_string, "ThingActions", command_taid, True)
+    if None != id:
+        world_db["ThingActions"][id] = {
+            "TA_EFFORT": 1,
+            "TA_NAME": "wait"
+        }
+
+
+test_ThingAction_id = test_for_id_maker(command_taid, "ThingAction")
+
+
+@test_ThingAction_id
+def command_taname(name):
+    """Set TA_NAME of selected ThingAction.
+
+    The name must match a valid thing action function. If after the name
+    setting no ThingAction with name "wait" remains, call set_world_inactive().
+    """
+    if name == "wait" or name == "move" or name == "use" or name == "drop" \
+       or name == "pick_up":
+        world_db["ThingActions"][command_taid.id]["TA_NAME"] = name
+        if 1 == world_db["WORLD_ACTIVE"]:
+            wait_defined = False
+            for id in world_db["ThingActions"]:
+                if "wait" == world_db["ThingActions"][id]["TA_NAME"]:
+                    wait_defined = True
+                    break
+            if not wait_defined:
+                set_world_inactive()
+    else:
+        print("Ignoring: Invalid action name.")
+    # In contrast to the original,naming won't map a function to a ThingAction.
+
+
+def command_ai():
+    """Call ai() on player Thing, then turn_over()."""
+    ai(world_db["Things"][0])
+    turn_over()
+
+
+"""Commands database.
+
+Map command start tokens to ([0]) number of expected command arguments, ([1])
+the command's meta-ness (i.e. is it to be written to the record file, is it to
+be ignored in replay mode if read from server input file), and ([2]) a function
+to be called on it.
+"""
+commands_db = {
+    "QUIT": (0, True, command_quit),
+    "PING": (0, True, command_ping),
+    "THINGS_HERE": (2, True, command_thingshere),
+    "MAKE_WORLD": (1, False, command_makeworld),
+    "SEED_MAP": (1, False, command_seedmap),
+    "SEED_RANDOMNESS": (1, False, command_seedrandomness),
+    "TURN": (1, False, setter(None, "TURN", 0, 65535)),
+    "PLAYER_TYPE": (1, False, setter(None, "PLAYER_TYPE", 0)),
+    "MAP_LENGTH": (1, False, command_maplength),
+    "WORLD_ACTIVE": (1, False, command_worldactive),
+    "TA_ID": (1, False, command_taid),
+    "TA_EFFORT": (1, False, setter("ThingAction", "TA_EFFORT", 0, 255)),
+    "TA_NAME": (1, False, command_taname),
+    "TT_ID": (1, False, command_ttid),
+    "TT_NAME": (1, False, command_ttname),
+    "TT_SYMBOL": (1, False, command_ttsymbol),
+    "TT_CORPSE_ID": (1, False, command_ttcorpseid),
+    "TT_CONSUMABLE": (1, False, setter("ThingType", "TT_CONSUMABLE",
+                                       0, 65535)),
+    "TT_START_NUMBER": (1, False, setter("ThingType", "TT_START_NUMBER",
+                                         0, 255)),
+    "TT_PROLIFERATE": (1, False, setter("ThingType", "TT_PROLIFERATE",
+                                        0, 255)),
+    "TT_LIFEPOINTS": (1, False, setter("ThingType", "TT_LIFEPOINTS", 0, 255)),
+    "T_ID": (1, False, command_tid),
+    "T_ARGUMENT": (1, False, setter("Thing", "T_ARGUMENT", 0, 255)),
+    "T_PROGRESS": (1, False, setter("Thing", "T_PROGRESS", 0, 255)),
+    "T_LIFEPOINTS": (1, False, setter("Thing", "T_LIFEPOINTS", 0, 255)),
+    "T_SATIATION": (1, False, setter("Thing", "T_SATIATION", -32768, 32767)),
+    "T_COMMAND": (1, False, command_tcommand),
+    "T_TYPE": (1, False, command_ttype),
+    "T_CARRIES": (1, False, command_tcarries),
+    "T_MEMMAP": (2, False, setter_map("T_MEMMAP")),
+    "T_MEMDEPTHMAP": (2, False, setter_map("T_MEMDEPTHMAP")),
+    "T_MEMTHING": (3, False, command_tmemthing),
+    "T_POSY": (1, False, setter_tpos("Y")),
+    "T_POSX": (1, False, setter_tpos("X")),
+    "wait": (0, False, play_commander("wait")),
+    "move": (1, False, play_commander("move")),
+    "pick_up": (0, False, play_commander("pick_up")),
+    "drop": (1, False, play_commander("drop", True)),
+    "use": (1, False, play_commander("use", True)),
+    "ai": (0, False, command_ai)
+}
+
+
+"""World state database. With sane default values. (Randomness is in rand.)"""
+world_db = {
+    "TURN": 0,
+    "MAP_LENGTH": 64,
+    "SEED_MAP": 0,
+    "PLAYER_TYPE": 0,
+    "WORLD_ACTIVE": 0,
+    "ThingActions": {},
+    "ThingTypes": {},
+    "Things": {}
+}
+
+"""Mapping of direction names to internal direction chars."""
+directions_db = {"east": "d", "south-east": "c", "south-west": "x",
+                 "west": "s", "north-west": "w", "north-east": "e"}
+
+"""File IO database."""
+io_db = {
+    "path_save": "save",
+    "path_record": "record_save",
+    "path_worldconf": "confserver/world",
+    "path_server": "server/",
+    "path_in": "server/in",
+    "path_out": "server/out",
+    "path_worldstate": "server/worldstate",
+    "tmp_suffix": "_tmp",
+    "kicked_by_rival": False,
+    "worldstate_updateable": False
+}
+
+
+try:
+    libpr = prep_library()
+    rand = RandomnessIO()
+    opts = parse_command_line_arguments()
+    if opts.savefile:
+        io_db["path_save"] = opts.savefile
+        io_db["path_record"] = "record_" + opts.savefile
+    setup_server_io()
+    if opts.verbose:
+        io_db["verbose"] = True
+    if None != opts.replay:
+        replay_game()
+    else:
+        play_game()
+except SystemExit as exit:
+    print("ABORTING: " + exit.args[0])
+except:
+    print("SOMETHING WENT WRONG IN UNEXPECTED WAYS")
+    raise
+finally:
+    cleanup_server_io()
diff --git a/roguelike-server.do b/roguelike-server.do
deleted file mode 100644 (file)
index ebe418d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# redo build file to build the executable "roguelike-server".
-
-# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
-# or any later version. For details on its copyright, license, and warranties,
-# see the file NOTICE in the root directory of the PlomRogue source package.
-
-redo-ifchange build/build_template
-TARGET=server
-LIBRARY_LINKS=-lm
-. ./build/build_template
diff --git a/roguelike_python b/roguelike_python
deleted file mode 100755 (executable)
index 74f7f34..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-# Wrapper to the script so that its suppressed server messages get read on exit.
-./start_server_python_client_union.sh "$@"
-
-# For some reason, mere sync won't ensure a log is written out, so wait a while.
-sync
-sleep 0.5
-if [ -e ./log ]
-then
-    cat log
-fi
diff --git a/src/server/ai.c b/src/server/ai.c
deleted file mode 100644 (file)
index 749a385..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-/* src/server/ai.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "ai.h"
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t, int16_t, UINT16_MAX */
-#include <stdlib.h> /* free() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "hardcoded_strings.h" /* s */
-#include "rrand.h" /* rrand() */
-#include "thing_actions.h" /* get_thing_action_id_by_name() */
-#include "things.h" /* Thing, ThingType, ThingInMemory, get_thing_type() */
-#include "world.h" /* world */
-
-#define N_DIRS 6
-
-
-
-/* Return "score_map"["pos"] unless "check_inhabitant" and cell is inhabited. */
-static uint16_t set_neighbor_val(uint16_t * score_map, uint8_t check_inhabitant,
-                                 uint16_t kill_score, uint16_t pos);
-
-/* Write into "neighbors" scores of the N_DIRS immediate neighbors of the
- * "score_map" cell at "pos_i" (array index), as found in the directions
- * north-east, east, south-east etc. (clockwise order). Use "kill_score" for
- * illegal neighborhoods (i.e. if direction would lead beyond the map's border,
- * or, if "check_inhabitants" is non-zero, into animate-inhabited cell).
- */
-static void get_neighbor_scores(uint16_t * score_map, uint16_t pos_i,
-                                uint16_t kill_score, uint16_t * neighbors,
-                                uint8_t check_inhabitants);
-
-/* Iterate over scored cells in "score_map" of world.map's geometry. Compare
- * each cell's score against the score of its immediate neighbors in N_DIRS
- * directions. If any neighbor's score is at least two points lower than the
- * current cell's score, re-set it to 1 point higher than its lowest-scored
- * neighbor. Repeat this whole process until all cells have settled on their
- * final score. Ignore cells whose score is greater than "max_score". Expect
- * "max_score" to be the maximum score for cells, marking them as unreachable.
- */
-static void dijkstra_map(uint16_t * score_map, uint16_t max_score);
-
-/* Helpers to init_score_map(), realizing individual filters. */
-static uint8_t score_map_filter_attack(uint8_t filter, uint16_t * score_map,
-                                       struct Thing * t_eye);
-static uint8_t score_map_filter_flee(uint8_t filter, uint16_t * score_map,
-                                     struct Thing * t_eye);
-static uint8_t score_map_filter_consume(uint8_t filter, uint16_t * score_map,
-                                        struct Thing * t_eye);
-static uint8_t score_map_filter_search(uint8_t filter, uint16_t * score_map,
-                                       struct Thing * t_eye);
-
-/* get_dir_to_nearest_target() helper: Prepare "score_map" for dijkstra_map(). */
-static void init_score_map(char filter, uint16_t * score_map, uint32_t map_size,
-                           struct Thing * t_eye);
-
-/* From "targets" select random "cmp" match as directory by order in "dirs". */
-static char rand_target_dir(char * dirs, uint16_t cmp, uint16_t * targets);
-
-/* Helper to get_dir_to_nearest_target(). */
-static char get_dir_from_neighbors(char filter, struct Thing * t_eye,
-                                   uint16_t * score_map);
-
-/* Set (if possible) as "t_eye"'s command a move to the path to the path-wise
- * nearest target that is not "t_eye" and fits criteria set by "filter". On
- * success, return !0, else 0. Values for "filter":
- * "a": thing in FOV is below a certain distance, animate, but of a type that is
- *      not "t_eye"'s, and starts out weaker than it is; build path as avoiding
- *      things of "t_eye"'s type
- * "f": neighbor cell (not inhabited by any animate thing) further away from
- *      animate thing not further than x steps away and in FOV and of a type
- *      that is not "t_eye"'s, and starts out stronger or as strong as "t_eye"
- *      is currently; or (cornered), if no such flight cell, but thing of above
- *      criteria is too near, a cell closer to it, or, if less near, just wait
- * "c": thing in memorized map is consumable
- * "s": memory map cell with greatest-reachable degree of unexploredness
- */
-static uint8_t get_dir_to_nearest_target(struct Thing * t_eye, char filter);
-
-/* Return 1 if any thing not "t_eye" is known and fulfills some criteria defined
- * by "filter", else 0. Values for "filter":
- * "a" or "f": thing in FOV is animate, but of type that not that of "t_eye",
- *             and starts out weaker ("a") / stronger ("f") than "t_eye" is
- * "c"       : thing in memorized map is consumable
- */
-static uint8_t seeing_thing(struct Thing * t_eye, char filter);
-
-/* Return slot ID of strongest consumable in "t_owner"'s inventory, else -1. */
-static int16_t get_inventory_slot_to_consume(struct Thing * t_owner);
-
-/* Return 1 if "t_standing" is standing on a consumable, else 0. */
-static uint8_t standing_on_consumable(struct Thing * t_standing);
-
-
-
-static uint16_t set_neighbor_val(uint16_t * score_map, uint8_t check_inhabitant,
-                                 uint16_t kill_score, uint16_t pos)
-{
-    if (check_inhabitant)
-    {
-        struct Thing * t = world.things;
-        for (; t; t = t->next)
-        {
-            if (t->lifepoints && pos == t->pos.y * world.map.length + t->pos.x)
-            {
-                return kill_score;
-            }
-        }
-    }
-    return score_map[pos];
-}
-
-
-
-static void get_neighbor_scores(uint16_t * score_map, uint16_t pos_i,
-                                uint16_t kill_score, uint16_t * neighbors,
-                                uint8_t check_inhabitants)
-{
-    uint32_t map_size = world.map.length * world.map.length;
-    uint8_t open_north     = pos_i >= world.map.length;
-    uint8_t open_east      = pos_i + 1 % world.map.length;
-    uint8_t open_south     = pos_i + world.map.length < map_size;
-    uint8_t open_west      = pos_i % world.map.length;
-    uint8_t is_indented    = (pos_i / world.map.length) % 2;
-    uint8_t open_diag_west = is_indented || open_west;
-    uint8_t open_diag_east = !is_indented || open_east;
-    neighbors[0] = !(open_north && open_diag_east) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i - world.map.length + is_indented);
-    neighbors[1] = !(open_east) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i + 1);
-    neighbors[2] = !(open_south && open_diag_east) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i + world.map.length + is_indented);
-    neighbors[3] = !(open_south && open_diag_west) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i + world.map.length - !is_indented);
-    neighbors[4] = !(open_west) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i - 1);
-    neighbors[5] = !(open_north && open_diag_west) ? kill_score :
-                   set_neighbor_val(score_map, check_inhabitants, kill_score,
-                                    pos_i - world.map.length - !is_indented);
-}
-
-
-
-static void dijkstra_map(uint16_t * score_map, uint16_t max_score)
-{
-    uint32_t map_size = world.map.length * world.map.length;
-    uint32_t pos;
-    uint16_t i_scans, neighbors[N_DIRS], min_neighbor;
-    uint8_t scores_still_changing = 1;
-    uint8_t i_dirs;
-    for (i_scans = 0; scores_still_changing; i_scans++)
-    {
-        scores_still_changing = 0;
-        for (pos = 0; pos < map_size; pos++)
-        {
-            if (score_map[pos] <= max_score)
-            {
-                get_neighbor_scores(score_map, pos, max_score, neighbors, 0);
-                min_neighbor = max_score;
-                for (i_dirs = 0; i_dirs < N_DIRS; i_dirs++)
-                {
-                    if (min_neighbor > neighbors[i_dirs])
-                    {
-                        min_neighbor = neighbors[i_dirs];
-                    }
-                }
-                if (score_map[pos] > min_neighbor + 1)
-                {
-                    score_map[pos] = min_neighbor + 1;
-                    scores_still_changing = 1;
-                }
-            }
-        }
-    }
-}
-
-
-
-static uint8_t score_map_filter_attack(uint8_t filter, uint16_t * score_map,
-                                       struct Thing * t_eye)
-{
-    if ('a' != filter)
-    {
-        return 0;
-    }
-    struct Thing * t = world.things;
-    for (; t; t = t->next)
-    {
-        if (   t != t_eye && t->lifepoints && t->type != t_eye->type
-            && 'v' == t_eye->fov_map[t->pos.y*world.map.length + t->pos.x]
-            && get_thing_type(t->type)->lifepoints < t_eye->lifepoints)
-        {
-            score_map[t->pos.y * world.map.length + t->pos.x] = 0;
-        }
-        else if (t->type == t_eye->type)
-        {
-            score_map[t->pos.y * world.map.length + t->pos.x] = UINT16_MAX;
-        }
-    }
-    return 1;
-}
-
-
-
-static uint8_t score_map_filter_flee(uint8_t filter, uint16_t * score_map,
-                                     struct Thing * t_eye)
-{
-    if ('f' != filter)
-    {
-        return 0;
-    }
-    struct Thing * t = world.things;
-    for (; t; t = t->next)
-    {
-        if (   t->lifepoints && t->type != t_eye->type
-            && 'v' == t_eye->fov_map[t->pos.y*world.map.length + t->pos.x]
-            && get_thing_type(t->type)->lifepoints >= t_eye->lifepoints)
-        {
-            score_map[t->pos.y * world.map.length + t->pos.x] = 0;
-        }
-    }
-    return 1;
-}
-
-
-
-static uint8_t score_map_filter_consume(uint8_t filter, uint16_t * score_map,
-                                        struct Thing * t_eye)
-{
-    if ('c' != filter)
-    {
-        return 0;
-    }
-    struct ThingInMemory * tm = t_eye->t_mem;
-    for (; tm; tm = tm->next)
-    {
-        if (   ' ' != t_eye->mem_map[tm->pos.y * world.map.length + tm->pos.x]
-            && get_thing_type(tm->type)->consumable)
-        {
-            score_map[tm->pos.y * world.map.length + tm->pos.x] = 0;
-        }
-    }
-    return 1;
-}
-
-
-
-static uint8_t score_map_filter_search(uint8_t filter, uint16_t * score_map,
-                                       struct Thing * t_eye)
-{
-    if (!(('0' < filter && '9' >= filter) || ' ' == filter))
-    {
-        return 0;
-    }
-    uint32_t i;
-    for (i = 0; i < (uint32_t) (world.map.length * world.map.length); i++)
-    {
-        score_map[i] = filter == t_eye->mem_depth_map[i] ? 0 : score_map[i];
-    }
-    return 1;
-}
-
-
-
-static void init_score_map(char filter, uint16_t * score_map, uint32_t map_size,
-                           struct Thing * t_eye)
-{
-    uint32_t i;
-    for (i = 0; i < map_size; i++)
-    {
-        score_map[i] = UINT16_MAX;
-        if ('.' == t_eye->mem_map[i])
-        {
-            score_map[i] = UINT16_MAX-1;
-        }
-    }
-    if (   score_map_filter_attack(filter, score_map, t_eye)
-        || score_map_filter_flee(filter, score_map, t_eye)
-        || score_map_filter_consume(filter, score_map, t_eye)
-        || score_map_filter_search(filter, score_map, t_eye))
-    {
-    }
-}
-
-
-static char rand_target_dir(char * dirs, uint16_t cmp, uint16_t * targets)
-{
-    char candidates[N_DIRS];
-    uint8_t n_candidates = 0;
-    uint8_t i;
-    for (i = 0; i < N_DIRS; i++)
-    {
-        if (cmp == targets[i])
-        {
-            candidates[n_candidates] = dirs[i];
-            n_candidates++;
-        }
-    }
-    return n_candidates ? candidates[rrand() % n_candidates] : 0;
-}
-
-
-
-static char get_dir_from_neighbors(char filter, struct Thing * t_eye,
-                                   uint16_t * score_map)
-{
-    char dir_to_nearest_target = 0;
-    uint16_t pos_i = (t_eye->pos.y * world.map.length) + t_eye->pos.x;
-    char * dirs = "edcxsw";   /* get_neighbor_scores()'s clockwise dir order. */
-    uint16_t neighbors[N_DIRS];
-    get_neighbor_scores(score_map, pos_i, UINT16_MAX, neighbors, 'f'==filter);
-    uint16_t minmax_start = 'f' == filter ? 0 : UINT16_MAX-1;
-    uint16_t minmax_neighbor = minmax_start;
-    uint8_t i;
-    for (i = 0; i < N_DIRS; i++)
-    {
-        if (   (   'f' == filter && score_map[pos_i] < neighbors[i]
-                && minmax_neighbor < neighbors[i] && UINT16_MAX != neighbors[i])
-            || ('f' != filter && minmax_neighbor > neighbors[i]))
-        {
-            minmax_neighbor = neighbors[i];
-        }
-    }
-    if (minmax_neighbor != minmax_start)
-    {
-        dir_to_nearest_target = rand_target_dir(dirs,minmax_neighbor,neighbors);
-    }
-    if ('f' == filter)
-    {
-        if (!dir_to_nearest_target)
-        {
-            if (1 == score_map[pos_i])     /* Attack if cornered too closely. */
-            {
-                dir_to_nearest_target = rand_target_dir(dirs, 0, neighbors);
-            }
-            else if (3 >= score_map[pos_i])    /* If less closely, just wait. */
-            {
-                t_eye->command = get_thing_action_id_by_name(s[S_CMD_WAIT]);
-                return 1;
-            }
-        }
-        else if (dir_to_nearest_target && 3 < score_map[pos_i]) /* Don't flee */
-        {                                                       /* enemy of   */
-            dir_to_nearest_target = 0;                          /* a certain  */
-        }                                                       /* distance.  */
-    }
-    else if ('a' == filter && 10 <= score_map[pos_i])
-    {
-        dir_to_nearest_target = 0;
-    }
-    return dir_to_nearest_target;
-}
-
-
-
-static uint8_t get_dir_to_nearest_target(struct Thing * t_eye, char filter)
-{
-    char dir_to_nearest_target = 0;
-    uint8_t mem_depth_char = ' ';
-    uint8_t run_i = 's' == filter ? 9 /* max explored mem depth age */ + 1 : 1;
-    while (    run_i && !dir_to_nearest_target
-           && ('s' == filter || seeing_thing(t_eye, filter)))
-    {
-        run_i--;
-        uint32_t map_size = world.map.length * world.map.length;
-        uint16_t * score_map = try_malloc(map_size * sizeof(uint16_t),__func__);
-        init_score_map('s' == filter ? mem_depth_char : filter,
-                       score_map, map_size, t_eye);
-        mem_depth_char = ' ' == mem_depth_char ? '9' : mem_depth_char - 1;
-        dijkstra_map(score_map, UINT16_MAX-1);
-        dir_to_nearest_target = get_dir_from_neighbors(filter,t_eye,score_map);
-        free(score_map);
-        if (dir_to_nearest_target && 1 != dir_to_nearest_target)
-        {
-            t_eye->command = get_thing_action_id_by_name(s[S_CMD_MOVE]);
-            t_eye->arg = dir_to_nearest_target;
-        }
-    }
-    return dir_to_nearest_target;
-}
-
-
-
-static uint8_t seeing_thing(struct Thing * t_eye, char filter)
-{
-    if (t_eye->fov_map && ('a' == filter || 'f' == filter))
-    {
-        struct Thing * t = world.things;
-        for (; t; t = t->next)
-        {
-            if (   t != t_eye && t->lifepoints && t->type != t_eye->type
-                && 'v' == t_eye->fov_map[t->pos.y*world.map.length + t->pos.x])
-            {
-                struct ThingType * tt = get_thing_type(t->type);
-                if (   ('f' == filter && tt->lifepoints >= t_eye->lifepoints)
-                    || ('a' == filter && tt->lifepoints <  t_eye->lifepoints))
-                {
-                    return 1;
-                }
-            }
-        }
-    }
-    else if (t_eye->mem_map && 'c' == filter)
-    {
-        struct ThingInMemory * tm = t_eye->t_mem;
-        for (; tm; tm = tm->next)
-        {
-            if (     ' ' != t_eye->mem_map[tm->pos.y*world.map.length+tm->pos.x]
-                && get_thing_type(tm->type)->consumable)
-            {
-                return 1;
-            }
-        }
-    }
-    return 0;
-}
-
-
-
-static int16_t get_inventory_slot_to_consume(struct Thing * t_owner)
-{
-    uint8_t compare_consumability = 0;
-    int16_t selection = -1;
-    struct Thing * t = t_owner->owns;;
-    uint8_t i;
-    for (i = 0; t; t = t->next, i++)
-    {
-        struct ThingType * tt = get_thing_type(t->type);
-        if (tt->consumable > compare_consumability)
-        {
-            compare_consumability = tt->consumable;
-            selection = i;
-        }
-    }
-    return selection;
-}
-
-
-
-static uint8_t standing_on_consumable(struct Thing * t_standing)
-{
-    struct Thing * t = world.things;
-    for (; t; t = t->next)
-    {
-        if (   t != t_standing
-            && t->pos.y == t_standing->pos.y && t->pos.x == t_standing->pos.x
-            && get_thing_type(t->type)->consumable)
-        {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
-
-extern void ai(struct Thing * t)
-{
-    t->command = get_thing_action_id_by_name(s[S_CMD_WAIT]);
-    if (!get_dir_to_nearest_target(t, 'f'))
-    {
-        int16_t sel = get_inventory_slot_to_consume(t);
-        if (-1 != sel)
-        {
-            t->command = get_thing_action_id_by_name(s[S_CMD_USE]);
-            t->arg = (uint8_t) sel;
-        }
-        else if (standing_on_consumable(t))
-        {
-            t->command = get_thing_action_id_by_name(s[S_CMD_PICKUP]);
-        }
-        else if (   !get_dir_to_nearest_target(t, 'c')
-                 && !get_dir_to_nearest_target(t, 'a'))
-        {
-            get_dir_to_nearest_target(t, 's');
-        }
-    }
-}
diff --git a/src/server/ai.h b/src/server/ai.h
deleted file mode 100644 (file)
index 966edd5..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* src/server/ai.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Pseudo AI for actor movement.
- */
-
-#ifndef AI_H
-#define AI_H
-
-struct Thing;
-
-
-
-/* Determine next non-player actor command / arguments by the actor's AI. Actors
- * will look for, and move towards, enemies (animate things not of their own
- * type); if they see none, they will consume consumables in their inventory; if
- * there are none, they will pick up any consumables they stand on; if they
- * stand on none, they will move towards the next consumable they see or
- * remember on the map; if they see or remember none, they'll explore parts of
- * the map unseen since ever or for at least one turn; if there is nothing to
- * explore, they will simply wait.
- */
-extern void ai(struct Thing * t);
-
-
-
-#endif
diff --git a/src/server/cleanup.c b/src/server/cleanup.c
deleted file mode 100644 (file)
index ec8017d..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/* src/server/cleanup.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "cleanup.h"
-#include <stdint.h> /* uint32_t */
-#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 */
-
-
-
-
-/* The clean-up flags set by set_cleanup_flag(). */
-static uint32_t cleanup_flags = 0x0000;
-
-
-
-extern void cleanup()
-{
-    free(world.queue);
-    free(world.map.cells);
-    if (cleanup_flags & CLEANUP_WORLDSTATE)
-    {
-        unlink(s[S_PATH_WORLDSTATE]);
-    }
-    if (cleanup_flags & CLEANUP_THINGS)
-    {
-        free_things(world.things);
-    }
-    if (cleanup_flags & CLEANUP_THING_TYPES)
-    {
-        free_thing_types(world.thing_types);
-    }
-    if (cleanup_flags & CLEANUP_THING_ACTIONS)
-    {
-        free_thing_actions(world.thing_actions);
-    }
-    if (cleanup_flags & CLEANUP_IN)
-    {
-        try_fclose(world.file_in, __func__);
-        unlink(s[S_PATH_IN]);
-    }
-    if (cleanup_flags & CLEANUP_OUT)
-    {
-        try_fclose(world.file_out, __func__);
-        free(world.server_test);
-        unlink(s[S_PATH_OUT]);
-    }
-}
-
-
-extern void set_cleanup_flag(enum cleanup_flag flag)
-{
-    cleanup_flags = cleanup_flags | flag;
-}
-
-
-
-extern void unset_cleanup_flag(enum cleanup_flag flag)
-{
-    cleanup_flags = cleanup_flags ^ flag;
-}
diff --git a/src/server/cleanup.h b/src/server/cleanup.h
deleted file mode 100644 (file)
index 2047510..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* src/server/cleanup.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Stuff defining / performing the cleanup called by rexit.h's exit functions.
- */
-
-#ifndef CLEANUP_H
-#define CLEANUP_H
-
-
-
-/* set_cleanup_flag() sets any of the flags defined in cleanup_flag to announce
- * the resources that need cleaning up upon program exit. It is to be called at
- * the earliest moment possible after resource creation / initialization.
- */
-enum cleanup_flag
-{
-    CLEANUP_FIFO          = 0x0001,
-    CLEANUP_WORLDSTATE    = 0x0002,
-    CLEANUP_THING_TYPES   = 0x0004,
-    CLEANUP_THINGS        = 0x0008,
-    CLEANUP_THING_ACTIONS = 0x0010,
-    CLEANUP_IN            = 0x0020,
-    CLEANUP_OUT           = 0x0040
-};
-
-/* In addition, unset_cleanup_flag() may be used to unset flags. */
-extern void set_cleanup_flag(enum cleanup_flag flag);
-extern void unset_cleanup_flag(enum cleanup_flag flag);
-
-/* Frees memory and unlinks some files. */
-extern void cleanup();
-
-
-
-#endif
diff --git a/src/server/field_of_view.c b/src/server/field_of_view.c
deleted file mode 100644 (file)
index 45c90e6..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-/* src/server/field_of_view.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "field_of_view.h"
-#include <math.h> /* pow() */
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t, int32_t, UINT8_MAX */
-#include <stdlib.h> /* free() */
-#include <string.h> /* memset() */
-#include "../common/rexit.h" /* exit_trouble() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "../common/yx_uint8.h" /* yx_uint8 */
-#include "map.h" /* mv_yx_in_dir_legal(), init_empty_map() */
-#include "rrand.h" /* rrand() */
-#include "things.h" /* Thing, ThingInMemory, add_thing_to_memory_map() */
-#include "world.h" /* world  */
-
-
-
-/* Number of degrees a circle is divided into. The greater it is, the greater
- * the angle precision. But make it one whole zero larger and bizarre FOV bugs
- * appear on large maps, probably due to value overflows (TODO: more research!).
- */
-#define CIRCLE 3600000
-
-
-
-/* Angle of a shadow. */
-struct shadow_angle
-{
-    struct shadow_angle * next;
-    uint32_t left_angle;
-    uint32_t right_angle;
-};
-
-
-
-/* Recalculate angle < 0 or > CIRCLE to a value between these two limits. */
-static uint32_t correct_angle(int32_t angle);
-
-/* Try merging the angle between "left_angle" and "right_angle" to "shadow" if
- * it meets the shadow from the right or the left. Returns 1 on success, else 0.
- */
-static uint8_t try_merge(struct shadow_angle * shadow,
-                         uint32_t left_angle, uint32_t right_angle);
-
-/* Try merging the shadow angle between "left_angle" and "right_angle" into an
- * existing shadow angle in "shadows". On success, see if this leads to any
- * additional shadow angle overlaps and merge these accordingly. Return 1 on
- * success, else 0.
- */
-static uint8_t try_merging_angles(uint32_t left_angle, uint32_t right_angle,
-                                  struct shadow_angle ** shadows);
-
-/* Test whether angle between "left_angle" and "right_angle", or at least
- * "middle_angle", is captured inside one of the shadow angles in "shadows". If
- * so, set hex in "fov_map" indexed by "pos_in_map" to 'H'. If the whole angle
- * and not just "middle_angle" is captured, return 1. Any other case: 0.
- */
-static uint8_t shade_hex(uint32_t left_angle, uint32_t right_angle,
-                         uint32_t middle_angle, struct shadow_angle ** shadows,
-                         uint16_t pos_in_map, char * fov_map);
-
-/* To "shadows", add shadow defined by "left_angle" and "right_angle", either as
- * new entry or as part of an existing shadow (swallowed whole or extending it).
- */
-static void set_shadow(uint32_t left_angle, uint32_t right_angle,
-                       struct shadow_angle ** shadows);
-
-/* Free shadow angles list "angles". */
-static void free_angles(struct shadow_angle * angles);
-
-/* Evaluate map position "test_pos" in distance "dist" to the view origin, and
- * on the circle of that distance to the origin on hex "hex_i" (as counted from
- * the circle's rightmost point), for setting shaded hexes in "fov_map" and
- * potentially adding a new shadow to linked shadow angle list "shadows".
- */
-static void eval_position(uint16_t dist, uint16_t hex_i, char * fov_map,
-                          struct yx_uint8 * test_pos,
-                          struct shadow_angle ** shadows);
-
-/* Update "t_eye"'s things-on-map memory by removing from its .t_mem all
- * memorized thing in FOV, and adding inanimate things in FOV to it.
- */
-static void add_things_to_map_memory(struct Thing * t_eye);
-
-
-
-static uint32_t correct_angle(int32_t angle)
-{
-    while (angle < 0)
-    {
-        angle = angle + CIRCLE;
-    }
-    while (angle > CIRCLE)
-    {
-        angle = angle - CIRCLE;
-    }
-    return angle;
-}
-
-
-
-static uint8_t try_merge(struct shadow_angle * shadow,
-                         uint32_t left_angle, uint32_t right_angle)
-{
-    if      (   shadow->right_angle <= left_angle + 1
-             && shadow->right_angle >= right_angle)
-    {
-        shadow->right_angle = right_angle;
-    }
-    else if (   shadow->left_angle + 1 >= right_angle
-             && shadow->left_angle     <= left_angle)
-    {
-        shadow->left_angle = left_angle;
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static uint8_t try_merging_angles(uint32_t left_angle, uint32_t right_angle,
-                                  struct shadow_angle ** shadows)
-{
-    uint8_t angle_merge = 0;
-    struct shadow_angle * shadow;
-    for (shadow = *shadows; shadow; shadow = shadow->next)
-    {
-        if (try_merge(shadow, left_angle, right_angle))
-        {
-            angle_merge = 1;
-        }
-    }
-    if (angle_merge)
-    {
-        struct shadow_angle * shadow1;
-        for (shadow1 = *shadows; shadow1; shadow1 = shadow1->next)
-        {
-            struct shadow_angle * last_shadow = NULL;
-            struct shadow_angle * shadow2;
-            for (shadow2 = *shadows; shadow2; shadow2 = shadow2->next)
-            {
-                if (   shadow1 != shadow2
-                    && try_merge(shadow1, shadow2->left_angle,
-                                          shadow2->right_angle))
-                {
-                    struct shadow_angle * to_free = shadow2;
-                    if (last_shadow)
-                    {
-                        last_shadow->next = shadow2->next;
-                        shadow2 = last_shadow;
-                    }
-                    else
-                    {
-                        *shadows = shadow2->next;
-                        shadow2 = *shadows;
-                    }
-                    free(to_free);
-                }
-                last_shadow = shadow2;
-            }
-        }
-    }
-    return angle_merge;
-}
-
-
-
-static uint8_t shade_hex(uint32_t left_angle, uint32_t right_angle,
-                         uint32_t middle_angle, struct shadow_angle ** shadows,
-                         uint16_t pos_in_map, char * fov_map)
-{
-    struct shadow_angle * shadow_i;
-    if (fov_map[pos_in_map] == 'v')
-    {
-        for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next)
-        {
-            if (   left_angle <=  shadow_i->left_angle
-                && right_angle >= shadow_i->right_angle)
-            {
-                fov_map[pos_in_map] = 'H';
-                return 1;
-            }
-            if (   middle_angle < shadow_i->left_angle
-                && middle_angle > shadow_i->right_angle)
-            {
-                fov_map[pos_in_map] = 'H';
-            }
-        }
-    }
-    return 0;
-}
-
-
-
-static void set_shadow(uint32_t left_angle, uint32_t right_angle,
-                       struct shadow_angle ** shadows)
-{
-    struct shadow_angle * shadow_i;
-    if (!try_merging_angles(left_angle, right_angle, shadows))
-    {
-        struct shadow_angle * shadow;
-        shadow = try_malloc(sizeof(struct shadow_angle), __func__);
-        shadow->left_angle  = left_angle;
-        shadow->right_angle = right_angle;
-        shadow->next = NULL;
-        if (*shadows)
-        {
-            for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next)
-            {
-                if (!shadow_i->next)
-                {
-                    shadow_i->next = shadow;
-                    return;
-                }
-            }
-        }
-        *shadows = shadow;
-    }
-}
-
-
-
-static void free_angles(struct shadow_angle * angles)
-{
-    if (angles->next)
-    {
-        free_angles(angles->next);
-    }
-    free(angles);
-}
-
-
-
-static void eval_position(uint16_t dist, uint16_t hex_i, char * fov_map,
-                          struct yx_uint8 * test_pos,
-                          struct shadow_angle ** shadows)
-{
-    int32_t left_angle_uncorrected =   ((CIRCLE / 12) / dist)
-                                     - (hex_i * (CIRCLE / 6) / dist);
-    int32_t right_angle_uncorrected =   left_angle_uncorrected
-                                      - (CIRCLE / (6 * dist));
-    uint32_t left_angle  = correct_angle(left_angle_uncorrected);
-    uint32_t right_angle = correct_angle(right_angle_uncorrected);
-    uint32_t right_angle_1st = right_angle > left_angle ? 0 : right_angle;
-    uint32_t middle_angle = 0;
-    if (right_angle_1st)
-    {
-        middle_angle = right_angle + ((left_angle - right_angle) / 2);
-    }
-    uint16_t pos_in_map = test_pos->y * world.map.length + test_pos->x;
-    uint8_t all_shaded = shade_hex(left_angle, right_angle_1st, middle_angle,
-                                   shadows, pos_in_map, fov_map);
-    if (!all_shaded && 'X' == world.map.cells[pos_in_map])
-    {
-        set_shadow(left_angle, right_angle_1st, shadows);
-        if (right_angle_1st != right_angle)
-        {
-            left_angle = CIRCLE;
-            set_shadow(left_angle, right_angle, shadows);
-        }
-    }
-}
-
-
-
-static void add_things_to_map_memory(struct Thing * t_eye)
-{
-    struct ThingInMemory * tm = t_eye->t_mem;
-    struct ThingInMemory * tm_prev = NULL;
-    struct ThingInMemory * tm_next = NULL;
-    for (; tm; tm = tm_next)
-    {
-        tm_next = tm->next;
-        if ('v' == t_eye->fov_map[tm->pos.y * world.map.length + tm->pos.x])
-        {
-            if (tm_prev)
-            {
-                tm_prev->next = tm->next;
-            }
-            else
-            {
-                t_eye->t_mem = tm->next;
-            }
-            free(tm);
-            continue;
-        }
-        tm_prev = tm;
-    }
-    struct Thing * t = world.things;
-    for (; t; t = t->next)
-    {
-        if (   !t->lifepoints
-            && 'v' == t_eye->fov_map[t->pos.y * world.map.length + t->pos.x])
-        {
-            add_thing_to_memory_map(t_eye, t->type, t->pos.y, t->pos.x);
-        }
-    }
-}
-
-
-
-extern void update_map_memory(struct Thing * t_eye, uint8_t age_map)
-{
-    if (!t_eye->mem_map)
-    {
-        init_empty_map(&(t_eye->mem_map));
-    }
-    if (!t_eye->mem_depth_map)
-    {
-        init_empty_map(&(t_eye->mem_depth_map));
-    }
-    uint32_t i;
-    for (i = 0; i < (uint32_t) (world.map.length * world.map.length); i++)
-    {
-        if ('v' == t_eye->fov_map[i])
-        {
-            t_eye->mem_depth_map[i] = '0';
-            if (' ' == t_eye->mem_map[i])
-            {
-                t_eye->mem_map[i] = world.map.cells[i];
-            }
-            continue;
-        }
-        if (age_map &&
-            '0' <= t_eye->mem_depth_map[i] && '9' > t_eye->mem_depth_map[i]
-            && !(rrand() % (uint16_t) pow(2, t_eye->mem_depth_map[i] - 48)))
-        {
-            t_eye->mem_depth_map[i]++;
-        }
-    }
-    add_things_to_map_memory(t_eye);
-}
-
-
-
-extern void build_fov_map(struct Thing * t)
-{
-    uint32_t map_size = world.map.length * world.map.length;
-    t->fov_map = t->fov_map ? t->fov_map : try_malloc(map_size, __func__);
-    memset(t->fov_map, 'v', map_size);
-    struct shadow_angle * shadows = NULL;
-    struct yx_uint8 test_pos = t->pos;
-    char * circledirs_string = "xswedc";
-    uint16_t circle_i;
-    uint8_t circle_is_on_map;
-    for (circle_i = 1, circle_is_on_map = 1; circle_is_on_map; circle_i++)
-    {
-        circle_is_on_map = 0;
-        if (1 < circle_i)                      /* All circles but the 1st are */
-        {                                      /* moved into starting from a  */
-            mv_yx_in_dir_legal('c', &test_pos);/* previous circle's last hex, */
-        }                                      /* i.e. from the upper left.   */
-        char dir_char = 'd'; /* Circle's 1st hex is entered by rightward move.*/
-        uint8_t dir_char_pos_in_circledirs_string = UINT8_MAX;
-        uint16_t dist_i, hex_i;
-        for (hex_i=0, dist_i=circle_i; hex_i < 6 * circle_i; dist_i++, hex_i++)
-        {
-            if (circle_i < dist_i)
-            {
-                dist_i = 1;
-                dir_char=circledirs_string[++dir_char_pos_in_circledirs_string];
-            }
-            if (mv_yx_in_dir_legal(dir_char, &test_pos))
-            {
-                eval_position(circle_i, hex_i, t->fov_map, &test_pos, &shadows);
-                circle_is_on_map = 1;
-            }
-        }
-    }
-    mv_yx_in_dir_legal(0, NULL);
-    free_angles(shadows);
-}
diff --git a/src/server/field_of_view.h b/src/server/field_of_view.h
deleted file mode 100644 (file)
index be6324b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* src/server/field_of_view.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Generate field of view maps.
- */
-
-#ifndef FIELD_OF_VIEW_H
-#define FIELD_OF_VIEW_H
-
-#include <stdint.h> /* uint8_t, uint32_t */
-struct Thing;
-
-
-
-/* Update "t_eye"'s .mem_map memory with what's in its current FOV, and update
- * and age the .mem_depth_map.
- */
-extern void update_map_memory(struct Thing * t_eye, uint8_t age_map);
-
-/* Build "t"'s field of view. */
-extern void build_fov_map(struct Thing * t);
-
-
-
-#endif
diff --git a/src/server/god_commands.c b/src/server/god_commands.c
deleted file mode 100644 (file)
index 0dbf1a3..0000000
+++ /dev/null
@@ -1,524 +0,0 @@
-/* src/server/god_commands.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "god_commands.h"
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t */
-#include <stdlib.h> /* atoi(), free() */
-#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(), update_map_memory() */
-#include "hardcoded_strings.h" /* s */
-#include "init.h" /* remake_world() */
-#include "map.h" /* init_empty_map(), remake_map() */
-#include "thing_actions.h" /* ThingAction, actor_wait(), actor_move(),
-                            * actor_use(), actor_pickup(), actor_drop()
-                            */
-#include "things.h" /* Thing, ThingType, add_thing(), get_thing(), own_thing(),
-                     * free_things(), add_thing_to_memory_map(),get_thing_type(),
-                     * get_player()
-                     */
-#include "world.h" /* world */
-
-
-
-/* Parse/apply god command in "tok0"/tok1" to manipulate a ThingType*/
-static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1);
-
-/* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction
- * .func from .name for set_members().
- */
-static uint8_t try_func_name(struct ThingAction * ta, char * name,
-                             void (* func) (struct Thing *));
-
-/* Parse/apply god command in "tok0"/"tok1" to manipulate a ThingAction. */
-static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1);
-
-/* 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 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" on "t" owning another thing. */
-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_1arg(char * tok0, char * tok1);
-
-/* Performs parse_world_active()'s world activation legality tests. */
-static uint8_t world_may_be_set_active();
-
-/* Unlink worldstate file if it exists. */
-static void remove_worldstate_file();
-
-/* Parse/apply god command in "tok0"/"tok1" on toggling world.exists. Unset if
- * argument is 0 and unlink worldstate file, but only set it on positive
- * argument if it is not already set and a thing action of name S_CMD_WAIT, a
- * player thing and a map are defined. On setting it, rebuild all FOVs.
- */
-static uint8_t parse_world_active(char * tok0, char * tok1);
-
-/* Parse/apply god command in "tok0"/"tok1" to reset world.map.length. On
- * re-set, set world.exists to 0, remove all things and free world.map.cells
- */
-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)
-{
-    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])
-         || !strcmp(tok0, s[S_CMD_TT_CORPS])  || !strcmp(tok0, s[S_CMD_TT_HP])
-         || !strcmp(tok0, s[S_CMD_TT_PROL])))
-    {
-        return err_line(1, "No thing type defined to manipulate yet.");
-    }
-    int16_t id;
-    if (   parse_val(tok0,tok1,s[S_CMD_TT_CONSUM],'u',(char *) &tt->consumable)
-        || parse_val(tok0,tok1,s[S_CMD_TT_HP],'8',(char *) &tt->lifepoints)
-        || parse_val(tok0,tok1,s[S_CMD_TT_STARTN],'8',(char *) &tt->start_n)
-        || parse_val(tok0,tok1,s[S_CMD_TT_SYMB],'c',(char *) &tt->char_on_map)
-        || parse_val(tok0,tok1,s[S_CMD_TT_PROL],'8',(char *) &tt->proliferate)
-        || parse_val(tok0,tok1,s[S_CMD_TT_NAME],'s',(char *) &tt->name))
-    {
-        ;
-    }
-    else if (parse_val(tok0, tok1, s[S_CMD_TT_CORPS],'8',(char *)&id))
-    {
-        if (!get_thing_type(id))
-        {
-            return err_line(1, "Corpse ID belongs to no known thing type.");
-        }
-        tt->corpse_id = id;
-    }
-    else if (parse_val(tok0, tok1, s[S_CMD_TT_ID], 'i', (char *) &id))
-    {
-        tt = get_thing_type(id);
-        if (!tt)
-        {
-            tt = add_thing_type(id);
-        }
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static uint8_t try_func_name(struct ThingAction * ta, char * name,
-                             void (* func) (struct Thing *))
-{
-    if (0 == strcmp(ta->name, name))
-    {
-        ta->func = func;
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1)
-{
-    if (!ta &&
-        (!strcmp(tok0, s[S_CMD_TA_EFFORT]) || !strcmp(tok0, s[S_CMD_TA_NAME])))
-    {
-        return err_line(1, "No thing action defined to manipulate yet.");
-    }
-    int16_t id;
-    if      (parse_val(tok0, tok1, s[S_CMD_TA_EFFORT],'8',(char *)&ta->effort));
-    else if (parse_val(tok0, tok1, s[S_CMD_TA_NAME], 's', (char *)&ta->name))
-    {
-        if (!(   try_func_name(ta, s[S_CMD_MOVE], actor_move)
-              || try_func_name(ta, s[S_CMD_PICKUP], actor_pick)
-              || try_func_name(ta, s[S_CMD_WAIT], actor_wait)
-              || try_func_name(ta, s[S_CMD_DROP], actor_drop)
-              || try_func_name(ta, s[S_CMD_USE], actor_use)))
-        {
-            return err_line(1, "Invalid action function name.");
-        }
-        if (world.exists)
-        {         /* Legal worlds have at least one thing action for waiting. */
-            world.exists = 0 != get_thing_action_id_by_name(s[S_CMD_WAIT]);
-            if (!world.exists)
-            {
-                remove_worldstate_file();
-            }
-        }
-    }
-    else if (parse_val(tok0, tok1, s[S_CMD_TA_ID], '8', (char *) &id))
-    {
-        ta = get_thing_action(id);
-        if (!ta)
-        {
-            ta = add_thing_action(id);
-        }
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
-{
-    uint8_t type;
-    if (parse_val(tok0, tok1, s[S_CMD_T_TYPE], '8', (char *) &type))
-    {
-        struct ThingType * tt = get_thing_type(type);
-        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[S_CMD_T_COMMAND], '8', (char *) &command))
-    {
-        if (!command)
-        {
-            t->command = command;
-            return 1;
-        }
-        struct ThingAction * ta = world.thing_actions;
-        for (; 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_position(char* tok0, char * tok1, struct Thing * t)
-{
-    char axis = 0;
-    if      (!strcmp(tok0, s[S_CMD_T_POSY]))
-    {
-        axis = 'y';
-    }
-    else if (!strcmp(tok0, s[S_CMD_T_POSX]))
-    {
-        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;
-            }
-            if (world.exists && t->lifepoints)
-            {
-                build_fov_map(t);
-                if (t == get_player())
-                {
-                    update_map_memory(t, 1);
-                }
-            }
-        }
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
-{
-    uint8_t id;
-    if (parse_val(tok0, tok1, s[S_CMD_T_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 not available for carrying."))
-            {
-                own_thing(&(t->owns), &world.things, id);
-                o->pos = t->pos;
-            }
-        }
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static uint8_t parse_thing_manipulation_1arg(char * tok0, char * tok1)
-{
-    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])
-         || !strcmp(tok0, s[S_CMD_T_POSY]) || !strcmp(tok0, s[S_CMD_T_ARGUMENT])
-         || !strcmp(tok0, s[S_CMD_T_HP]) || !strcmp(tok0, s[S_CMD_T_COMMAND])
-         || !strcmp(tok0, s[S_CMD_T_SATIATION])))
-    {
-        return err_line(1, "No thing defined to manipulate yet.");
-    }
-    int16_t id;
-    if (   parse_thing_type(tok0, tok1, t)
-        || parse_thing_command(tok0, tok1, t)
-        || parse_val(tok0,tok1, s[S_CMD_T_ARGUMENT], '8', (char *)&t->arg)
-        || parse_val(tok0,tok1, s[S_CMD_T_PROGRESS], '8', (char *)&t->progress)
-        || parse_val(tok0,tok1, s[S_CMD_T_HP], '8', (char *) &t->lifepoints)
-        || parse_val(tok0,tok1, s[S_CMD_T_SATIATION], 'i',(char *)&t->satiation)
-        || parse_position(tok0, tok1, t)
-        || parse_carry(tok0, tok1, t));
-    else if (parse_val(tok0, tok1, s[S_CMD_T_ID], 'i', (char *) &id))
-    {
-        t = get_thing(world.things, id, 1);
-        char * err = "No thing type found to initialize new thing.";
-        if (!t && !err_line(!world.thing_types, err))
-        {
-            t = add_thing(id, world.thing_types->id, 0, 0);
-        }
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static uint8_t world_may_be_set_active()
-{
-    if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
-    {
-        err_line(1, "No thing action of name 'wait' found.");
-        return 0;
-    }
-    if (!get_player())
-    {
-        err_line(1, "No un-owned player thing (of id=0) found.");
-        return 0;
-    }
-    if (!world.map.cells)
-    {
-        err_line(1, "No map found.");
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static void remove_worldstate_file()
-{
-    if (!access(s[S_PATH_WORLDSTATE], F_OK))
-    {
-        int test = unlink(s[S_PATH_WORLDSTATE]);
-        exit_trouble(-1 == test, __func__, "unlink");
-    }
-}
-
-
-
-static uint8_t parse_world_active(char * tok0, char * tok1)
-{
-    if (!strcmp(tok0, s[S_CMD_WORLD_ACTIVE]) && !parsetest_int(tok1, '8'))
-    {
-        if (!parsetest_int(tok1, '8'))
-        {
-            uint8_t argument = atoi(tok1);
-            if (!argument)
-            {
-                remove_worldstate_file();
-                world.exists = 0;
-            }
-            else if (world.exists)
-            {
-                err_line(1, "World already active.");
-            }
-            else if (world_may_be_set_active())
-            {
-                struct Thing * ti;
-                for (ti = world.things; ti; ti = ti->next)
-                {
-                    if (ti->lifepoints)
-                    {
-                        build_fov_map(ti);
-                        if (ti == get_player())
-                        {
-                            update_map_memory(ti, 0);
-                        }
-                    }
-                }
-                world.exists = 1;
-            }
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
-
-static uint8_t set_map_length(char * tok0, char * tok1)
-{
-    if (!strcmp(tok0, s[S_CMD_MAPLENGTH]) && !parsetest_int(tok1, 'u'))
-    {
-        uint16_t argument = atoi(tok1);
-        if (argument < 1 || argument > 256)
-        {
-            return err_line(1, "Value must be >= 1 and <= 256.");
-        }
-        world.exists = 0;
-        remove_worldstate_file();
-        free_things(world.things);
-        free(world.map.cells);
-        world.map.cells = NULL;    /* Since remake_map() runs free() on this. */
-        world.map.length = argument;
-        return 1;
-    }
-    return 0;
-}
-
-
-
-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_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)
-        || parse_val(tok0,tok1,s[S_CMD_PLAYTYPE],'8',(char *)&world.player_type)
-        || parse_world_active(tok0, tok1));
-    else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
-
-    {
-        remake_map();
-    }
-    else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
-    {
-        uint8_t test = remake_world();
-        err_line(1 == test, "No player type with start number of >0 defined.");
-        err_line(2 == test, "No thing action with name 'wait' defined.");
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-extern uint8_t parse_god_command_2arg(char * tok0, char * tok1, char * tok2)
-{
-    if (!t && (   !strcmp(tok0, s[S_CMD_T_MEMMAP])
-               || !strcmp(tok0, s[S_CMD_T_MEMDEPTHMAP])))
-    {
-        return err_line(1, "No thing defined to manipulate yet.");
-    }
-    if (!strcmp(tok0,s[S_CMD_T_MEMMAP]) || !strcmp(tok0,s[S_CMD_T_MEMDEPTHMAP]))
-    {
-        uint8_t y = atoi(tok1);
-        if (parsetest_int(tok1, '8') || y >= world.map.length)
-        {
-            return err_line(1, "Illegal value for map line number.");
-        }
-        if (strlen(tok2) != world.map.length)
-        {
-            return err_line(1, "Map line length is unequal map width.");
-        }
-        if (!strcmp(tok0,s[S_CMD_T_MEMMAP]))
-        {
-            if (!t->mem_map)
-            {
-                init_empty_map(&(t->mem_map));
-            }
-            memcpy(t->mem_map + y * world.map.length, tok2, world.map.length);
-        }
-        else
-        {
-            if (!t->mem_depth_map)
-            {
-                init_empty_map(&(t->mem_depth_map));
-            }
-            memcpy(t->mem_depth_map+y*world.map.length, tok2, world.map.length);
-        }
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-extern uint8_t parse_god_command_3arg(char * tok0, char * tok1, char * tok2,
-                                      char * tok3)
-{
-    if (!t && !strcmp(tok0, s[S_CMD_T_MEMTHING]))
-    {
-        return err_line(1, "No thing defined to manipulate yet.");
-    }
-    if (!strcmp(tok0, s[S_CMD_T_MEMTHING]))
-    {
-        uint8_t id = atoi(tok1);
-        uint8_t y  = atoi(tok2);
-        uint8_t x  = atoi(tok3);
-        if (   parsetest_int(tok1, '8') || !get_thing_type(id)
-            || parsetest_int(tok2, '8') || y >= world.map.length
-            || parsetest_int(tok3, '8') || x >= world.map.length)
-        {
-            return err_line(1, "Illegal value for thing type or position.");
-        }
-        add_thing_to_memory_map(t, id, y, x);
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
diff --git a/src/server/god_commands.h b/src/server/god_commands.h
deleted file mode 100644 (file)
index bfe7454..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* src/server/god_commands.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * God commands and their interpretation by the server.
- */
-
-#ifndef GOD_COMMANDS_H
-#define GOD_COMMANDS_H
-
-#include <stdint.h> /* uint8_t */
-
-
-
-/* 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);
-extern uint8_t parse_god_command_3arg(char * tok0, char * tok1, char * tok2,
-                                      char * tok3);
-
-
-
-#endif
diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c
deleted file mode 100644 (file)
index dfb30e1..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/* hardcoded_strings.c *
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "hardcoded_strings.h"
-
-
-
-char * s[44];
-
-
-
-extern void init_strings()
-{
-    s[S_PATH_CONFIG] = "confserver/world";
-    s[S_PATH_WORLDSTATE] = "server/worldstate";
-    s[S_PATH_OUT] = "server/out";
-    s[S_PATH_IN] = "server/in";
-    s[S_PATH_RECORD] = "record";
-    s[S_PATH_SAVE] = "savefile";
-    s[S_FCN_SPRINTF] = "sprintf";
-    s[S_CMD_TA_ID] = "TA_ID";
-    s[S_CMD_TA_EFFORT] = "TA_EFFORT";
-    s[S_CMD_TA_NAME] = "TA_NAME";
-    s[S_CMD_TT_ID] = "TT_ID";
-    s[S_CMD_TT_CONSUM] = "TT_CONSUMABLE";
-    s[S_CMD_TT_STARTN] = "TT_START_NUMBER";
-    s[S_CMD_TT_HP] = "TT_LIFEPOINTS";
-    s[S_CMD_TT_SYMB] = "TT_SYMBOL";
-    s[S_CMD_TT_NAME] = "TT_NAME";
-    s[S_CMD_TT_CORPS] = "TT_CORPSE_ID";
-    s[S_CMD_TT_PROL] = "TT_PROLIFERATE";
-    s[S_CMD_T_ID] = "T_ID";
-    s[S_CMD_T_TYPE] = "T_TYPE";
-    s[S_CMD_T_POSY] = "T_POSY";
-    s[S_CMD_T_POSX] = "T_POSX";
-    s[S_CMD_T_COMMAND] =  "T_COMMAND";
-    s[S_CMD_T_ARGUMENT] = "T_ARGUMENT";
-    s[S_CMD_T_PROGRESS] = "T_PROGRESS";
-    s[S_CMD_T_HP] = "T_LIFEPOINTS";
-    s[S_CMD_T_SATIATION] = "T_SATIATION";
-    s[S_CMD_T_CARRIES] = "T_CARRIES";
-    s[S_CMD_T_MEMMAP] = "T_MEMMAP";
-    s[S_CMD_T_MEMDEPTHMAP] = "T_MEMDEPTHMAP";
-    s[S_CMD_T_MEMTHING] = "T_MEMTHING";
-    s[S_CMD_AI] = "ai";
-    s[S_CMD_WAIT] = "wait";
-    s[S_CMD_MOVE] = "move";
-    s[S_CMD_PICKUP] = "pick_up";
-    s[S_CMD_DROP] = "drop";
-    s[S_CMD_USE] = "use";
-    s[S_CMD_MAKE_WORLD] = "MAKE_WORLD";
-    s[S_CMD_WORLD_ACTIVE] = "WORLD_ACTIVE";
-    s[S_CMD_SEED_MAP] = "SEED_MAP";
-    s[S_CMD_SEED_RAND] = "SEED_RANDOMNESS";
-    s[S_CMD_TURN] = "TURN";
-    s[S_CMD_MAPLENGTH] = "MAP_LENGTH";
-    s[S_CMD_PLAYTYPE] = "PLAYER_TYPE";
-}
diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h
deleted file mode 100644 (file)
index 4725bc2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* hardcoded_strings.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * For re-used hardcoded strings.
- */
-
-#ifndef STRINGS_H
-#define STRINGS_H
-
-
-
-enum string_num
-{
-    S_PATH_CONFIG,
-    S_PATH_WORLDSTATE,
-    S_PATH_OUT,
-    S_PATH_IN,
-    S_PATH_RECORD,
-    S_PATH_SAVE,
-    S_FCN_SPRINTF,
-    S_CMD_TA_ID,
-    S_CMD_TA_EFFORT,
-    S_CMD_TA_NAME,
-    S_CMD_TT_ID,
-    S_CMD_TT_CONSUM,
-    S_CMD_TT_STARTN,
-    S_CMD_TT_HP,
-    S_CMD_TT_SYMB,
-    S_CMD_TT_NAME,
-    S_CMD_TT_CORPS,
-    S_CMD_TT_PROL,
-    S_CMD_T_ID,
-    S_CMD_T_TYPE,
-    S_CMD_T_POSY,
-    S_CMD_T_POSX,
-    S_CMD_T_COMMAND,
-    S_CMD_T_ARGUMENT,
-    S_CMD_T_PROGRESS,
-    S_CMD_T_HP,
-    S_CMD_T_SATIATION,
-    S_CMD_T_CARRIES,
-    S_CMD_T_MEMMAP,
-    S_CMD_T_MEMDEPTHMAP,
-    S_CMD_T_MEMTHING,
-    S_CMD_AI,
-    S_CMD_WAIT,
-    S_CMD_MOVE,
-    S_CMD_PICKUP,
-    S_CMD_DROP,
-    S_CMD_USE,
-    S_CMD_MAKE_WORLD,
-    S_CMD_WORLD_ACTIVE,
-    S_CMD_SEED_MAP,
-    S_CMD_SEED_RAND,
-    S_CMD_TURN,
-    S_CMD_MAPLENGTH,
-    S_CMD_PLAYTYPE
-};
-
-extern void init_strings();
-
-extern char * s[44];
-
-
-
-#endif
diff --git a/src/server/init.c b/src/server/init.c
deleted file mode 100644 (file)
index f44c504..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/* src/server/init.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#define _POSIX_C_SOURCE 2 /* getopt(), optarg */
-#include "init.h"
-#include <errno.h> /* global errno, EEXIST */
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint32_t */
-#include <stdio.h> /* FILE, sprintf(), fflush() */
-#include <stdlib.h> /* exit(), free(), atoi() */
-#include <string.h> /* strlen() */
-#include <sys/stat.h> /* mkdir() */
-#include <sys/types.h> /* defines pid_t, time_t */
-#include <time.h> /* time() */
-#include <unistd.h> /* optarg, getopt(), access(), getpid() */
-#include "../common/parse_file.h" /* err_line_zero(), err_line_inc() */
-#include "../common/readwrite.h" /* try_fopen(), try_fclose(), textfile_width(),
-                                  * try_fgets(), try_fwrite(),
-                                  * detect_atomic_leftover()
-                                  */
-#include "../common/rexit.h" /* exit_err(), exit_trouble() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "cleanup.h" /* set_cleanup_flag() */
-#include "field_of_view.h" /* update_map_memory() */
-#include "hardcoded_strings.h" /* s */
-#include "map.h" /* remake_map() */
-#include "things.h" /* Thing, ThingType, free_things(), add_things(),
-                     * get_thing_id_action_id_by_name(), get_player()
-                     */
-#include "run.h" /* obey_msg(), io_loop(), record(), send_to_outfile() */
-#include "world.h" /* global world */
-
-
-
-
-/* Pass to obey_msg() lines from file at "path", on "record" write to same. Do
- * not pass lines that consist only of a newline character. Transform newline
- * in the line passed to \0.
- */
-static void obey_lines_from_file(char * path, uint8_t record);
-
-/* 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();
-
-/* Return 1 if the type defined by world.player_type has a .start_n of 0.
- * Return 2 if no thing action with .name of s[S_CMD_WAIT] is defined.
- * Else, return 0.
- */
-static uint8_t world_cannot_be_made();
-
-
-static void obey_lines_from_file(char * path, uint8_t record)
-{
-    FILE * file = try_fopen(path, "r", __func__);
-    uint32_t linemax = textfile_width(file);
-    char * line = try_malloc(linemax + 1, __func__);
-    while (try_fgets(line, linemax + 1, file, __func__))
-    {
-        if (strlen(line))
-        {
-            if (strcmp("\n", line))
-            {
-                char * nl = strchr(line, '\n');
-                if (nl)
-                {
-                    *nl = '\0';
-                }
-                obey_msg(line, record);
-            }
-            err_line_inc();
-        }
-    }
-    free(line);
-    try_fclose(file, __func__);
-}
-
-
-
-static void replay_game()
-{
-    exit_err(access(s[S_PATH_RECORD], F_OK), "No record found to replay.");
-    FILE * file = try_fopen(s[S_PATH_RECORD], "r", __func__);
-    uint32_t linemax = textfile_width(file);
-    char * line = try_malloc(linemax + 1, __func__);
-    while (   world.turn < world.replay
-           && try_fgets(line, linemax + 1, file, __func__))
-    {
-        obey_msg(line, 0);
-        err_line_inc();
-    }
-    uint8_t end = 0;
-    while (3 == io_loop(2))
-    {
-        if (!end)
-        {
-            end = (NULL == try_fgets(line, linemax + 1, file, __func__));
-            if (!end)
-            {
-                obey_msg(line, 0);
-                err_line_inc();
-            }
-        }
-    }
-    free(line);
-    try_fclose(file, __func__);
-}
-
-
-
-static uint8_t world_cannot_be_made()
-{
-    uint8_t player_will_be_generated = 0;
-    struct ThingType * tt;
-    for (tt = world.thing_types; tt; tt = tt->next)
-    {
-        if (world.player_type == tt->id)
-        {
-            player_will_be_generated = 0 < tt->start_n;
-            break;
-        }
-    }
-    if (!player_will_be_generated)
-    {
-        return 1;
-    }
-    if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
-    {
-        return 2;
-    }
-    return 0;
-}
-
-
-
-extern void obey_argv(int argc, char * argv[])
-{
-    int opt;
-    while (-1 != (opt = getopt(argc, argv, "vs::")))
-    {
-        if      ('v' == opt)
-        {
-            world.is_verbose = 1;
-        }
-        else if ('s' == opt)
-        {
-            world.replay = 1;
-            if (optarg)
-            {
-                world.replay = atoi(optarg);
-            }
-        }
-        else
-        {
-            exit(EXIT_FAILURE);
-        }
-    }
-}
-
-
-
-extern void setup_server_io()
-{
-    int test = mkdir("server", 0777);
-    exit_trouble(test && EEXIST != errno, __func__, "mkdir");
-    world.file_out = try_fopen(s[S_PATH_OUT], "w", __func__);
-    world.server_test = try_malloc(10 + 1 + 10 + 1 + 1, __func__);
-    test = sprintf(world.server_test, "%d %d\n", getpid(), (int) time(0));
-    exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-    try_fwrite(world.server_test, strlen(world.server_test), 1,
-               world.file_out, __func__);
-    fflush(world.file_out);
-    set_cleanup_flag(CLEANUP_OUT);
-    char * path_in = s[S_PATH_IN];
-    if (!access(path_in, F_OK))        /* This keeps out input from old input */
-    {                                  /* file streams of clients             */
-        unlink(path_in)   ;            /* communicating with server processes */
-    }                                  /* superseded by this current one.     */
-    world.file_in = try_fopen(path_in, "w", __func__);
-    try_fclose(world.file_in, __func__);
-    world.file_in = try_fopen(path_in, "r", __func__);
-    set_cleanup_flag(CLEANUP_IN);
-}
-
-
-
-extern uint8_t remake_world()
-{
-    uint8_t test = world_cannot_be_made();
-    if (test)
-    {
-        return test;
-    }
-    world.seed_map = world.seed;
-    free_things(world.things);
-    remake_map();
-    world.exists = 1;
-    struct ThingType * tt;
-    for (tt = world.thing_types; tt; tt = tt->next)
-    {
-        if (world.player_type == tt->id)
-        {
-            add_things(tt->id, tt->start_n);
-            break;
-        }
-    }
-    update_map_memory(get_player(), 1);
-    for (tt = world.thing_types; tt; tt = tt->next)
-    {
-        if (world.player_type != tt->id)
-        {
-            add_things(tt->id, tt->start_n);
-        }
-    }
-    world.turn = 1;
-    send_to_outfile("NEW_WORLD\n", 1);
-    return 0;
-}
-
-
-
-extern void run_game()
-{
-    detect_atomic_leftover(s[S_PATH_SAVE]);
-    detect_atomic_leftover(s[S_PATH_RECORD]);
-    err_line_zero();
-    if (world.replay)
-    {
-        replay_game();
-        return;
-    }
-    if (!access(s[S_PATH_SAVE], F_OK))
-    {
-        obey_lines_from_file(s[S_PATH_SAVE], 0);
-    }
-    else
-    {
-        char * err = "No world config file from which to start a new world.";
-        exit_err(access(s[S_PATH_CONFIG], F_OK), err);
-        obey_lines_from_file(s[S_PATH_CONFIG], 1);
-        err_line_zero();
-        char * command = s[S_CMD_MAKE_WORLD];
-        char * msg = try_malloc(strlen(command) + 1 + 11 + 1, __func__);
-        int test = sprintf(msg, "%s %d", command, (int) time(NULL));
-        exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-        obey_msg(msg, 1);
-        free(msg);
-    }
-    err_line_zero();
-    io_loop(1);
-    record(NULL, 1);
-}
diff --git a/src/server/init.h b/src/server/init.h
deleted file mode 100644 (file)
index 6161200..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* src/server/init.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Server, world and game state initialization.
- */
-
-#ifndef INIT_H
-#define INIT_H
-
-#include <stdint.h> /* uint8_t */
-
-
-
-/* Parses command line arguments -v and -s into server configuration. */
-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, generates a new one from world.seed.
- * The map is populated according to world.thing_types start numbers. world.turn
- * is set to 1, as is .exists and .do_update, so that io_round() is told to
- * update the worldstate file. Returns 0 on success, and if the world cannot be
- * generated 1 since there is no player type or it has .n_start of 0, 2 if no
- * "wait" thing action is defined.
- */
-extern uint8_t remake_world();
-
-/* Create a game world state, then enter play or replay mode.
- *
- * 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 first
- * following the commands from the world config file, then running the
- * MAKE_WORLD command. Manual replay as well as manual play mode take place
- * inside io_loop().
- */
-extern void run_game();
-
-
-
-#endif
diff --git a/src/server/io.c b/src/server/io.c
deleted file mode 100644 (file)
index 06ee838..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-/* src/server/io.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#define _POSIX_C_SOURCE 200112L /* snrpintf() */
-#include "io.h"
-#include <errno.h> /* global errno */
-#include <limits.h> /* PIPE_BUF */
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t, int32_t, UINT8_MAX */
-#include <stdio.h> /* defines FILE, sprintf(), fprintf() */
-#include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), snprintf(), memcpy(), strchr() */
-#include <sys/types.h> /* time_t */
-#include <time.h> /* time(), nanosleep() */
-#include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(),
-                                  * get_message_from_queue(), try_fwrite(),
-                                  * read_file_into_queue(), try_fputc()
-                                  */
-#include "../common/rexit.h" /* exit_trouble() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "cleanup.h" /* set_cleanup_flag() */
-#include "hardcoded_strings.h" /* s */
-#include "map.h" /* init_empty_map() */
-#include "run.h" /* send_to_outfile() */
-#include "things.h" /* Thing, ThingType, ThingInMemory, ThingAction,
-                     * get_thing_type(), get_player()
-                     */
-#include "world.h" /* global world  */
-
-
-
-/* Helpers to write lines of god commands to recreate thing "t". */
-static void write_key_space(FILE * file, char * key);
-static void write_uvalue(FILE * file, uint32_t value);
-static void write_string(FILE * file, char * string);
-static void write_key_space_uvalue(FILE * file, char * key, uint32_t value);
-static void write_key_space_svalue(FILE * file, char * key, int32_t value);
-static void write_key_space_string(FILE * file, char * key, char * string);
-
-/* Write to "file" game-map-sized "map" in "command"-prefixed numbered lines. */
-static void write_mem_map(FILE * file, char * map, char * command);
-
-/* Write to "file" \n-delimited line of "key" + space + "value" as string. */
-static void write_thing(FILE * file, struct Thing * t);
-
-/* Poll input file for world.queue input. Wait a few seconds until giving up;
- * poll only every 0.03 seconds. Translate '\n' chars in input file into '\0'.
- */
-static void try_growing_queue();
-
-/* Write world state as visible to clients to its file. Write single dot line to
- * server output file to satisfy client ping mechanisms.
- */
-static void update_worldstate_file();
-
-/* Write "value" to new \n-delimited line of "file". */
-static void write_value_as_line(int32_t value, FILE * file);
-
-/* Write to "file" player's inventory, one item name per line. End in "%\n". */
-static void write_inventory(struct Thing * player, FILE * file);
-
-/* Return map cells sequence as visible to the "player", with invisible cells as
- * whitespace. Super-impose over visible map cells things positioned there,
- * with animate things overwriting inanimate things, and inanimate consumable
- * things overwriting inanimate non-consumable things.
- */
-static char * build_visible_map(struct Thing * player);
-
-/* 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 and .t_mem. Write one row per \n-delimited line.
- */
-static void write_map(struct Thing * player, FILE * file);
-
-
-
-static void write_key_space(FILE * file, char * key)
-{
-    try_fwrite(key, strlen(key), 1, file, __func__);
-    try_fputc(' ', file, __func__);
-}
-
-
-
-static void write_uvalue(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);
-}
-
-
-
-static void write_string(FILE * file, char * string)
-{
-    try_fputc('\'', file, __func__);
-    try_fwrite(string, strlen(string), 1, file, __func__);
-    try_fputc('\'', file, __func__);
-}
-
-
-
-static void write_key_space_uvalue(FILE * file, char * key, uint32_t value)
-{
-    write_key_space(file, key);
-    write_uvalue(file, value);
-    try_fputc('\n', file, __func__);
-}
-
-
-
-static void write_key_space_svalue(FILE * file, char * key, int32_t value)
-{
-    write_key_space(file, key);
-    char * line = try_malloc(11, __func__);
-    exit_trouble(-1 == sprintf(line, "%d", 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_space_string(FILE * file, char * key, char * string)
-{
-    write_key_space(file, key);
-    write_string(file, string);
-    try_fputc('\n', file, __func__);
-}
-
-
-
-static void write_mem_map(FILE * file, char * map, char * command)
-{
-    if (map)
-    {
-        uint32_t map_size = world.map.length * world.map.length;/* snprintf() */
-        char * map_copy = try_malloc(map_size + 1, __func__);    /* reads one */
-        memcpy(map_copy, map, map_size);              /* byte beyond map_size */
-        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",
-                                map_copy + (y * world.map.length));
-            exit_trouble(test < 0, __func__, "snprintf()");
-            write_key_space(file, command);
-            write_uvalue(file, y);
-            try_fputc(' ', file, __func__);
-            write_string(file, string);
-            try_fputc('\n', file, __func__);
-        }
-        free(map_copy);
-    }
-}
-
-
-
-static void write_thing(FILE * file, struct Thing * t)
-{
-    struct Thing * o;
-    for (o = t->owns; o; o = o->next)
-    {
-        write_thing(file, o);
-    }
-    write_key_space_uvalue(file, s[S_CMD_T_ID], t->id);
-    struct ThingInMemory * tm = t->t_mem;
-    for (; tm; tm = tm->next)
-    {
-        write_key_space(file, s[S_CMD_T_MEMTHING]);
-        write_uvalue(file, tm->type);
-        try_fputc(' ', file, __func__);
-        write_uvalue(file, tm->pos.y);
-        try_fputc(' ', file, __func__);
-        write_uvalue(file, tm->pos.x);
-        try_fputc('\n', file, __func__);
-    }
-    write_key_space_uvalue(file, s[S_CMD_T_COMMAND], t->command);
-    write_key_space_uvalue(file, s[S_CMD_T_HP], t->lifepoints);
-    write_key_space_uvalue(file, s[S_CMD_T_TYPE], t->type);
-    write_key_space_uvalue(file, s[S_CMD_T_ARGUMENT], t->arg);
-    write_key_space_uvalue(file, s[S_CMD_T_POSY], t->pos.y);
-    write_key_space_uvalue(file, s[S_CMD_T_POSX], t->pos.x);
-    write_key_space_uvalue(file, s[S_CMD_T_PROGRESS], t->progress);
-    write_mem_map(file, t->mem_depth_map, s[S_CMD_T_MEMDEPTHMAP]);
-    write_key_space_svalue(file, s[S_CMD_T_SATIATION], t->satiation);
-    write_mem_map(file, t->mem_map, s[S_CMD_T_MEMMAP]);
-}
-
-
-
-static void write_thing_carrying(FILE * file, struct Thing * t)
-{
-    if (t->owns)
-    {
-        write_key_space_uvalue(file, s[S_CMD_T_ID], t->id);
-        struct Thing * o;
-        for (o = t->owns; o; o = o->next)
-        {
-            write_key_space_uvalue(file, s[S_CMD_T_CARRIES], o->id);
-        }
-    }
-}
-
-
-
-static void try_growing_queue()
-{
-    uint8_t wait_seconds = 5;
-    time_t now = time(0);
-    struct timespec dur;
-    dur.tv_sec = 0;
-    dur.tv_nsec = 33333333;
-    while (1)
-    {
-        if (read_file_into_queue(world.file_in, &world.queue))
-        {
-            return;
-        }
-        nanosleep(&dur, NULL);
-        if (time(0) > now + wait_seconds)
-        {
-            return;
-        }
-    }
-}
-
-
-
-static void update_worldstate_file()
-{
-    char * path_tmp;
-    FILE * file = atomic_write_start(s[S_PATH_WORLDSTATE], &path_tmp);
-    struct Thing * player = get_player();
-    write_value_as_line(world.turn, file);
-    write_value_as_line(player->lifepoints, file);
-    write_value_as_line(player->satiation, file);
-    write_inventory(player, file);
-    write_value_as_line(player->pos.y, file);
-    write_value_as_line(player->pos.x, file);
-    write_value_as_line(world.map.length, file);
-    write_map(player, file);
-    atomic_write_finish(file, s[S_PATH_WORLDSTATE], path_tmp);
-    set_cleanup_flag(CLEANUP_WORLDSTATE);
-    char * dot = ".\n";
-    try_fwrite(dot, strlen(dot), 1, world.file_out, __func__);
-    fflush(world.file_out);
-}
-
-
-
-static void write_value_as_line(int32_t value, FILE * file)
-{
-    char write_buf[13]; /* Hold "+"/"-" + 10 digits of int32_t max + \n + \0. */
-    exit_trouble(sprintf(write_buf,"%u\n",value) < 0,__func__,s[S_FCN_SPRINTF]);
-    try_fwrite(write_buf, strlen(write_buf), 1, file, __func__);
-}
-
-
-
-static void write_inventory(struct Thing * player, FILE * file)
-{
-    struct Thing * owned = player->owns;
-    if (!owned)
-    {
-        char * empty = "(none)\n";
-        try_fwrite(empty, strlen(empty), 1, file, __func__);
-    }
-    else
-    {
-        uint8_t q;
-        for (q = 0; owned; q++)
-        {
-            struct ThingType * tt = get_thing_type(owned->type);
-            try_fwrite(tt->name, strlen(tt->name), 1, file, __func__);
-            try_fputc('\n', file, __func__);
-            owned = owned->next;
-        }
-    }
-    try_fputc('%', file, __func__);
-    try_fputc('\n', file, __func__);
-}
-
-
-
-static char * build_visible_map(struct Thing * player)
-{
-    char * visible_map;
-    init_empty_map(&visible_map);
-    if (player->fov_map) /* May fail if player thing was created / positioned */
-    {                    /* by god command after turning off FOV building.    */
-        uint32_t pos_i = 0;
-        for (; pos_i < (uint32_t) world.map.length * world.map.length; pos_i++)
-        {
-            if (player->fov_map[pos_i] == 'v')
-            {
-                visible_map[pos_i] = world.map.cells[pos_i];
-            }
-        }
-        struct Thing * t;
-        char c;
-        uint8_t i;
-        for (i = 0; i < 3; i++)
-        {
-            for (t = world.things; t != 0; t = t->next)
-            {
-                if ('v' == player->fov_map[t->pos.y*world.map.length+t->pos.x])
-                {
-                    struct ThingType * tt = get_thing_type(t->type);
-                    if (   (0 == i && !t->lifepoints && !tt->consumable)
-                        || (1 == i && !t->lifepoints &&  tt->consumable)
-                        || (2 == i &&  t->lifepoints))
-                    {
-                        c = tt->char_on_map;
-                        visible_map[t->pos.y * world.map.length + t->pos.x] = c;
-                    }
-                }
-            }
-        }
-    }
-    return visible_map;
-}
-
-
-
-static void write_map(struct Thing * player, FILE * file)
-{
-    char * visible_map = build_visible_map(player);
-    uint16_t x, y;
-    for (y = 0; y < world.map.length; y++)
-    {
-        for (x = 0; x < world.map.length; x++)
-        {
-            try_fputc(visible_map[y * world.map.length + x], file, __func__);
-        }
-        try_fputc('\n', file, __func__);
-    }
-    free(visible_map);
-    uint32_t map_size = world.map.length * world.map.length;
-    char * mem_map = try_malloc(map_size, __func__);
-    memcpy(mem_map, player->mem_map, map_size);
-    uint8_t i;
-    struct ThingInMemory * tm;
-    for (i = 0; i < 2; i++)
-    {
-        for (tm = player->t_mem; tm; tm = tm->next)
-        {
-            if (' ' != player->mem_map[tm->pos.y*world.map.length+tm->pos.x])
-            {
-                struct ThingType * tt = get_thing_type(tm->type);
-                if (   (0 == i && !tt->consumable)
-                    || (1 == i &&  tt->consumable))
-                {
-                    char c = tt->char_on_map;
-                    mem_map[tm->pos.y * world.map.length + tm->pos.x] = c;
-                }
-            }
-        }
-    }
-    for (y = 0; y < world.map.length; y++)
-    {
-        for (x = 0; x < world.map.length; x++)
-        {
-            try_fputc(mem_map[y * world.map.length + x], file, __func__);
-        }
-        try_fputc('\n', file, __func__);
-    }
-    free(mem_map);
-}
-
-
-
-extern char * io_round()
-{
-    if (world.queue && strlen(world.queue))
-    {
-        return get_message_from_queue(&world.queue);
-    }
-    if (world.do_update)
-    {
-        update_worldstate_file();
-        send_to_outfile("WORLD_UPDATED\n", 1);
-        world.do_update = 0;
-    }
-    try_growing_queue();
-    return get_message_from_queue(&world.queue);
-}
-
-
-
-extern void save_world()
-{
-    char * path_tmp;
-    FILE * file = atomic_write_start(s[S_PATH_SAVE], &path_tmp);
-    write_key_space_uvalue(file, s[S_CMD_TURN], world.turn);
-    write_key_space_uvalue(file, s[S_CMD_PLAYTYPE], world.player_type);
-    write_key_space_uvalue(file, s[S_CMD_MAPLENGTH], world.map.length);
-    write_key_space_uvalue(file, s[S_CMD_SEED_MAP], world.seed_map);
-    struct ThingAction * ta;
-    for (ta = world.thing_actions; ta; ta = ta->next)
-    {
-        write_key_space_uvalue(file, s[S_CMD_TA_ID], ta->id);
-        write_key_space_uvalue(file, s[S_CMD_TA_EFFORT], ta->effort);
-        write_key_space_string(file, s[S_CMD_TA_NAME], ta->name);
-    }
-    struct ThingType * tt;
-    for (tt = world.thing_types; tt; tt = tt->next)
-    {
-        write_key_space_uvalue(file, s[S_CMD_TT_ID], tt->id);
-        write_key_space_uvalue(file, s[S_CMD_TT_STARTN], tt->start_n);
-        int test = fprintf(file, "%s '%c'\n", s[S_CMD_TT_SYMB], tt->char_on_map);
-        exit_trouble(test < 0, __func__, "fprintf");
-        write_key_space_string(file, s[S_CMD_TT_NAME], tt->name);
-        write_key_space_uvalue(file, s[S_CMD_TT_PROL], tt->proliferate);
-        write_key_space_uvalue(file, s[S_CMD_TT_HP], tt->lifepoints);
-        write_key_space_uvalue(file, s[S_CMD_TT_CONSUM], tt->consumable);
-    }
-    for (tt = world.thing_types; tt; tt = tt->next)
-    {
-        write_key_space_uvalue(file, s[S_CMD_TT_ID], tt->id);
-        write_key_space_uvalue(file, s[S_CMD_TT_CORPS], tt->corpse_id);
-    }
-    struct Thing * t;
-    for (t = world.things; t; t = t->next)
-    {
-        write_thing(file, t);
-    }
-    for (t = world.things; t; t = t->next)
-    {
-        write_thing_carrying(file, t);
-    }
-    write_key_space_uvalue(file, s[S_CMD_SEED_RAND], world.seed);
-    write_key_space_uvalue(file, s[S_CMD_WORLD_ACTIVE], 1);
-    atomic_write_finish(file, s[S_PATH_SAVE], path_tmp);
-}
diff --git a/src/server/io.h b/src/server/io.h
deleted file mode 100644 (file)
index 7c9148d..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* io.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Communication of the server with the outside world and its client via input,
- * output and world state files.
- */
-
-#ifndef IO_H
-#define IO_H
-
-
-
-/* Return single \0-terminated string read from input queue (world.queue); or,
- * if queue is empty and world.do_update is set, update world state file (and
- * unset world.do_update) 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.
- * (Queueing ensures only complete messages are interpreted.)
- */
-extern char * io_round();
-
-/* Write to savefile (atomically) god commands (one per line) to rebuild the
- * current world state.
- */
-extern void save_world();
-
-
-
-#endif
diff --git a/src/server/main.c b/src/server/main.c
deleted file mode 100644 (file)
index 1ae00f1..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* src/server/main.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include <stdio.h> /* printf() */
-#include <stdlib.h> /* exit() */
-#include "../common/rexit.h" /* exit_err, set_cleanup_func() */
-#include "cleanup.h" /* set_cleanup_flag(), cleanup() */
-#include "hardcoded_strings.h" /* s */
-#include "init.h" /* run_game(), obey_argv(), obey_argv(), setup_server_io() */
-#include "world.h" /* struct World */
-
-
-
-struct World world;
-
-
-
-int main(int argc, char ** argv)
-{
-    /* So error exits also go through the server's cleanup() function. */
-    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)
-    {
-        char * printf_err = "Trouble in main() with printf().";
-        int test = printf("Starting plomrogue-server.\n");
-        exit_err(-1 == test, printf_err);
-        if (world.replay)
-        {
-            test = printf("Replay mode. Auto-replaying up to turn %d.\n",
-                          world.replay);
-            exit_err(-1 == test, printf_err);
-        }
-    }
-    world.map.length = 64;                      /* Just a sane default value. */
-
-    /* Init server i/o, Enter play or replay mode loops, then leave properly. */
-    setup_server_io();
-    run_game();
-    cleanup();
-    exit(EXIT_SUCCESS);
-}
diff --git a/src/server/map.c b/src/server/map.c
deleted file mode 100644 (file)
index 3a5a0a2..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/* src/server/map.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "map.h"
-#include <stdint.h> /* uint8_t, int8_t, uint16_t, uint32_t, (U)INT*_(MIN|MAX) */
-#include <stdlib.h> /* free() */
-#include <string.h> /* memset() */
-#include "../common/rexit.h" /* exit_err() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "../common/yx_uint8.h" /* yx_uint8 */
-#include "rrand.h" /* rrand() */
-#include "world.h" /* global world */
-
-
-
-/* Helper to mv_yx_in_dir_legal(). Move "yx" into hex direction "d". */
-static void mv_yx_in_dir(char d, struct yx_uint8 * yx);
-
-/* Call this too often with "init" of 0 and the game exits with an error message
- * about reaching an iteration limit. An "init" of 1 sets the iteration counter
- * to 0. Iteration limit is currently 256 * UINT16_MAX.
- */
-static uint8_t iter_limit(uint8_t init);
-
-/* Return 1 if cell on "pos" is neighbor to a cell of "type", else return 0. */
-static uint8_t is_neighbor(struct yx_uint8 pos, char type);
-
-/* Fill map with '~' cells. */
-static void make_sea();
-
-/* Put island of '.' cells inside map sea. */
-static void make_sea();
-
-/* Put tree cells of 'X' on island. */
-static void make_trees();
-
-
-
-static void mv_yx_in_dir(char d, struct yx_uint8 * yx)
-{
-    if      (d == 'e')
-    {
-        yx->x = yx->x + (yx->y % 2);
-        yx->y--;
-    }
-    else if (d == 'd')
-    {
-        yx->x++;
-    }
-    else if (d == 'c')
-    {
-        yx->x = yx->x + (yx->y % 2);
-        yx->y++;
-    }
-    else if (d == 'x')
-    {
-        yx->x = yx->x - !(yx->y % 2);
-        yx->y++;
-    }
-    else if (d == 's')
-    {
-        yx->x--;
-    }
-    else if (d == 'w')
-    {
-        yx->x = yx->x - !(yx->y % 2);
-        yx->y--;
-    }
-}
-
-
-
-static uint8_t iter_limit(uint8_t init)
-{
-    static uint32_t i = 0;
-    char * err = "Map generation reached iteration limit. Change map size?";
-    if (init)
-    {
-        i = 0;
-        return 0;
-    }
-    i++;
-    exit_err(256 * UINT16_MAX == i, err);
-    return 1;
-}
-
-
-
-static uint8_t is_neighbor(struct yx_uint8 pos, char type)
-{
-    uint8_t ind = pos.y % 2;
-    uint8_t diag_west = pos.x + ind > 0;
-    uint8_t diag_east = pos.x + ind <= world.map.length - 1;
-    uint16_t pos_i = (pos.y * world.map.length) + pos.x;
-    if (   (   pos.y > 0                    && diag_east
-            && type == world.map.cells[pos_i - world.map.length + ind])
-        || (   pos.x < world.map.length - 1
-            && type == world.map.cells[pos_i + 1])
-        || (   pos.y < world.map.length - 1 && diag_east
-            && type == world.map.cells[pos_i + world.map.length + ind])
-        || (   pos.y > 0                    && diag_west
-            && type == world.map.cells[pos_i - world.map.length - !ind])
-        || (   pos.x > 0
-            && type == world.map.cells[pos_i - 1])
-        || (   pos.y < world.map.length - 1 && diag_west
-            && type == world.map.cells[pos_i + world.map.length - !ind]))
-    {
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static void make_sea()
-{
-    uint16_t y, x;
-    for (y = 0; y < world.map.length; y++)
-    {
-        for (x = 0;
-             x < world.map.length;
-             world.map.cells[(y * world.map.length) + x] = '~', x++);
-    }
-}
-
-
-
-static void make_island()
-{
-    char type = '.';
-    uint8_t add_half_width = !(world.map.length % 2) * (world.map.length / 2);
-    uint32_t size = world.map.length * world.map.length;
-    world.map.cells[(size / 2) + add_half_width] = type;
-    struct yx_uint8 pos;
-    iter_limit(1);
-    while (iter_limit(0))
-    {
-        pos.y = rrand() % world.map.length;
-        pos.x = rrand() % world.map.length;
-        uint16_t pos_i = (pos.y * world.map.length) + pos.x;
-        if ('~' == world.map.cells[pos_i] && is_neighbor(pos, type))
-        {
-            if (   pos.y == 0 || pos.y == world.map.length - 1
-                || pos.x == 0 || pos.x == world.map.length - 1)
-            {
-                break;
-            }
-            world.map.cells[pos_i] = type;
-        }
-    }
-}
-
-
-
-static void make_trees()
-{
-    char type = 'X';
-    struct yx_uint8 pos;
-    uint16_t n_trees = (world.map.length * world.map.length) / 16;
-    uint16_t i_trees = 0;
-    iter_limit(1);
-    while (i_trees <= n_trees && iter_limit(0))
-    {
-        uint8_t single_allowed = rrand() % 32;
-        pos.y = rrand() % world.map.length;
-        pos.x = rrand() % world.map.length;
-        uint16_t pos_i = (pos.y * world.map.length) + pos.x;
-        if ('.' == world.map.cells[pos_i]
-            && (!single_allowed || is_neighbor(pos, type)))
-        {
-            world.map.cells[pos_i] = type;
-            i_trees++;
-        }
-    }
-}
-
-
-
-extern void remake_map()
-{
-    free(world.map.cells);
-    world.map.cells = try_malloc(world.map.length * world.map.length, __func__);
-    uint32_t store_seed = world.seed;
-    world.seed = world.seed_map;
-    make_sea();
-    make_island();
-    make_trees();
-    world.seed = store_seed;
-}
-
-
-
-extern uint8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx)
-{
-    static int8_t wrap_west_east   = 0;
-    static int8_t wrap_north_south = 0;
-    if (!yx)
-    {
-        wrap_west_east = wrap_north_south = 0;
-        return 0;
-    }
-    char * err = "Too much wrapping in mv_yx_in_dir_legal().";
-    exit_err(   INT8_MIN == wrap_west_east || INT8_MIN == wrap_north_south
-             || INT8_MAX == wrap_west_east || INT8_MAX == wrap_north_south,err);
-    struct yx_uint8 original = *yx;
-    mv_yx_in_dir(dir, yx);
-    if      (('e' == dir || 'd' == dir || 'c' == dir) && yx->x < original.x)
-    {
-        wrap_west_east++;
-    }
-    else if (('x' == dir || 's' == dir || 'w' == dir) && yx->x > original.x)
-    {
-        wrap_west_east--;
-    }
-    if      (('w' == dir || 'e' == dir)               && yx->y > original.y)
-    {
-        wrap_north_south--;
-    }
-    else if (('x' == dir || 'c' == dir)               && yx->y < original.y)
-    {
-        wrap_north_south++;
-    }
-    if (   !wrap_west_east && !wrap_north_south
-        && yx->x < world.map.length && yx->y < world.map.length)
-    {
-        return 1;
-    }
-    return 0;
-}
-
-
-
-extern void init_empty_map(char ** map)
-{
-    *map = try_malloc(world.map.length * world.map.length, __func__);
-    memset(*map, ' ', world.map.length * world.map.length);
-}
diff --git a/src/server/map.h b/src/server/map.h
deleted file mode 100644 (file)
index d9ebf74..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/* src/server/map.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Routines to create and navigate game map.
- */
-
-#ifndef MAP_H_SERVER
-#define MAP_H_SERVER
-
-#include <stdint.h> /* uint8_t */
-struct yx_uint8;
-
-
-
-/* (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 remake_map();
-
-/* Move "yx" into hex direction "dir". Available hex directions are: 'e'
- * (north-east), 'd' (east), 'c' (south-east), 'x' (south-west), 's' (west), 'w'
- * (north-west). Returns 1 if the move was legal, else 0.
- *
- * A move is legal if "yx" ends up in the confines of the map and the original
- * wrap space. The latter is left to a neighbor wrap space if "yx" moves beyond
- * the minimal (0) or maximal (UINT8_MAX) column or row of possible map space –
- * in which case "yx".y or "yx".x will snap to the respective opposite side. The
- * current wrapping state is kept between successive calls until a "yx" of NULL
- * is passed, in which case the function does nothing but zero the wrap state.
- * Successive wrapping may move "yx" several wrap spaces into either direction,
- * or return it into the original wrap space.
- */
-extern uint8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx);
-
-/* Initialize (empty) map array at "map". */
-extern void init_empty_map(char ** map);
-
-
-#endif
diff --git a/src/server/rrand.c b/src/server/rrand.c
deleted file mode 100644 (file)
index 034b0e8..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/* src/server/rrand.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "rrand.h"
-#include <stdint.h> /* uint16_t */
-#include "world.h" /* global world */
-
-
-
-extern uint16_t rrand()
-{   /* Constants as recommended by POSIX.1-2001 (see man page rand(3)). */
-    world.seed = ((world.seed * 1103515245) + 12345) % 4294967296;
-    return (world.seed >> 16); /* Ignore less random least significant bits. */
-}
diff --git a/src/server/rrand.h b/src/server/rrand.h
deleted file mode 100644 (file)
index 74f12fd..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* src/server/rrand.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Provides deterministic pseudo-randomness.
- */
-
-#ifndef RRAND_H
-#define RRAND_H
-
-#include <stdint.h> /* uint16_t */
-
-
-
-/* Return 16-bit number pseudo-randomly generated via Linear Congruential
- * Generator algorithm with some proven constants. Use instead of rand() to
- * ensure portability of the same pseudo-randomness across systems.
- */
-extern uint16_t rrand();
-
-
-
-#endif
diff --git a/src/server/run.c b/src/server/run.c
deleted file mode 100644 (file)
index ad04f71..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-/* src/server/run.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#define _POSIX_C_SOURCE 200809L /* strdup() */
-#include "run.h"
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t, int16_t */
-#include <stdio.h> /* FILE, printf(), fflush() */
-#include <stdlib.h> /* atoi(), free() */
-#include <string.h> /* strlen(), strcmp(), strncmp(), strdup() */
-#include <time.h> /* time_t, time() */
-#include <unistd.h> /* access() */
-#include "../common/parse_file.h" /* set_err_line_options(), token_from_line(),
-                                   * err_line(), err_line_inc(), parse_val(),
-                                   * parestest_int()
-                                   */
-#include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
-                                  * try_fgets(), textfile_width(), try_fputc(),
-                                  * atomic_write_finish(), build_temp_path()
-                                  */
-#include "../common/rexit.h" /* exit_trouble(), exit_err() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "ai.h" /* ai() */
-#include "cleanup.h" /* unset_cleanup_flag() */
-#include "field_of_view.h" /* update_map_memory() */
-#include "god_commands.h" /* parse_god_command_(1|2|3)arg() */
-#include "hardcoded_strings.h" /* s */
-#include "io.h" /* io_round(), save_world() */
-#include "thing_actions.h" /* hunger(), try_healing() */
-#include "things.h" /* Thing, ThingType, ThingInMemory, get_player(),
-                     * get_thing_action_id_by_name(), try_thing_proliferation()
-                     */
-#include "world.h" /* world */
-
-
-
-/* If "string" and "comparand" match in string, set "c_to_set" to value."  */
-static uint8_t set_char_by_string_comparison(char * string, char * comparand,
-                                             char * c_to_set, char value);
-
-/* Return 1 on world.exists, else 0 and err_line() appropriate error message. */
-static uint8_t player_commands_allowed();
-
-/* Parse player commands "tok0" with optional argument "tok1" to player action.
- * Return 1 on success, 0 on failure.
- */
-static uint8_t parse_player_command_0arg(char * tok0);
-static uint8_t parse_player_command_1arg(char * tok0, char * tok1);
-
-/* Parse/apply (non-)meta command "tok0" (read further tokens as necessary).
- * Return 0 on failure, 1 on success, 2 on QUIT meta command.
- */
-static uint8_t parse_command_nonmeta(char * tok0);
-static uint8_t parse_command_meta(char * tok0);
-
-/* Compare 1st line of server out file to world.server_test, abort if they don't
- * match, but not before unsetting 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();
-
-/* Return array of IDs of non-owned things in game world, ended by non-ID -1. */
-static int16_t * build_whitelist();
-
-/* Return 1 if value of "id" appears in "whitelist", else 0. */
-static uint8_t thing_in_whitelist(uint8_t id, int16_t * whitelist);
-
-/* Run the game world and its inhabitants (and their actions) until the player
- * avatar is free to receive new commands (or is dead). Only actions and
- * proliferations for non-owned things are performed that exist at the start of
- * the turn jumped into, or started anew by the cycle.
- */
-static void turn_over();
-
-
-
-static uint8_t set_char_by_string_comparison(char * string, char * comparand,
-                                             char * c_to_set, char value)
-{
-    if (!strcmp(string, comparand))
-    {
-        * c_to_set = value;
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static uint8_t player_commands_allowed()
-{
-    if (!world.exists)
-    {
-        return !err_line(1, "No world exists in which to run player commands.");
-    }
-    return 1;
-}
-
-
-
-static uint8_t parse_player_command_0arg(char * tok0)
-{
-    struct Thing * player = get_player();
-    if (   !strcmp(tok0, s[S_CMD_WAIT]) || !strcmp(tok0, s[S_CMD_PICKUP])
-        || !strcmp(tok0, s[S_CMD_AI]))
-    {
-        if (player_commands_allowed())
-        {
-            if (!strcmp(tok0, s[S_CMD_AI]))
-            {
-                ai(player);
-            }
-            else
-            {
-                player->command = get_thing_action_id_by_name(tok0);
-            }
-            turn_over();
-        }
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static uint8_t parse_player_command_1arg(char * tok0, char * tok1)
-{
-    struct Thing * player = get_player();
-    if (   (   parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg)
-            || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg))
-        && player_commands_allowed())
-    {
-        player->command = get_thing_action_id_by_name(tok0);
-        turn_over();
-    }
-    else if (!strcmp(tok0, s[S_CMD_MOVE]) && player_commands_allowed())
-    {
-        char dir = '\0';
-        if (!(   set_char_by_string_comparison(tok1, "east",       &dir, 'd')
-              || set_char_by_string_comparison(tok1, "south-east", &dir, 'c')
-              || set_char_by_string_comparison(tok1, "south-west", &dir, 'x')
-              || set_char_by_string_comparison(tok1, "west",       &dir, 's')
-              || set_char_by_string_comparison(tok1, "north-west", &dir, 'w')
-              || set_char_by_string_comparison(tok1, "north-east", &dir, 'e')))
-        {
-            return 0;
-        }
-        player->arg = dir;
-        player->command = get_thing_action_id_by_name(tok0);
-        turn_over();
-    }
-    else
-    {
-        return 0;
-    }
-    return 1;
-}
-
-
-
-static uint8_t parse_command_nonmeta(char * tok0)
-{
-    if (parse_player_command_0arg(tok0))
-    {
-        return 1;
-    }
-    else
-    {
-        char * tok1 = token_from_line(NULL);
-        if (tok1 && (   parse_player_command_1arg(tok0, tok1)
-                     || parse_god_command_1arg(tok0, tok1)))
-        {
-            return 1;
-        }
-        else
-        {
-            char * tok2 = token_from_line(NULL);
-            if (tok2 && parse_god_command_2arg(tok0, tok1, tok2))
-            {
-                return 1;
-            }
-            else
-            {
-                char * tok3 = token_from_line(NULL);
-                if (tok2 && parse_god_command_3arg(tok0, tok1, tok2, tok3))
-                {
-                    return 1;
-                }
-            }
-        }
-    }
-    return 0;
-}
-
-
-
-static uint8_t parse_command_meta(char * tok0)
-{
-    if (!strcmp("QUIT", tok0))
-    {
-        return 2;
-    }
-    if (!strcmp("PING", tok0))
-    {
-        send_to_outfile("PONG\n", 1);
-        return 1;
-    }
-    if (!strcmp("THINGS_HERE", tok0))
-    {
-        char * tok1 = token_from_line(NULL);
-        char * tok2 = token_from_line(NULL);
-        if (tok1&&tok2 && !parsetest_int(tok1, '8')&&!parsetest_int(tok2, '8'))
-        {
-            if (!world.exists)
-            {
-                err_line(1, "Command only works on existing worlds.");
-                return 0;
-            }
-            send_to_outfile("THINGS_HERE START\n", 1);
-            struct Thing * player = get_player();
-            if (player->fov_map &&
-                'v' == player->fov_map[atoi(tok1)*world.map.length+atoi(tok2)])
-            {
-                struct Thing * t;
-                for (t = world.things; t; t = t->next)
-                {
-                    if (t->pos.y == atoi(tok1) && t->pos.x == atoi(tok2))
-                    {
-                        struct ThingType * tt = get_thing_type(t->type);
-                        send_to_outfile(tt->name, 0);
-                        send_to_outfile("\n", 1);
-                    }
-                }
-            }
-            else
-            {
-                struct ThingInMemory * t_mem;
-                for (t_mem = player->t_mem; t_mem; t_mem = t_mem->next)
-                {
-                    if (t_mem->pos.y == atoi(tok1) && t_mem->pos.x == atoi(tok2))
-                    {
-                        struct ThingType * tt = get_thing_type(t_mem->type);
-                        send_to_outfile(tt->name, 0);
-                        send_to_outfile("\n", 1);
-                    }
-                }
-            }
-            send_to_outfile("THINGS_HERE END\n", 1);
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
-
-static void server_test()
-{
-    char test[10 + 1 + 10 + 1 + 1];
-    FILE * file = try_fopen(s[S_PATH_OUT], "r", __func__);
-    try_fgets(test, 10 + 10 + 1 + 1, file, __func__);
-    try_fclose(file, __func__);
-    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);
-    }
-}
-
-
-
-static int16_t * build_whitelist()
-{
-    uint16_t i_things = NULL != world.things;
-    struct Thing * t = world.things;
-    for (; t; t = t->next, i_things++);
-    int16_t * whitelist = try_malloc(i_things * sizeof(int16_t), __func__);
-    for (i_things = 0, t = world.things; t;
-         whitelist[i_things] = t->id, t = t->next, i_things++);
-    whitelist[i_things] = -1;
-    return whitelist;
-}
-
-
-
-static uint8_t thing_in_whitelist(uint8_t id, int16_t * whitelist)
-{
-    uint16_t i;
-    for (i = 0; -1 < whitelist[i]; i++)
-    {
-        if ((int16_t) id == whitelist[i])
-        {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
-
-static void turn_over()
-{
-    struct Thing * player = get_player();
-    struct Thing * thing = player;
-    int16_t * whitelist = build_whitelist();
-    while (0 < player->lifepoints)
-    {
-        if (!thing)
-        {
-            world.turn++;
-            thing = world.things;
-            free(whitelist);
-            whitelist = build_whitelist();/* The whitelist excludes things    */
-        }                                 /* that appear only during the turn.*/
-        if (thing_in_whitelist(thing->id, whitelist))
-        {
-            if (0 < thing->lifepoints)
-            {
-                if (0 == thing->command)
-                {
-                    update_map_memory(thing, 1);
-                    if (thing == player)
-                    {
-                        break;
-                    }
-                    ai(thing);
-                }
-                try_healing(thing);
-                thing->progress++;
-                struct ThingAction * ta = get_thing_action(thing->command);
-                if (thing->progress == ta->effort)
-                {
-                    ta->func(thing);
-                    thing->command = 0;
-                    thing->progress = 0;
-                }
-                hunger(thing);
-            }
-            try_thing_proliferation(thing);
-        }
-        thing = thing->next;
-    }
-    free(whitelist);
-}
-
-
-
-extern void send_to_outfile(char * answer, uint8_t flush)
-{
-    try_fwrite(answer, strlen(answer), 1, world.file_out, __func__);
-    if (flush)
-    {
-        fflush(world.file_out);
-    }
-}
-
-
-
-extern void record(char * msg, uint8_t force)
-{
-    static FILE * file_tmp = NULL;
-    static time_t save_wait = 0;
-    static char * path_tmp;
-    if (!file_tmp)
-    {
-        path_tmp = build_temp_path(s[S_PATH_RECORD]);
-        file_tmp = try_fopen(path_tmp, "w", __func__);
-        if (!access(s[S_PATH_RECORD], F_OK))
-        {
-            FILE * file_read = try_fopen(s[S_PATH_RECORD], "r", __func__);
-            uint32_t linemax = textfile_width(file_read);
-            char * line = try_malloc(linemax + 1, __func__);
-            while (try_fgets(line, linemax + 1, file_read, __func__))
-            {
-                try_fwrite(line, strlen(line), 1, file_tmp, __func__);
-            }
-            free(line);
-            try_fclose(file_read, __func__);
-        }
-    }
-    if (msg)
-    {
-        try_fwrite(msg, strlen(msg), 1, file_tmp, __func__);
-        try_fputc('\n', file_tmp, __func__);
-    }
-    if (force || time(NULL) > save_wait + 15)
-    {
-        save_wait = time(NULL);
-        save_world();
-        atomic_write_finish(file_tmp, s[S_PATH_RECORD], path_tmp);
-        file_tmp = NULL;
-    }
-}
-
-
-
-extern uint8_t obey_msg(char * msg, uint8_t obey_state)
-{
-    if (world.is_verbose)
-    {
-        exit_trouble(-1 == printf("Input: %s\n", msg), __func__, "printf");
-    }
-    set_err_line_options("Trouble with message: ", msg, 0);
-    char * msg_copy = strdup(msg);
-    char * tok0 = token_from_line(msg_copy);
-    uint8_t ret = 0;
-    if (tok0)
-    {
-        ret = parse_command_meta(tok0);
-        if (ret || (obey_state < 2 && parse_command_nonmeta(tok0)))
-        {
-            if (!ret)
-            {
-                if (world.exists)
-                {
-                    world.do_update = 1;
-                }
-                if (1 == obey_state)
-                {
-                   record(msg, 0);
-                }
-            }
-            char * tokplus = token_from_line(NULL);
-            err_line(!(!tokplus), "Too many arguments, ignoring overflow.");
-        }
-        else if (world.replay)
-        {
-            ret = 3;
-        }
-        else if (!world.replay)
-        {
-            err_line(1, "Invalid command/argument or bad number of tokens.");
-        }
-    }
-    free(msg_copy);
-    return ret;
-}
-
-
-
-extern uint8_t io_loop(uint8_t obey_state)
-{
-    while (1)
-    {
-        char * msg = io_round();
-        server_test();
-        if (msg)
-        {
-            uint8_t ret = obey_msg(msg, obey_state);
-            free(msg);
-            if (   (!world.replay && 2 == ret)
-                || ( world.replay && 2 <= ret))
-            {
-                return ret;
-            }
-        }
-    }
-}
diff --git a/src/server/run.h b/src/server/run.h
deleted file mode 100644 (file)
index 414df1b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* src/server/run.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Process commands and act on them. Stuff that furthers the state of the game.
- */
-
-#ifndef RUN_H
-#define RUN_H
-
-#include <stdint.h> /* uint8_t */
-
-
-
-/* Append "answer" to server output file, with instant fflush() if "flush". */
-extern void send_to_outfile(char * answer, uint8_t flush);
-
-/* Record save and record file data. Both are only written if "force" is set, or
- * on the first run with unset "force", or if 15 seconds have passed since the
- * last file writing. "msg" is appended to the record file if it is set.
- */
-extern void record(char * msg, uint8_t force);
-
-/* Try parsing "msg" into a command to apply. Output "msg" if world.is_verbose.
- * If "obey_state" is > 1 and world.replay is set, any non-meta command message
- * is not executed, but merely returns 3. The QUIT meta command (if well-formed)
- * always returns 2. Other meta commands and (with "obey_state" < 2) non-meta
- * commands return 1 if well-formed. Malformed or empty command messages return
- * 0. If "obey_state" is 1, "msg" is recorded via a non-forced record(). If a
- * non-meta command is executed and world.exists, world.do_update is set.
- */
-extern uint8_t obey_msg(char * msg, uint8_t obey_state);
-
-/* Loop to read commands via io_round() and call obey_msg() with "obey_state"
- * set on them. If latter returns 2 and world.replay is not set, or returns > 1
- * and world.replay is set, abort loop to return that result. After io_round(),
- * compares 1st line of the server out file is compared with world.server_test
- * to ensure the server process hasn't been superseded by a new one.
- */
-extern uint8_t io_loop(uint8_t obey_state);
-
-
-
-#endif
diff --git a/src/server/thing_actions.c b/src/server/thing_actions.c
deleted file mode 100644 (file)
index 6a8803d..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/* src/server/thing_actions.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#include "thing_actions.h"
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, INT16_MIN, INT16_MAX */
-#include <stdio.h> /* sprintf() */
-#include <stdlib.h> /* free() */
-#include <string.h> /* strlen() */
-#include "../common/rexit.h" /* exit_err(), exit_trouble() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "../common/yx_uint8.h" /* yx_uint8 */
-#include "field_of_view.h" /* build_fov_map() */
-#include "hardcoded_strings.h" /* s */
-#include "things.h" /* Thing, ThingType, get_player(), free_things_in_memory(),
-                     * own_thing(), set_thing_position(), get_thing_type(),
-                     */
-#include "map.h" /* mv_yx_in_dir_legal() */
-#include "rrand.h" /* rrand() */
-#include "run.h" /* send_to_outfile() */
-#include "world.h" /* global world */
-
-
-
-/* Send "text" as log message to server out file. */
-static void update_log(char * text);
-
-/* Decrement "t"'s lifepoints, and if to zero, kill it with log update. */
-static void decrement_lifepoints(struct Thing * t);
-
-/* One actor "wounds" another actor, decrementing his lifepoints. */
-static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted);
-
-/* Bonus stuff to actor_*() to happen if actor==player. Mostly writing of log
- * messages; _pick and _drop also decrement world.inventory_sel by 1 if >0.
- * (match_dir() is just a little helper to playerbonus_move().)
- */
-static void playerbonus_wait();
-static uint8_t match_dir(char d, char ** dsc_d, char match, char * dsc_match);
-static void playerbonus_move(char d, uint8_t passable);
-static void playerbonus_drop(uint8_t owns_none);
-static void playerbonus_pick(uint8_t picked);
-static void playerbonus_use(uint8_t no_thing, uint8_t wrong_thing);
-
-
-
-static void update_log(char * text)
-{
-    send_to_outfile("LOG ", 0);
-    send_to_outfile(text, 0);
-    send_to_outfile("\n", 1);
-}
-
-
-
-static void decrement_lifepoints(struct Thing * t)
-{
-    struct Thing * player = get_player();
-    t->lifepoints--;
-    if (0 == t->lifepoints)
-    {
-        t->type = get_thing_type(t->type)->corpse_id;
-        if (player == t)
-        {
-            update_log("You die.");
-            memset(t->fov_map, ' ', world.map.length * world.map.length);
-            return;
-        }
-        else
-        {
-            free(t->fov_map);
-            t->fov_map = NULL;
-            free(t->mem_map);
-            t->mem_map = NULL;
-            free(t->mem_depth_map);
-            t->mem_depth_map = NULL;
-            free_things_in_memory(t->t_mem);
-            t->t_mem = NULL;
-        }
-        update_log("It dies.");
-    }
-}
-
-
-
-static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted)
-{
-    struct ThingType * tt_hitter = get_thing_type(hitter->type);
-    struct ThingType * tt_hitted = get_thing_type(hitted->type);
-    struct Thing * player = get_player();
-    char * msg1 = "You";
-    char * msg2 = "wound";
-    char * msg3 = "you";
-    if      (player != hitter)
-    {
-        msg1 = tt_hitter->name;
-        msg2 = "wounds";
-    }
-    if (player != hitted)
-    {
-        msg3 = tt_hitted->name;
-    }
-    uint8_t len = strlen(msg1) + 1 + strlen(msg2) + 1 + strlen(msg3) + 2;
-    char * msg = try_malloc(len, __func__);
-    int test = sprintf(msg, "%s %s %s.", msg1, msg2, msg3);
-    exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-    update_log(msg);
-    free(msg);
-    decrement_lifepoints(hitted);
-}
-
-
-
-static void playerbonus_wait()
-{
-        update_log("You wait.");
-}
-
-
-
-static uint8_t match_dir(char d, char ** dsc_d, char match, char * dsc_match)
-{
-    if (d == match)
-    {
-        * dsc_d = dsc_match;
-        return 1;
-    }
-    return 0;
-}
-
-
-
-static void playerbonus_move(char d, uint8_t passable)
-{
-    char * dsc_dir = "north-east";
-    if (   match_dir(d, &dsc_dir, 'd', "east")
-        || match_dir(d, &dsc_dir, 'c', "south-east")
-        || match_dir(d, &dsc_dir, 'x', "south-west")
-        || match_dir(d, &dsc_dir, 's', "west")
-        || match_dir(d, &dsc_dir, 'w', "north-west"))
-    {
-        ;
-    }
-    char * dsc_move = "You move ";
-    if (0 == passable)
-    {
-        dsc_move = "You fail to move ";
-    }
-    char * msg = try_malloc(strlen(dsc_move) + strlen (dsc_dir) + 2, __func__);
-    int test = sprintf(msg, "%s%s.", dsc_move, dsc_dir);
-    exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-    update_log(msg);
-    free(msg);
-}
-
-
-
-static void playerbonus_drop(uint8_t owns_none)
-{
-    if (0 != owns_none)
-    {
-        update_log("You try to drop an object, but you own none.");
-        return;
-    }
-    update_log("You drop an object.");
-}
-
-
-
-static void playerbonus_pick(uint8_t picked)
-{
-    if (picked)
-    {
-        update_log("You pick up an object.");
-        return;
-    }
-    update_log("You try to pick up an object, but there is none.");
-}
-
-
-
-static void playerbonus_use(uint8_t no_thing, uint8_t wrong_thing)
-{
-    if      (no_thing)
-    {
-        update_log("You try to use an object, but you own none.");
-        return;
-    }
-    else if (wrong_thing)
-    {
-        update_log("You try to use this object, but fail.");
-        return;
-    }
-    update_log("You consume this object.");
-}
-
-
-
-extern void actor_wait(struct Thing * t)
-{
-    if (t == get_player())
-    {
-        playerbonus_wait();
-    }
-}
-
-
-
-extern void actor_move(struct Thing * t)
-{
-    char d = t->arg;
-    struct Thing * other_t;
-    struct yx_uint8 target = t->pos;
-    uint8_t legal_move = mv_yx_in_dir_legal(d, &target);
-    mv_yx_in_dir_legal(0, NULL);
-    uint8_t passable = 0;
-    if (legal_move)
-    {
-        passable = '.' == world.map.cells[target.y*world.map.length + target.x];
-        for (other_t = world.things; other_t != 0; other_t = other_t->next)
-        {
-            if (0 == other_t->lifepoints || other_t == t)
-            {
-                continue;
-            }
-            if (target.y == other_t->pos.y && target.x == other_t->pos.x)
-            {
-               actor_hits_actor(t, other_t);
-               return;
-            }
-        }
-    }
-    if (passable)
-    {
-        set_thing_position(t, target);
-        build_fov_map(t);
-    }
-    if (t == get_player())
-    {
-        playerbonus_move(d, passable);
-    }
-}
-
-
-
-extern void actor_drop(struct Thing * t)
-{
-    uint8_t owns_none = (!t->owns);
-    if (!owns_none)
-    {
-        uint8_t select = t->arg;
-        struct Thing * owned = t->owns;
-        uint8_t i = 0;
-        for (; i != select; i++, owned = owned->next);
-        own_thing(&world.things, &t->owns, owned->id);
-    }
-    if (t == get_player())
-    {
-        playerbonus_drop(owns_none);
-    }
-}
-
-
-
-extern void actor_pick(struct Thing * t)
-{
-    struct Thing * picked = NULL;
-    struct Thing * t_i;
-    uint8_t highest_id = 0;
-    for (t_i = world.things; t_i; t_i = t_i->next)
-    {
-        if (t_i != t && t_i->pos.y == t->pos.y && t_i->pos.x == t->pos.x)
-        {
-            if (t_i->id >= highest_id)   /* With several Things to pick,      */
-            {                            /* pick the one with the highest ID. */
-                highest_id = t_i->id;
-                picked = t_i;
-            }
-        }
-    }
-    if (picked)
-    {
-        own_thing(&t->owns, &world.things, picked->id);
-        set_thing_position(picked, t->pos);
-    }
-    if (t == get_player())
-    {
-        playerbonus_pick(!(!picked));
-    }
-}
-
-
-
-extern void actor_use(struct Thing * t)
-{
-    uint8_t wrong_thing = 1;
-    uint8_t no_thing = (!t->owns);
-    if (!no_thing)
-    {
-        uint8_t select = t->arg;
-        uint8_t i = 0;
-        struct Thing * selected = t->owns;
-        for (; i != select; i++, selected = selected->next);
-        struct ThingType * tt = get_thing_type(selected->type);
-        if (tt->consumable)
-        {
-            wrong_thing = 0;
-            struct Thing * next = selected->next;
-            free(selected);
-            if (0 < select)
-            {
-                select--;
-                selected = t->owns;
-                for (i = 0; i != select; i++, selected = selected->next);
-                selected->next = next;
-            }
-            else
-            {
-                t->owns = next;
-            }
-            t->satiation = t->satiation + tt->consumable > INT16_MAX ?
-                           INT16_MAX : t->satiation + tt->consumable;
-        }
-    }
-    if (t == get_player())
-    {
-        playerbonus_use(no_thing, wrong_thing);
-    }
-}
-
-
-
-extern void try_healing(struct Thing * t)
-{
-    struct ThingType * tt = get_thing_type(t->type);
-    if (   t->satiation > 0 && t->lifepoints < tt->lifepoints
-        && 0 == (rrand() % 31)
-        && get_thing_action_id_by_name(s[S_CMD_WAIT]) == t->command)
-    {
-        t->lifepoints++;
-        t->satiation = t->satiation - 32;
-        if (get_player() == t)
-        {
-            update_log("You heal.");
-        }
-        else
-        {
-            char * msg_part = " heals.";
-            uint8_t len = strlen(tt->name) + strlen(msg_part) + 1;
-            char * msg = try_malloc(len, __func__);
-            int test = sprintf(msg, "%s%s", tt->name, msg_part);
-            exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-            update_log(msg);
-            free(msg);
-        }
-    }
-}
-
-
-
-extern void hunger(struct Thing * t)
-{
-    if (t->satiation > INT16_MIN)
-    {
-        t->satiation--;
-    }
-    struct ThingType * tt = get_thing_type(t->type);
-    uint16_t testbase = t->satiation < 0 ? -(t->satiation) : t->satiation;
-    exit_err(!(tt->lifepoints), "A thing that should not hunger is hungering.");
-    uint16_t endurance = INT16_MAX / tt->lifepoints;
-    if ((testbase / endurance) / ((rrand() % endurance) + 1))
-    {
-        if (get_player() == t)
-        {
-            update_log("You suffer from hunger.");
-        }
-        else
-        {
-            char * msg_part = " suffers from hunger.";
-            uint8_t len = strlen(tt->name) + strlen(msg_part) + 1;
-            char * msg = try_malloc(len, __func__);
-            int test = sprintf(msg, "%s%s", tt->name, msg_part);
-            exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
-            update_log(msg);
-            free(msg);
-        }
-        decrement_lifepoints(t);
-    }
-}
diff --git a/src/server/thing_actions.h b/src/server/thing_actions.h
deleted file mode 100644 (file)
index 97ab3cc..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* src/server/thing_actions.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Actions that can be performed by living things / "actors". Note that apart
- * from the consequences described below, each action may also trigger log
- * messages and other minor stuff if the actor is equal to the player.
- */
-
-#ifndef THING_ACTIONS_H
-#define THING_ACTIONS_H
-
-struct Thing;
-
-
-
-/* Actor "t" does nothing. */
-extern void actor_wait(struct Thing * t);
-
-/* Actor "t" tries to move one step in direction described by char t->arg (where
- * north-east is 'e', east 'd' etc.) Move either succeeds, or another actor is
- * encountered and hit (which leads ot its lifepoint decreasing by one and
- * eventually death), or the move fails due to an impassable target square. On
- * success, update thing's field of view map.
- */
-extern void actor_move(struct Thing * t);
-
-/* Actor "t" tries to drop from inventory thing indexed by number t->args. */
-extern void actor_drop(struct Thing * t);
-
-/* Actor "t" tries to pick up topmost thing from ground into its inventory. */
-extern void actor_pick(struct Thing * t);
-
-/* Actor "t" tries to use thing in inventory indexed by number t->args.
- * (Currently the only valid use is consuming items defined as consumable.)
- */
-extern void actor_use(struct Thing * t);
-
-/* Increment "t"'s lifepoints to a 1/32 chance if its .satiation is positive,
- * its lifepoints are below "t"'s type's .lifepoints, and "t"'s .command is the
- * ID of the waiting action. On success, also decrement .satiation by by 32.
- */
-extern void try_healing(struct Thing * t);
-
-/* Decrement "t"'s satiation and trigger a chance (dependent on over-/under-
- * satiation value) of lifepoint decrement.
- */
-extern void hunger(struct Thing * t);
-
-
-
-#endif
diff --git a/src/server/things.c b/src/server/things.c
deleted file mode 100644 (file)
index f114ea4..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/* src/server/things.c
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- */
-
-#define _POSIX_C_SOURCE 200809L /* strdup() */
-#include "things.h"
-#include <stddef.h> /* NULL, size_t */
-#include <stdint.h> /* uint8_t, uint16_t, int16_t, UINT8_MAX, UINT16_MAX */
-#include <stdlib.h> /* free() */
-#include <string.h> /* memset(), strcmp(), strdup(), strlen() */
-#include "../common/rexit.h" /* exit_err() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "../common/yx_uint8.h" /* yx_uint8 */
-#include "cleanup.h" /* set_cleanup_flag() */
-#include "hardcoded_strings.h" /* s */
-#include "field_of_view.h" /* build_fov_map() */
-#include "map.h" /* mv_yx_in_dir_legal() */
-#include "rrand.h" /* rrand() */
-#include "thing_actions.h" /* actor_wait */
-#include "world.h" /* world */
-
-
-
-/* Used to treat structs Thing, ThingType and ThingAction the same. */
-struct NextAndId
-{
-    struct NextAndId * next;
-    uint8_t id;
-};
-
-
-
-/* To linked list of NextAndId structs (or rather structs whose start region is
- * compatible to it) starting at "start", add newly allocated element of
- * "n_size" and an ID that is either "id" or, if "id" is <= UINT8_MAX and >=
- * "id_start", get lowest ID >= "start_id" and <= UINT8_MAX for new thing
- * ("struct_id"=0), thing type ("struct_id"=1) or thing action ("struct_id"=2).
- */
-static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id,
-                                             int16_t id, uint8_t struct_id,
-                                             struct NextAndId ** start);
-
-/* Return 1 if cell at "test_pos" is proliferable by "t", i.e. it is passable,
- * it is not inhabited by another thing of "t"'s type, and, if "t" is animate,
- * neither by any other animate thing; else return 0.
- */
-static uint8_t cell_is_proliferable(struct yx_uint8 test_pos, struct Thing * t);
-
-
-static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id,
-                                             int16_t id, uint8_t struct_id,
-                                             struct NextAndId ** start)
-{
-    struct NextAndId * nai  = try_malloc(n_size, __func__);
-    memset(nai, 0, n_size);
-    if (start_id <= id && id <= UINT8_MAX)
-    {
-        nai->id = id;
-    }
-    else
-    {
-        while (1)
-        {
-            if (   (0 == struct_id && !get_thing(world.things, start_id, 1))
-                || (1 == struct_id && !get_thing_type(start_id))
-                || (2 == struct_id && !get_thing_action(start_id)))
-            {
-                nai->id = start_id;
-                break;
-            }
-            char * err =  "No unused ID available to add to ID list.";
-            exit_err(start_id == UINT8_MAX, err);
-            start_id++;
-        }
-    }
-    struct NextAndId ** nai_ptr_ptr = start;
-    for (; * nai_ptr_ptr; nai_ptr_ptr = &(*nai_ptr_ptr)->next);
-    *nai_ptr_ptr = nai;
-    return nai;
-}
-
-
-
-static uint8_t cell_is_proliferable(struct yx_uint8 test_pos, struct Thing * t)
-{
-    if ('.' == world.map.cells[test_pos.y * world.map.length + test_pos.x])
-    {
-        struct Thing * t_test;
-        for (t_test = world.things; t_test; t_test = t_test->next)
-        {
-            if (t_test->pos.y == test_pos.y && t_test->pos.x == test_pos.x)
-            {
-                if (t_test->type == t->type)
-                {
-                    return 0;
-                }
-                if (t_test->lifepoints && t->lifepoints)
-                {
-                    return 0;
-                }
-            }
-        }
-        return 1;
-    }
-    return 0;
-}
-
-
-
-extern struct ThingAction * add_thing_action(uint8_t id)
-{
-    struct ThingAction * ta;
-    ta = (struct ThingAction *) add_to_struct_list(sizeof(struct ThingAction),
-                                                   1, (int16_t) id, 2,
-                                                   (struct NextAndId **)
-                                                   &world.thing_actions);
-    set_cleanup_flag(CLEANUP_THING_ACTIONS);
-    ta->name = strdup(s[S_CMD_WAIT]);
-    ta->effort = 1;
-    ta->func = actor_wait;
-    return ta;
-}
-
-
-
-extern struct ThingType * add_thing_type(int16_t id)
-{
-    struct ThingType * tt;
-    tt = (struct ThingType *) add_to_struct_list(sizeof(struct ThingType),
-                                                 0, id, 1,
-                                                 (struct NextAndId **)
-                                                 &world.thing_types);
-    set_cleanup_flag(CLEANUP_THING_TYPES);
-    tt->name = strdup("(none)");
-    tt->corpse_id = tt->id;
-    return tt;
-}
-
-
-
-extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x)
-{
-    struct Thing * t;
-    t = (struct Thing *) add_to_struct_list(sizeof(struct Thing), 0, id, 0,
-                                            (struct NextAndId **)&world.things);
-    struct ThingType * tt = get_thing_type(type);
-    set_cleanup_flag(CLEANUP_THINGS);
-    t->type       = tt->id;
-    t->lifepoints = tt->lifepoints;
-    t->pos.y      = y;
-    t->pos.x      = x;
-    if (t->lifepoints && world.exists)
-    {
-        build_fov_map(t);
-    }
-    return t;
-}
-
-
-
-extern void add_thing_to_memory_map(struct Thing * t, uint8_t type,
-                                    uint8_t y, uint8_t x)
-{
-    struct ThingInMemory * tm=try_malloc(sizeof(struct ThingInMemory),__func__);
-    tm->type = type;
-    tm->pos.y = y;
-    tm->pos.x = x;
-    tm->next = t->t_mem;
-    t->t_mem = tm;
-}
-
-
-
-extern void free_thing_actions(struct ThingAction * ta)
-{
-    if (!ta)
-    {
-        return;
-    }
-    free_thing_actions(ta->next);
-    free(ta->name);
-    free(ta);
-}
-
-
-
-extern void free_thing_types(struct ThingType * tt)
-{
-    if (!tt)
-    {
-        return;
-    }
-    free_thing_types(tt->next);
-    free(tt->name);
-    free(tt);
-}
-
-
-
-extern void free_things(struct Thing * t)
-{
-    if (!t)
-    {
-        return;
-    }
-    free_things(t->owns);
-    free_things(t->next);
-    free(t->fov_map);
-    free(t->mem_map);
-    free(t->mem_depth_map);
-    free_things_in_memory(t->t_mem);
-    free(t);
-    if (t == world.things)         /* So add_things()' NULL-delimited thing   */
-    {                              /* iteration loop does not iterate over    */
-        world.things = NULL;       /* freed memory when called the first time */
-    }                              /* after world re-seeding.                 */
-}
-
-
-
-extern void free_things_in_memory(struct ThingInMemory * tm)
-{
-    if (!tm)
-    {
-        return;
-    }
-    free_things_in_memory(tm->next);
-    free(tm);
-}
-
-
-
-extern struct ThingAction * get_thing_action(uint8_t id)
-{
-    struct ThingAction * ta = world.thing_actions;
-    for (; ta && id != ta->id; ta = ta->next);
-    return ta;
-}
-
-
-
-extern struct ThingType * get_thing_type(uint8_t id)
-{
-    struct ThingType * tt = world.thing_types;
-    for (; tt && id != tt->id; tt = tt->next);
-    return tt;
-}
-
-
-
-extern uint8_t get_thing_action_id_by_name(char * name)
-{
-    struct ThingAction * ta = world.thing_actions;
-    while (ta)
-    {
-        if (0 == strcmp(ta->name, name))
-        {
-            break;
-        }
-        ta = ta->next;
-    }
-    if (!ta)
-    {
-        return 0;
-    }
-    return ta->id;
-}
-
-
-
-extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep)
-{
-    while (1)
-    {
-        if (!ptr || id == ptr->id)
-        {
-            return ptr;
-        }
-        if (deep)
-        {
-            struct Thing * owned_thing = get_thing(ptr->owns, id, 1);
-            if (owned_thing)
-            {
-                return ptr;
-            }
-        }
-        ptr = ptr->next;
-    }
-}
-
-
-
-extern struct Thing * get_player()
-{
-    return get_thing(world.things, 0, 0);
-}
-
-
-
-extern void try_thing_proliferation(struct Thing * t)
-{
-    struct ThingType * tt = get_thing_type(t->type);
-    if (tt->proliferate)
-    {
-        if (1 == tt->proliferate || 1 == (rrand() % tt->proliferate))
-        {
-            struct yx_uint8 candidates[6];
-            uint8_t n_candidates = 0;
-            char dirs[7] = "cxswed";
-            struct yx_uint8 test = t->pos;
-            uint8_t i;
-            for (i = 0; i < strlen(dirs); i++)
-            {
-                if (   mv_yx_in_dir_legal(dirs[i], &test)
-                    && cell_is_proliferable(test, t))
-                {
-                        candidates[n_candidates] = test;
-                        n_candidates++;
-                }
-            }
-            if (!n_candidates)
-            {
-                return;
-            }
-            i = rrand() % n_candidates;
-            add_thing(-1, tt->id, candidates[i].y, candidates[i].x);
-        }
-    }
-}
-
-
-
-extern void add_things(uint8_t type, uint8_t n)
-{
-    uint8_t i;
-    for (i = 0; i < n; i++)
-    {
-        struct yx_uint8 pos;
-        while (1)
-        {
-            char * err="Space to put thing on too hard to find. Map too small?";
-            uint16_t i_pos = 0;
-            for (pos.y = pos.x = 0;
-                 '.' != world.map.cells[pos.y * world.map.length + pos.x];
-                 i_pos++)
-            {
-                exit_err(UINT16_MAX == i_pos, err);
-                pos.y = rrand() % world.map.length;
-                pos.x = rrand() % world.map.length;
-            }
-            struct Thing * t;
-            uint8_t clear = 1;
-            for (t = world.things; t; t = t->next)
-            {
-                if (0 != t->lifepoints && pos.y==t->pos.y && pos.x==t->pos.x)
-                {
-                    clear = 0;
-                    break;
-                }
-            }
-            if (1 == clear)
-            {
-                break;
-            }
-        }
-        add_thing(-1, type, pos.y, pos.x);
-    }
-}
-
-
-
-extern void own_thing(struct Thing ** target, struct Thing ** source,
-                      uint8_t id)
-{
-    struct Thing * t;
-    if (id == (*source)->id)
-    {
-        t = * source;
-        * source = t->next;
-    }
-    else
-    {
-        struct Thing * penult = * source;
-        while (1)
-        {
-            if (id == penult->next->id)
-            {
-                break;
-            }
-            penult = penult->next;
-        }
-        t = penult->next;
-        penult->next = t->next;
-    }
-    struct Thing ** t_ptr_ptr = target;
-    for (; * t_ptr_ptr; t_ptr_ptr = &(*t_ptr_ptr)->next);
-    * t_ptr_ptr = t;
-    t->next = NULL;
-}
-
-
-
-extern void set_thing_position(struct Thing * t, struct yx_uint8 pos)
-{
-    t->pos = pos;
-    struct Thing * owned = t->owns;
-    for (; owned; set_thing_position(owned, pos), owned = owned->next);
-}
diff --git a/src/server/things.h b/src/server/things.h
deleted file mode 100644 (file)
index 33a07db..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/* src/server/things.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Structs for things and their type and action definitions, and routines to
- * initialize these.
- */
-
-#ifndef THINGS_H
-#define THINGS_H
-
-#include <stdint.h> /* uint8_t, int16_t, uint16_t */
-#include "../common/yx_uint8.h" /* yx_uint8 */
-
-
-
-struct Thing
-{
-    struct Thing * next;
-    uint8_t id;                   /* individual thing's unique identifier */
-    struct Thing * owns;          /* chain of things owned / in inventory */
-    struct ThingInMemory * t_mem; /* chain of things remembered */
-    struct yx_uint8 pos;          /* coordinate on map */
-    char * fov_map;               /* thing's FOV map; 'v':visible, 'H':hidden */
-    char * mem_map;               /* map knowledge of thing by FOV and memory */
-    char * mem_depth_map;         /* map of map memory up-to-dateness */
-    int16_t satiation;            /* negative: hungry; positive: over-fed */
-    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 */
-    uint8_t arg;                  /* optional field for .command argument */
-    uint8_t progress;             /* turns already passed to realize .command */
-};
-
-struct ThingInMemory
-{
-    struct ThingInMemory * next;
-    struct yx_uint8 pos;                             /* position on memorized */
-    uint8_t type;                                    /* thing type identifier */
-};
-
-struct ThingType
-{
-    struct ThingType * next;
-    uint8_t id;          /* thing type identifier / sets .type */
-    char char_on_map;    /* thing symbol to appear on map */
-    char * name;         /* string to describe thing in game log */
-    uint16_t consumable; /* can be eaten if !0, for so much .satiation win */
-    uint8_t corpse_id;   /* type to change thing into upon destruction */
-    uint8_t lifepoints;  /* default start value for thing's .lifepoints */
-    uint8_t start_n;     /* how many of these does the map start with? */
-    uint8_t proliferate; /* if >0: inverse of chance to proliferate */
-};
-
-struct ThingAction
-{
-    struct ThingAction * next;
-    uint8_t id;   /* identifies action in Thing.command; therefore must be >0 */
-    void (* func) (struct Thing *);    /* function called after .effort turns */
-    char * name;                       /* human-readable identifier */
-    uint8_t effort;                    /* how many turns the action takes */
-};
-
-
-
-/* Add thing action of "id" to world.thing_actions, with .name defaulting to
- * s[S_CMD_WAIT], .func to actor_wait() and .effort to 1. If "id" is not >= 1
- * and <= UINT8_MAX, use lowest unused id. Return thing action.
- */
-extern struct ThingAction * add_thing_action(uint8_t id);
-
-/* Add thing type of "id" to world.thing_types, with .corpse_id defaulting to
- * the new thing type's .id, .name to "(none)" and the remaining values to 0. If
- * "id" is not >= 0 and <= UINT8_MAX, use lowest unused id. Return thing type.
- */
-extern struct ThingType * add_thing_type(int16_t id);
-
-/* Add thing of "id" and "type" on position of "y"/x" to world.things. If "id"
- * is not >= 0 and <= UINT8_MAX, use lowest unused id. Build .fov_map if
- * world.exists is non-zero. Return thing.
- */
-extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x);
-
-/* Add to thing memory of "t" thing of type id "type" and position "y"/"x". */
-extern void add_thing_to_memory_map(struct Thing * t, uint8_t type,
-                                    uint8_t y, uint8_t x);
-
-/* Free ThingAction / ThingType / Thing / ThingInMemory chain starting at "ta" /
- * "tt" / "t" / "tm".
- */
-extern void free_thing_actions(struct ThingAction * ta);
-extern void free_thing_types(struct ThingType * tt);
-extern void free_things(struct Thing * t);
-extern void free_things_in_memory(struct ThingInMemory * tm);
-
-/* Return pointer to ThingAction/ThingType of "id", or NULL if none found. */
-extern struct ThingAction * get_thing_action(uint8_t id);
-extern struct ThingType * get_thing_type(uint8_t id);
-
-/* Return world.thing_actions ThingAction.id for "name" or 0 if none found. */
-extern uint8_t get_thing_action_id_by_name(char * name);
-
-/* Return thing of "id" in chain at "ptr", search inventories too if "deep".
- * Return NULL if nothing found.
- */
-extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep);
-
-/* Get pointer to the non-owend Thing struct that represents the player, or NULL
- * if none found.
- */
-extern struct Thing * get_player();
-
-/* Try to create "t" offspring on random passable neighbor cell if available
- * (and, if "t" is of animate thing type, not inhabited by animate thing) and
- * "t"'s type's .proliferation is >0, with a chance of 1/.proliferation.
- */
-extern void try_thing_proliferation(struct Thing * t);
-
-/* Add thing(s) ("n": how many?) of "type" to map on random passable
- * position(s). New animate things are never placed in the same square with
- * other animate ones.
- */
-extern void add_things(uint8_t type, uint8_t n);
-
-/* Move thing of "id" from "source" inventory to "target" inventory. */
-extern void own_thing(struct Thing ** target, struct Thing ** source,
-                      uint8_t id);
-
-/* Move not only "t" to "pos", but also all things owned by it. */
-extern void set_thing_position(struct Thing * t, struct yx_uint8 pos);
-
-
-
-#endif
diff --git a/src/server/world.h b/src/server/world.h
deleted file mode 100644 (file)
index f6deb10..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* src/server/world.h
- *
- * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
- * or any later version. For details on its copyright, license, and warranties,
- * see the file NOTICE in the root directory of the PlomRogue source package.
- *
- * Contains the World struct holding all game data together.
- */
-
-#ifndef MAIN_H
-#define MAIN_H
-
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t */
-#include <stdio.h> /* define FILE */
-#include "../common/map.h" /* struct Map */
-struct ThingType;
-struct ThingAction;
-struct Thing;
-
-
-
-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; /* 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 * server_test; /* String uniquely identifying server process. */
-    char * queue; /* Stores un-processed messages read from the input file. */
-    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. */
-    uint8_t do_update; /* Update worldstate file if !0. */
-    uint8_t exists; /* If !0, remake_world() has been run successfully. */
-    uint8_t player_type; /* Thing type that player will start as. */
-    uint8_t is_verbose; /* Should server send debugging info to stdout? */
-};
-
-extern struct World world;
-
-
-
-#endif