From: Christian Heller Date: Wed, 26 Aug 2020 23:34:43 +0000 (+0200) Subject: Merge branch 'master' into 7drl2016 X-Git-Tag: TCE_proper_minor_fixes X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/move_up?a=commitdiff_plain;h=d6659c644efbfaa3906c7ab36b17ca68c0dd1961;hp=32c8b0d55c091b10ba683621d7881ef57ce8a88a;p=plomrogue Merge branch 'master' into 7drl2016 --- diff --git a/README b/README index 0ad300f..176d025 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ plomrogue ========= -plomlompom tries to build his own roguelike engine. There is one game for it so -far, which is loaded by default: "Please the Island God". See ./README_PtIG for -details on it. +plomlompom tries to build his own roguelike engine. There is two games for it so +far, one of which is loaded by default: "The Crawling Eater". See ./README_TCE +for details on it. System requirements / installation / running the game ----------------------------------------------------- diff --git a/README_TCE b/README_TCE new file mode 100644 index 0000000..8a1e49b --- /dev/null +++ b/README_TCE @@ -0,0 +1 @@ +Just start the game with "./roguelike", the documentation currently is in-game. diff --git a/client/config/windows.py b/client/config/windows.py index 676d94c..2e0c7b8 100644 --- a/client/config/windows.py +++ b/client/config/windows.py @@ -7,9 +7,24 @@ from client.windows import win_info, win_log, win_inventory, win_look, win_map windows_config = [ - {"config": [1, 33], "func": win_info, "title": "Info"}, - {"config": [-7, 33], "func": win_log, "title": "Log"}, - {"config": [4, 16], "func": win_inventory, "title": "Inventory"}, - {"config": [4, 16], "func": win_look, "title": "Things here"}, - {"config": [0, -34], "func": win_map, "title": "Map"} + {"config": [1, 33], + "func": win_info, + "scroll_hints": True, + "title": "Info"}, + {"config": [-7, 33], + "func": win_log, + "scroll_hints": True, + "title": "Log"}, + {"config": [4, 16], + "func": win_inventory, + "scroll_hints": True, + "title": "Inventory"}, + {"config": [4, 16], + "func": win_look, + "scroll_hints": True, + "title": "Things here"}, + {"config": [0, -34], + "func": win_map, + "scroll_hints": True, + "title": "Map"} ] diff --git a/client/window_management.py b/client/window_management.py index a896b02..9d78737 100644 --- a/client/window_management.py +++ b/client/window_management.py @@ -18,10 +18,11 @@ screen_size = [0,0] class Window: - def __init__(self, title, draw_function, size): + def __init__(self, title, draw_function, size, draw_scroll_hints): self.title = title self.draw = types.MethodType(draw_function, self) self.size = size + self.draw_scroll_hints = draw_scroll_hints def set_windows(): @@ -99,7 +100,8 @@ def set_windows(): windows = [] for config in windows_config: size = size_window(config["config"]) - window = Window(config["title"], config["func"], size) + window = Window(config["title"], config["func"], size, + config["scroll_hints"]) windows.append(window) place_window(window) redraw_windows = True @@ -217,7 +219,8 @@ def draw_screen(): for win in windows: offset, winmap_size, winmap = win.draw() draw_winmap() - draw_scroll_hints() + if win.draw_scroll_hints: + draw_scroll_hints() stdscr.erase() draw_window_border_lines() diff --git a/confserver/TheCrawlingEater b/confserver/TheCrawlingEater new file mode 100644 index 0000000..eb67b73 --- /dev/null +++ b/confserver/TheCrawlingEater @@ -0,0 +1,34 @@ +PLUGIN TheCrawlingEater + +MAP_LENGTH 64 +PLAYER_TYPE 0 + +TA_ID 1 +TA_NAME wait + +TA_ID 2 +TA_NAME move + +TA_ID 3 +TA_NAME drop + +TA_ID 4 +TA_NAME drink + +TA_ID 5 +TA_NAME pee + +TA_ID 6 +TA_NAME eat + +TT_ID 0 +TT_START_NUMBER 1 +TT_LIFEPOINTS 1 +TT_SYMBOL @ +TT_NAME CREATURE + +TT_ID 5 +TT_SYMBOL % + +TT_ID 0 +TT_CORPSE_ID 5 diff --git a/libplomrogue.c b/libplomrogue.c index 9f872e1..33da37a 100644 --- a/libplomrogue.c +++ b/libplomrogue.c @@ -566,6 +566,110 @@ extern uint8_t dijkstra_map() return 0; } + +/* 7DRL/TCE addition: Init AI score map to all-eatable unknown fields. */ +extern uint8_t TCE_init_score_map() +{ + uint32_t map_size = maplength * maplength; + score_map = malloc(map_size * sizeof(uint16_t)); + if (!score_map) + { + return 1; + } + uint32_t i = 0; + for (; i < map_size; i++) + { + score_map[i] = UINT16_MAX - 1; + } + return 0; +} + +/* 7DRL/TCE addition: movement cost map setting. */ +static uint8_t * TCE_move_cost_map = NULL; +extern uint8_t TCE_set_movement_cost_map(char * mem_map) +{ + uint32_t map_size = maplength * maplength; + free(TCE_move_cost_map); + TCE_move_cost_map = malloc(map_size * sizeof(uint8_t)); + uint32_t pos = 0; + for (; pos < map_size; pos++) + { + TCE_move_cost_map[pos] = 0; + } + if (!TCE_move_cost_map) + { + return 1; + } + for (pos = 0; pos < map_size; pos++) + { + switch(mem_map[pos]) { + case '-': + case '+': + case '$': + case '0': + TCE_move_cost_map[pos] = 1; + break; + case '1': + TCE_move_cost_map[pos] = 2; + break; + case '2': + TCE_move_cost_map[pos] = 4; + break; + case '3': + TCE_move_cost_map[pos] = 3; + break; + case '4': + TCE_move_cost_map[pos] = 6; + break; + } + } + return 0; +} + + +/* 7DRL/TCE addition: Like dijkstra_map(), but with movement costs applied. */ +extern uint8_t TCE_dijkstra_map_with_movement_cost() +{ + if (!score_map || !TCE_move_cost_map) + { + return 1; + } + uint16_t max_score = UINT16_MAX - 1; + uint32_t map_size = maplength * maplength; + uint32_t pos; + uint16_t i_scans, neighbors[6], 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++) + { + uint16_t score = score_map[pos]; + uint8_t mov_cost = TCE_move_cost_map[pos]; + if (score <= max_score && mov_cost > 0 && score > i_scans) + { + get_neighbor_scores(pos, max_score, neighbors); + min_neighbor = max_score; + for (i_dirs = 0; i_dirs < 6; i_dirs++) + { + if (min_neighbor > neighbors[i_dirs]) + { + min_neighbor = neighbors[i_dirs]; + } + } + if (score_map[pos] > min_neighbor + mov_cost) + { + score_map[pos] = min_neighbor + mov_cost; + scores_still_changing = 1; + } + } + } + } + return 0; +} + + extern uint8_t zero_score_map_where_char_on_memdepthmap(char c, char * memdepthmap) { @@ -651,7 +755,16 @@ extern void write_score_map() { for (x = 0; x < maplength; x++) { - fprintf(f, "%2X", score_map[y * maplength + x] % 256); + uint32_t pos = y * maplength + x; + uint16_t val = score_map[pos]; + if (val == UINT16_MAX) + { + fprintf(f, " Z"); + } else if (val == UINT16_MAX - 1) { + fprintf(f, " Y"); + } else { + fprintf(f, "%2X", score_map[pos] % 256); + } } fprintf(f, "\n"); } diff --git a/plugins/client/PleaseTheIslandGod.py b/plugins/client/PleaseTheIslandGod.py index 808d793..7348e76 100644 --- a/plugins/client/PleaseTheIslandGod.py +++ b/plugins/client/PleaseTheIslandGod.py @@ -1,3 +1,8 @@ +# 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. + + from client.config.io import io from client.config.world_data import world_data world_data["log"][-2] = "STATS OVERVIEW: " \ @@ -137,11 +142,26 @@ def win_map(self): from client.config.windows import windows_config from client.windows import win_log, win_inventory, win_look windows_config[:] = [ - {"config": [1, 33], "func": win_info, "title": "Stats"}, - {"config": [-7, 33], "func": win_log, "title": "Log"}, - {"config": [4, 16], "func": win_inventory, "title": "Inventory"}, - {"config": [4, 16], "func": win_look, "title": "Things here"}, - {"config": [0, -34], "func": win_map, "title": "PLEASE THE ISLAND GOD"} + {"config": [1, 33], + "func": win_info, + "scroll_hints": True, + "title": "Stats"}, + {"config": [-7, 33], + "func": win_log, + "scroll_hints": True, + "title": "Log"}, + {"config": [4, 16], + "func": win_inventory, + "scroll_hints": True, + "title": "Inventory"}, + {"config": [4, 16], + "func": win_look, + "scroll_hints": True, + "title": "Things here"}, + {"config": [0, -34], + "func": win_map, + "scroll_hints": True, + "title": "PLEASE THE ISLAND GOD"} ] from client.window_management import set_windows set_windows() diff --git a/plugins/client/TheCrawlingEater.py b/plugins/client/TheCrawlingEater.py new file mode 100644 index 0000000..2cf93b9 --- /dev/null +++ b/plugins/client/TheCrawlingEater.py @@ -0,0 +1,215 @@ +# 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. + + +curses.init_pair(76, curses.COLOR_BLACK, curses.COLOR_WHITE) +curses.init_pair(77, curses.COLOR_WHITE, curses.COLOR_GREEN) +curses.init_pair(78, curses.COLOR_BLACK, curses.COLOR_RED) +curses.init_pair(79, curses.COLOR_WHITE, curses.COLOR_BLUE) +curses.init_pair(80, curses.COLOR_BLACK, curses.COLOR_YELLOW) + + +def win_bar_maker(color_number, symbol, title): + def win_bar(self): + winmap = [] + for i in range(world_data[title]): + if i == 32: + break + winmap += [(symbol, curses.color_pair(color_number))] + winmap_size = [1, len(winmap)] + offset = [0, 0] + return offset, winmap_size, winmap + return win_bar + + +def win_map(self): + charmap = { + "0": "_", + "1": ".", + "2": ":", + "3": "%", + "4": "#", + "5": "X", + "*": "O", + "&": "0", + } + win_size = self.size + offset = [0, 0] + for i in range(2): + if world_data["map_center"][i] * (i + 1) > win_size[i] / 2 and \ + win_size[i] < world_data["map_size"] * (i + 1): + if world_data["map_center"][i] * (i + 1) \ + < world_data["map_size"] * (i + 1) - win_size[i] / 2: + offset[i] = world_data["map_center"][i] * (i + 1) \ + - int(win_size[i] / 2) + if i == 1: + offset[1] = offset[1] + world_data["map_center"][0] % 2 + else: + offset[i] = world_data["map_size"] * (i + 1) - win_size[i] + i + winmap_size = [world_data["map_size"], world_data["map_size"] * 2 + 1] + winmap = [] + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) + #curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) + curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_BLACK) + curses.init_pair(4, curses.COLOR_GREEN, curses.COLOR_BLACK) + curses.init_pair(5, curses.COLOR_YELLOW, curses.COLOR_BLACK) + curses.init_pair(6, curses.COLOR_RED, curses.COLOR_BLACK) + curses.init_pair(7, curses.COLOR_MAGENTA, curses.COLOR_BLACK) + curses.init_pair(8, curses.COLOR_BLACK, curses.COLOR_WHITE) + curses.init_pair(9, curses.COLOR_BLACK, curses.COLOR_BLUE) + curses.init_pair(10, curses.COLOR_BLACK, curses.COLOR_CYAN) + curses.init_pair(11, curses.COLOR_BLACK, curses.COLOR_GREEN) + curses.init_pair(12, curses.COLOR_BLACK, curses.COLOR_YELLOW) + curses.init_pair(13, curses.COLOR_BLACK, curses.COLOR_RED) + curses.init_pair(14, curses.COLOR_BLACK, curses.COLOR_MAGENTA) + col_unknown = curses.color_pair(1) + col_creature = curses.color_pair(13) + col_player = curses.color_pair(8) + col_altar = curses.color_pair(3) + earth_colors = [ + curses.color_pair(4), + curses.color_pair(5), + curses.color_pair(5), + curses.color_pair(6), + curses.color_pair(6), + curses.color_pair(7), + ] + water_colors = [ + curses.color_pair(9), + curses.color_pair(10), + curses.color_pair(11), + curses.color_pair(12), + curses.color_pair(14), + ] + for y in range(world_data["map_size"]): + for x in range(world_data["map_size"]): + pos = y * world_data["map_size"] + x + char = world_data["fov_map"][pos] + if world_data["look_mode"] and y == world_data["map_center"][0] \ + and x == world_data["map_center"][1]: + if char == " ": + bonus = "?" + else: + bonus = world_data["wetmap"][pos] + char = world_data["mem_map"][pos] + winmap += [(char, curses.A_REVERSE), (bonus, curses.A_REVERSE)] + continue + bonus = " " + attribute = col_unknown + if char == " ": + if world_data["soundmap"][pos] != "0": + bonus = "?" + char = world_data["mem_map"][pos] + if char in charmap: + char = charmap[char] + winmap += [(char, attribute), bonus] + else: + wetval = ord(world_data["wetmap"][pos]) - ord("0") + if char in "012345-+": + mapval = 0 + if char not in "-+": + mapval = ord(char) - ord("0") + if 1 <= wetval <= 5: + attribute = water_colors[wetval + (mapval - 1)] + else: + attribute = earth_colors[mapval] + if char == "&": + attribute = col_player + elif char == "$": + attribute = col_altar + if char in charmap: + char = charmap[char] + elif char == "@": + attribute = col_creature + av_pos = world_data["avatar_position"] + if pos == av_pos[0] * world_data["map_size"] + av_pos[1]: + attribute = col_player + winmap += [(char, attribute), bonus] + if y % 2 == 0: + winmap += " " + return offset, winmap_size, winmap + + +from client.config.world_data import world_data +world_data["grace"] = 0 +world_data["kidney"] = 0 +world_data["stomach"] = 0 +world_data["bowel"] = 0 +world_data["bladder"] = 0 +world_data["wetmap"] = " " * (world_data["map_size"] ** 2) +world_data["soundmap"] = " " * (world_data["map_size"] ** 2) +world_data["log"] = [ +"This is not the environment you are used to. This is not the body you are use" +"d to. Surely this is a dream. But you have a feeling you might not wake up. U" +"nless you find a way out of here. Until then, you must survive. Explore, keep" +" fed, and hydrated. And avoid things that move by themselves.", +"", +"[hit '0' for help]" +] +from client.config.io import io +io["worldstate_read_order"] += [["stomach", "int"]] +io["worldstate_read_order"] += [["kidney", "int"]] +io["worldstate_read_order"] += [["bowel", "int"]] +io["worldstate_read_order"] += [["bladder", "int"]] +io["worldstate_read_order"] += [["wetmap", "map"]] +io["worldstate_read_order"] += [["soundmap", "map"]] +io["worldstate_read_order"] += [["grace", "int"]] +from client.config.windows import windows_config +from client.windows import win_log +windows_config[:] = [ + {"config": [0, -33], + "func": win_map, + "scroll_hints": False, + "title": "THE CRAWLING EATER"}, + {"config": [1, 32], + "func": win_bar_maker(76, "+", "grace"), + "scroll_hints": False, + "title": "grace"}, + {"config": [1, 32], + "func": win_bar_maker(77, "%", "stomach"), + "scroll_hints": False, + "title": "calories"}, + {"config": [1, 32], + "func": win_bar_maker(79, "~", "kidney"), + "scroll_hints": False, + "title": "hydration"}, + {"config": [1, 32], + "func": win_bar_maker(78, "%", "bowel"), + "scroll_hints": False, + "title": "bowel"}, + {"config": [1, 32], + "func": win_bar_maker(80, "~", "bladder"), + "scroll_hints": False, + "title": "bladder"}, + {"config": [-10, 32], + "func": win_log, + "scroll_hints": False, + "title": "log"} +] +from client.window_management import set_windows +set_windows() +from client.commands import command_sender +from client.config.commands import commands +commands["o"] = (command_sender("drop"),) +commands["i"] = (command_sender("drink"),) +commands["p"] = (command_sender("pee"),) +commands["."] = (command_sender("wait"),) +commands["0"] = (command_sender("HELP 0"),) +commands["1"] = (command_sender("HELP 1"),) +commands["2"] = (command_sender("HELP 2"),) +commands["3"] = (command_sender("HELP 3"),) +commands["4"] = (command_sender("HELP 4"),) +commands["5"] = (command_sender("HELP 5"),) +commands["6"] = (command_sender("HELP 6"),) +commands["7"] = (command_sender("HELP 7"),) +commands["8"] = (command_sender("HELP 8"),) +commands["9"] = (command_sender("HELP 9"),) +commands["D"] = (lambda: None,) +commands["J"] = (lambda: None,) +commands["K"] = (lambda: None,) +commands["P"] = (lambda: None,) +commands["U"] = (lambda: None,) +commands["j"] = (lambda: None,) +commands["k"] = (lambda: None,) +commands["W"] = (lambda: None,) diff --git a/plugins/server/PleaseTheIslandGod.py b/plugins/server/PleaseTheIslandGod.py index ca9a39a..d29d5be 100644 --- a/plugins/server/PleaseTheIslandGod.py +++ b/plugins/server/PleaseTheIslandGod.py @@ -529,7 +529,7 @@ server.config.commands.play_use_attempt_hook = play_use_attempt_hook server.config.commands.play_pickup_attempt_hook = play_pickup_attempt_hook import server.config.misc -server.config.misc.make_map = make_map +#server.config.misc.make_map = make_map server.config.misc.decrement_lifepoints = decrement_lifepoints server.config.misc.calc_effort = calc_effort diff --git a/plugins/server/TheCrawlingEater.py b/plugins/server/TheCrawlingEater.py new file mode 100644 index 0000000..1cf25a1 --- /dev/null +++ b/plugins/server/TheCrawlingEater.py @@ -0,0 +1,1013 @@ +# 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. + + +from server.config.world_data import world_db + + +def command_help(str_int): + val = integer_test(str_int, 0, 9) + if None != val: + if val == 0: + log(" ") + log("[HELP SCREEN 0: help screen list]") + log("0 - this screen") + log("1 - key bindings") + log("2 - environment 1/3") + log("3 - environment 2/3") + log("4 - environment 3/3") + log("5 - other beings") + log("6 - winning") + log("7 - restart and replay") + log("For more, see ./README or ./README_TCE files.") + pass + elif val == 1: + log(" ") + log("[HELP SCREEN 1: key bindings]") + log("movement/eating: w/e/d/c/x/s") + log("drink: i") + log("defecate: o") + log("pee: p") + log("wait: .") + log("autopilot: A") + log("look mode: l") + log("quit: Q") + log("help screens: 1/2/3/4/5/6/7/8/9") + log("help overview: 0") + elif val == 2: + log(" ") + log("[HELP SCREEN 2: environment 1/3]") + log("There is mostly solid matter and fluid. " + "'_', '.', ':' are ground, '%', '#', 'X' are walls. " + "Less thick ones can be eaten." + "Background color indicates fluid concentration. " + "'O'/'0' is holes; stepping into them is dangerous." + "'-' and '+' are cracks in the ground, and may becomes holes. " + "'$' is special places that will change you. " + "Learn more in HELP SCREEN 3." + ) + elif val == 3: + log(" ") + log("[HELP SCREEN 3: environment 2/3]") + log("Use the 'l' command to investigate the environment. " + "Its cursor may describe the area pointed at by two numbers: " + "ground elevation/matter thickness (left), wetness (right). " + "You cannot breathe there if the sum of both is > 5. " + "Greater ground elevation means slower movement." + ) + elif val == 4: + log(" ") + log("[HELP SCREEN 4: environment 3/3]") + log("Waste will pile up to ground matter an walls. " + "Fluid will trickle down into the environment. " + "General humidity will sparkle up new fluid on the ground " + "level ('_'). " + "Only from there can you drink fluid. " + "Much fluid on a ground level tile creates potential for " + "cracks to appear there. They may be sealed with waste." + ) + elif val == 5: + log(" ") + log("[HELP SCREEN 5: other beings]") + log(" '?' on the memory map indicate sounds of movement. " + "You may encounter the sources. They appear as the general " + "humidity of the environment grows, grown from its fluids and " + "earth. " + ) + elif val == 6: + log(" ") + log("[HELP SCREEN 6: winning]") + log("Find '$' tiles. Don't dump waste and fluids on fields with " + "great elevation and wetness sums. Control ground wetness " + "by providing great or small ground space for fluid sparkling " + "up to concentrate in. " + "A special kind of hole will only evolve from a 'O' hole that " + "is surrounded by six other holes." + ) + elif val == 7: + log(" ") + log("[HELP SCREEN 7: restart and replay]") + log("You can restart in a new game world by running " + "./roguelike -n. You can replay a record of your current game" + " by running ./roguelike -s." + "See file ./README for more details." + ) + else: + log(" ") + log("No HELP SCREEN defined for this number.") + log(" ") + + +def command_ai(): + if not (world_db["WORLD_ACTIVE"] + and world_db["Things"][0]["T_LIFEPOINTS"] > 0): + return + world_db["ai"](world_db["Things"][0]) + world_db["turn_over"]() + + +def play_drink(): + if not (action_exists("drink") and world_db["WORLD_ACTIVE"] + and world_db["Things"][0]["T_LIFEPOINTS"] > 0): + return + pos = world_db["Things"][0]["pos"] + if not (chr(world_db["MAP"][pos]) in "0-+" + and world_db["wetmap"][pos] > ord("0")): + log("NOTHING to drink here.") + return + elif world_db["Things"][0]["T_KIDNEY"] >= 32: + log("You're too FULL to drink more.") + return + world_db["set_command"]("drink") + + +def actor_drink(t): + pos = t["pos"] + if chr(world_db["MAP"][pos]) in "0-+" and \ + world_db["wetmap"][pos] > ord("0") and t["T_KIDNEY"] < 32: + if world_db["Things"][0] == t: + log("You DRINK.") + t["T_KIDNEY"] += 1 + world_db["wetmap"][pos] -= 1 + + +def play_pee(): + if not (action_exists("pee") and world_db["WORLD_ACTIVE"] + and world_db["Things"][0]["T_LIFEPOINTS"] > 0): + return + if world_db["Things"][0]["T_BLADDER"] < 1: + log("NOTHING to drop from empty bladder.") + return + world_db["set_command"]("pee") + + +def actor_pee(t): + if t["T_BLADDER"] < 1: + return + if t == world_db["Things"][0]: + log("You LOSE fluid.") + if not world_db["test_air"](t): + return + t["T_BLADDER"] -= 1 + if chr(world_db["MAP"][t["pos"]]) not in "*&": + world_db["wetmap"][t["pos"]] += 1 + + +def play_drop(): + if not (action_exists("drop") and world_db["WORLD_ACTIVE"] + and world_db["Things"][0]["T_LIFEPOINTS"] > 0): + return + if world_db["Things"][0]["T_BOWEL"] < 1: + log("NOTHING to drop from empty bowel.") + return + world_db["set_command"]("drop") + + +def actor_drop(t): + if t["T_BOWEL"] < 1: + return + if t == world_db["Things"][0]: + log("You DROP waste.") + if not world_db["test_air"](t): + return + if world_db["MAP"][t["pos"]] == ord("+"): + world_db["MAP"][t["pos"]] = ord("-") + elif world_db["MAP"][t["pos"]] == ord("-"): + world_db["MAP"][t["pos"]] = ord("0") + elif chr(world_db["MAP"][t["pos"]]) not in "*&": + world_db["MAP"][t["pos"]] += 1 + t["T_BOWEL"] -= 1 + + +def play_move(str_arg): + if not (action_exists("move") and world_db["WORLD_ACTIVE"] + and world_db["Things"][0]["T_LIFEPOINTS"] > 0): + return + from server.config.world_data import directions_db, symbols_passable + t = world_db["Things"][0] + if not str_arg in directions_db: + print("Illegal move direction string.") + return + d = ord(directions_db[str_arg]) + from server.utils import mv_yx_in_dir_legal + move_result = mv_yx_in_dir_legal(chr(d), t["T_POSY"], t["T_POSX"]) + if 1 == move_result[0]: + pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2] + hitted = [tid for tid in world_db["Things"] + if world_db["Things"][tid]["T_POSY"] == move_result[1] + if world_db["Things"][tid]["T_POSX"] == move_result[2]] + if len(hitted) > 0: + if t["T_STOMACH"] >= 32 and t["T_KIDNEY"] >= 32: + if t == world_db["Things"][0]: + log("You're too FULL to suck resources from another creature.") + return + world_db["Things"][0]["T_ARGUMENT"] = d + world_db["set_command"]("eat") + return + legal_targets = "34" + if world_db["GRACE"] >= 8: + legal_targets += "5" + if chr(world_db["MAP"][pos]) in legal_targets: + if t["T_STOMACH"] >= 32: + if t == world_db["Things"][0]: + log("You're too FULL to eat.") + return + world_db["Things"][0]["T_ARGUMENT"] = d + world_db["set_command"]("eat") + return + if chr(world_db["MAP"][pos]) in symbols_passable: + world_db["Things"][0]["T_ARGUMENT"] = d + world_db["set_command"]("move") + return + log("You CAN'T eat your way through there.") + + +def suck_out_creature(t, tid): + if t == None: + t = world_db["Things"][tid] + elif tid == None: + tid = next(tid for tid in world_db["Things"] + if world_db["Things"][tid] == t) + room_stomach = 32 - world_db["Things"][0]["T_STOMACH"] + room_kidney = 32 - world_db["Things"][0]["T_KIDNEY"] + if t["T_STOMACH"] > room_stomach: + t["T_STOMACH"] -= room_stomach + world_db["Things"][0]["T_STOMACH"] = 32 + else: + world_db["Things"][0]["T_STOMACH"] + t["T_STOMACH"] + t["T_STOMACH"] = 0 + if t["T_KIDNEY"] > room_stomach: + t["T_KIDNEY"] -= room_stomach + world_db["Things"][0]["T_KIDNEY"] = 32 + else: + world_db["Things"][0]["T_KIDNEY"] + t["T_KIDNEY"] + t["T_KIDNEY"] = 0 + hitted_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"] + log("You SUCK EVERYTHING from " + hitted_name + ", killing them.") + world_db["die"](t, "FOO") +world_db["suck_out_creature"] = suck_out_creature + + +def actor_eat(t): + from server.utils import mv_yx_in_dir_legal, rand + from server.config.world_data import symbols_passable + 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] + hitted = [tid for tid in world_db["Things"] + if world_db["Things"][tid]["T_POSY"] == move_result[1] + if world_db["Things"][tid]["T_POSX"] == move_result[2]] + if len(hitted): + hit_id = hitted[0] + hitted_tid = world_db["Things"][hit_id]["T_TYPE"] + if t == world_db["Things"][0]: + if world_db["GRACE"] >= 16: + world_db["suck_out_creature"](None, hit_id) + return + hitted_name = world_db["ThingTypes"][hitted_tid]["TT_NAME"] + log("You SUCK resources from " + hitted_name + ".") + elif 0 == hit_id: + if world_db["GRACE"] >= 16: + world_db["suck_out_creature"](t, None) + return + hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"] + log(hitter_name +" SUCKS resources from you.") + hitted = world_db["Things"][hit_id] + if t["T_STOMACH"] < 32: + t["T_STOMACH"] = t["T_STOMACH"] + 1 + hitted["T_STOMACH"] -= 1 + if t["T_KIDNEY"] < 32: + t["T_KIDNEY"] = t["T_KIDNEY"] + 1 + hitted["T_KIDNEY"] -= 1 + return + passable = chr(world_db["MAP"][pos]) in symbols_passable + if passable and t == world_db["Things"][0]: + log("You try to EAT, but fail.") + else: + height = world_db["MAP"][pos] - ord("0") + if t["T_STOMACH"] >= 32: + return + if height == 5 and not \ + (t == world_db["Things"][0] and world_db["GRACE"] >= 8): + return + t["T_STOMACH"] += 1 + if t == world_db["Things"][0]: + log("You EAT.") + eaten = (height == 3 and 0 == int(rand.next() % 2)) or \ + (height == 4 and 0 == int(rand.next() % 5)) or \ + (height == 5 and 0 == int(rand.next() % 10)) + if eaten: + world_db["MAP"][pos] = ord("0") + if t["T_STOMACH"] > 32: + t["T_STOMACH"] = 32 + + +def actor_move(t): + from server.build_fov_map import build_fov_map + from server.utils import mv_yx_in_dir_legal, rand + from server.config.world_data import symbols_passable + 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] + hitted = [tid for tid in world_db["Things"] + if world_db["Things"][tid]["T_POSY"] == move_result[1] + if world_db["Things"][tid]["T_POSX"] == move_result[2]] + if len(hitted): + hit_id = hitted[0] + hitted_tid = world_db["Things"][hit_id]["T_TYPE"] + if t == world_db["Things"][0]: + if world_db["GRACE"] >= 16: + world_db["suck_out_creature"](None, hit_id) + return + hitted_name = world_db["ThingTypes"][hitted_tid]["TT_NAME"] + log("You BUMP into " + hitted_name + ".") + elif 0 == hit_id: + if world_db["GRACE"] >= 16: + world_db["suck_out_creature"](t, None) + return + hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"] + log(hitter_name +" BUMPS into you.") + return + passable = chr(world_db["MAP"][pos]) in symbols_passable + if passable: + t["T_POSY"] = move_result[1] + t["T_POSX"] = move_result[2] + t["pos"] = move_result[1] * world_db["MAP_LENGTH"] + move_result[2] + world_db["soundmap"][t["pos"]] = ord("9") + if t == world_db["Things"][0] and world_db["MAP"][t["pos"]] == ord("$"): + world_db["MAP"][t["pos"]] = ord("0") + log("You feel a strange AURA from this place.") + if world_db["GRACE"] == 0: + log("You can now eat ALL walls.") + elif world_db["GRACE"] == 8: + log("You now have a DEATH touch towards other creatures.") + elif world_db["GRACE"] == 16: + log("You can now LEVITATE over holes.") + elif world_db["GRACE"] == 24: + log("You are now READY to leave through a special hole.") + elif world_db["GRACE"] == 32: + log("You already have all the GRACE you can get.") + if world_db["GRACE"] <= 24: + world_db["GRACE"] += 8 + elif t == world_db["Things"][0]: + log("You try to MOVE there, but fail.") + + +def test_hole(t): + if t == world_db["Things"][0]: + if world_db["GRACE"] < 32 and world_db["MAP"][t["pos"]] == ord("&"): + log("You feel you need more GRACE to leave through this hole.") + elif world_db["GRACE"] >= 32 and world_db["MAP"][t["pos"]] == ord("&"): + world_db["die"](t, "You FLY through the hole, into your waking life." + "Good bye, and CONGRATULATIONS.") + return False + if world_db["GRACE"] >= 24: + return True + if chr(world_db["MAP"][t["pos"]]) in "*&": + world_db["die"](t, "You FALL into a hole, and die.") + return False + return True +world_db["test_hole"] = test_hole + + +def test_air(t): + if world_db["terrain_fullness"](t["pos"]) > 5: + world_db["die"](t, "You SUFFOCATE.") + return False + return True +world_db["test_air"] = test_air + + +def die(t, message): + t["T_LIFEPOINTS"] = 0 + if t == world_db["Things"][0]: + t["fovmap"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2)) + t["T_MEMMAP"][t["pos"]] = ord("@") + log(message) + else: + if world_db["MAP"][t["pos"]] != ord("$"): + world_db["MAP"][t["pos"]] = ord("5") + world_db["HUMILITY"] = t["T_KIDNEY"] + t["T_BLADDER"] + \ + (world_db["wetmap"][t["pos"]] - ord("0")) + world_db["wetmap"][t["pos"]] = 0 + tid = next(tid for tid in world_db["Things"] + if world_db["Things"][tid] == t) + del world_db["Things"][tid] +world_db["die"] = die + + +def make_map(): + from server.make_map import new_pos, is_neighbor + from server.utils import rand + world_db["MAP"] = bytearray(b'5' * (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("4") + while (1): + y, x, pos = new_pos() + if "5" == chr(world_db["MAP"][pos]) and is_neighbor((y, x), "4"): + if y == 0 or y == (length - 1) or x == 0 or x == (length - 1): + break + world_db["MAP"][pos] = ord("4") + n_ground = int((length ** 2) / 16) + i_ground = 0 + while (i_ground <= n_ground): + single_allowed = rand.next() % 32 + y, x, pos = new_pos() + if "4" == chr(world_db["MAP"][pos]) \ + and ((not single_allowed) or is_neighbor((y, x), "0")): + world_db["MAP"][pos] = ord("0") + i_ground += 1 + n_water = int((length ** 2) / 32) + i_water = 0 + while (i_water <= n_water): + y, x, pos = new_pos() + if ord("0") == world_db["MAP"][pos] and \ + ord("0") == world_db["wetmap"][pos]: + world_db["wetmap"][pos] = ord("3") + i_water += 1 + n_altars = 8 + i_altars = 0 + while (i_altars < n_altars): + y, x, pos = new_pos() + if ord("0") == world_db["MAP"][pos]: + world_db["MAP"][pos] = ord("$") + i_altars += 1 + + +def calc_effort(ta, t): + from server.utils import mv_yx_in_dir_legal + if ta["TA_NAME"] == "move": + 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] + if chr(world_db["MAP"][pos]) in "012": + narrowness = world_db["MAP"][pos] - ord("0") + return 2 ** narrowness + return 1 +world_db["calc_effort"] = calc_effort + + +def turn_over(): + from server.ai import ai + from server.config.actions import action_db + from server.update_map_memory import update_map_memory + from server.io import try_worldstate_update + from server.config.io import io_db + from server.utils import rand + from server.build_fov_map import build_fov_map + while world_db["Things"][0]["T_LIFEPOINTS"]: + for tid in [tid for tid in world_db["Things"]]: + if not tid in world_db["Things"]: + continue + t = world_db["Things"][tid] + if t["T_LIFEPOINTS"]: + if not (world_db["test_air"](t) and world_db["test_hole"](t)): + continue + if not t["T_COMMAND"]: + update_map_memory(t) + build_fov_map(t) + if 0 == tid: + return + world_db["ai"](t) + if t["T_LIFEPOINTS"]: + t["T_PROGRESS"] += 1 + taid = [a for a in world_db["ThingActions"] + if a == t["T_COMMAND"]][0] + ThingAction = world_db["ThingActions"][taid] + effort = world_db["calc_effort"](ThingAction, t) + if t["T_PROGRESS"] >= effort: + action = action_db["actor_" + ThingAction["TA_NAME"]] + action(t) + if t["T_LIFEPOINTS"] <= 0: + continue + t["T_COMMAND"] = 0 + t["T_PROGRESS"] = 0 + if t["T_BOWEL"] > 16: + if 0 == (rand.next() % (33 - t["T_BOWEL"])): + action_db["actor_drop"](t) + if t["T_BLADDER"] > 16: + if 0 == (rand.next() % (33 - t["T_BLADDER"])): + action_db["actor_pee"](t) + if 0 == world_db["TURN"] % 5: + t["T_STOMACH"] -= 1 + t["T_BOWEL"] += 1 + t["T_KIDNEY"] -= 1 + t["T_BLADDER"] += 1 + if t["T_STOMACH"] <= 0: + world_db["die"](t, "You DIE of hunger.") + elif t["T_KIDNEY"] <= 0: + world_db["die"](t, "You DIE of dehydration.") + mapsize = world_db["MAP_LENGTH"] ** 2 + for pos in range(mapsize): + wetness = world_db["wetmap"][pos] - ord("0") + height = world_db["MAP"][pos] - ord("0") + if world_db["MAP"][pos] == ord("-"): + height = -1 + elif world_db["MAP"][pos] == ord("+"): + height = -2 + elif world_db["MAP"][pos] == ord("$"): + height = -3 + if height == -2 and wetness > 1 \ + and 0 == rand.next() % ((2 ** 10) / (2 ** wetness)): + world_db["MAP"][pos] = ord("*") + world_db["HUMIDITY"] += wetness + if height == -1 and wetness > 1 \ + and 0 == rand.next() % ((2 ** 9) / (2 ** wetness)): + world_db["MAP"][pos] = ord("+") + if height == 0 and wetness > 1 \ + and 0 == rand.next() % ((2 ** 8) / (2 ** wetness)): + world_db["MAP"][pos] = ord("-") + if ((wetness > 0 and height > 0) or wetness > 1) \ + and 0 == rand.next() % 16: + world_db["wetmap"][pos] -= 1 + world_db["HUMIDITY"] += 1 + if world_db["HUMIDITY"] > 0: + if world_db["HUMIDITY"] > 2 and 0 == rand.next() % 2: + world_db["NEW_SPAWN"] += 1 + world_db["HUMIDITY"] -= 1 + if world_db["NEW_SPAWN"] >= 16: + world_db["NEW_SPAWN"] -= 16 + from server.new_thing import new_Thing + 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]) !=\ + "5": + from server.utils import id_setter + tid = id_setter(-1, "Things") + world_db["Things"][tid] = new_Thing( + world_db["PLAYER_TYPE"], (y, x)) + pos = y * world_db["MAP_LENGTH"] + x + break + positions_to_wet = [] + for pos in range(mapsize): + if chr(world_db["MAP"][pos]) in "0-+" \ + and world_db["wetmap"][pos] < ord("5"): + positions_to_wet += [pos] + while world_db["HUMIDITY"] > 0 and len(positions_to_wet) > 0: + select = rand.next() % len(positions_to_wet) + pos = positions_to_wet[select] + world_db["wetmap"][pos] += 1 + positions_to_wet.remove(pos) + world_db["HUMIDITY"] -= 1 + for pos in range(mapsize): + if world_db["soundmap"][pos] > ord("0"): + world_db["soundmap"][pos] -= 1 + from server.utils import libpr + libpr.init_score_map() + 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().") + [set_map_score(pos, 1) for pos in range(mapsize) + if world_db["MAP"][pos] == ord("*")] + for pos in range(mapsize): + if world_db["MAP"][pos] == ord("*"): + if libpr.ready_neighbor_scores(pos): + raise RuntimeError("No score map allocated for " + + "ready_neighbor_scores.()") + score = 0 + dirs = "edcxsw" + for i in range(len(dirs)): + score += libpr.get_neighbor_score(i) + if score == 6: + world_db["MAP"][pos] = ord("&") + libpr.free_score_map() + world_db["TURN"] += 1 + io_db["worldstate_updateable"] = True + try_worldstate_update() +world_db["turn_over"] = turn_over + + +def set_command(action): + """Set player's T_COMMAND, then call turn_over().""" + tid = [x for x in world_db["ThingActions"] + if world_db["ThingActions"][x]["TA_NAME"] == action][0] + world_db["Things"][0]["T_COMMAND"] = tid + world_db["turn_over"]() +world_db["set_command"] = set_command + + +def play_wait(): + """Try "wait" as player's T_COMMAND.""" + if world_db["WORLD_ACTIVE"]: + world_db["set_command"]("wait") + + +def save_maps(): + length = world_db["MAP_LENGTH"] + string = "" + for i in range(length): + line = world_db["wetmap"][i * length:(i * length) + length].decode() + string = string + "WETMAP" + " " + str(i) + " " + line + "\n" + for i in range(length): + line = world_db["soundmap"][i * length:(i * length) + length].decode() + string = string + "SOUNDMAP" + " " + str(i) + " " + line + "\n" + return string + + +def soundmapset(str_int, mapline): + def valid_map_line(str_int, mapline): + from server.utils import integer_test + 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: + return val + return None + val = valid_map_line(str_int, mapline) + if None != val: + length = world_db["MAP_LENGTH"] + if not world_db["soundmap"]: + m = bytearray(b' ' * (length ** 2)) + else: + m = world_db["soundmap"] + m[val * length:(val * length) + length] = mapline.encode() + if not world_db["soundmap"]: + world_db["soundmap"] = m + + +def wetmapset(str_int, mapline): + def valid_map_line(str_int, mapline): + from server.utils import integer_test + 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: + return val + return None + val = valid_map_line(str_int, mapline) + if None != val: + length = world_db["MAP_LENGTH"] + if not world_db["wetmap"]: + m = bytearray(b' ' * (length ** 2)) + else: + m = world_db["wetmap"] + m[val * length:(val * length) + length] = mapline.encode() + if not world_db["wetmap"]: + world_db["wetmap"] = m + + +def write_soundmap(): + from server.worldstate_write_helpers import write_map + length = world_db["MAP_LENGTH"] + return write_map(world_db["soundmap"], world_db["MAP_LENGTH"]) + + +def write_wetmap(): + from server.worldstate_write_helpers import write_map + length = world_db["MAP_LENGTH"] + visible_wetmap = bytearray(b' ' * (length ** 2)) + for i in range(length ** 2): + if world_db["Things"][0]["fovmap"][i] == ord('v'): + visible_wetmap[i] = world_db["wetmap"][i] + return write_map(visible_wetmap, world_db["MAP_LENGTH"]) + + +def get_dir_to_target(t, target): + + from server.utils import rand, libpr, c_pointer_to_bytearray + from server.config.world_data import symbols_passable + + 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 zero_score_map_where_char_on_memdepthmap(c): + map = c_pointer_to_bytearray(t["T_MEMDEPTHMAP"]) + if libpr.zero_score_map_where_char_on_memdepthmap(c, map): + raise RuntimeError("No score map allocated for " + "zero_score_map_where_char_on_memdepthmap().") + + 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 set_movement_cost_map(): + copy_memmap = t["T_MEMMAP"][:] + copy_memmap.replace(b' ', b'4') + memmap = c_pointer_to_bytearray(copy_memmap) + if libpr.TCE_set_movement_cost_map(memmap): + raise RuntimeError("No movement cost map allocated for " + "set_movement_cost_map().") + + def animates_in_fov(maplength): + return [Thing for Thing in world_db["Things"].values() + if Thing["T_LIFEPOINTS"] and 118 == t["fovmap"][Thing["pos"]] + and (not Thing == t)] + + def seeing_thing(): + def exists(gen): + try: + next(gen) + except StopIteration: + return False + return True + mapsize = world_db["MAP_LENGTH"] ** 2 + if target == "food" and t["T_MEMMAP"]: + return exists(pos for pos in range(mapsize) + if ord("2") < t["T_MEMMAP"][pos] < ord("5")) + elif target == "fluid_certain" and t["fovmap"]: + return exists(pos for pos in range(mapsize) + if t["fovmap"] == ord("v") + if world_db["MAP"][pos] == ord("0") + if world_db["wetmap"][pos] > ord("0")) + elif target == "crack" and t["T_MEMMAP"]: + return exists(pos for pos in range(mapsize) + if t["T_MEMMAP"][pos] == ord("-")) + elif target == "fluid_potential" and t["T_MEMMAP"] and t["fovmap"]: + return exists(pos for pos in range(mapsize) + if t["T_MEMMAP"][pos] == ord("0") + if t["fovmap"] != ord("v")) + elif target == "space" and t["T_MEMMAP"] and t["fovmap"]: + return exists(pos for pos in range(mapsize) + if ord("-") <= t["T_MEMMAP"][pos] <= ord("2") + if (t["fovmap"] != ord("v") + or world_db["terrain_fullness"](pos) < 5)) + elif target in {"hunt", "flee"} and t["fovmap"]: + return exists(Thing for + Thing in animates_in_fov(world_db["MAP_LENGTH"])) \ + or exists(pos for pos in range(mapsize) + if world_db["soundmap"][pos] > ord("0") + if t["fovmap"][pos] != ord("v")) + return False + + def init_score_map(): + mapsize = world_db["MAP_LENGTH"] ** 2 + test = libpr.TCE_init_score_map() + [set_map_score(pos, 65535) for pos in range(mapsize) + if chr(t["T_MEMMAP"][pos]) in "5*&"] + set_movement_cost_map() + if test: + raise RuntimeError("Malloc error in init_score_map().") + if target == "food" and t["T_MEMMAP"]: + [set_map_score(pos, 0) for pos in range(mapsize) + if ord("2") < t["T_MEMMAP"][pos] < ord("5")] + elif target == "fluid_certain" and t["fovmap"]: + [set_map_score(pos, 0) for pos in range(mapsize) + if t["fovmap"] == ord("v") + if world_db["MAP"][pos] == ord("0") + if world_db["wetmap"][pos] > ord("0")] + elif target == "crack" and t["T_MEMMAP"]: + [set_map_score(pos, 0) for pos in range(mapsize) + if t["T_MEMMAP"][pos] == ord("-")] + elif target == "fluid_potential" and t["T_MEMMAP"] and t["fovmap"]: + [set_map_score(pos, 0) for pos in range(mapsize) + if t["T_MEMMAP"][pos] == ord("0") + if t["fovmap"] != ord("v")] + elif target == "space" and t["T_MEMMAP"] and t["fovmap"]: + [set_map_score(pos, 0) for pos in range(mapsize) + if ord("-") <= t["T_MEMMAP"][pos] <= ord("2") + if (t["fovmap"] != ord("v") + or world_db["terrain_fullness"](pos) < 5)] + elif target == "search": + zero_score_map_where_char_on_memdepthmap(mem_depth_c[0]) + elif target in {"hunt", "flee"}: + [set_map_score(Thing["pos"], 0) for + Thing in animates_in_fov(world_db["MAP_LENGTH"])] + [set_map_score(pos, 0) for pos in range(mapsize) + if world_db["soundmap"][pos] > ord("0") + if t["fovmap"][pos] != ord("v")] + + 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(): + import math + dir_to_target = False + dirs = "edcxsw" + eye_pos = t["pos"] + neighbors = get_neighbor_scores(dirs, eye_pos) + minmax_start = 0 if "flee" == target else 65535 - 1 + minmax_neighbor = minmax_start + for i in range(len(dirs)): + if ("flee" == target and get_map_score(t["pos"]) < neighbors[i] and + minmax_neighbor < neighbors[i] and 65535 != neighbors[i]) \ + or ("flee" != target 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 "flee" == target: + distance = get_map_score(t["pos"]) + fear_distance = 5 + attack_distance = 1 + if not dir_to_target: + if attack_distance >= distance: + dir_to_target = rand_target_dir(neighbors, + distance - 1, dirs) + elif dir_to_target and fear_distance < distance: + dir_to_target = 0 + return dir_to_target, minmax_neighbor + + dir_to_target = False + mem_depth_c = b' ' + run_i = 9 + 1 if "search" == target else 1 + minmax_neighbor = 0 + while run_i and not dir_to_target and \ + ("search" == target 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.TCE_dijkstra_map_with_movement_cost(): + raise RuntimeError("No score map allocated for dijkstra_map().") + dir_to_target, minmax_neighbor = get_dir_from_neighbors() + libpr.free_score_map() + if dir_to_target and str == type(dir_to_target): + action = "move" + from server.utils import mv_yx_in_dir_legal + move_result = mv_yx_in_dir_legal(dir_to_target, t["T_POSY"], + t["T_POSX"]) + if 1 != move_result[0]: + return False, 0 + pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2] + hitted = [tid for tid in world_db["Things"] + if world_db["Things"][tid]["pos"] == pos] + if world_db["MAP"][pos] > ord("2") or len(hitted) > 0: + action = "eat" + t["T_COMMAND"] = [taid for taid in world_db["ThingActions"] + if world_db["ThingActions"][taid]["TA_NAME"] + == action][0] + t["T_ARGUMENT"] = ord(dir_to_target) + return dir_to_target, minmax_neighbor +world_db["get_dir_to_target"] = get_dir_to_target + + +def terrain_fullness(pos): + wetness = world_db["wetmap"][pos] - ord("0") + if chr(world_db["MAP"][pos]) in "-+": + height = 0 + else: + height = world_db["MAP"][pos] - ord("0") + return wetness + height +world_db["terrain_fullness"] = terrain_fullness + + +def ai(t): + + if t["T_LIFEPOINTS"] == 0: + return + + def standing_on_fluid(t): + if world_db["MAP"][t["pos"]] == ord("0") and \ + world_db["wetmap"][t["pos"]] > ord("0"): + return True + else: + return False + + def thing_action_id(name): + return [taid for taid in world_db["ThingActions"] + if world_db["ThingActions"][taid] + ["TA_NAME"] == name][0] + + t["T_COMMAND"] = thing_action_id("wait") + needs = { + "fix_cracks": 24, + "flee": 24, + "safe_pee": (world_db["terrain_fullness"](t["pos"]) * t["T_BLADDER"]) / 4, + "safe_drop": (world_db["terrain_fullness"](t["pos"]) * t["T_BOWEL"]) / 4, + "food": 33 - t["T_STOMACH"], + "fluid_certain": 33 - t["T_KIDNEY"], + "fluid_potential": 32 - t["T_KIDNEY"], + "search": 1, + } + from operator import itemgetter + needs = sorted(needs.items(), key=itemgetter(1,0)) + needs.reverse() + for need in needs: + if need[1] > 0: + if need[0] == "fix_cracks": + if world_db["MAP"][t["pos"]] == ord("-") and \ + t["T_BOWEL"] > 0 and \ + world_db["terrain_fullness"](t["pos"]) <= 3: + t["T_COMMAND"] = thing_action_id("drop") + return + elif world_db["get_dir_to_target"](t, "crack")[0]: + return + if need[0] in {"fluid_certain", "fluid_potential"}: + if standing_on_fluid(t): + t["T_COMMAND"] = thing_action_id("drink") + return + elif t["T_BLADDER"] > 0 and \ + world_db["MAP"][t["pos"]] == ord("0"): + t["T_COMMAND"] = thing_action_id("pee") + return + elif need[0] in {"safe_pee", "safe_drop"}: + action_name = need[0][len("safe_"):] + if world_db["terrain_fullness"](t["pos"]) <= 3: + t["T_COMMAND"] = thing_action_id(action_name) + return + test = world_db["get_dir_to_target"](t, "space") + if test[0]: + if test[1] < 5: + return + elif world_db["terrain_fullness"](t["pos"]) < 5: + t["T_COMMAND"] = thing_action_id(action_name) + return + if t["T_STOMACH"] < 32 and \ + world_db["get_dir_to_target"](t, "food")[0]: + return + continue + if need[0] in {"fluid_certain", "fluid_potential", "food"}: + if world_db["get_dir_to_target"](t, need[0])[0]: + return + elif world_db["get_dir_to_target"](t, "hunt")[0]: + return + elif need[0] != "food" and t["T_STOMACH"] < 32 and \ + world_db["get_dir_to_target"](t, "food")[0]: + return + elif world_db["get_dir_to_target"](t, need[0])[0]: + return +world_db["ai"] = ai + + +from server.config.io import io_db +io_db["worldstate_write_order"] += [["T_STOMACH", "player_int"]] +io_db["worldstate_write_order"] += [["T_KIDNEY", "player_int"]] +io_db["worldstate_write_order"] += [["T_BOWEL", "player_int"]] +io_db["worldstate_write_order"] += [["T_BLADDER", "player_int"]] +io_db["worldstate_write_order"] += [[write_wetmap, "func"]] +io_db["worldstate_write_order"] += [[write_soundmap, "func"]] +io_db["worldstate_write_order"] += [["GRACE", "world_int"]] +import server.config.world_data +server.config.world_data.symbols_hide = "345" +server.config.world_data.symbols_passable = "012-+*&$" +server.config.world_data.thing_defaults["T_STOMACH"] = 16 +server.config.world_data.thing_defaults["T_BOWEL"] = 0 +server.config.world_data.thing_defaults["T_KIDNEY"] = 16 +server.config.world_data.thing_defaults["T_BLADDER"] = 0 +world_db["soundmap"] = bytearray(b"0" * world_db["MAP_LENGTH"] ** 2) +world_db["wetmap"] = bytearray(b"0" * world_db["MAP_LENGTH"] ** 2) +if not "NEW_SPAWN" in world_db: + world_db["NEW_SPAWN"] = 0 +if not "HUMIDITY" in world_db: + world_db["HUMIDITY"] = 0 +if not "GRACE" in world_db: + world_db["GRACE"] = 0 +io_db["hook_save"] = save_maps +import server.config.make_world_helpers +server.config.make_world_helpers.make_map = make_map +from server.config.commands import commands_db +commands_db["THINGS_HERE"] = (2, True, lambda x, y: None) +commands_db["HELP"] = (1, False, command_help) +commands_db["ai"] = (0, False, command_ai) +commands_db["move"] = (1, False, play_move) +commands_db["eat"] = (1, False, play_move) +commands_db["wait"] = (0, False, play_wait) +commands_db["drop"] = (0, False, play_drop) +commands_db["drink"] = (0, False, play_drink) +commands_db["pee"] = (0, False, play_pee) +commands_db["use"] = (1, False, lambda x: None) +commands_db["pickup"] = (0, False, lambda: None) +commands_db["GRACE"] = (1, False, setter(None, "GRACE", 0, 255)) +commands_db["NEW_SPAWN"] = (1, False, setter(None, "NEW_SPAWN", 0, 255)) +commands_db["HUMIDITY"] = (1, False, setter(None, "HUMIDITY", 0, 65535)) +commands_db["T_STOMACH"] = (1, False, setter("Thing", "T_STOMACH", 0, 255)) +commands_db["T_KIDNEY"] = (1, False, setter("Thing", "T_KIDNEY", 0, 255)) +commands_db["T_BOWEL"] = (1, False, setter("Thing", "T_BOWEL", 0, 255)) +commands_db["T_BLADDER"] = (1, False, setter("Thing", "T_BLADDER", 0, 255)) +commands_db["WETMAP"] = (2, False, wetmapset) +commands_db["SOUNDMAP"] = (2, False, soundmapset) +from server.actions import actor_wait +import server.config.actions +server.config.actions.action_db = { + "actor_wait": actor_wait, + "actor_move": actor_move, + "actor_drop": actor_drop, + "actor_drink": actor_drink, + "actor_pee": actor_pee, + "actor_eat": actor_eat, +} + +strong_write(io_db["file_out"], "PLUGIN TheCrawlingEater\n") diff --git a/roguelike-server b/roguelike-server index c8bf3f7..414f765 100755 --- a/roguelike-server +++ b/roguelike-server @@ -64,11 +64,16 @@ try: if opts.savefile: io_db["path_save"] = opts.savefile io_db["path_record"] = "record_" + opts.savefile + import os + if opts.new: + if os.access(io_db["path_record"], os.F_OK): + os.remove(io_db["path_record"]) + if os.access(io_db["path_save"], os.F_OK): + os.remove(io_db["path_save"]) from server.io import setup_server_io setup_server_io() if opts.verbose: io_db["verbose"] = True - import os from server.config.world_data import world_db from server.io import read_command, try_worldstate_update, obey if None != opts.replay: diff --git a/server/ai.py b/server/ai.py index eca980f..f9455f5 100644 --- a/server/ai.py +++ b/server/ai.py @@ -166,19 +166,18 @@ def get_dir_to_target(t, filter): import math dir_to_target = False dirs = "edcxsw" - eye_pos = t["pos"] - neighbors = get_neighbor_scores(dirs, eye_pos) + neighbors = get_neighbor_scores(dirs, t["pos"]) 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 + if ("f" == filter and get_map_score(t["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: - distance = get_map_score(eye_pos) + distance = get_map_score(t["pos"]) fear_distance = world_db["MAP_LENGTH"] if t["T_SATIATION"] < 0 and math.sqrt(-t["T_SATIATION"]) > 0: fear_distance = fear_distance / math.sqrt(-t["T_SATIATION"]) diff --git a/server/commands.py b/server/commands.py index b8216a0..df4f962 100644 --- a/server/commands.py +++ b/server/commands.py @@ -314,8 +314,8 @@ def command_taname(name): 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 == "pickup": + from server.config.commands import commands_db + if name in commands_db and name.islower(): world_db["ThingActions"][command_taid.id]["TA_NAME"] = name if 1 == world_db["WORLD_ACTIVE"]: for id in world_db["ThingActions"]: diff --git a/server/config/io.py b/server/config/io.py index 4631638..dabe255 100644 --- a/server/config/io.py +++ b/server/config/io.py @@ -31,5 +31,6 @@ io_db = { ["MAP_LENGTH", "world_int"], [write_fov_map, "func"], [write_mem_map, "func"] - ] + ], + "hook_save": lambda: "", } diff --git a/server/io.py b/server/io.py index ce3ee1a..5e02ed4 100644 --- a/server/io.py +++ b/server/io.py @@ -212,6 +212,7 @@ def save_world(): string = string + "T_ID " + str(tid) + "\n" for carried in sorted(world_db["Things"][tid]["T_CARRIES"]): string = string + "T_CARRIES " + str(carried) + "\n" + string = string + io_db["hook_save"]() string = string + "SEED_RANDOMNESS " + str(rand.seed) + "\n" + \ "WORLD_ACTIVE " + str(world_db["WORLD_ACTIVE"]) atomic_write(io_db["path_save"], string) diff --git a/server/utils.py b/server/utils.py index c8f1c38..f712891 100644 --- a/server/utils.py +++ b/server/utils.py @@ -98,12 +98,13 @@ def parse_command_line_arguments(): """Return settings values read from command line arguments.""" import argparse parser = argparse.ArgumentParser() + parser.add_argument('-n', dest='new', action='store_true') 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('-w', type=str, - default="confserver/PleaseTheIslandGod", + default="confserver/TheCrawlingEater", dest='worldconf', action='store') parser.add_argument('-v', dest='verbose', action='store_true') opts, unknown = parser.parse_known_args() diff --git a/server/world.py b/server/world.py index ff8043e..d472e1f 100644 --- a/server/world.py +++ b/server/world.py @@ -62,6 +62,7 @@ def set_world_inactive(): def turn_over(): """Run game world and its inhabitants until new player input expected.""" + # TODO: Add build_fov_map, actor_move trigger not enough on changing maps. from server.ai import ai from server.config.actions import action_db from server.config.misc import calc_effort @@ -69,7 +70,6 @@ def turn_over(): from server.thingproliferation import thingproliferation from server.io import try_worldstate_update from server.config.io import io_db - id = 0 while world_db["Things"][0]["T_LIFEPOINTS"]: proliferable_map = world_db["MAP"][:] for tid in [tid for tid in world_db["Things"]