+
+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_nearest_target(t, c):
+ # Dummy
+ return False
+
+
+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_nearest_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_nearest_target(t, "c")) and \
+ (not get_dir_to_nearest_target(t, "a")):
+ get_dir_to_nearest_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 is from start!
+ if not id in world_db["Things"] or \
+ world_db["Things"][id]["carried"]:# Thing may have been consumed
+ continue # or 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)
+ Thing["T_COMMAND"] = 1
+ 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 and <= 255, else >= -32768 and <=
+ 32767), but <0 or (if start_at_1) <1, calculate new ID: lowest unused ID
+ >=0 or (if start_at_1) >= 1, and <= 255. None is always returned when no
+ new object is created, otherwise the new object's ID.
+ """
+ min = 0 if start_at_1 else -32768
+ max = 255 if start_at_1 else 32767
+ if str == type(id):
+ id = integer_test(id, min, max)
+ 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 or id > 255)):
+ id = -1
+ while 1:
+ id = id + 1
+ if id not in world_db[category]:
+ break
+ if id > 255:
+ print("Ignoring: "
+ "No unused ID available to add to ID list.")
+ 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."""
+ 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"]:
+ # write_thing_if_here()
+ 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()