naev 0.11.5
asteroid.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "physfs.h"
11
12#include "naev.h"
15#include "asteroid.h"
16
17#include "conf.h"
18#include "array.h"
19#include "camera.h"
20#include "gatherable.h"
21#include "space.h"
22#include "opengl.h"
23#include "toolkit.h"
24#include "ndata.h"
25#include "player.h"
26#include "nlua_asteroid.h"
27
31typedef struct Debris_ {
32 const glTexture *gfx;
35 double ang;
36 double height;
37 double alpha;
38} Debris;
39
40const double DEBRIS_BUFFER = 1000.;
42static const double SCAN_FADE = 10.;
44static Debris *debris_stack = NULL;
45static glTexture **debris_gfx = NULL;
47/*
48 * Useful data for asteroids.
49 */
52static glTexture **asteroid_gfx = NULL;
53static int asteroid_creating = 0;
54
55/* Prototypes. */
56static int asttype_cmp( const void *p1, const void *p2 );
57static int asttype_parse( AsteroidType *at, const char *file );
58static int asteroid_loadPLG( AsteroidType *temp, const char *buf );
59static int astgroup_cmp( const void *p1, const void *p2 );
60static int astgroup_parse( AsteroidTypeGroup *ag, const char *file );
61static int asttype_load (void);
62
63static void asteroid_renderSingle( const Asteroid *a );
64static void debris_renderSingle( const Debris *d, double cx, double cy );
65static void debris_init( Debris *deb );
66static int asteroid_init( Asteroid *ast, const AsteroidAnchor *field );
67
73void asteroids_update( double dt )
74{
75 /* Asteroids/Debris update */
76 for (int i=0; i<array_size(cur_system->asteroids); i++) {
77 int has_exclusion = 0;
79
80 for (int k=0; k<array_size(cur_system->astexclude); k++) {
81 AsteroidExclusion *exc = &cur_system->astexclude[k];
82 if (vec2_dist2( &ast->pos, &exc->pos ) < pow2(ast->radius+exc->radius)) {
83 exc->affects = 1;
84 has_exclusion = 1;
85 }
86 else
87 exc->affects = 0;
88 }
89
90 qt_clear( &ast->qt );
91 for (int j=0; j<ast->nb; j++) {
92 double offx, offy, d;
93 Asteroid *a = &ast->asteroids[j];
94 int setvel = 0;
95
96 /* Skip inexistent asteroids. */
97 if (a->state == ASTEROID_XX) {
98 a->timer -= dt;
99 if (a->timer < 0.) {
100 a->state = ASTEROID_XX_TO_BG;
101 a->timer_max = a->timer = 1. + 3.*RNGF();
102 }
103 continue;
104 }
105
106 /* Push back towards center. */
107 offx = ast->pos.x - a->sol.pos.x;
108 offy = ast->pos.y - a->sol.pos.y;
109 d = pow2(offx)+pow2(offy);
110 if (d >= pow2(ast->radius)) {
111 d = sqrt(d);
112 a->sol.vel.x += ast->accel * dt * offx / d;
113 a->sol.vel.y += ast->accel * dt * offy / d;
114 setvel = 1;
115 }
116 else if (has_exclusion) {
117 /* Push away from exclusion areas. */
118 for (int k=0; k<array_size(cur_system->astexclude); k++) {
119 AsteroidExclusion *exc = &cur_system->astexclude[k];
120 double ex, ey, ed;
121
122 /* Ignore exclusion zones that shouldn't affect. */
123 if (!exc->affects)
124 continue;
125
126 ex = a->sol.pos.x - exc->pos.x;
127 ey = a->sol.pos.y - exc->pos.y;
128 ed = pow2(ex) + pow2(ey);
129 if (ed <= pow2(exc->radius)) {
130 ed = sqrt(ed);
131 a->sol.vel.x += ast->accel * dt * ex / ed;
132 a->sol.vel.y += ast->accel * dt * ey / ed;
133 setvel = 1;
134 }
135 }
136 }
137
138 if (setvel) {
139 /* Enforce max speed. */
140 d = MOD(a->sol.vel.x, a->sol.vel.y);
141 if (d > ast->maxspeed) {
142 a->sol.vel.x *= ast->maxspeed / d;
143 a->sol.vel.y *= ast->maxspeed / d;
144 }
145 }
146
147 /* Update position. */
148 /* TODO use physics.c */
149 a->sol.pre = a->sol.pos;
150 a->sol.pos.x += a->sol.vel.x * dt;
151 a->sol.pos.y += a->sol.vel.y * dt;
152
153 /* Update angle. */
154 a->ang += a->spin * dt;
155
156 /* igure out state change if applicable. */
157 a->timer -= dt;
158 if (a->timer < 0.) {
159 switch (a->state) {
160 /* Transition states. */
161 case ASTEROID_FG:
162 pilot_untargetAsteroid( a->parent, a->id );
163 FALLTHROUGH;
164 case ASTEROID_XB:
165 case ASTEROID_BX:
166 case ASTEROID_XX_TO_BG:
167 a->timer_max = a->timer = 1. + 3.*RNGF();
168 break;
169
170 /* Longer states. */
171 case ASTEROID_FG_TO_BG:
172 a->timer_max = a->timer = 10. + 20.*RNGF();
173 break;
174 case ASTEROID_BG_TO_FG:
175 a->timer_max = a->timer = 90. + 30.*RNGF();
176 break;
177
178 /* Special case needs to respawn. */
179 case ASTEROID_BG_TO_XX:
180 asteroid_init( a, ast );
181 a->timer_max = a->timer = 10. + 20.*RNGF();
182 break;
183
184 case ASTEROID_XX:
185 /* Do nothing. */
186 break;
187 }
188 /* States should be in proper order. */
189 a->state = (a->state+1) % ASTEROID_STATE_MAX;
190 }
191
192 /* Update scanned state if necessary. */
193 if (a->scanned) {
194 if (a->state == ASTEROID_FG)
195 a->scan_alpha = MIN( a->scan_alpha+SCAN_FADE*dt, 1.);
196 else
197 a->scan_alpha = MAX( a->scan_alpha-SCAN_FADE*dt, 0.);
198 }
199
200 /* Add to quadtree if in foreground. */
201 if (a->state == ASTEROID_FG) {
202 int x, y, w2, h2, px, py;
203 x = round(a->sol.pos.x);
204 y = round(a->sol.pos.y);
205 px = round(a->sol.pre.x);
206 py = round(a->sol.pre.y);
207 w2 = ceil(a->gfx->sw*0.5);
208 h2 = ceil(a->gfx->sh*0.5);
209 qt_insert( &ast->qt, j, MIN(x,px)-w2, MIN(y,py)-h2, MAX(x,px)+w2, MAX(y,py)+h2 );
210 }
211 }
212 }
213
214 /* Only have to update stuff if not simulating. */
215 if (!space_isSimulation()) {
216 double dx, dy;
217 cam_getDPos( &dx, &dy );
218
219 for (int j=0; j<array_size(debris_stack); j++) {
220 Debris *d = &debris_stack[j];
221 int infield;
222 vec2 v;
223
224 d->pos.x += d->vel.x * dt - dx;
225 d->pos.y += d->vel.y * dt - dy;
226
227 /* Check boundaries */
228 if (d->pos.x > SCREEN_W + DEBRIS_BUFFER)
229 d->pos.x -= SCREEN_W + 2.*DEBRIS_BUFFER;
230 else if (d->pos.y > SCREEN_H + DEBRIS_BUFFER)
231 d->pos.y -= SCREEN_H + 2.*DEBRIS_BUFFER;
232 else if (d->pos.x < -DEBRIS_BUFFER)
233 d->pos.x += SCREEN_W + 2.*DEBRIS_BUFFER;
234 else if (d->pos.y < -DEBRIS_BUFFER)
235 d->pos.y += SCREEN_H + 2.*DEBRIS_BUFFER;
236
237 /* Set alpha based on position. */
238 /* TODO there seems to be some offset mistake or something going on
239 * here, not too big of an issue though. */
240 gl_screenToGameCoords( &v.x, &v.y, d->pos.x, d->pos.y );
241 infield = asteroids_inField( &v );
242 if (infield>=0)
243 d->alpha = MIN( 1.0, d->alpha + 0.5 * dt );
244 else
245 d->alpha = MAX( 0.0, d->alpha - 0.5 * dt );
246 }
247 }
248}
249
253void asteroids_init (void)
254{
255 double density_max = 0.;
256 int ndebris;
257 asteroid_creating = 1;
258
259 if (debris_gfx==NULL)
262
263 /* Set up asteroids. */
264 for (int i=0; i<array_size(cur_system->asteroids); i++) {
266 int qx, qy, qr;
267 ast->id = i;
268
269 /* Add graphics to debris. */
270 for (int j=0; j<array_size(ast->groups); j++) {
271 AsteroidTypeGroup *ag = ast->groups[j];
272 for (int k=0; k<array_size(ag->types); k++) {
273 AsteroidType *at = ag->types[k];
274 for (int x=0; x<array_size(at->gfxs); x++)
276 }
277 }
278
279 /* Build quadtree. */
280 if (ast->qt_init)
281 qt_destroy( &ast->qt );
282 qx = round(ast->pos.x);
283 qy = round(ast->pos.y);
284 qr = ceil(ast->radius);
285 qt_create( &ast->qt, qx-qr, qy-qr, qx+qr, qy+qr, 2, 5 );
286 ast->qt_init = 1;
287
288 /* Add the asteroids to the anchor */
289 ast->asteroids = realloc( ast->asteroids, (ast->nb) * sizeof(Asteroid) );
290 for (int j=0; j<ast->nb; j++) {
291 double r = RNGF();
292 Asteroid *a = &ast->asteroids[j];
293 a->id = j;
294 if (asteroid_init(a, ast))
295 continue;
296 if (r > 0.6)
297 a->state = ASTEROID_FG;
298 else if (r > 0.8)
299 a->state = ASTEROID_XB;
300 else if (r > 0.9)
301 a->state = ASTEROID_BX;
302 else
303 a->state = ASTEROID_XX;
304 a->timer = a->timer_max = 30.*RNGF();
305 a->ang = RNGF() * M_PI * 2.;
306 }
307
308 density_max = MAX( density_max, ast->density );
309 }
310
311 /* Add the debris to the anchor */
312 if (debris_stack == NULL)
314
315 /* We compute a fixed amount and scale depending on how big the screen is
316 * compared the reference (minimum resolution). */
317 ndebris = density_max * 100. * (SCREEN_W+2.*DEBRIS_BUFFER * SCREEN_H+2.*DEBRIS_BUFFER) / (RESOLUTION_W_MIN * RESOLUTION_H_MIN);
318 array_resize( &debris_stack, ndebris );
319
320 for (int j=0; j<array_size(debris_stack); j++)
322
323 asteroid_creating = 0;
324}
325
331static int asteroid_init( Asteroid *ast, const AsteroidAnchor *field )
332{
333 double mod, theta, wmax, r;
334 AsteroidType *at = NULL;
335 int outfield, id;
336 int attempts = 0;
337 vec2 pos, vel;
338
339 ast->parent = field->id;
340 ast->scanned = 0;
341
342 do {
343 double n, a;
344 n = sqrt(RNGF())*field->radius;
345 a = RNGF() * 2. * M_PI;
346 /* Try to keep density uniform using cartesian coordinates. */
347 vec2_cset( &pos, field->pos.x + n*cos(a), field->pos.y + n*sin(a) );
348
349 /* Check if out of the field. */
350 outfield = (asteroids_inField(&pos) < 0);
351
352 /* If this is the first time and it's spawned outside the field,
353 * we get rid of it so that density remains roughly consistent. */
354 if (asteroid_creating && outfield) {
355 ast->state = ASTEROID_XX;
356 ast->timer_max = ast->timer = HUGE_VAL; /* Don't reappear. */
357 /* TODO probably do a more proper solution removing total number of asteroids. */
358 return -1;
359 }
360
361 } while (outfield && (attempts++ < 1000));
362
363 /* Randomly init the type of asteroid */
364 r = field->groupswtotal * RNGF();
365 wmax = 0.;
366 for (int i=0; i<array_size(field->groups); i++) {
367 wmax += field->groupsw[i];
368 if (r > wmax)
369 continue;
370 AsteroidTypeGroup *grp = field->groups[i];
371 double wi = 0.;
372 r = grp->wtotal * RNGF();
373 for (int j=0; j<array_size(grp->types); j++) {
374 wi += grp->weights[j];
375 if (r > wi)
376 continue;
377 at = grp->types[j];
378 break;
379 }
380 break;
381 }
382
383 /* Randomly init the gfx ID, and associated polygon */
384 id = RNG(0, array_size(at->gfxs)-1);
385 ast->gfx = at->gfxs[ id ];
386 ast->polygon = &at->polygon[ id ];
387
388 ast->type = at;
389 ast->armour = at->armour_min + RNGF() * (at->armour_max-at->armour_min);
390
391 /* And a random velocity/spin */
392 theta = RNGF()*2.*M_PI;
393 ast->spin = (1-2*RNGF())*field->maxspin;
394 mod = RNGF()*field->maxspeed;
395
396 /* Fade in stuff. */
397 ast->state = ASTEROID_XX;
398 ast->timer_max = ast->timer = -1.;
399 ast->ang = RNGF() * M_PI * 2.;
400
401 /* Set up the solid. */
402 //vec2_cset( &pos, x, y );
403 vec2_pset( &vel, mod, theta );
404 /* TODO set a proper mass. */
405 solid_init( &ast->sol, 1., theta, &pos, &vel, SOLID_UPDATE_EULER );
406
407 return 0;
408}
409
414static void debris_init( Debris *deb )
415{
416 double theta, mod;
417 /* Position */
418 deb->pos.x = -DEBRIS_BUFFER + RNGF()*(SCREEN_W + 2.*DEBRIS_BUFFER);
419 deb->pos.y = -DEBRIS_BUFFER + RNGF()*(SCREEN_H + 2.*DEBRIS_BUFFER);
420
421 /* And a random velocity */
422 theta = RNGF()*2.*M_PI;
423 mod = RNGF() * 20.;
424 vec2_pset( &deb->vel, mod, theta );
425
426 /* Randomly init the gfx ID */
427 //deb->gfx = asteroid_gfx[ RNG(0,(int)array_size(asteroid_gfx)-1) ];
428 deb->gfx = debris_gfx[ RNG(0,(int)array_size(debris_gfx)-1) ];
429
430 /* Random height vs player. */
431 deb->height = 0.8 + RNGF()*0.4;
432 deb->alpha = 0.;
433 deb->ang = RNGF() * M_PI * 2.;
434}
435
440{
441 /* Calculate area */
442 a->area = M_PI * pow2(a->radius);
443
444 /* Compute number of asteroids */
445 a->nb = floor( a->area / ASTEROID_REF_AREA * a->density );
446
447 /* Computed from your standard physics equations (with a bit of margin). */
448 a->margin = pow2(a->maxspeed) / (4.*a->accel) + 50.;
449
450 /* Compute weight total. */
451 a->groupswtotal = 0.;
452 for (int i=0; i<array_size(a->groupsw); i++)
453 a->groupswtotal += a->groupsw[i];
454}
455
462{
463 int ret;
464 char **asteroid_files, file[PATH_MAX];
465
466 /* Load asteroid types. */
467 ret = asttype_load();
468 if (ret < 0)
469 return ret;
470
471 /* Load asteroid graphics. */
472 asteroid_files = PHYSFS_enumerateFiles( SPOB_GFX_SPACE_PATH"asteroid/" );
474
475 for (size_t i=0; asteroid_files[i]!=NULL; i++) {
476 snprintf( file, sizeof(file), "%s%s", SPOB_GFX_SPACE_PATH"asteroid/", asteroid_files[i] );
477 array_push_back( &asteroid_gfx, gl_newImage( file, OPENGL_TEX_MIPMAPS ) );
478 }
479
480 PHYSFS_freeList( asteroid_files );
481 return 0;
482}
483
487static int asttype_cmp( const void *p1, const void *p2 )
488{
489 const AsteroidType *at1, *at2;
490 at1 = (const AsteroidType*) p1;
491 at2 = (const AsteroidType*) p2;
492 return strcmp(at1->name,at2->name);
493}
494
498static int astgroup_cmp( const void *p1, const void *p2 )
499{
500 const AsteroidTypeGroup *at1, *at2;
501 at1 = (const AsteroidTypeGroup*) p1;
502 at2 = (const AsteroidTypeGroup*) p2;
503 return strcmp(at1->name,at2->name);
504}
505
511static int asttype_load (void)
512{
513 char **asteroid_files = ndata_listRecursive( ASTEROID_TYPES_DATA_PATH );
515
516 for (int i=0; i<array_size( asteroid_files ); i++) {
517 if (ndata_matchExt( asteroid_files[i], "xml" )) {
518 AsteroidType at;
519 int ret = asttype_parse( &at, asteroid_files[i] );
520 if (ret == 0)
522 }
523 free( asteroid_files[i] );
524 }
525 array_free( asteroid_files );
528
529 /* Check for name collisions. */
530 for (int i=0; i<array_size(asteroid_types)-1; i++)
531 if (strcmp( asteroid_types[i].name, asteroid_types[i+1].name )==0)
532 WARN(_("Asteroid Types with same name '%s'"),asteroid_types[i].name);
533
534 /* Load the asteroid groups from XML definitions. */
536 asteroid_files = ndata_listRecursive( ASTEROID_GROUPS_DATA_PATH );
537 for (int i=0; i<array_size( asteroid_files ); i++) {
538 if (ndata_matchExt( asteroid_files[i], "xml" )) {
540 int ret = astgroup_parse( &atg, asteroid_files[i] );
541 if (ret == 0)
543 }
544 free( asteroid_files[i] );
545 }
546 array_free( asteroid_files );
547 /* Add asteroid types as individual groups. */
548 for (int i=0; i<array_size( asteroid_types ); i++) {
550 AsteroidTypeGroup grp = {
551 .name = strdup(at->name),
552 .types = array_create( AsteroidType* ),
553 .weights= array_create( double ),
554 .wtotal = 1.
555 };
556 array_push_back( &grp.types, at );
557 array_push_back( &grp.weights, 1. );
559 }
562
563 /* Check for name collisions. */
564 for (int i=0; i<array_size(asteroid_groups)-1; i++)
565 if (strcmp( asteroid_groups[i].name, asteroid_groups[i+1].name )==0)
566 WARN(_("Asteroid Type Groups with same name '%s'"),asteroid_groups[i].name);
567
568 return 0;
569}
570
577static int asttype_parse( AsteroidType *at, const char *file )
578{
579 xmlNodePtr parent, node;
580 xmlDocPtr doc;
581
582 /* Load the data. */
583 doc = xml_parsePhysFS( file );
584 if (doc == NULL)
585 return -1;
586
587 /* Get the root node. */
588 parent = doc->xmlChildrenNode;
589 if (!xml_isNode(parent,"asteroid")) {
590 WARN( _("Malformed '%s' file: missing root element 'asteroid'"), file);
591 return -1;
592 }
593
594 /* Set up the element. */
595 memset( at, 0, sizeof(AsteroidType) );
596 at->gfxs = array_create( glTexture* );
599 at->damage = 100;
600 at->penetration = 100.;
601 at->exp_radius = 50.;
602 at->alert_range = 7000.;
603
604 xmlr_attr_strd(parent,"name",at->name);
605 if (at->name == NULL)
606 WARN(_("Asteroid '%s' has invalid or no name"), file);
607
608 node = parent->xmlChildrenNode;
609 do {
610 /* Only handle nodes. */
611 xml_onlyNodes(node);
612
613 xmlr_strd( node, "scanned", at->scanned_msg );
614 xmlr_float( node, "armour_min", at->armour_min );
615 xmlr_float( node, "armour_max", at->armour_max );
616 xmlr_float( node, "absorb", at->absorb );
617 xmlr_float( node, "damage", at->damage );
618 xmlr_float( node, "disable", at->disable );
619 xmlr_float( node, "penetration", at->penetration );
620 xmlr_float( node, "exp_radius", at->exp_radius );
621 xmlr_float( node, "alert_range", at->alert_range );
622
623 if (xml_isNode(node,"gfx")) {
624 array_push_back( &at->gfxs, xml_parseTexture( node, SPOB_GFX_SPACE_PATH"asteroid/%s", 1, 1, OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS ) );
625 asteroid_loadPLG( at, xml_get(node) );
626 continue;
627 }
628 else if (xml_isNode(node,"commodity")) {
629 /* Check that name and quantity are defined. */
630 int namdef = 0;
631 AsteroidReward material;
632 memset( &material, 0, sizeof(material) );
633
634 xmlNodePtr cur = node->xmlChildrenNode;
635 do {
636 xml_onlyNodes(cur);
637
638 xmlr_int( cur, "quantity", material.quantity );
639 xmlr_int( cur, "rarity", material.rarity );
640
641 if (xml_isNode(cur,"name")) {
642 const char *str = xml_get(cur);
643 material.material = commodity_get( str );
644 if (material.material->gfx_space==NULL)
645 WARN(_("Asteroid Type '%s' has Commodity '%s' with no 'gfx_space'."),at->name,str);
646 namdef = 1;
647 continue;
648 }
649
650 WARN(_("Asteroid Type '%s' has unknown node '%s'"), at->name, cur->name);
651 } while (xml_nextNode(cur));
652
653 if (namdef==0 || material.quantity==0)
654 WARN(_("Asteroid Type '%s' has commodity that lacks name or quantity."), at->name);
655
656 array_push_back( &at->material, material );
657 continue;
658 }
659 WARN(_("Asteroid Type '%s' has unknown node '%s'"), at->name, node->name);
660 } while (xml_nextNode(node));
661
662 /* Clean up. */
663 xmlFreeDoc(doc);
664
665 /* Some post-process. */
666 at->absorb = CLAMP( 0., 1., at->absorb / 100. );
667 at->penetration = CLAMP( 0., 1., at->penetration / 100. );
668
669 /* Checks. */
670 if (at->armour_max < at->armour_min)
671 WARN(_("Asteroid Type '%s' has armour_max below armour_min"), at->name);
672
673#define MELEMENT(o,s) \
674if (o) WARN(_("Asteroid Type '%s' missing/invalid '%s' element"), at->name, s)
675 MELEMENT(at->scanned_msg==NULL,"scanned");
676 MELEMENT(array_size(at->gfxs)==0,"gfx");
677 MELEMENT(at->armour_min <= 0.,"armour_min");
678 MELEMENT(at->armour_max <= 0.,"armour_max");
679#undef MELEMENT
680
681 return 0;
682}
683
690static int asteroid_loadPLG( AsteroidType *temp, const char *buf )
691{
692 char file[PATH_MAX];
693 CollPoly *polygon;
694 xmlDocPtr doc;
695 xmlNodePtr node;
696
697 snprintf( file, sizeof(file), "%s%s.xml", ASTEROID_POLYGON_PATH, buf );
698
699 /* There is only one polygon per gfx, but it has to be added to all the other polygons */
700 /* associated to each gfx of current AsteroidType. */
701 /* In case it fails to load for some reason, its size will be set to 0. */
702 polygon = &array_grow( &temp->polygon );
703 polygon->npt = 0;
704
705 /* See if the file does exist. */
706 if (!PHYSFS_exists(file)) {
707 WARN(_("%s xml collision polygon does not exist!\n \
708 Please use the script 'polygon_from_sprite.py'\n \
709 This file can be found in Naev's artwork repo."), file);
710 return 0;
711 }
712
713 /* Load the XML. */
714 doc = xml_parsePhysFS( file );
715 if (doc == NULL)
716 return 0;
717
718 node = doc->xmlChildrenNode; /* First polygon node */
719 if (node == NULL) {
720 xmlFreeDoc(doc);
721 WARN(_("Malformed %s file: does not contain elements"), file);
722 return 0;
723 }
724
725 do { /* load the polygon data */
726 if (xml_isNode(node,"polygons")) {
727 xmlNodePtr cur = node->children;
728 do {
729 if (xml_isNode(cur,"polygon")) {
730 LoadPolygon( polygon, cur );
731 }
732 } while (xml_nextNode(cur));
733 }
734 } while (xml_nextNode(node));
735
736 xmlFreeDoc(doc);
737 return 0;
738}
739
747static int astgroup_parse( AsteroidTypeGroup *ag, const char *file )
748{
749 xmlNodePtr parent, node;
750 xmlDocPtr doc;
751
752 /* Load the data. */
753 doc = xml_parsePhysFS( file );
754 if (doc == NULL)
755 return -1;
756
757 /* Get the root node. */
758 parent = doc->xmlChildrenNode;
759 if (!xml_isNode(parent,"asteroid_group")) {
760 WARN( _("Malformed '%s' file: missing root element 'asteroid_group'"), file);
761 return -1;
762 }
763
764 /* Set up the element. */
765 memset( ag, 0, sizeof(AsteroidTypeGroup) );
767 ag->weights = array_create( double );
768
769 xmlr_attr_strd(parent,"name",ag->name);
770 if (ag->name == NULL)
771 WARN(_("Asteroid '%s' has invalid or no name"), file);
772
773 node = parent->xmlChildrenNode;
774 do {
775 /* Only handle nodes. */
776 xml_onlyNodes(node);
777
778 if (xml_isNode(node,"type")) {
779 double w;
780 xmlr_attr_float_def(node, "weight", w, 1.);
781 AsteroidType *at = asttype_getName( xml_get(node) );
782 array_push_back( &ag->types, at );
783 array_push_back( &ag->weights, w );
784 ag->wtotal += w;
785 continue;
786 }
787 WARN(_("Asteroid Type Group '%s' has unknown node '%s'"), ag->name, node->name);
788 } while (xml_nextNode(node));
789
790 /* Clean up. */
791 xmlFreeDoc(doc);
792
793 return 0;
794}
795
800{
801 double cx, cy;
802 cam_getPos( &cx, &cy );
803 cx -= SCREEN_W/2.;
804 cy -= SCREEN_H/2.;
805
806 /* Render the debris. */
807 for (int j=0; j<array_size(debris_stack); j++) {
808 const Debris *d = &debris_stack[j];
809 if (d->height > 1.)
810 debris_renderSingle( d, cx, cy );
811 }
812}
813
818{
819 double cx, cy, z;
820 cam_getPos( &cx, &cy );
821 z = cam_getZoom();
822 cx -= SCREEN_W/2.;
823 cy -= SCREEN_H/2.;
824
825 /* Render the asteroids & debris. */
826 for (int i=0; i<array_size(cur_system->asteroids); i++) {
827 const AsteroidAnchor *ast = &cur_system->asteroids[i];
828 double x, y, r;
829
830 /* See if the asteroid field is in range, if not skip. */
831 gl_gameToScreenCoords( &x, &y, ast->pos.x, ast->pos.y );
832 r = ast->radius * z;
833 if ((x < -r) || (x > SCREEN_W+r) ||
834 (y < -r) || (y > SCREEN_H+r))
835 continue;
836
837 /* Render all asteroids. */
838 for (int j=0; j<ast->nb; j++)
840 }
841
842 /* Render the debris. */
843 for (int j=0; j<array_size(debris_stack); j++) {
844 const Debris *d = &debris_stack[j];
845 if (d->height <= 1.)
846 debris_renderSingle( d, cx, cy );
847 }
848
849 /* Render gatherable stuff. */
851}
852
856static void asteroid_renderSingle( const Asteroid *a )
857{
858 double nx, ny;
859 const AsteroidType *at;
860 glColour col;
861 double progress;
862 const glColour darkcol = cGrey20;
863
864 /* Skip invisible asteroids */
865 if (a->state == ASTEROID_XX)
866 return;
867
868 progress = a->timer / a->timer_max;
869 switch (a->state) {
870 case ASTEROID_XX_TO_BG:
871 col = darkcol;
872 col.a = 1.-progress;
873 break;
874 case ASTEROID_XB:
875 case ASTEROID_BX:
876 col = darkcol;
877 break;
878 case ASTEROID_BG_TO_FG:
879 col_blend( &col, &darkcol, &cWhite, progress );
880 break;
881 case ASTEROID_FG:
882 col = cWhite;
883 break;
884 case ASTEROID_FG_TO_BG:
885 col_blend( &col, &cWhite, &darkcol, progress );
886 break;
887 case ASTEROID_BG_TO_XX:
888 col = darkcol;
889 col.a = progress;
890 break;
891
892 default:
893 break;
894 }
895
896 at = a->type;
897 gl_renderSpriteRotate( a->gfx, a->sol.pos.x, a->sol.pos.y, a->ang, 0, 0, &col );
898
899 /* Add the commodities if scanned. */
900 if (!a->scanned)
901 return;
902 col = cFontWhite;
903 col.a = a->scan_alpha;
904 gl_gameToScreenCoords( &nx, &ny, a->sol.pos.x, a->sol.pos.y );
905 gl_printRaw( &gl_smallFont, nx+a->gfx->sw/2, ny-gl_smallFont.h/2, &col, -1., _(at->scanned_msg) );
906 /*
907 for (int i=0; i<array_size(at->material); i++) {
908 AsteroidReward *mat = &at->material[i];
909 Commodity *com = mat->material;
910 if (com->gfx_space!=NULL)
911 gl_renderSprite( com->gfx_space, a->pos.x, a->pos.y-10.*i, 0, 0, NULL );
912 snprintf(c, sizeof(c), "x%i", mat->quantity);
913 gl_printRaw( &gl_smallFont, nx+10, ny-5-10.*i, &cFontWhite, -1., c );
914 }
915 */
916}
917
921static void debris_renderSingle( const Debris *d, double cx, double cy )
922{
923 const double scale = 0.5;
924 const glColour col = COL_ALPHA( cInert, d->alpha );
925
926 gl_renderSpriteScaleRotate( d->gfx, d->pos.x+cx, d->pos.y+cy, scale, scale, d->ang, 0, 0, &col );
927}
928
935{
936 if (ast->qt_init)
937 qt_destroy( &ast->qt );
938 free(ast->label);
939 free(ast->asteroids);
940 array_free(ast->groups);
941 array_free(ast->groupsw);
942}
943
947void asteroids_free (void)
948{
949 /* Free asteroid graphics. */
950 for (int i=0; i<array_size(asteroid_gfx); i++)
954
955 /* Free the asteroid types. */
956 for (int i=0; i<array_size(asteroid_types); i++) {
958 free(at->name);
959 free(at->scanned_msg);
960 array_free(at->material);
961 for (int j=0; j<array_size(at->gfxs); j++)
962 gl_freeTexture(at->gfxs[j]);
963 array_free(at->gfxs);
964
965 /* Free collision polygons. */
966 for (int j=0; j<array_size(at->polygon); j++)
967 FreePolygon( &at->polygon[j] );
968 array_free(at->polygon);
969 }
971 asteroid_types = NULL;
972
973 /* Free the asteroid groups. */
974 for (int i=0; i<array_size(asteroid_groups); i++) {
976 free(ag->name);
977 array_free(ag->types);
978 array_free(ag->weights);
979 }
981 asteroid_groups = NULL;
982
983 /* Clean up debris. */
985 debris_stack = NULL;
986
987 /* Free the gatherable stack. */
989}
990
997int asteroids_inField( const vec2 *p )
998{
999 /* Always return -1 if in an exclusion zone */
1000 for (int i=0; i<array_size(cur_system->astexclude); i++) {
1001 AsteroidExclusion *e = &cur_system->astexclude[i];
1002 if (vec2_dist2( p, &e->pos ) <= pow2(e->radius))
1003 return -1;
1004 }
1005
1006 /* Check if in asteroid field */
1007 for (int i=0; i<array_size(cur_system->asteroids); i++) {
1009 if (vec2_dist2( p, &a->pos ) <= pow2(a->radius))
1010 return i;
1011 }
1012
1013 return -1;
1014}
1015
1022{
1023 return asteroid_types;
1024}
1025
1032AsteroidType *asttype_getName( const char *name )
1033{
1034 const AsteroidType q = { .name=(char*)name };
1036 if (at == NULL)
1037 WARN(_("Unknown Asteroid Type '%s'"),name);
1038 return at;
1039}
1040
1047{
1048 return asteroid_groups;
1049}
1050
1058{
1059 const AsteroidTypeGroup q = { .name=(char*)name };
1061 if (ag == NULL)
1062 WARN(_("Unknown Asteroid Type Group '%s'"),name);
1063 return ag;
1064}
1065
1074void asteroid_hit( Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus )
1075{
1076 double darmour;
1077 double absorb = 1. - CLAMP( 0., 1., a->type->absorb - dmg->penetration );
1078 dtype_calcDamage( NULL, &darmour, absorb, NULL, dmg, NULL );
1079
1080 a->armour -= darmour;
1081 if (a->armour <= 0)
1082 asteroid_explode( a, max_rarity, mining_bonus );
1083}
1084
1092void asteroid_explode( Asteroid *a, int max_rarity, double mining_bonus )
1093{
1094 Damage dmg;
1095 char buf[16];
1096 double rad2;
1097 LuaAsteroid_t la;
1098 const AsteroidType *at = a->type;
1099 const AsteroidAnchor *field = &cur_system->asteroids[a->parent];
1100 Pilot *const* pilot_stack = pilot_getAll();
1101
1102 /* Manage the explosion */
1103 dmg.type = dtype_get("explosion_splash");
1104 dmg.damage = at->damage;
1105 dmg.penetration = at->penetration; /* Full penetration. */
1106 dmg.disable = 0.;
1107 expl_explode( a->sol.pos.x, a->sol.pos.y, a->sol.vel.x, a->sol.vel.y,
1108 at->exp_radius, &dmg, NULL, EXPL_MODE_SHIP );
1109
1110 /* Play random explosion sound. */
1111 snprintf(buf, sizeof(buf), "explosion%d", RNG(0,2));
1112 sound_playPos( sound_get(buf), a->sol.pos.x, a->sol.pos.y, a->sol.vel.x, a->sol.vel.y );
1113
1114 /* Alert nearby pilots. */
1115 rad2 = pow2( at->alert_range );
1116 la.parent = a->parent;
1117 la.id = a->id;
1118 lua_pushasteroid( naevL, la );
1119 for (int i=0; i<array_size(pilot_stack); i++) {
1120 Pilot *p = pilot_stack[i];
1121
1122 if (vec2_dist2( &p->solid.pos, &a->sol.pos ) > rad2)
1123 continue;
1124
1125 pilot_msg( NULL, p, "asteroid", -1 );
1126 }
1127 lua_pop(naevL,1);
1128
1129 /* Release commodity rewards. */
1130 if (max_rarity >= 0) {
1131 int ndrops = 0;
1132 for (int i=0; i<array_size(at->material); i++) {
1133 const AsteroidReward *mat = &at->material[i];
1134 if (mat->rarity > max_rarity)
1135 continue;
1136 ndrops++;
1137 }
1138 if (ndrops > 0) {
1139 double r = RNGF();
1140 double prob = 1./(double)ndrops;
1141 double accum = 0.;
1142 for (int i=0; i<array_size(at->material); i++) {
1143 const AsteroidReward *mat = &at->material[i];
1144 if (mat->rarity > max_rarity)
1145 continue;
1146 accum += prob;
1147 if (r > accum)
1148 continue;
1149
1150 int nb = RNG(0, round((double)mat->quantity * mining_bonus));
1151 for (int j=0; j<nb; j++) {
1152 vec2 pos, vel;
1153 pos = a->sol.pos;
1154 vel = a->sol.vel;
1155 pos.x += (RNGF()*30.-15.);
1156 pos.y += (RNGF()*30.-15.);
1157 vel.x += (RNGF()*20.-10.);
1158 vel.y += (RNGF()*20.-10.);
1159 gatherable_init( mat->material, &pos, &vel, -1., RNG(1,5), 0 );
1160 }
1161 break;
1162 }
1163 }
1164 }
1165
1166 /* Remove the asteroid target to any pilot. */
1167 pilot_untargetAsteroid( a->parent, a->id );
1168
1169 /* Make it respawn elsewhere */
1170 asteroid_init( a, field );
1171 a->state = ASTEROID_BG_TO_XX;
1172 a->timer_max = a->timer = 0.5;
1173}
1174
1175void asteroid_collideQueryIL( AsteroidAnchor *anc, IntList *il, int x1, int y1, int x2, int y2 )
1176{
1177 qt_query( &anc->qt, il, x1, y1, x2, y2 );
1178}
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_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition array.h:112
#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_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:149
#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
void asteroids_render(void)
Renders the current systems' spobs.
Definition asteroid.c:817
static int astgroup_parse(AsteroidTypeGroup *ag, const char *file)
Parses an asteroid type group from a file.
Definition asteroid.c:747
void asteroids_computeInternals(AsteroidAnchor *a)
Updates internal alues of an asteroid field.
Definition asteroid.c:439
static Debris * debris_stack
Definition asteroid.c:44
static void asteroid_renderSingle(const Asteroid *a)
Renders an asteroid.
Definition asteroid.c:856
static AsteroidTypeGroup * asteroid_groups
Definition asteroid.c:51
static glTexture ** debris_gfx
Definition asteroid.c:45
void asteroids_free(void)
Cleans up the system.
Definition asteroid.c:947
void asteroid_hit(Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus)
Hits an asteroid.
Definition asteroid.c:1074
const double DEBRIS_BUFFER
Definition asteroid.c:40
int asteroids_load(void)
Loads the asteroids.
Definition asteroid.c:461
static AsteroidType * asteroid_types
Definition asteroid.c:50
static void debris_renderSingle(const Debris *d, double cx, double cy)
Renders a debris.
Definition asteroid.c:921
static glTexture ** asteroid_gfx
Definition asteroid.c:52
AsteroidType * asttype_getName(const char *name)
Gets the ID of an asteroid type by name.
Definition asteroid.c:1032
static int astgroup_cmp(const void *p1, const void *p2)
Compares two asteroid type groups.
Definition asteroid.c:498
const AsteroidTypeGroup * astgroup_getAll(void)
Gets all the asteroid type groups.
Definition asteroid.c:1046
void asteroid_free(AsteroidAnchor *ast)
Frees an asteroid anchor.
Definition asteroid.c:934
AsteroidTypeGroup * astgroup_getName(const char *name)
Gets an asteroid type group by name.
Definition asteroid.c:1057
static int asttype_cmp(const void *p1, const void *p2)
Compares two asteroid types.
Definition asteroid.c:487
static int asttype_parse(AsteroidType *at, const char *file)
Parses the XML of an asteroid type.
Definition asteroid.c:577
void asteroid_explode(Asteroid *a, int max_rarity, double mining_bonus)
Makes an asteroid explode.
Definition asteroid.c:1092
static int asttype_load(void)
Loads the asteroids types.
Definition asteroid.c:511
static const double SCAN_FADE
Definition asteroid.c:42
static void debris_init(Debris *deb)
Initializes a debris.
Definition asteroid.c:414
void asteroids_init(void)
Initializes the system.
Definition asteroid.c:253
void asteroids_renderOverlay(void)
Renders the system overlay.
Definition asteroid.c:799
static int asteroid_init(Asteroid *ast, const AsteroidAnchor *field)
Initializes an asteroid.
Definition asteroid.c:331
const AsteroidType * asttype_getAll(void)
Gets all the asteroid types.
Definition asteroid.c:1021
int asteroids_inField(const vec2 *p)
See if the position is in an asteroid field.
Definition asteroid.c:997
void asteroids_update(double dt)
Controls fleet spawning.
Definition asteroid.c:73
static int asteroid_loadPLG(AsteroidType *temp, const char *buf)
Loads the collision polygon for an asteroid type.
Definition asteroid.c:690
void cam_getDPos(double *dx, double *dy)
Gets the camera position differential (change in last frame).
Definition camera.c:127
void cam_getPos(double *x, double *y)
Gets the camera position.
Definition camera.c:118
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:97
void LoadPolygon(CollPoly *polygon, xmlNodePtr node)
Loads a polygon from an xml node.
Definition collision.c:35
void col_blend(glColour *blend, const glColour *fg, const glColour *bg, float alpha)
Blends two colours.
Definition colour.c:192
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:127
void dtype_calcDamage(double *dshield, double *darmour, double absorb, double *knockback, const Damage *dmg, const ShipStats *s)
Gives the real shield damage, armour damage and knockback modifier.
Definition damagetype.c:276
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition damagetype.c:159
void expl_explode(double x, double y, double vx, double vy, double radius, const Damage *dmg, const Pilot *parent, int mode)
Does explosion in a radius (damage and graphics).
Definition explosion.c:42
glFont gl_smallFont
Definition font.c:154
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition font.c:617
void gatherable_free(void)
Frees all the gatherables.
Definition gatherable.c:142
int gatherable_init(const Commodity *com, const vec2 *pos, const vec2 *vel, double lifeleng, int qtt, unsigned int player_only)
Initializes a gatherable object.
Definition gatherable.c:66
void gatherable_render(void)
Renders all the gatherables.
Definition gatherable.c:150
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define CLAMP(a, b, x)
Definition naev.h:41
#define pow2(x)
Definition naev.h:46
#define MAX(x, y)
Definition naev.h:39
#define PATH_MAX
Definition naev.h:50
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
LuaAsteroid_t * lua_pushasteroid(lua_State *L, LuaAsteroid_t asteroid)
Pushes a asteroid on the stack.
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_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_renderSpriteScaleRotate(const glTexture *sprite, double bx, double by, double scalew, double scaleh, double angle, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player with scaling and rotation.
void gl_screenToGameCoords(double *nx, double *ny, int bx, int by)
Converts screen coordinates to in-game coordinates.
void gl_renderSpriteRotate(const glTexture *sprite, double bx, double by, double angle, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player with rotation.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:675
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:862
void pilot_msg(const Pilot *p, const Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition pilot.c:4120
static Pilot ** pilot_stack
Definition pilot.c:61
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:94
void pilot_untargetAsteroid(int anchor, int asteroid)
Loops over pilot stack to remove an asteroid as target.
Definition pilot.c:2960
static const double d[]
Definition rng.c:273
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition sound.c:827
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition sound.c:756
StarSystem * cur_system
Definition space.c:106
int space_isSimulation(void)
returns whether we're just simulating.
Definition space.c:1530
Represents an asteroid field anchor.
Definition asteroid.h:100
Quadtree qt
Definition asteroid.h:117
double radius
Definition asteroid.h:107
double density
Definition asteroid.h:104
double maxspin
Definition asteroid.h:113
double * groupsw
Definition asteroid.h:110
double maxspeed
Definition asteroid.h:112
Asteroid * asteroids
Definition asteroid.h:105
double groupswtotal
Definition asteroid.h:111
AsteroidTypeGroup ** groups
Definition asteroid.h:109
Represents an asteroid exclusion zone.
Definition asteroid.h:124
Represents a potential reward from the asteroid.
Definition asteroid.h:39
Commodity * material
Definition asteroid.h:40
Represents a group of asteroids.
Definition asteroid.h:67
double * weights
Definition asteroid.h:70
AsteroidType ** types
Definition asteroid.h:69
Represents a type of asteroid.
Definition asteroid.h:48
double exp_radius
Definition asteroid.h:60
double penetration
Definition asteroid.h:59
double alert_range
Definition asteroid.h:61
double armour_max
Definition asteroid.h:55
CollPoly * polygon
Definition asteroid.h:52
double armour_min
Definition asteroid.h:54
double damage
Definition asteroid.h:57
char * name
Definition asteroid.h:49
AsteroidReward * material
Definition asteroid.h:53
double absorb
Definition asteroid.h:56
char * scanned_msg
Definition asteroid.h:50
double disable
Definition asteroid.h:58
glTexture ** gfxs
Definition asteroid.h:51
Represents a single asteroid.
Definition asteroid.h:77
CollPoly * polygon
Definition asteroid.h:84
int id
Definition asteroid.h:79
double timer
Definition asteroid.h:91
int state
Definition asteroid.h:81
double timer_max
Definition asteroid.h:92
int parent
Definition asteroid.h:80
double armour
Definition asteroid.h:85
const glTexture * gfx
Definition asteroid.h:83
Solid sol
Definition asteroid.h:87
int scanned
Definition asteroid.h:94
const AsteroidType * type
Definition asteroid.h:82
double ang
Definition asteroid.h:88
double spin
Definition asteroid.h:89
Represents a polygon used for collision detection.
Definition collision.h:13
int npt
Definition collision.h:20
glTexture * gfx_space
Definition commodity.h:54
Core damage that an outfit does.
Definition outfit.h:138
int type
Definition outfit.h:139
double disable
Definition outfit.h:142
double penetration
Definition outfit.h:140
double damage
Definition outfit.h:141
Represents a small asteroid debris rendered in the player frame.
Definition asteroid.c:31
vec2 pos
Definition asteroid.c:33
double height
Definition asteroid.c:36
double ang
Definition asteroid.c:35
vec2 vel
Definition asteroid.c:34
const glTexture * gfx
Definition asteroid.c:32
double alpha
Definition asteroid.c:37
The representation of an in-game pilot.
Definition pilot.h:217
int h
Definition font.h:18
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:36
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33