3b801da6d50ba0df2e554a971f86837a3b81a981
[open-adventure.git] / newdungeon.py
1 #!/usr/bin/python3
2
3 # 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.
4
5 import yaml
6
7 yaml_name = "adventure.yaml"
8 h_name = "newdb.h"
9 c_name = "newdb.c"
10
11 statedefines = ""
12
13 h_template = """/* Generated from adventure.yaml - do not hand-hack! */
14 #ifndef NEWDB_H
15 #define NEWDB_H
16
17 #include <stdio.h>
18 #include <stdbool.h>
19
20 #define SILENT  -1      /* no sound */
21
22 typedef struct {{
23   const char* inventory;
24   const char** longs;
25   const char** sounds;
26   const char** texts;
27 }} object_description_t;
28
29 typedef struct {{
30   const char* small;
31   const char* big;
32 }} descriptions_t;
33
34 typedef struct {{
35   descriptions_t description;
36   const long sound;
37   const bool loud;
38 }} location_t;
39
40 typedef struct {{
41   const char* query;
42   const char* yes_response;
43 }} obituary_t;
44
45 typedef struct {{
46   const int threshold;
47   const int point_loss;
48   const char* message;
49 }} turn_threshold_t;
50
51 typedef struct {{
52   const int threshold;
53   const char* message;
54 }} class_t;
55
56 typedef struct {{
57   const int number;
58   const int turns;
59   const int penalty;
60   const char* question;
61   const char* hint;
62 }} hint_t;
63
64 extern location_t locations[];
65 extern object_description_t object_descriptions[];
66 extern const char* arbitrary_messages[];
67 extern const class_t classes[];
68 extern turn_threshold_t turn_thresholds[];
69 extern obituary_t obituaries[];
70 extern hint_t hints[];
71 extern long conditions[];
72
73 #define NLOCATIONS              {}
74 #define NOBJECTS        {}
75 #define NHINTS          {}
76 #define NCLASSES        {}
77 #define NDEATHS         {}
78 #define NTHRESHOLDS     {}
79
80 enum arbitrary_messages_refs {{
81 {}
82 }};
83
84 enum locations_refs {{
85 {}
86 }};
87
88 enum object_descriptions_refs {{
89 {}
90 }};
91
92 /* State definitions */
93
94 {}
95 #endif /* end NEWDB_H */
96 """
97
98 c_template = """/* Generated from adventure.yaml - do not hand-hack! */
99
100 #include "common.h"
101 #include "{}"
102
103 const char* arbitrary_messages[] = {{
104 {}
105 }};
106
107 const class_t classes[] = {{
108 {}
109 }};
110
111 turn_threshold_t turn_thresholds[] = {{
112 {}
113 }};
114
115 location_t locations[] = {{
116 {}
117 }};
118
119 object_description_t object_descriptions[] = {{
120 {}
121 }};
122
123 obituary_t obituaries[] = {{
124 {}
125 }};
126
127 hint_t hints[] = {{
128 {}
129 }};
130
131 long conditions[] = {{
132 {}
133 }};
134
135 /* end */
136 """
137
138 def make_c_string(string):
139     """Render a Python string into C string literal format."""
140     if string == None:
141         return "NULL"
142     string = string.replace("\n", "\\n")
143     string = string.replace("\t", "\\t")
144     string = string.replace('"', '\\"')
145     string = string.replace("'", "\\'")
146     string = '"' + string + '"'
147     return string
148
149 def get_refs(l):
150     reflist = [x[0] for x in l]
151     ref_str = ""
152     for ref in reflist:
153         ref_str += "    {},\n".format(ref)
154     ref_str = ref_str[:-1] # trim trailing newline
155     return ref_str
156
157 def get_arbitrary_messages(arb):
158     template = """    {},
159 """
160     arb_str = ""
161     for item in arb:
162         arb_str += template.format(make_c_string(item[1]))
163     arb_str = arb_str[:-1] # trim trailing newline
164     return arb_str
165
166 def get_class_messages(cls):
167     template = """    {{
168         .threshold = {},
169         .message = {},
170     }},
171 """
172     cls_str = ""
173     for item in cls:
174         threshold = item["threshold"]
175         message = make_c_string(item["message"])
176         cls_str += template.format(threshold, message)
177     cls_str = cls_str[:-1] # trim trailing newline
178     return cls_str
179
180 def get_turn_thresholds(trn):
181     template = """    {{
182         .threshold = {},
183         .point_loss = {},
184         .message = {},
185     }},
186 """
187     trn_str = ""
188     for item in trn:
189         threshold = item["threshold"]
190         point_loss = item["point_loss"]
191         message = make_c_string(item["message"])
192         trn_str += template.format(threshold, point_loss, message)
193     trn_str = trn_str[:-1] # trim trailing newline
194     return trn_str
195
196 def get_locations(loc):
197     template = """    {{
198         .description = {{
199             .small = {},
200             .big = {},
201         }},
202         .sound = {},
203         .loud = {},
204     }},
205 """
206     loc_str = ""
207     for item in loc:
208         short_d = make_c_string(item[1]["description"]["short"])
209         long_d = make_c_string(item[1]["description"]["long"])
210         sound = item[1].get("sound", "SILENT")
211         loud = "true" if item[1].get("loud") else "false"
212         loc_str += template.format(short_d, long_d, sound, loud)
213     loc_str = loc_str[:-1] # trim trailing newline
214     return loc_str
215
216 def get_object_descriptions(obj):
217     template = """    {{
218         .inventory = {},
219         .longs = (const char* []) {{
220 {}
221         }},
222         .sounds = (const char* []) {{
223 {}
224         }},
225         .texts = (const char* []) {{
226 {}
227         }},
228     }},
229 """
230     obj_str = ""
231     for item in obj:
232         i_msg = make_c_string(item[1]["inventory"])
233         longs_str = ""
234         if item[1]["longs"] == None:
235             longs_str = " " * 12 + "NULL,"
236         else:
237             labels = []
238             for l_msg in item[1]["longs"]:
239                 if not isinstance(l_msg, str):
240                     labels.append(l_msg)
241                     l_msg = l_msg[1]
242                 longs_str += " " * 12 + make_c_string(l_msg) + ",\n"
243             longs_str = longs_str[:-1] # trim trailing newline
244             if labels:
245                 global statedefines
246                 statedefines += "/* States for %s */\n" % item[0]
247                 for (i, (label, message)) in enumerate(labels):
248                     if len(message) >= 45:
249                         message = message[:45] + "..."
250                     statedefines += "#define %s\t%d /* %s */\n" % (label, i, message)
251                 statedefines += "\n"
252         sounds_str = ""
253         if item[1].get("sounds") == None:
254             sounds_str = " " * 12 + "NULL,"
255         else:
256              for l_msg in item[1]["sounds"]:
257                  sounds_str += " " * 12 + make_c_string(l_msg) + ",\n"
258              sounds_str = sounds_str[:-1] # trim trailing newline
259         texts_str = ""
260         if item[1].get("texts") == None:
261             texts_str = " " * 12 + "NULL,"
262         else:
263              for l_msg in item[1]["texts"]:
264                  texts_str += " " * 12 + make_c_string(l_msg) + ",\n"
265              texts_str = texts_str[:-1] # trim trailing newline
266         obj_str += template.format(i_msg, longs_str, sounds_str, texts_str)
267     obj_str = obj_str[:-1] # trim trailing newline
268     return obj_str
269
270 def get_obituaries(obit):
271     template = """    {{
272         .query = {},
273         .yes_response = {},
274     }},
275 """
276     obit_str = ""
277     for o in obit:
278         query = make_c_string(o["query"])
279         yes = make_c_string(o["yes_response"])
280         obit_str += template.format(query, yes)
281     obit_str = obit_str[:-1] # trim trailing newline
282     return obit_str
283
284 def get_hints(hnt, arb):
285     template = """    {{
286         .number = {},
287         .penalty = {},
288         .turns = {},
289         .question = {},
290         .hint = {},
291     }},
292 """
293     hnt_str = ""
294     md = dict(arb)
295     for member in hnt:
296         item = member["hint"]
297         number = item["number"]
298         penalty = item["penalty"]
299         turns = item["turns"]
300         question = make_c_string(md[item["question"]])
301         hint = make_c_string(md[item["hint"]])
302         hnt_str += template.format(number, penalty, turns, question, hint)
303     hnt_str = hnt_str[:-1] # trim trailing newline
304     return hnt_str
305
306 def get_condbits(locations):
307     cnd_str = ""
308     for (name, loc) in locations:
309         conditions = loc["conditions"]
310         hints = loc.get("hints") or []
311         flaglist = []
312         for flag in conditions:
313             if conditions[flag]:
314                 flaglist.append(flag)
315         line = "|".join([("(1<<COND_%s)" % f) for f in flaglist])
316         trail = "|".join([("(1<<COND_H%s)" % f['name']) for f in hints])
317         if trail:
318             line += "|" + trail
319         if line.startswith("|"):
320             line = line[1:]
321         if not line:
322             line = "0"
323         cnd_str += "    " + line + ",\t// " + name + "\n"
324     return cnd_str
325
326 if __name__ == "__main__":
327     with open(yaml_name, "r") as f:
328         db = yaml.load(f)
329
330     c = c_template.format(
331         h_name,
332         get_arbitrary_messages(db["arbitrary_messages"]),
333         get_class_messages(db["classes"]),
334         get_turn_thresholds(db["turn_thresholds"]),
335         get_locations(db["locations"]),
336         get_object_descriptions(db["object_descriptions"]),
337         get_obituaries(db["obituaries"]),
338         get_hints(db["hints"], db["arbitrary_messages"]),
339         get_condbits(db["locations"]),
340     )
341
342     h = h_template.format(
343         len(db["locations"]),
344         len(db["object_descriptions"]),
345         len(db["hints"]),
346         len(db["classes"]),
347         len(db["obituaries"]),
348         len(db["turn_thresholds"]),
349         get_refs(db["arbitrary_messages"]),
350         get_refs(db["locations"]),
351         get_refs(db["object_descriptions"]),
352         statedefines,
353     )
354
355     with open(h_name, "w") as hf:
356         hf.write(h)
357
358     with open(c_name, "w") as cf:
359         cf.write(c)
360
361 # end