X-Git-Url: https://plomlompom.com/repos/berlin_corona.txt?a=blobdiff_plain;f=roguelike-server;h=ece7c86e595c2975966fca584cbfcc2b059be2f8;hb=c1044de74f1f47e91e14095e64cbc5abce2cad56;hp=75606ac32f53c5a995e25f0f0ad2320c242ed8d7;hpb=458d6dd3a3ccc56c04e459a6c8ca289d7bcf92b6;p=plomrogue diff --git a/roguelike-server b/roguelike-server index 75606ac..ece7c86 100755 --- a/roguelike-server +++ b/roguelike-server @@ -110,7 +110,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"]: @@ -124,6 +125,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() @@ -140,7 +143,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"] @@ -172,10 +176,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,10 +211,10 @@ def save_world(): string = "" for key in world_db: - if dict != type(world_db[key]) and key != "MAP" and \ - key != "WORLD_ACTIVE" and key != "SEED_MAP": + if dict != type(world_db[key]) \ + and key != "MAP" and 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 world_db["ThingTypes"]: @@ -413,7 +418,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, @@ -422,6 +427,8 @@ 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). """ + # 7DRL: Also add some ":" cells as land to which plants may proliferate. + def is_neighbor(coordinates, type): y = coordinates[0] x = coordinates[1] @@ -444,8 +451,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) @@ -469,12 +475,23 @@ 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(). + n_colons = int((length ** 2) / 16) # # + i_colons = 0 # # + while (i_colons <= n_colons): # # + single_allowed = rand.next() % 256 # # + 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), ":"))): # # + world_db["MAP"][pos] = ord(":") # # + i_colons += 1 # # 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") @@ -490,6 +507,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"]: @@ -651,7 +669,8 @@ def actor_move(t): 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]) + passable = "." == chr(world_db["MAP"][pos]) or \ + ":" == 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"] @@ -764,16 +783,18 @@ def thingproliferation(t, prol_map): """To chance of 1/TT_PROLIFERATE,create t offspring in open neighbor cell. Naturally only works with TT_PROLIFERATE > 0. The neighbor cell must be be - marked '.' in prol_map. If there are several map cell candidates, one is + marked "." in prol_map. If there are several map cell candidates, one is selected randomly. """ # # 7DRL: success increments God's mood + # # 7DRL: Things proliferate only on ":" ground. 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 ord('.') == prol_map[mv_result[1] + # if mv_result[0] and ord(":") == prol_map[mv_result[1] + if mv_result[0] and ord(".") == prol_map[mv_result[1] # # * world_db["MAP_LENGTH"] + mv_result[2]]: candidates.append((mv_result[1], mv_result[2])) @@ -1177,7 +1198,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") @@ -1251,32 +1273,30 @@ 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. """ - def free_pos(): + # def free_pos(plant=False): + def free_pos(plant=False): # # 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]): + pos = y * world_db["MAP_LENGTH"] + x; + if (not plant # # + and "." == chr(world_db["MAP"][pos])) \ + or ":" == chr(world_db["MAP"][pos]): # # break i += 1 if i == 65535: @@ -1294,7 +1314,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"]: @@ -1303,30 +1322,32 @@ 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"]: 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.") + print("Ignoring: 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"]): if type != playertype: id = id_setter(-1, "Things") - world_db["Things"][id] = new_Thing(type, free_pos()) + plantness = world_db["ThingTypes"][type]["TT_PROLIFERATE"] # # + world_db["Things"][id] = new_Thing(type, free_pos(plantness)) strong_write(io_db["file_out"], "NEW_WORLD\n") @@ -1335,6 +1356,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) @@ -1344,10 +1366,9 @@ def command_worldactive(worldactive_string): """Toggle world_db["WORLD_ACTIVE"] if possible. An active world can always be set inactive. An inactive world can only be - set active with a "wait" ThingAction, and a player Thing (of ID 0). On - activation, rebuild all Things' FOVs, and the player's map memory. + set active with a "wait" ThingAction, and a player Thing (of ID 0), and a + map. On activation, rebuild all Things' FOVs, and the player's map memory. """ - # In original version, map existence was also tested (unnecessarily?). val = integer_test(worldactive_string, 0, 1) if val: if 0 != world_db["WORLD_ACTIVE"]: @@ -1366,13 +1387,18 @@ 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 + else: + print("Ignoring: Not all conditions for world activation met.") def test_for_id_maker(object, category): @@ -1468,12 +1494,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"]: @@ -1481,15 +1507,35 @@ 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): @@ -1618,7 +1664,6 @@ 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)), "GOD_MOOD": (1, False, setter(None, "GOD_MOOD", -32768, 32767)), # # @@ -1626,6 +1671,7 @@ commands_db = { "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), @@ -1662,17 +1708,18 @@ 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, "GOD_MOOD": 0, # # "GOD_FAVOR": 0, # # + "MAP": False, "ThingActions": {}, "ThingTypes": {}, "Things": {}