84bc880ba23608c3660d0aec63ecf1a487e0f161
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / effects / effectinfo.qc
1 #include "effectinfo.qh"
2 #define EFFECTINFO_PARSER(on, MY) \
3     on(type,                                        MY(type) \
4     ,{ demand(n == 1 && "type");                    MY(type) = strzone(argv(1)); \
5     }, sprintf(" %s",                               (MY(type)) \
6     )) \
7     on(airfriction,                                 MY(airfriction) \
8     ,{ demand(n == 1 && "airfriction");             MY(airfriction) = stof(argv(1)); \
9     }, sprintf(" %s",                               ftos(MY(airfriction)) \
10     )) \
11     on(alpha,                                       MY(alpha_min) ||                    MY(alpha_max) ||                MY(alpha_fade) \
12     ,{ demand(n == 3 && "alpha");                   MY(alpha_min) = stof(argv(1));      MY(alpha_max) = stof(argv(2));  MY(alpha_fade) = stof(argv(3)); \
13     }, sprintf(" %s %s %s",                         ftos(MY(alpha_min)),                ftos(MY(alpha_max)),            ftos(MY(alpha_fade)) \
14     )) \
15     on(blend,                                       MY(blend) \
16     ,{ demand(n == 1 && "blend");                   MY(blend) = strzone(argv(1)); \
17     }, sprintf(" %s",                               (MY(blend)) \
18     )) \
19     on(bounce,                                      MY(bounce) \
20     ,{ demand(n == 1 && "bounce");                  MY(bounce) = stof(argv(1)); \
21     }, sprintf(" %s",                               ftos(MY(bounce)) \
22     )) \
23     on(color,                                       MY(color_min) ||                    MY(color_max) \
24     ,{ demand(n == 2 && "color");                   MY(color_min) = strzone(argv(1));   MY(color_max) = strzone(argv(2)); \
25     }, sprintf(" %s %s",                            (MY(color_min)),                    (MY(color_max)) \
26     )) \
27     on(countabsolute,                               MY(countabsolute) \
28     ,{ demand(n == 1 && "countabsolute");           MY(countabsolute) = stof(argv(1)); \
29     }, sprintf(" %s",                               ftos(MY(countabsolute)) \
30     )) \
31     on(count,                                       MY(count) \
32     ,{ demand(n == 1 && "count");                   MY(count) = stof(argv(1)); \
33     }, sprintf(" %s",                               ftos(MY(count)) \
34     )) \
35     on(gravity,                                     MY(gravity) \
36     ,{ demand(n == 1 && "gravity");                 MY(gravity) = stof(argv(1)); \
37     }, sprintf(" %s",                               ftos(MY(gravity)) \
38     )) \
39     on(lightcolor,                                  MY(lightcolor) \
40     ,{ demand(n == 3 && "lightcolor");              MY(lightcolor) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
41     }, sprintf(" %v",                               (MY(lightcolor)) \
42     )) \
43     on(lightradiusfade,                             MY(lightradiusfade) \
44     ,{ demand(n == 1 && "lightradiusfade");         MY(lightradiusfade) = stof(argv(1)); \
45     }, sprintf(" %s",                               ftos(MY(lightradiusfade)) \
46     )) \
47     on(lightradius,                                 MY(lightradius) \
48     ,{ demand(n == 1 && "lightradius");             MY(lightradius) = stof(argv(1)); \
49     }, sprintf(" %s",                               ftos(MY(lightradius)) \
50     )) \
51     on(lighttime,                                   MY(lighttime) \
52     ,{ demand(n == 1 && "lighttime");               MY(lighttime) = stof(argv(1)); \
53     }, sprintf(" %s",                               ftos(MY(lighttime)) \
54     )) \
55     on(liquidfriction,                              MY(liquidfriction) \
56     ,{ demand(n == 1 && "liquidfriction");          MY(liquidfriction) = stof(argv(1)); \
57     }, sprintf(" %s",                               ftos(MY(liquidfriction)) \
58     )) \
59     on(notunderwater,                               MY(notunderwater) \
60     ,{ demand(n == 0 && "notunderwater");           MY(notunderwater) = true; \
61     }, "" \
62     ) \
63     on(orientation,                                 MY(orientation) \
64     ,{ demand(n == 1 && "orientation");             MY(orientation) = strzone(argv(1)); \
65     }, sprintf(" %s",                               (MY(orientation)) \
66     )) \
67     on(originjitter,                                MY(originjitter) \
68     ,{ demand(n == 3 && "originjitter");            MY(originjitter) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
69     }, sprintf(" %v",                               (MY(originjitter)) \
70     )) \
71     on(originoffset,                                MY(originoffset) \
72     ,{ demand(n == 3 && "originoffset");            MY(originoffset) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
73     }, sprintf(" %v",                               (MY(originoffset)) \
74     )) \
75     on(relativeoriginoffset,                        MY(relativeoriginoffset) \
76     ,{ demand(n == 3 && "relativeoriginoffset");    MY(relativeoriginoffset) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
77     }, sprintf(" %v",                               (MY(relativeoriginoffset)) \
78     )) \
79     on(relativevelocityoffset,                      MY(relativevelocityoffset) \
80     ,{ demand(n == 3 && "relativevelocityoffset");  MY(relativevelocityoffset) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
81     }, sprintf(" %v",                               (MY(relativevelocityoffset)) \
82     )) \
83     on(rotate,                                      MY(startangle_min) ||               MY(startangle_max) ||               MY(spin_min) ||                 MY(spin_max) \
84     ,{ demand(n == 4 && "rotate");                  MY(startangle_min) = stof(argv(1)); MY(startangle_max) = stof(argv(2)); MY(spin_min) = stof(argv(3));   MY(spin_max) = stof(argv(4)); \
85     }, sprintf(" %s %s %s %s",                      ftos(MY(startangle_min)),           ftos(MY(startangle_max)),           ftos(MY(spin_min)),             ftos(MY(spin_max)) \
86     )) \
87     on(sizeincrease,                                MY(sizeincrease) \
88     ,{ demand(n == 1 && "sizeincrease");            MY(sizeincrease) = stof(argv(1)); \
89     }, sprintf(" %s",                               ftos(MY(sizeincrease)) \
90     )) \
91     on(size,                                        MY(size_min) ||                     MY(size_max) \
92     ,{ demand(n == 2 && "size");                    MY(size_min) = stof(argv(1));       MY(size_max) = stof(argv(2)); \
93     }, sprintf(" %s %s",                            ftos(MY(size_min)),                 ftos(MY(size_max)) \
94     )) \
95     on(staincolor,                                  MY(staincolor_min) ||                   MY(staincolor_max) \
96     ,{ demand(n == 2 && "staincolor");              MY(staincolor_min) = strzone(argv(1));  MY(staincolor_max) = strzone(argv(2)); \
97     }, sprintf(" %s %s",                            (MY(staincolor_min)),                   (MY(staincolor_max)) \
98     )) \
99     on(stainsize,                                   MY(stainsize_min) ||                MY(stainsize_max) \
100     ,{ demand(n == 2 && "stainsize");               MY(stainsize_min) = stof(argv(1));  MY(stainsize_max) = stof(argv(2)); \
101     }, sprintf(" %s %s",                            ftos(MY(stainsize_min)),            ftos(MY(stainsize_max)) \
102     )) \
103     on(staintex,                                    MY(staintex_min) ||                 MY(staintex_max) \
104     ,{ demand(n == 2 && "staintex");                MY(staintex_min) = stof(argv(1));   MY(staintex_max) = stof(argv(2)); \
105     }, sprintf(" %s %s",                            ftos(MY(staintex_min)),             ftos(MY(staintex_max)) \
106     )) \
107     on(stretchfactor,                               MY(stretchfactor) \
108     ,{ demand(n == 1 && "stretchfactor");           MY(stretchfactor) = stof(argv(1)); \
109     }, sprintf(" %s",                               ftos(MY(stretchfactor)) \
110     )) \
111     on(tex,                                         MY(tex_min) ||                      MY(tex_max) \
112     ,{ demand(n == 2 && "tex");                     MY(tex_min) = stof(argv(1));        MY(tex_max) = stof(argv(2)); \
113     }, sprintf(" %s %s",                            ftos(MY(tex_min)),                  ftos(MY(tex_max)) \
114     )) \
115     on(time,                                        MY(time_min) ||                     MY(time_max) \
116     ,{ demand(n == 2 && "time");                    MY(time_min) = stof(argv(1));       MY(time_max) = stof(argv(2)); \
117     }, sprintf(" %s %s",                            ftos(MY(time_min)),                 ftos(MY(time_max)) \
118     )) \
119     on(trailspacing,                                MY(trailspacing) \
120     ,{ demand(n == 1 && "trailspacing");            MY(trailspacing) = stof(argv(1)); \
121     }, sprintf(" %s",                               ftos(MY(trailspacing)) \
122     )) \
123     on(underwater,                                  MY(underwater) \
124     ,{ demand(n == 0 && "underwater");              MY(underwater) = true; \
125     }, "" \
126     ) \
127     on(velocityjitter,                              MY(velocityjitter) \
128     ,{ demand(n == 3 && "velocityjitter");          MY(velocityjitter) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
129     }, sprintf(" %v",                               (MY(velocityjitter)) \
130     )) \
131     on(velocitymultiplier,                          MY(velocitymultiplier) \
132     ,{ demand(n == 1 && "velocitymultiplier");      MY(velocitymultiplier) = stof(argv(1)); \
133     }, sprintf(" %s",                               ftos(MY(velocitymultiplier)) \
134     )) \
135     on(velocityoffset,                              MY(velocityoffset) \
136     ,{ demand(n == 3 && "velocityoffset");          MY(velocityoffset) = vec3(stof(argv(1)), stof(argv(2)), stof(argv(3))); \
137     }, sprintf(" %v",                               (MY(velocityoffset)) \
138     )) \
139     /**/
140
141 CLASS(EffectInfo, Object)
142     ATTRIB(EffectInfo, effectinfo_name, string);
143     CONSTRUCTOR(EffectInfo, string s) {
144         CONSTRUCT(EffectInfo);
145         this.effectinfo_name = s;
146     }
147
148     #define FIELDS(MY) \
149     MY(airfriction, float, 0) \
150     MY(alpha_min, float, 0) \
151     MY(alpha_max, float, 0) \
152     MY(alpha_fade, float, 0) \
153     MY(blend, string, string_null) \
154     MY(bounce, float, 0) \
155     MY(color_min, string, string_null) \
156     MY(color_max, string, string_null) \
157     MY(countabsolute, float, 0) \
158     MY(count, float, 0) \
159     MY(gravity, float, 0) \
160     MY(lightcolor, vector, '0 0 0') \
161     MY(lightradiusfade, float, 0) \
162     MY(lightradius, float, 0) \
163     MY(lighttime, float, 0) \
164     MY(liquidfriction, float, 0) \
165     MY(notunderwater, bool, false) \
166     MY(orientation, string, string_null) \
167     MY(originjitter, vector, '0 0 0') \
168     MY(originoffset, vector, '0 0 0') \
169     MY(relativeoriginoffset, vector, '0 0 0') \
170     MY(relativevelocityoffset, vector, '0 0 0') \
171     MY(startangle_min, float, 0) \
172     MY(startangle_max, float, 0) \
173     MY(spin_min, float, 0) \
174     MY(spin_max, float, 0) \
175     MY(sizeincrease, float, 0) \
176     MY(size_min, float, 0) \
177     MY(size_max, float, 0) \
178     MY(staincolor_min, string, string_null) \
179     MY(staincolor_max, string, string_null) \
180     MY(stainsize_min, float, 0) \
181     MY(stainsize_max, float, 0) \
182     MY(staintex_min, float, 0) \
183     MY(staintex_max, float, 0) \
184     MY(stretchfactor, float, 0) \
185     MY(tex_min, float, 0) \
186     MY(tex_max, float, 0) \
187     MY(time_min, float, 0) \
188     MY(time_max, float, 0) \
189     MY(trailspacing, float, 0) \
190     MY(type, string, string_null) \
191     MY(underwater, bool, false) \
192     MY(velocityjitter, vector, '0 0 0') \
193     MY(velocitymultiplier, float, 0) \
194     MY(velocityoffset, vector, '0 0 0') \
195     /**/
196
197     #define MY(f, type, val) ATTRIB(EffectInfo, effectinfo_##f, type, val);
198     FIELDS(MY)
199     #undef MY
200
201     METHOD(EffectInfo, describe, string(EffectInfo this))
202     {
203         TC(EffectInfo, this);
204         string s = sprintf("SUB(%s) {\n", this.effectinfo_name);
205         #define str_bool(it) (it ? "true" : "false")
206         #define str_float(it) ftos(it)
207         #define str_vector(it) vtos(it)
208         #define str_string(it) strcat("\"", it, "\"")
209         #define p(f, type, default) if (this.effectinfo_##f) { s = strcat(s, "\t", "MY("#f") = ", str_##type(this.effectinfo_##f), ";\n"); }
210         FIELDS(p)
211         #undef p
212         return strcat(s, "}\n");
213     }
214
215     METHOD(EffectInfo, dump, string(EffectInfo this))
216     {
217         TC(EffectInfo, this);
218         string s = sprintf("effect %s\n", this.effectinfo_name);
219         #define MY(f) this.effectinfo_##f
220         #define p(k, isset, parse, unparse) if (isset) { s = strcat(s, "\t", #k, unparse, "\n"); }
221         EFFECTINFO_PARSER(p, MY)
222         #undef p
223         #undef MY
224         return s;
225     }
226
227     #undef FIELDS
228 ENDCLASS(EffectInfo)
229
230 CLASS(EffectInfoGroup, Object)
231     ATTRIBARRAY(EffectInfoGroup, children, EffectInfo, 16);
232     ATTRIB(EffectInfoGroup, children_count, int, 0);
233 ENDCLASS(EffectInfoGroup)
234
235 void effectinfo_read()
236 {
237     int fh = fopen("effectinfo.txt", FILE_READ);
238     EffectInfo info = NULL;
239     for (string line; (line = fgets(fh)); ) {
240         int n = tokenize_console(line);
241         if (n == 0) continue;
242         n--;
243         string k = argv(0);
244         if (k == "effect") {
245             demand(n == 1);
246             info = NEW(EffectInfo, strzone(argv(1)));
247             continue;
248         }
249         demand(info != NULL);
250         switch (k) {
251             #define MY(f) info.effectinfo_##f
252             #define p(k, isset, parse, unparse) case #k: parse break;
253             EFFECTINFO_PARSER(p, MY)
254             #undef p
255             #undef MY
256             default:
257                 LOG_WARNF("Unknown property '%s'", k);
258                 break;
259         }
260     }
261     fclose(fh);
262 }
263
264 void effectinfo_dump(int fh, bool alsoprint)
265 {
266     #define WRITE(s) MACRO_BEGIN \
267         fputs(fh, s); \
268         if (alsoprint) LOG_INFO(s); \
269     MACRO_END
270     WRITE("// ********************************************** //\n");
271     WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
272     WRITE("// **                                          ** //\n");
273     WRITE("// **  This file is automatically generated by ** //\n");
274     WRITE("// **  code with the command 'dumpeffectinfo'. ** //\n");
275     WRITE("// **                                          ** //\n");
276     WRITE("// **  If you modify an effect, please         ** //\n");
277     WRITE("// **  regenerate this file with that command. ** //\n");
278     WRITE("// **                                          ** //\n");
279     WRITE("// ********************************************** //\n");
280     WRITE("\n");
281
282     for (EffectInfo it = NULL; (it = findfloat(it, instanceOfEffectInfo, true)); ) {
283         if (it.classname == "vtbl") continue;
284         string s = it.dump(it);
285         WRITE(s);
286     }
287     #undef WRITE
288 }
289
290 GENERIC_COMMAND(dumpeffectinfo, "Dump all effectinfo to effectinfo_dump.txt")
291 {
292     switch (request) {
293         case CMD_REQUEST_COMMAND: {
294             string filename = argv(1);
295                         bool alsoprint = false;
296             if (filename == "") {
297                 filename = "effectinfo_dump.txt";
298                 alsoprint = false;
299             } else if (filename == "-") {
300                 filename = "effectinfo_dump.txt";
301                 alsoprint = true;
302             }
303             int fh = fopen(filename, FILE_WRITE);
304             if (fh >= 0) {
305                 effectinfo_dump(fh, alsoprint);
306                 LOG_INFOF("Dumping effectinfo... File located at ^2data/data/%s^7.", filename);
307                                 LOG_INFOF("Reload with ^2cl_particles_reloadeffects data/%s^7.", filename);
308                 fclose(fh);
309             } else {
310                 LOG_WARNF("Could not open file '%s'!", filename);
311             }
312             return;
313         }
314         default:
315         case CMD_REQUEST_USAGE: {
316             LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " dumpeffectinfo [filename]");
317             LOG_INFO("  Where 'filename' is the file to write (default is effectinfo_dump.txt),");
318             LOG_INFO("  if supplied with '-' output to console as well as default,");
319             LOG_INFO("  if left blank, it will only write to default.");
320             return;
321         }
322     }
323 }
324
325
326 REGISTRY(EffectInfos, BITS(9))
327 #define EffectInfos_from(i) _EffectInfos_from(i, NULL)
328 REGISTER_REGISTRY(EffectInfos)
329 #define EFFECTINFO(name) \
330     ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { } \
331     REGISTER(EffectInfos, EFFECTINFO, name, m_id, NEW(EffectInfoGroup)) { \
332         effectinfo_##name(this, NULL); \
333     }
334
335 #define MY(f) this.effectinfo_##f
336 #define DEF(name) EFFECTINFO(name)
337 #define SUB(name) \
338     ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { parent = EFFECTINFO_##name; parent.children[parent.children_count++] = this = NEW(EffectInfo, #name); } \
339     ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this)
340 #include "effectinfo.inc"
341 #undef MY
342 #undef DEF
343 #undef SUB