X-Git-Url: https://plomlompom.com/repos/?a=blobdiff_plain;f=roguelike-server;h=ff799fb74a36d86eedaf6cb51b5cebaa2e0c8444;hb=aec8de097645ebfda6d92adcdf27efa5deb00af0;hp=5b37324c0d5f203c4efdacf28a7989c333a1f797;hpb=ed59f4a7166dc367bde9555d582a64e48b38a1ca;p=plomrogue diff --git a/roguelike-server b/roguelike-server index 5b37324..ff799fb 100755 --- a/roguelike-server +++ b/roguelike-server @@ -109,7 +109,8 @@ def obey(command, prefix, replay=False, do_record=False): 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. + is preceded by a server_test() call. Commands that start with a lowercase + letter are ignored when world_db["WORLD_ACTIVE"] is False/0. """ server_test() if io_db["verbose"]: @@ -123,6 +124,8 @@ def obey(command, prefix, replay=False, do_record=False): and len(tokens) == commands_db[tokens[0]][0] + 1: if commands_db[tokens[0]][1]: commands_db[tokens[0]][2](*tokens[1:]) + elif tokens[0][0].islower() and not world_db["WORLD_ACTIVE"]: + print("Ignoring lowercase-starting commands when world inactive.") elif replay: print("Due to replay mode, reading command as 'go on in record'.") line = io_db["file_record"].readline() @@ -139,7 +142,8 @@ def obey(command, prefix, replay=False, do_record=False): if time.time() > io_db["save_wait"] + 15: atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True) - save_world() + if world_db["WORLD_ACTIVE"]: + save_world() io_db["record_chunk"] = "" io_db["save_wait"] = time.time() io_db["worldstate_updateable"] = world_db["WORLD_ACTIVE"] @@ -171,10 +175,11 @@ def save_world(): return '"' + string.replace('"', '\u005C"') + '"' def mapsetter(key): - def helper(id): + def helper(id=None): string = "" - if world_db["Things"][id][key]: - map = world_db["Things"][id][key] + if key == "MAP" or world_db["Things"][id][key]: + map = world_db["MAP"] if key == "MAP" \ + else world_db["Things"][id][key] length = world_db["MAP_LENGTH"] for i in range(length): line = map[i * length:(i * length) + length].decode() @@ -206,9 +211,9 @@ def save_world(): string = "" for key in sorted(world_db.keys()): if (not isinstance(world_db[key], dict)) and key != "MAP" and \ - key != "WORLD_ACTIVE" and key != "SEED_MAP": + key != "WORLD_ACTIVE": string = string + key + " " + str(world_db[key]) + "\n" - string = string + "SEED_MAP " + str(world_db["SEED_MAP"]) + "\n" + string = string + mapsetter("MAP")() string = string + helper("ThingActions", "TA_ID") string = string + helper("ThingTypes", "TT_ID", {"TT_CORPSE_ID": False}) for id in sorted(world_db["ThingTypes"].keys()): @@ -304,7 +309,7 @@ def try_worldstate_update(): 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"] + consumable = world_db["ThingTypes"][type]["TT_TOOL"] == "food" alive = world_db["ThingTypes"][type]["TT_LIFEPOINTS"] if (0 == run and not consumable and not alive) \ or (1 == run and consumable and not alive) \ @@ -348,7 +353,7 @@ def try_worldstate_update(): 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"] + consumable = world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food" 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) @@ -409,7 +414,7 @@ def play_game(): obey(read_command(), "in file", do_record=True) -def remake_map(): +def make_map(): """(Re-)make island map. Let "~" represent water, "." land, "X" trees: Build island shape randomly, @@ -418,6 +423,7 @@ def remake_map(): 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] @@ -440,8 +446,7 @@ def remake_map(): 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) @@ -465,12 +470,12 @@ def remake_map(): 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.""" + def age_some_memdepthmap_on_nonfov_cells(): # OUTSOURCED FOR PERFORMANCE REASONS TO libplomrogue.so: # ord_v = ord("v") @@ -486,6 +491,7 @@ def update_map_memory(t, age_map=True): memdepthmap = c_pointer_to_bytearray(t["T_MEMDEPTHMAP"]) fovmap = c_pointer_to_bytearray(t["fovmap"]) libpr.age_some_memdepthmap_on_nonfov_cells(memdepthmap, fovmap) + if not t["T_MEMMAP"]: t["T_MEMMAP"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2)) if not t["T_MEMDEPTHMAP"]: @@ -572,6 +578,11 @@ def build_fov_map(t): raise RuntimeError("Malloc error in build_fov_Map().") +def log_help(): + """Send quick usage info to log.""" + strong_write(io_db["file_out"], "LOG See README file for help.\n") + + def decrement_lifepoints(t): """Decrement t's lifepoints by 1, and if to zero, corpse it. @@ -590,6 +601,8 @@ def decrement_lifepoints(t): 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") + strong_write(io_db["file_out"], + "LOG See README on how to start over.\n") else: t["fovmap"] = False t["T_MEMMAP"] = False @@ -665,8 +678,9 @@ def actor_pick_up(t): nutritious = 0 for id in ids: type = world_db["Things"][id]["T_TYPE"] - if world_db["ThingTypes"][type]["TT_CONSUMABLE"] > nutritious: - nutritious = world_db["ThingTypes"][type]["TT_CONSUMABLE"] + if world_db["ThingTypes"][type]["TT_TOOL"] == "food" \ + and world_db["ThingTypes"][type]["TT_TOOLPOWER"] > nutritious: + nutritious = world_db["ThingTypes"][type]["TT_TOOLPOWER"] highest_id = id world_db["Things"][highest_id]["carried"] = True t["T_CARRIES"].append(highest_id) @@ -697,10 +711,10 @@ def actor_use(t): 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"]: + if world_db["ThingTypes"][type]["TT_TOOL"] == "food": t["T_CARRIES"].remove(id) del world_db["Things"][id] - t["T_SATIATION"] += world_db["ThingTypes"][type]["TT_CONSUMABLE"] + t["T_SATIATION"] += world_db["ThingTypes"][type]["TT_TOOLPOWER"] if t == world_db["Things"][0]: strong_write(io_db["file_out"], "LOG You consume this object.\n") @@ -826,7 +840,7 @@ def get_dir_to_target(t, 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"]: + and world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food": return True return False @@ -878,7 +892,7 @@ def get_dir_to_target(t, filter): if ord_blank != t["T_MEMMAP"][mt[1] * world_db["MAP_LENGTH"] + mt[2]] - if world_db["ThingTypes"][mt[0]]["TT_CONSUMABLE"]]: + if world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food"]: 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]) @@ -964,26 +978,27 @@ def get_dir_to_target(t, filter): return dir_to_target -def standing_on_consumable(t): +def standing_on_food(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"]]: + ["TT_TOOL"] == "food"]: 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 + cmp_food = 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"] + if world_db["ThingTypes"][type]["TT_TOOL"] == "food" \ + and world_db["ThingTypes"][type]["TT_TOOLPOWER"] > cmp_food: + cmp_food = world_db["ThingTypes"][type]["TT_TOOLPOWER"] selection = i i += 1 return selection @@ -1009,7 +1024,7 @@ def ai(t): if world_db["ThingActions"][id]["TA_NAME"] == "use"][0] t["T_ARGUMENT"] = sel - elif standing_on_consumable(t): + elif standing_on_food(t): t["T_COMMAND"] = [id for id in world_db["ThingActions"] if world_db["ThingActions"][id]["TA_NAME"] == "pick_up"][0] @@ -1042,15 +1057,16 @@ def turn_over(): 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) + if Thing["T_LIFEPOINTS"]: + 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 thingproliferation(Thing, proliferable_map) if whilebreaker: break @@ -1116,7 +1132,8 @@ def command_ping(): def command_quit(): """Abort server process.""" if None == opts.replay: - save_world() + if world_db["WORLD_ACTIVE"]: + save_world() atomic_write(io_db["path_record"], io_db["record_chunk"], do_append=True) raise SystemExit("received QUIT command") @@ -1190,22 +1207,17 @@ def command_seedrandomness(seed_string): 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 + Seed rand with seed. Do more only with a "wait" ThingAction and + world["PLAYER_TYPE"] matching ThingType of TT_START_NUMBER > 0. Then, + world_db["Things"] emptied, call make_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. + Call log_help(). """ def free_pos(): @@ -1233,7 +1245,6 @@ def command_makeworld(seed_string): 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"]: @@ -1242,8 +1253,7 @@ def command_makeworld(seed_string): 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.") + print("Ignoring: No player type with start number >0 defined.") return wait_action = False for ThingAction in world_db["ThingActions"]: @@ -1254,12 +1264,15 @@ def command_makeworld(seed_string): "No thing action with name 'wait' defined.") return world_db["Things"] = {} - remake_map() + make_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()) + if not world_db["Things"][0]["fovmap"]: + empty_fovmap = bytearray(b" " * world_db["MAP_LENGTH"] ** 2) + world_db["Things"][0]["fovmap"] = empty_fovmap update_map_memory(world_db["Things"][0]) for type in world_db["ThingTypes"]: for i in range(world_db["ThingTypes"][type]["TT_START_NUMBER"]): @@ -1267,6 +1280,7 @@ def command_makeworld(seed_string): id = id_setter(-1, "Things") world_db["Things"][id] = new_Thing(type, free_pos()) strong_write(io_db["file_out"], "NEW_WORLD\n") + log_help() def command_maplength(maplength_string): @@ -1274,6 +1288,7 @@ def command_maplength(maplength_string): val = integer_test(maplength_string, 1, 256) if None != val: world_db["MAP_LENGTH"] = val + world_db["MAP"] = False set_world_inactive() world_db["Things"] = {} libpr.set_maplength(val) @@ -1283,12 +1298,12 @@ def command_worldactive(worldactive_string): """Toggle world_db["WORLD_ACTIVE"] if possible. An active world can always be set inactive. An inactive world can only be - set active with a "wait" ThingAction, and a player Thing (of ID 0). On - activation, rebuild all Things' FOVs, and the player's map memory. + set active with a "wait" ThingAction, and a player Thing (of ID 0), and a + map. On activation, rebuild all Things' FOVs, and the player's map memory. + Also call log_help(). """ - # In original version, map existence was also tested (unnecessarily?). val = integer_test(worldactive_string, 0, 1) - if val: + if None != val: if 0 != world_db["WORLD_ACTIVE"]: if 0 == val: set_world_inactive() @@ -1305,13 +1320,19 @@ def command_worldactive(worldactive_string): if 0 == Thing: player_exists = True break - if wait_exists and player_exists: + if wait_exists and player_exists and world_db["MAP"]: 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) + if not world_db["Things"][0]["T_LIFEPOINTS"]: + empty_fovmap = bytearray(b" " * world_db["MAP_LENGTH"] ** 2) + world_db["Things"][0]["fovmap"] = empty_fovmap world_db["WORLD_ACTIVE"] = 1 + log_help() + else: + print("Ignoring: Not all conditions for world activation met.") def test_for_id_maker(object, category): @@ -1407,12 +1428,12 @@ def command_tmemthing(str_t, str_y, str_x): def setter_map(maptype): - """Set selected Thing's map of maptype's int(str_int)-th line to mapline. + """Set (world or 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. + If no map of maptype exists yet, initialize it with ' ' bytes first. """ - @test_Thing_id - def helper(str_int, mapline): + + def valid_map_line(str_int, mapline): val = integer_test(str_int, 0, 255) if None != val: if val >= world_db["MAP_LENGTH"]: @@ -1420,15 +1441,36 @@ def setter_map(maptype): 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() + return val + return None + + def nonThingMap_helper(str_int, mapline): + val = valid_map_line(str_int, mapline) + if None != val: + length = world_db["MAP_LENGTH"] + if not world_db["MAP"]: + map = bytearray(b' ' * (length ** 2)) + else: + map = world_db["MAP"] + map[val * length:(val * length) + length] = mapline.encode() + if not world_db["MAP"]: + world_db["MAP"] = map + + @test_Thing_id + def ThingMap_helper(str_int, mapline): + val = valid_map_line(str_int, mapline) + if None != val: + length = world_db["MAP_LENGTH"] + 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() + if not world_db["Things"][command_tid.id][maptype]: world_db["Things"][command_tid.id][maptype] = map - return helper + + return nonThingMap_helper if maptype == "MAP" else ThingMap_helper + def setter_tpos(axis): @@ -1455,18 +1497,20 @@ def setter_tpos(axis): 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. + Default new ThingType's TT_SYMBOL to "?", TT_CORPSE_ID to self, TT_TOOL to + "", others: 0. """ id = id_setter(id_string, "ThingTypes", command_ttid) if None != id: world_db["ThingTypes"][id] = { "TT_NAME": "(none)", - "TT_CONSUMABLE": 0, + "TT_TOOLPOWER": 0, "TT_LIFEPOINTS": 0, "TT_PROLIFERATE": 0, "TT_START_NUMBER": 0, "TT_SYMBOL": "?", - "TT_CORPSE_ID": id + "TT_CORPSE_ID": id, + "TT_TOOL": "" } @@ -1479,6 +1523,12 @@ def command_ttname(name): world_db["ThingTypes"][command_ttid.id]["TT_NAME"] = name +@test_ThingType_id +def command_tttool(name): + """Set TT_TOOL of selected ThingType.""" + world_db["ThingTypes"][command_ttid.id]["TT_TOOL"] = name + + @test_ThingType_id def command_ttsymbol(char): """Set TT_SYMBOL of selected ThingType. """ @@ -1556,25 +1606,25 @@ commands_db = { "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), + "MAP": (2, False, setter_map("MAP")), "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_TOOL": (1, False, command_tttool), "TT_SYMBOL": (1, False, command_ttsymbol), "TT_CORPSE_ID": (1, False, command_ttcorpseid), - "TT_CONSUMABLE": (1, False, setter("ThingType", "TT_CONSUMABLE", - 0, 65535)), + "TT_TOOLPOWER": (1, False, setter("ThingType", "TT_TOOLPOWER", 0, 65535)), "TT_START_NUMBER": (1, False, setter("ThingType", "TT_START_NUMBER", 0, 255)), "TT_PROLIFERATE": (1, False, setter("ThingType", "TT_PROLIFERATE", - 0, 255)), + 0, 65535)), "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)), @@ -1596,15 +1646,16 @@ commands_db = { "use": (1, False, play_commander("use", True)), "ai": (0, False, command_ai) } +# TODO: Unhandled cases: (Un-)killing animates (esp. player!) with T_LIFEPOINTS. """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, + "MAP": False, "ThingActions": {}, "ThingTypes": {}, "Things": {}