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