naev 0.11.5
effect.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "naev.h"
13#include "effect.h"
14
15#include "array.h"
16#include "conf.h"
17#include "log.h"
18#include "nxml.h"
19#include "ndata.h"
20#include "gui.h"
21#include "nlua_pilot.h"
22
23static EffectData *effect_list = NULL; /* List of all available effects. */
24
28static int effect_cmp( const void *p1, const void *p2 )
29{
30 const EffectData *e1 = p1;
31 const EffectData *e2 = p2;
32 return strcmp( e1->name, e2->name );
33}
34
38static int effect_cmpTimer( const void *p1, const void *p2 )
39{
40 const Effect *e1 = p1;
41 const Effect *e2 = p2;
42 if (e1->data->priority < e2->data->priority)
43 return -1;
44 else if (e1->data->priority > e2->data->priority)
45 return +1;
46 return e1->timer - e2->timer;
47}
48
52static int effect_parse( EffectData *efx, const char *file )
53{
54 xmlNodePtr node, parent;
55 xmlDocPtr doc = xml_parsePhysFS( file );
56 if (doc == NULL)
57 return -1;
58
59 parent = doc->xmlChildrenNode; /* first system node */
60 if (parent == NULL) {
61 ERR( _("Malformed '%s' file: does not contain elements"), file );
62 return -1;
63 }
64
65 /* Clear data. */
66 memset( efx, 0, sizeof(EffectData) );
67 efx->duration = -1.;
68 efx->priority = 5;
69 efx->lua_env = LUA_NOREF;
70 efx->lua_add = LUA_NOREF;
71 efx->lua_extend= LUA_NOREF;
72 efx->lua_remove= LUA_NOREF;
73
74 xmlr_attr_strd(parent,"name",efx->name);
75 if (efx->name == NULL)
76 WARN(_("Effect '%s' has invalid or no name"), file);
77
78 node = parent->xmlChildrenNode;
79 do { /* load all the data */
80 /* Only handle nodes. */
81 xml_onlyNodes(node);
82
83 xmlr_strd(node, "description", efx->desc);
84 xmlr_strd(node, "overwrite", efx->overwrite);
85 xmlr_int(node, "priority", efx->priority);
86 xmlr_float(node, "duration", efx->duration);
87 if (xml_isNode(node,"buff")) {
88 efx->flags |= EFFECT_BUFF;
89 continue;
90 }
91 if (xml_isNode(node,"debuff")) {
92 efx->flags |= EFFECT_DEBUFF;
93 continue;
94 }
95 if (xml_isNode(node,"icon")) {
96 efx->icon = xml_parseTexture( node, "%s", 1, 1, OPENGL_TEX_MIPMAPS );
97 continue;
98 }
99 if (xml_isNode(node,"shader")) {
100 char *vertex;
101 xmlr_attr_strd(node,"vertex",vertex);
102 if (vertex == NULL)
103 vertex = strdup("effect.vert");
104 efx->program = gl_program_vert_frag( vertex, xml_get(node), NULL );
105 free( vertex );
106 efx->vertex = glGetAttribLocation( efx->program, "vertex" );
107 efx->projection= glGetUniformLocation( efx->program, "projection" );
108 efx->tex_mat = glGetUniformLocation( efx->program, "tex_mat" );
109 efx->dimensions= glGetUniformLocation( efx->program, "dimensions" );
110 efx->u_r = glGetUniformLocation( efx->program, "u_r" );
111 efx->u_tex = glGetUniformLocation( efx->program, "u_tex" );
112 efx->u_timer = glGetUniformLocation( efx->program, "u_timer" );
113 efx->u_elapsed = glGetUniformLocation( efx->program, "u_elapsed" );
114 efx->u_dir = glGetUniformLocation( efx->program, "u_dir" );
115 continue;
116 }
117 if (xml_isNode(node,"lua")) {
118 nlua_env env;
119 size_t sz;
120 const char *filename = xml_get(node);
121 char *dat = ndata_read( filename, &sz );
122 if (dat==NULL) {
123 WARN(_("Effect '%s' failed to read Lua '%s'!"), efx->name, filename );
124 continue;
125 }
126
127 env = nlua_newEnv();
128 nlua_loadStandard( env );
129 if (nlua_dobufenv( env, dat, sz, filename ) != 0) {
130 WARN(_("Effect '%s' Lua error:\n%s"), efx->name, lua_tostring(naevL,-1));
131 lua_pop(naevL,1);
132 nlua_freeEnv( env );
133 free( dat );
134 continue;
135 }
136 efx->lua_env = env;
137 efx->lua_add = nlua_refenvtype( env, "add", LUA_TFUNCTION );
138 efx->lua_extend= nlua_refenvtype( env, "extend",LUA_TFUNCTION );
139 efx->lua_remove= nlua_refenvtype( env, "remove",LUA_TFUNCTION );
140 free( dat );
141 continue;
142 }
143
144 if (xml_isNode(node,"stats")) {
145 xmlNodePtr cur = node->children;
146 do {
147 ShipStatList *ll;
148 xml_onlyNodes(cur);
149 /* Stats. */
150 ll = ss_listFromXML( cur );
151 if (ll != NULL) {
152 ll->next = efx->stats;
153 efx->stats = ll;
154 continue;
155 }
156 WARN(_("Effect '%s' has unknown node '%s'"), efx->name, cur->name);
157 } while (xml_nextNode(cur));
158 continue;
159 }
160 WARN(_("Effect '%s' has unknown node '%s'"),efx->name, node->name);
161 } while (xml_nextNode(node));
162
163#define MELEMENT(o,s) \
164if (o) WARN( _("Effect '%s' missing/invalid '%s' element"), efx->name, s)
165 MELEMENT(efx->name==NULL,"name");
166 MELEMENT(efx->desc==NULL,"description");
167 MELEMENT(efx->duration<0.,"duration");
168 MELEMENT(efx->icon==NULL,"icon");
169#undef MELEMENT
170
171 xmlFreeDoc(doc);
172
173 return 0;
174}
175
181int effect_load (void)
182{
183 int ne;
184#if DEBUGGING
185 Uint32 time = SDL_GetTicks();
186#endif /* DEBUGGING */
187 char **effect_files = ndata_listRecursive( EFFECT_DATA_PATH );
188 effect_list = array_create( EffectData );
189
190 for (int i=0; i<array_size(effect_files); i++) {
191 if (ndata_matchExt( effect_files[i], "xml" )) {
192 EffectData ed;
193 int ret = effect_parse( &ed, effect_files[i] );
194 if (ret == 0)
195 array_push_back( &effect_list, ed );
196 }
197 free( effect_files[i] );
198 }
199 array_free( effect_files );
200
201 ne = array_size(effect_list);
202 qsort( effect_list, ne, sizeof(EffectData), effect_cmp );
203
204#if DEBUGGING
205 /* Check to see if there are name collisions. */
206 for (int i=1; i<array_size(effect_list); i++)
207 if (strcmp( effect_list[i-1].name, effect_list[i].name )==0)
208 WARN(_("Duplicated effect name '%s' detected!"), effect_list[i].name);
209
210 if (conf.devmode) {
211 time = SDL_GetTicks() - time;
212 DEBUG( n_( "Loaded %d Effect in %.3f s", "Loaded %d Effects in %.3f s", ne ), ne, time/1000. );
213 }
214 else
215 DEBUG( n_( "Loaded %d Effect", "Loaded %d Effects", ne ), ne );
216#endif /* DEBUGGING */
217
218 return 0;
219}
220
224void effect_exit (void)
225{
226 for (int i=0; i<array_size(effect_list); i++) {
227 EffectData *e = &effect_list[i];
228 nlua_freeEnv( e->lua_env );
229 free( e->name );
230 free( e->desc );
231 free( e->overwrite );
232 gl_freeTexture( e->icon );
233 ss_free( e->stats );
234 }
235 array_free( effect_list );
236}
237
244const EffectData *effect_get( const char *name )
245{
246 const EffectData k = { .name = (char*)name };
247 EffectData *e = bsearch( &k, effect_list, array_size(effect_list), sizeof(EffectData), effect_cmp );
248 if (e==NULL)
249 WARN(_("Trying to get unknown effect data '%s'!"), name);
250 return e;
251}
252
260int effect_update( Effect **efxlist, double dt )
261{
262 int n = 0;
263 for (int i=array_size(*efxlist)-1; i>=0; i--) {
264 Effect *e = &(*efxlist)[i];
265 e->timer -= dt;
266 e->elapsed += dt;
267 if (e->timer > 0.)
268 continue;
269
270 /* Run Lua if necessary. */
271 if (e->data->lua_remove != LUA_NOREF) {
272 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
273 lua_pushpilot(naevL, e->parent);
274 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
275 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
276 lua_pop(naevL,1);
277 }
278 }
279
280 /* Get rid of it. */
281 array_erase( efxlist, e, e+1 );
282 n++;
283 }
284 if (n>0)
285 gui_updateEffects();
286 return n;
287}
288
299int effect_add( Effect **efxlist, const EffectData *efx, double duration, double strength, unsigned int parent )
300{
301 Effect *e = NULL;
302 int overwrite = 0;
303 duration = (duration > 0.) ? duration : efx->duration;
304
305 if (*efxlist == NULL)
306 *efxlist = array_create( Effect );
307
308 /* See if we should overwrite. */
309 if (efx->overwrite != NULL) {
310 for (int i=0; i<array_size(*efxlist); i++) {
311 Effect *el = &(*efxlist)[i];
312 if ((el->data->overwrite!=NULL) &&
313 (efx->priority <= el->data->priority) &&
314 (strcmp(efx->overwrite, el->data->overwrite)==0)) {
315 e = el;
316 if (e->data == efx) {
317 /* Case the effect is weaker when both are the same, we just ignore. */
318 if (el->strength > strength)
319 return 0;
320 /* Case the base effect has a longer timer with same strength we ignore. */
321 if ((fabs(el->strength-strength)<1e-5) && (el->timer > duration))
322 return 0;
323 /* Procede to overwrite. */
324 overwrite = 1;
325 }
326 else {
327 /* Here we remove the effect and replace it as they overlap while not being exactly the same.
328 * Can't do a strength check because they may not be comparable. */
329 if (e->data->lua_remove != LUA_NOREF) {
330 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
331 lua_pushpilot(naevL, e->parent);
332 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
333 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
334 lua_pop(naevL,1);
335 }
336 }
337 }
338 break;
339 }
340 }
341 }
342
343 /* Add new effect if necessary. */
344 if (e==NULL) {
345 e = &array_grow( efxlist );
346 e->elapsed = 0.; /* Don't 0. when overwriting. */
347 e->r = RNGF();
348 }
349 e->data = efx;
350 e->duration = duration;
351 e->timer = e->duration;
352 e->strength = strength;
353 e->parent = parent;
354
355 /* Run Lua if necessary. */
356 if (overwrite) {
357 if (efx->lua_extend != LUA_NOREF) {
358 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_extend); /* f */
359 lua_pushpilot(naevL, e->parent);
360 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
361 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "extend", lua_tostring(naevL,-1));
362 lua_pop(naevL,1);
363 }
364 }
365 }
366 else {
367 if (efx->lua_add != LUA_NOREF) {
368 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_add); /* f */
369 lua_pushpilot(naevL, e->parent);
370 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
371 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "add", lua_tostring(naevL,-1));
372 lua_pop(naevL,1);
373 }
374 }
375 }
376
377 /* Sort and update. */
378 qsort( *efxlist, array_size(*efxlist), sizeof(Effect), effect_cmpTimer );
379 gui_updateEffects();
380 return 0;
381}
382
390int effect_rm( Effect **efxlist, int idx )
391{
392 const Effect *e;
393
394 if ((idx < 0) || (idx > array_size(efxlist))) {
395 WARN(_("Trying to remove invalid effect ID!"));
396 return -1;
397 }
398 e = &(*efxlist)[idx];
399
400 /* Run Lua if necessary. */
401 if (e->data->lua_remove != LUA_NOREF) {
402 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
403 lua_pushpilot(naevL, e->parent);
404 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
405 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
406 lua_pop(naevL,1);
407 }
408 }
409 array_erase( efxlist, &e[0], &e[1] );
410 return 1;
411}
412
421int effect_rmType( Effect **efxlist, const EffectData *efx, int all )
422{
423 int ret = 0;
424 for (int i=array_size(*efxlist)-1; i>=0; i++) {
425 const Effect *e = &(*efxlist)[i];
426 if (e->data != efx)
427 continue;
428 /* Run Lua if necessary. */
429 if (e->data->lua_remove != LUA_NOREF) {
430 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
431 lua_pushpilot(naevL, e->parent);
432 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
433 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
434 lua_pop(naevL,1);
435 }
436 }
437 array_erase( efxlist, &e[0], &e[1] );
438 if (!all)
439 return 1;
440 ret++;
441 }
442 return ret;
443}
444
453void effect_clearSpecific( Effect **efxlist, int debuffs, int buffs, int others )
454{
455 for (int i=array_size(*efxlist)-1; i>=0; i++) {
456 const Effect *e = &(*efxlist)[i];
457
458 /* See if should be eliminated. */
459 if (e->data->flags & EFFECT_BUFF) {
460 if (!buffs)
461 continue;
462 }
463 else if (e->data->flags & EFFECT_DEBUFF) {
464 if (!debuffs)
465 continue;
466 }
467 else {
468 if (!others)
469 continue;
470 }
471
472 /* Run Lua if necessary. */
473 if (e->data->lua_remove != LUA_NOREF) {
474 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
475 lua_pushpilot(naevL, e->parent);
476 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
477 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
478 lua_pop(naevL,1);
479 }
480 }
481
482 /* Clear effect. */
483 array_erase( efxlist, &e[0], &e[1] );
484 }
485}
486
492void effect_clear( Effect **efxlist )
493{
494 for (int i=0; i<array_size(*efxlist); i++) {
495 const Effect *e = &(*efxlist)[i];
496 /* Run Lua if necessary. */
497 if (e->data->lua_remove != LUA_NOREF) {
498 lua_rawgeti(naevL, LUA_REGISTRYINDEX, e->data->lua_remove); /* f */
499 lua_pushpilot(naevL, e->parent);
500 if (nlua_pcall( e->data->lua_env, 1, 0 )) {
501 WARN(_("Effect '%s' failed to run '%s':\n%s"), e->data->name, "remove", lua_tostring(naevL,-1));
502 lua_pop(naevL,1);
503 }
504 }
505 }
506 array_erase( efxlist, array_begin(*efxlist), array_end(*efxlist) );
507}
508
515void effect_compute( ShipStats *s, const Effect *efxlist )
516{
517 for (int i=0; i<array_size(efxlist); i++) {
518 const Effect *e = &efxlist[i];
520 }
521}
522
528void effect_cleanup( Effect *efxlist )
529{
530 array_free( efxlist );
531}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:202
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:119
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:194
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int effect_rmType(Effect **efxlist, const EffectData *efx, int all)
Removes an effect type from an effect list.
Definition effect.c:421
int effect_rm(Effect **efxlist, int idx)
Removes an effect from an effect list by index.
Definition effect.c:390
static int effect_cmp(const void *p1, const void *p2)
Compares effects based on name.
Definition effect.c:28
int effect_add(Effect **efxlist, const EffectData *efx, double duration, double strength, unsigned int parent)
Adds an effect to an effect list.
Definition effect.c:299
static int effect_parse(EffectData *efx, const char *file)
Parsess an effect.
Definition effect.c:52
void effect_clearSpecific(Effect **efxlist, int debuffs, int buffs, int others)
Clears specific types of effects.
Definition effect.c:453
const EffectData * effect_get(const char *name)
Gets an effect by name.
Definition effect.c:244
void effect_cleanup(Effect *efxlist)
Cleans up an effect list freeing it.
Definition effect.c:528
void effect_clear(Effect **efxlist)
Clears an effect list, removing all active effects.
Definition effect.c:492
static int effect_cmpTimer(const void *p1, const void *p2)
Compares effects based on priority, then timer.
Definition effect.c:38
void effect_compute(ShipStats *s, const Effect *efxlist)
Updates shipstats from effect list.
Definition effect.c:515
int effect_load(void)
Loads all the effects.
Definition effect.c:181
void effect_exit(void)
Gets rid of all the effects.
Definition effect.c:224
int effect_update(Effect **efxlist, double dt)
Updates an effect list.
Definition effect.c:260
Header file with generic functions and naev-specifics.
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:154
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:365
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:231
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:798
int nlua_refenvtype(nlua_env env, const char *name, int type)
Gets the reference of a global in a lua environment if it matches a type.
Definition nlua.c:920
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:563
glTexture * xml_parseTexture(xmlNodePtr node, const char *path, int defsx, int defsy, const unsigned int flags)
Parses a texture handling the sx and sy elements.
Definition nxml.c:29
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:75
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:862
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
Definition shipstats.c:887
int ss_statsMergeFromListScale(ShipStats *stats, const ShipStatList *list, double scale)
Updates a stat structure from a stat list.
Definition shipstats.c:645
ShipStatList * ss_listFromXML(xmlNodePtr node)
Creates a shipstat list element from an xml node.
Definition shipstats.c:292
Pilot ship effect data.
Definition effect.h:16
int lua_extend
Definition effect.h:39
nlua_env lua_env
Definition effect.h:37
char * desc
Definition effect.h:18
int lua_remove
Definition effect.h:40
int priority
Definition effect.h:20
int lua_add
Definition effect.h:38
double duration
Definition effect.h:21
ShipStatList * stats
Definition effect.h:23
char * name
Definition effect.h:17
glTexture * icon
Definition effect.h:25
unsigned int flags
Definition effect.h:22
char * overwrite
Definition effect.h:19
Pilot ship effect.
Definition effect.h:46
double r
Definition effect.h:52
double strength
Definition effect.h:51
const EffectData * data
Definition effect.h:47
double elapsed
Definition effect.h:53
unsigned int parent
Definition effect.h:48
double timer
Definition effect.h:49
double duration
Definition effect.h:50
int devmode
Definition conf.h:157
Represents relative ship statistics as a linked list.
Definition shipstats.h:167
struct ShipStatList_ * next
Definition shipstats.h:168
Represents ship statistics, properties ship can use.
Definition shipstats.h:198