X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=make_dungeon.py;h=03b5b04229fc60023a378208cc8525fb8873bb97;hp=6f4715238a5465c7fe51e2cb32563f3063625a67;hb=e8a627f964a8337caf0c71dd87b1d7533f489f57;hpb=db281a96d70a43c6a6d5b516eceb6e4f67637c57 diff --git a/make_dungeon.py b/make_dungeon.py index 6f47152..03b5b04 100755 --- a/make_dungeon.py +++ b/make_dungeon.py @@ -1,48 +1,11 @@ -#!/usr/bin/python3 +#!/usr/bin/env python -# This is the new open-adventure dungeon generator. It'll eventually -# replace the existing dungeon.c It currently outputs a .h and .c pair -# for C code. +# This is the open-adventure dungeon generator. It consumes a YAML description of +# the dungeon and outputs a dungeon.h and dungeon.c pair of C code files. # # The nontrivial part of this is the compilation of the YAML for # movement rules to the travel array that's actually used by -# playermove(). This program first compiles the YAML to a form -# identical to the data in section 3 of the old adventure.text file, -# then a second stage unpacks that data into the travel array. -# -# Here are the rules of the intermediate form: -# -# Each row of data contains a location number (X), a second -# location number (Y), and a list of motion numbers (see section 4). -# each motion represents a verb which will go to Y if currently at X. -# Y, in turn, is interpreted as follows. Let M=Y/1000, N=Y mod 1000. -# If N<=300 it is the location to go to. -# If 300500 message N-500 from section 6 is printed, -# and he stays wherever he is. -# Meanwhile, M specifies the conditions on the motion. -# If M=0 it's unconditional. -# If 0= 45: - message = message[:45] + "..." - statedefines += "#define %s\t%d /* %s */\n" % (label, i, message) + for (j, label) in enumerate(labels): + statedefines += "#define %s\t%d\n" % (label, j) statedefines += "\n" sounds_str = "" if attr.get("sounds") == None: @@ -439,14 +398,12 @@ def get_objects(obj): immovable = attr.get("immovable", False) try: if type(locs) == str: - locs = [locnames.index(locs), -1 if immovable else 0] - else: - locs = [locnames.index(x) for x in locs] + locs = [locs, -1 if immovable else 0] except IndexError: sys.stderr.write("dungeon: unknown object location in %s\n" % locs) sys.exit(1) treasure = "true" if attr.get("treasure") else "false" - obj_str += template.format(i, words_str, i_msg, locs[0], locs[1], treasure, descriptions_str, sounds_str, texts_str, changes_str) + obj_str += template.format(i, item[0], words_str, i_msg, locs[0], locs[1], treasure, descriptions_str, sounds_str, texts_str, changes_str) obj_str = obj_str[:-1] # trim trailing newline return obj_str @@ -506,18 +463,6 @@ def get_condbits(locations): cnd_str += " " + line + ",\t// " + name + "\n" return cnd_str -def recompose(type_word, value): - "Compose the internal code for a vocabulary word from its YAML entry" - parts = ("motion", "action", "object", "special") - try: - return value + 1000 * parts.index(type_word) - except KeyError: - sys.stderr.write("dungeon: %s is not a known word\n" % word) - sys.exit(1) - except IndexError: - sys.stderr.write("%s is not a known word classifier\n" % attrs["type"]) - sys.exit(1) - def get_motions(motions): template = """ {{ .words = {}, @@ -531,29 +476,45 @@ def get_motions(motions): else: words_str = get_string_group(contents["words"]) mot_str += template.format(words_str) + global ignore + if contents.get("oldstyle", True) == False: + for word in contents["words"]: + if len(word) == 1: + ignore += word.upper() return mot_str def get_actions(actions): template = """ {{ .words = {}, .message = {}, + .noaction = {}, }}, """ act_str = "" for action in actions: contents = action[1] - + if contents["words"] == None: words_str = get_string_group([]) else: words_str = get_string_group(contents["words"]) if contents["message"] == None: - message = "NO_MESSAGE" + message = "NULL" + else: + message = make_c_string(contents["message"]) + + if contents.get("noaction") == None: + noaction = "false" else: - message = contents["message"] - - act_str += template.format(words_str, message) + noaction = "true" + + act_str += template.format(words_str, message, noaction) + global ignore + if contents.get("oldstyle", True) == False: + for word in contents["words"]: + if len(word) == 1: + ignore += word.upper() act_str = act_str[:-1] # trim trailing newline return act_str @@ -564,23 +525,71 @@ def bigdump(arr): if out and out[-1] == ' ': out = out[:-1] out += "\n " - out += str(arr[i]) + ", " + out += str(arr[i]).lower() + ", " out = out[:-2] + "\n" return out -def buildtravel(locs, objs, voc): +def buildtravel(locs, objs): + assert len(locs) <= 300 + assert len(objs) <= 100 + # THIS CODE IS WAAAY MORE COMPLEX THAN IT NEEDS TO BE. It's the + # result of a massive refactoring exercise that concentrated all + # the old nastiness in one spot. It hasn't been finally simplified + # because there's no need to do it until one of the asserions + # fails. Hint: if you try cleaning this up, the acceptance test is + # simple - the output dungeon.c must not change. + # + # This function first compiles the YAML to a form identical to the + # data in section 3 of the old adventure.text file, then a second + # stage unpacks that data into the travel array. Here are the + # rules of that intermediate form: + # + # Each row of data contains a location number (X), a second + # location number (Y), and a list of motion numbers (see section 4). + # each motion represents a verb which will go to Y if currently at X. + # Y, in turn, is interpreted as follows. Let M=Y/1000, N=Y mod 1000. + # If N<=300 it is the location to go to. + # If 300500 message N-500 from section 6 is printed, + # and he stays wherever he is. + # Meanwhile, M specifies the conditions on the motion. + # If M=0 it's unconditional. + # If 0 500: + desttype = "dest_speak"; + destval = msgnames[dest - 500] + else: + desttype = "dest_special"; + destval = locnames[dest - 300] + travel.append([len(tkey)-1, + locnames[len(tkey)-1], + rule.pop(0), + condtype, + condarg1, + condarg2, + desttype, + destval, + "true" if nodwarves else "false", + "false"]) + travel[-1][-1] = "true" return (travel, tkey) def get_travel(travel): - template = """ {{ + template = """ {{ // from {}: {} .motion = {}, - .dest = {}, + .condtype = {}, + .condarg1 = {}, + .condarg2 = {}, + .desttype = {}, + .destval = {}, + .nodwarves = {}, .stop = {}, }}, """ out = "" for entry in travel: - out += template.format(entry[0], entry[1], entry[2]).lower() + out += template.format(*entry) out = out[:-1] # trim trailing newline return out @@ -703,11 +744,11 @@ if __name__ == "__main__": locnames = [x[0] for x in db["locations"]] msgnames = [el[0] for el in db["arbitrary_messages"]] objnames = [el[0] for el in db["objects"]] + motionnames = [el[0] for el in db["motions"]] (travel, tkey) = buildtravel(db["locations"], - db["objects"], - db["vocabulary"]) - + db["objects"]) + ignore = "" c = c_template.format( h_name, get_arbitrary_messages(db["arbitrary_messages"]), @@ -720,11 +761,15 @@ if __name__ == "__main__": get_condbits(db["locations"]), get_motions(db["motions"]), get_actions(db["actions"]), - get_actions(db["specials"]), - "const long tkey[] = {%s};" % bigdump(tkey), + bigdump(tkey), get_travel(travel), + ignore, ) + # 0-origin index of birds's last song. Bird should + # die after player hears this. + deathbird = len(dict(db["objects"])["BIRD"]["sounds"]) - 1 + h = h_template.format( len(db["locations"])-1, len(db["objects"])-1, @@ -734,15 +779,14 @@ if __name__ == "__main__": len(db["turn_thresholds"]), len(db["motions"]), len(db["actions"]), - len(db["specials"]), len(travel), len(tkey), + deathbird, get_refs(db["arbitrary_messages"]), get_refs(db["locations"]), get_refs(db["objects"]), get_refs(db["motions"]), get_refs(db["actions"]), - get_refs(db["specials"]), statedefines, )