Add make target for coverage
[open-adventure.git] / newdungeon.py
index 68dbc39b1130c13d697758aa1dab9a03a2167015..d1ea21c8f04c696429960ddb3ccd74107885f624 100755 (executable)
@@ -8,7 +8,7 @@
 # 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 packs that data into the travel array.
+# then a second stage unpacks that data into the travel array.
 #
 # Here are the rules of the intermediate form:
 #
@@ -85,12 +85,19 @@ h_template = """/* Generated from adventure.yaml - do not hand-hack! */
 #define COND_HJADE     20      /* Found all treasures except jade */
 
 typedef struct {{
+  const char** strs;
+  const int n;
+}} string_group_t;
+
+typedef struct {{
+  const string_group_t words;
   const char* inventory;
   int plac, fixd;
   bool is_treasure;
-  const char** longs;
+  const char** descriptions;
   const char** sounds;
   const char** texts;
+  const char** changes;
 }} object_t;
 
 typedef struct {{
@@ -129,14 +136,32 @@ typedef struct {{
 }} hint_t;
 
 typedef struct {{
-  const char** words;
+  const string_group_t words;
 }} motion_t;
 
 typedef struct {{
-  const char** words;
+  const string_group_t words;
   const long message;
 }} action_t;
 
+typedef struct {{
+  const long motion;
+  const long dest;
+  const bool stop;
+}} travelop_t;
+
+/* Abstract out the encoding of words in the travel array.  Gives us
+ * some hope of getting to a less cryptic representation than we
+ * inherited from FORTRAN, someday. To understand these, read the
+ * encoding description for travel.
+ */
+#define T_DESTINATION(entry)   MOD((entry).dest, 1000)
+#define T_CONDITION(entry)     ((entry).dest / 1000)
+#define T_NODWARVES(entry)     (T_CONDITION(entry) == 100)
+#define T_HIGH(entry)          ((entry).dest)
+#define T_TERMINATE(entry)     ((entry).motion == 1)
+#define L_SPEAK(loc)           ((loc) - 500)
+
 extern const location_t locations[];
 extern const object_t objects[];
 extern const char* arbitrary_messages[];
@@ -147,7 +172,8 @@ extern const hint_t hints[];
 extern long conditions[];
 extern const motion_t motions[];
 extern const action_t actions[];
-extern const long travel[];
+extern const action_t specials[];
+extern const travelop_t travel[];
 extern const long tkey[];
 
 #define NLOCATIONS     {}
@@ -156,7 +182,9 @@ extern const long tkey[];
 #define NCLASSES       {}
 #define NDEATHS                {}
 #define NTHRESHOLDS    {}
+#define NMOTIONS        {}
 #define NACTIONS       {}
+#define NSPECIALS       {}
 #define NTRAVEL                {}
 #define NKEYS          {}
 
@@ -180,6 +208,10 @@ enum action_refs {{
 {}
 }};
 
+enum special_refs {{
+{}
+}};
+
 /* State definitions */
 
 {}
@@ -231,9 +263,15 @@ const action_t actions[] = {{
 {}
 }};
 
+const action_t specials[] = {{
+{}
+}};
+
 {}
 
+const travelop_t travel[] = {{
 {}
+}};
 
 /* end */
 """
@@ -257,6 +295,19 @@ def get_refs(l):
     ref_str = ref_str[:-1] # trim trailing newline
     return ref_str
 
+def get_string_group(strings):
+    template = """{{
+            .strs = {},
+            .n = {},
+        }}"""
+    if strings == []:
+        strs = "NULL"
+    else:
+        strs = "(const char* []) {" + ", ".join([make_c_string(s) for s in strings]) + "}"
+    n = len(strings)
+    sg_str = template.format(strs, n)
+    return sg_str
+
 def get_arbitrary_messages(arb):
     template = """    {},
 """
@@ -318,17 +369,21 @@ def get_locations(loc):
 
 def get_objects(obj):
     template = """    {{ // {}
+        .words = {},
         .inventory = {},
         .plac = {},
         .fixd = {},
         .is_treasure = {},
-        .longs = (const char* []) {{
+        .descriptions = (const char* []) {{
 {}
         }},
         .sounds = (const char* []) {{
 {}
         }},
         .texts = (const char* []) {{
+{}
+        }},
+        .changes = (const char* []) {{
 {}
         }},
     }},
@@ -336,18 +391,22 @@ def get_objects(obj):
     obj_str = ""
     for (i, item) in enumerate(obj):
         attr = item[1]
+        try:
+            words_str = get_string_group(attr["words"])
+        except KeyError:
+            words_str = get_string_group([])
         i_msg = make_c_string(attr["inventory"])
-        longs_str = ""
-        if attr["longs"] == None:
-            longs_str = " " * 12 + "NULL,"
+        descriptions_str = ""
+        if attr["descriptions"] == None:
+            descriptions_str = " " * 12 + "NULL,"
         else:
             labels = []
-            for l_msg in attr["longs"]:
+            for l_msg in attr["descriptions"]:
                 if not isinstance(l_msg, str):
                     labels.append(l_msg)
                     l_msg = l_msg[1]
-                longs_str += " " * 12 + make_c_string(l_msg) + ",\n"
-            longs_str = longs_str[:-1] # trim trailing newline
+                descriptions_str += " " * 12 + make_c_string(l_msg) + ",\n"
+            descriptions_str = descriptions_str[:-1] # trim trailing newline
             if labels:
                 global statedefines
                 statedefines += "/* States for %s */\n" % item[0]
@@ -370,6 +429,13 @@ def get_objects(obj):
              for l_msg in attr["texts"]:
                  texts_str += " " * 12 + make_c_string(l_msg) + ",\n"
              texts_str = texts_str[:-1] # trim trailing newline
+        changes_str = ""
+        if attr.get("changes") == None:
+            changes_str = " " * 12 + "NULL,"
+        else:
+             for l_msg in attr["changes"]:
+                 changes_str += " " * 12 + make_c_string(l_msg) + ",\n"
+             changes_str = changes_str[:-1] # trim trailing newline
         locs = attr.get("locations", ["LOC_NOWHERE", "LOC_NOWHERE"])
         immovable = attr.get("immovable", False)
         try:
@@ -381,7 +447,7 @@ def get_objects(obj):
             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, i_msg, locs[0], locs[1], treasure, longs_str, sounds_str, texts_str)
+        obj_str += template.format(i, 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
 
@@ -462,10 +528,9 @@ def get_motions(motions):
     for motion in motions:
         contents = motion[1]
         if contents["words"] == None:
-            mot_str += template.format("NULL")
-            continue
-        c_words = [make_c_string(s) for s in contents["words"]]
-        words_str = "(const char* []) {" + ", ".join(c_words) + "}"
+            words_str = get_string_group([])
+        else:
+            words_str = get_string_group(contents["words"])
         mot_str += template.format(words_str)
     return mot_str
 
@@ -480,10 +545,9 @@ def get_actions(actions):
         contents = action[1]
         
         if contents["words"] == None:
-            words_str = "NULL"
+            words_str = get_string_group([])
         else:
-            c_words = [make_c_string(s) for s in contents["words"]]
-            words_str = "(const char* []) {" + ", ".join(c_words) + "}"
+            words_str = get_string_group(contents["words"])
 
         if contents["message"] == None:
             message = "NO_MESSAGE"
@@ -552,13 +616,13 @@ def buildtravel(locs, objs, voc):
                 if type(cond[2]) == int:
                     state = cond[2]
                 else:
-                    for (i, stateclause) in enumerate(objs[obj][1]["longs"]):
+                    for (i, stateclause) in enumerate(objs[obj][1]["descriptions"]):
                         if type(stateclause) == list:
                             if stateclause[0] == cond[2]:
                                 state = i
                                 break
                     else:
-                        sys.stderr.write("dungeon: unmatched state symbol %s in not caluase of %s\n" % (cond[2], name))
+                        sys.stderr.write("dungeon: unmatched state symbol %s in not clause of %s\n" % (cond[2], name))
                         sys.exit(0);
                 return 300 + obj + 100 * state
             except ValueError:
@@ -600,7 +664,10 @@ def buildtravel(locs, objs, voc):
     #     }
     #     TRAVEL[TRVS - 1] = -TRAVEL[TRVS - 1];
     # }
-    travel = [0]
+    #
+    # In order to de-crypticize the runtime code, we're going to break these
+    # magic numbers up into a struct.
+    travel = [[0, 0, False]]
     tkey = [0]
     oldloc = 0
     while ltravel:
@@ -611,12 +678,25 @@ def buildtravel(locs, objs, voc):
             tkey.append(len(travel))
             oldloc = loc 
         elif travel:
-            travel[-1] *= -1
+            travel[-1][2] = not travel[-1][2]
         while rule:
-            travel.append(rule.pop(0) + newloc * 1000)
-        travel[-1] *= -1
+            travel.append([rule.pop(0), newloc, False])
+        travel[-1][2] = True
     return (travel, tkey)
 
+def get_travel(travel):
+    template = """    {{
+        .motion = {},
+        .dest = {},
+        .stop = {},
+    }},
+"""
+    out = ""
+    for entry in travel:
+        out += template.format(entry[0], entry[1], entry[2]).lower()
+    out = out[:-1] # trim trailing newline
+    return out
+
 if __name__ == "__main__":
     with open(yaml_name, "r") as f:
         db = yaml.load(f)
@@ -641,8 +721,9 @@ 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),
-        "const long travel[] = {%s};" % bigdump(travel), 
+        get_travel(travel), 
     )
 
     h = h_template.format(
@@ -652,7 +733,9 @@ if __name__ == "__main__":
         len(db["classes"])-1,
         len(db["obituaries"]),
         len(db["turn_thresholds"]),
+        len(db["motions"]),
         len(db["actions"]),
+        len(db["specials"]),
         len(travel),
         len(tkey),
         get_refs(db["arbitrary_messages"]),
@@ -660,6 +743,7 @@ if __name__ == "__main__":
         get_refs(db["objects"]),
         get_refs(db["motions"]),
         get_refs(db["actions"]),
+        get_refs(db["specials"]),
         statedefines,
     )