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