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