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