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