naev 0.10.4
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
68
74void asteroids_update( double dt )
75{
76 /* Asteroids/Debris update */
77 for (int i=0; i<array_size(cur_system->asteroids); i++) {
78 int has_exclusion = 0;
79 AsteroidAnchor *ast = &cur_system->asteroids[i];
80
81 for (int k=0; k<array_size(cur_system->astexclude); k++) {
82 AsteroidExclusion *exc = &cur_system->astexclude[k];
83 if (vec2_dist2( &ast->pos, &exc->pos ) < pow2(ast->radius+exc->radius)) {
84 exc->affects = 1;
85 has_exclusion = 1;
86 }
87 else
88 exc->affects = 0;
89 }
90
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->pos.x;
108 offy = ast->pos.y - a->pos.y;
109 d = pow2(offx)+pow2(offy);
110 if (d >= pow2(ast->radius)) {
111 d = sqrt(d);
112 a->vel.x += ast->thrust * dt * offx / d;
113 a->vel.y += ast->thrust * 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->pos.x - exc->pos.x;
127 ey = a->pos.y - exc->pos.y;
128 ed = pow2(ex) + pow2(ey);
129 if (ed <= pow2(exc->radius)) {
130 ed = sqrt(ed);
131 a->vel.x += ast->thrust * dt * ex / ed;
132 a->vel.y += ast->thrust * dt * ey / ed;
133 setvel = 1;
134 }
135 }
136 }
137
138 if (setvel) {
139 /* Enforce max speed. */
140 d = MOD(a->vel.x, a->vel.y);
141 if (d > ast->maxspeed) {
142 a->vel.x *= ast->maxspeed / d;
143 a->vel.y *= ast->maxspeed / d;
144 }
145 }
146
147 /* Update position. */
148 a->pos.x += a->vel.x * dt;
149 a->pos.y += a->vel.y * dt;
150
151 /* Update angle. */
152 a->ang += a->spin * dt;
153
154 /* Update scanned state if necessary. */
155 if (a->scanned) {
156 if (a->state == ASTEROID_FG)
157 a->scan_alpha = MIN( a->scan_alpha+SCAN_FADE*dt, 1.);
158 else
159 a->scan_alpha = MAX( a->scan_alpha-SCAN_FADE*dt, 0.);
160 }
161
162 a->timer -= dt;
163 if (a->timer < 0.) {
164 switch (a->state) {
165 /* Transition states. */
166 case ASTEROID_FG:
167 pilot_untargetAsteroid( a->parent, a->id );
168 FALLTHROUGH;
169 case ASTEROID_XB:
170 case ASTEROID_BX:
171 case ASTEROID_XX_TO_BG:
172 a->timer_max = a->timer = 1. + 3.*RNGF();
173 break;
174
175 /* Longer states. */
176 case ASTEROID_FG_TO_BG:
177 a->timer_max = a->timer = 10. + 20.*RNGF();
178 break;
179 case ASTEROID_BG_TO_FG:
180 a->timer_max = a->timer = 90. + 30.*RNGF();
181 break;
182
183 /* Special case needs to respawn. */
184 case ASTEROID_BG_TO_XX:
185 asteroid_init( a, ast );
186 a->timer_max = a->timer = 10. + 20.*RNGF();
187 break;
188
189 case ASTEROID_XX:
190 /* Do nothing. */
191 break;
192 }
193 /* States should be in proper order. */
194 a->state = (a->state+1) % ASTEROID_STATE_MAX;
195 }
196 }
197 }
198
199 /* Only have to update stuff if not simulating. */
200 if (!space_isSimulation()) {
201 double dx, dy;
202 cam_getDPos( &dx, &dy );
203
204 for (int j=0; j<array_size(debris_stack); j++) {
205 Debris *d = &debris_stack[j];
206 int infield;
207 vec2 v;
208
209 d->pos.x += d->vel.x * dt - dx;
210 d->pos.y += d->vel.y * dt - dy;
211
212 /* Check boundaries */
213 if (d->pos.x > SCREEN_W + DEBRIS_BUFFER)
214 d->pos.x -= SCREEN_W + 2.*DEBRIS_BUFFER;
215 else if (d->pos.y > SCREEN_H + DEBRIS_BUFFER)
216 d->pos.y -= SCREEN_H + 2.*DEBRIS_BUFFER;
217 else if (d->pos.x < -DEBRIS_BUFFER)
218 d->pos.x += SCREEN_W + 2.*DEBRIS_BUFFER;
219 else if (d->pos.y < -DEBRIS_BUFFER)
220 d->pos.y += SCREEN_H + 2.*DEBRIS_BUFFER;
221
222 /* Set alpha based on position. */
223 /* TODO there seems to be some offset mistake or something going on
224 * here, not too big of an issue though. */
225 gl_screenToGameCoords( &v.x, &v.y, d->pos.x, d->pos.y );
226 infield = asteroids_inField( &v );
227 if (infield>=0)
228 d->alpha = MIN( 1.0, d->alpha + 0.5 * dt );
229 else
230 d->alpha = MAX( 0.0, d->alpha - 0.5 * dt );
231 }
232 }
233}
234
238void asteroids_init (void)
239{
240 double density_max = 0.;
241 int ndebris;
242 asteroid_creating = 1;
243
244 if (debris_gfx==NULL)
247
248 /* Set up asteroids. */
249 for (int i=0; i<array_size(cur_system->asteroids); i++) {
250 AsteroidAnchor *ast = &cur_system->asteroids[i];
251 ast->id = i;
252
253 /* Add graphics to debris. */
254 for (int j=0; j<array_size(ast->groups); j++) {
255 AsteroidTypeGroup *ag = ast->groups[j];
256 for (int k=0; k<array_size(ag->types); k++) {
257 AsteroidType *at = ag->types[k];
258 for (int x=0; x<array_size(at->gfxs); x++)
260 }
261 }
262
263 /* Add the asteroids to the anchor */
264 ast->asteroids = realloc( ast->asteroids, (ast->nb) * sizeof(Asteroid) );
265 for (int j=0; j<ast->nb; j++) {
266 double r = RNGF();
267 Asteroid *a = &ast->asteroids[j];
268 a->id = j;
269 if (asteroid_init(a, ast))
270 continue;
271 if (r > 0.6)
272 a->state = ASTEROID_FG;
273 else if (r > 0.8)
274 a->state = ASTEROID_XB;
275 else if (r > 0.9)
276 a->state = ASTEROID_BX;
277 else
278 a->state = ASTEROID_XX;
279 a->timer = a->timer_max = 30.*RNGF();
280 a->ang = RNGF() * M_PI * 2.;
281 }
282
283 density_max = MAX( density_max, ast->density );
284 }
285
286 /* Add the debris to the anchor */
287 if (debris_stack == NULL)
289
290 /* We compute a fixed amount and scale depending on how big the screen is
291 * compared the reference (minimum resolution). */
292 ndebris = density_max * 100. * (SCREEN_W+2.*DEBRIS_BUFFER * SCREEN_H+2.*DEBRIS_BUFFER) / (RESOLUTION_W_MIN * RESOLUTION_H_MIN);
293 array_resize( &debris_stack, ndebris );
294
295 for (int j=0; j<array_size(debris_stack); j++)
297
298 asteroid_creating = 0;
299}
300
306static int asteroid_init( Asteroid *ast, const AsteroidAnchor *field )
307{
308 double mod, theta, wmax, r, r2;
309 AsteroidType *at = NULL;
310 int outfield, id;
311 int attempts = 0;
312
313 ast->parent = field->id;
314 ast->scanned = 0;
315 r2 = pow2( field->radius );
316
317 do {
318 /* Try to keep density uniform using cartesian coordinates. */
319 ast->pos.x = field->pos.x + (RNGF()*2.-1.)*field->radius;
320 ast->pos.y = field->pos.y + (RNGF()*2.-1.)*field->radius;
321
322 /* Check if out of the field. */
323 outfield = (asteroids_inField(&ast->pos) < 0);
324
325 /* If this is the first time and it's spawned outside the field,
326 * we get rid of it so that density remains roughly consistent. */
327 if (asteroid_creating && outfield && (vec2_dist2( &ast->pos, &field->pos ) < r2)) {
328 ast->state = ASTEROID_XX;
329 ast->timer_max = ast->timer = HUGE_VAL; /* Don't reappear. */
330 /* TODO probably do a more proper solution removing total number of asteroids. */
331 return -1;
332 }
333
334 } while (outfield && (attempts++ < 1000));
335
336 /* Randomly init the type of asteroid */
337 r = field->groupswtotal * RNGF();
338 wmax = 0.;
339 for (int i=0; i<array_size(field->groups); i++) {
340 wmax += field->groupsw[i];
341 if (r > wmax)
342 continue;
343 AsteroidTypeGroup *grp = field->groups[i];
344 double wi = 0.;
345 r = grp->wtotal * RNGF();
346 for (int j=0; j<array_size(grp->types); j++) {
347 wi += grp->weights[j];
348 if (r > wi)
349 continue;
350 at = grp->types[j];
351 break;
352 }
353 break;
354 }
355
356 /* Randomly init the gfx ID, and associated polygon */
357 id = RNG(0, array_size(at->gfxs)-1);
358 ast->gfx = at->gfxs[ id ];
359 ast->polygon = &at->polygon[ id ];
360
361 ast->type = at;
362 ast->armour = at->armour_min + RNGF() * (at->armour_max-at->armour_min);
363
364 /* And a random velocity/spin */
365 theta = RNGF()*2.*M_PI;
366 ast->spin = (1-2*RNGF())*field->maxspin;
367 mod = RNGF()*field->maxspeed;
368 vec2_pset( &ast->vel, mod, theta );
369
370 /* Fade in stuff. */
371 ast->state = ASTEROID_XX;
372 ast->timer_max = ast->timer = -1.;
373 ast->ang = RNGF() * M_PI * 2.;
374
375 return 0;
376}
377
382static void debris_init( Debris *deb )
383{
384 double theta, mod;
385 /* Position */
386 deb->pos.x = -DEBRIS_BUFFER + RNGF()*(SCREEN_W + 2.*DEBRIS_BUFFER);
387 deb->pos.y = -DEBRIS_BUFFER + RNGF()*(SCREEN_H + 2.*DEBRIS_BUFFER);
388
389 /* And a random velocity */
390 theta = RNGF()*2.*M_PI;
391 mod = RNGF() * 20.;
392 vec2_pset( &deb->vel, mod, theta );
393
394 /* Randomly init the gfx ID */
395 //deb->gfx = asteroid_gfx[ RNG(0,(int)array_size(asteroid_gfx)-1) ];
396 deb->gfx = debris_gfx[ RNG(0,(int)array_size(debris_gfx)-1) ];
397
398 /* Random height vs player. */
399 deb->height = 0.8 + RNGF()*0.4;
400 deb->alpha = 0.;
401 deb->ang = RNGF() * M_PI * 2.;
402}
403
408{
409 /* Calculate area */
410 a->area = M_PI * pow2(a->radius);
411
412 /* Compute number of asteroids */
413 a->nb = floor( a->area / ASTEROID_REF_AREA * a->density );
414
415 /* Computed from your standard physics equations (with a bit of margin). */
416 a->margin = pow2(a->maxspeed) / (4.*a->thrust) + 50.;
417
418 /* Compute weight total. */
419 a->groupswtotal = 0.;
420 for (int i=0; i<array_size(a->groupsw); i++)
421 a->groupswtotal += a->groupsw[i];
422}
423
430{
431 int ret;
432 char **asteroid_files, file[PATH_MAX];
433
434 /* Load asteroid types. */
435 ret = asttype_load();
436 if (ret < 0)
437 return ret;
438
439 /* Load asteroid graphics. */
440 asteroid_files = PHYSFS_enumerateFiles( SPOB_GFX_SPACE_PATH"asteroid/" );
442
443 for (size_t i=0; asteroid_files[i]!=NULL; i++) {
444 snprintf( file, sizeof(file), "%s%s", SPOB_GFX_SPACE_PATH"asteroid/", asteroid_files[i] );
445 array_push_back( &asteroid_gfx, gl_newImage( file, OPENGL_TEX_MIPMAPS ) );
446 }
447
448 PHYSFS_freeList( asteroid_files );
449 return 0;
450}
451
455static int asttype_cmp( const void *p1, const void *p2 )
456{
457 const AsteroidType *at1, *at2;
458 at1 = (const AsteroidType*) p1;
459 at2 = (const AsteroidType*) p2;
460 return strcmp(at1->name,at2->name);
461}
462
466static int astgroup_cmp( const void *p1, const void *p2 )
467{
468 const AsteroidTypeGroup *at1, *at2;
469 at1 = (const AsteroidTypeGroup*) p1;
470 at2 = (const AsteroidTypeGroup*) p2;
471 return strcmp(at1->name,at2->name);
472}
473
479static int asttype_load (void)
480{
481 char **asteroid_files = ndata_listRecursive( ASTEROID_TYPES_DATA_PATH );
483
484 for (int i=0; i<array_size( asteroid_files ); i++) {
485 if (ndata_matchExt( asteroid_files[i], "xml" )) {
486 AsteroidType at;
487 int ret = asttype_parse( &at, asteroid_files[i] );
488 if (ret == 0)
490 }
491 free( asteroid_files[i] );
492 }
493 array_free( asteroid_files );
496
497 /* Check for name collisions. */
498 for (int i=0; i<array_size(asteroid_types)-1; i++)
499 if (strcmp( asteroid_types[i].name, asteroid_types[i+1].name )==0)
500 WARN(_("Asteroid Types with same name '%s'"),asteroid_types[i].name);
501
502 /* Load the asteroid groups from XML definitions. */
504 asteroid_files = ndata_listRecursive( ASTEROID_GROUPS_DATA_PATH );
505 for (int i=0; i<array_size( asteroid_files ); i++) {
506 if (ndata_matchExt( asteroid_files[i], "xml" )) {
508 int ret = astgroup_parse( &atg, asteroid_files[i] );
509 if (ret == 0)
511 }
512 free( asteroid_files[i] );
513 }
514 array_free( asteroid_files );
515 /* Add asteroid types as individual groups. */
516 for (int i=0; i<array_size( asteroid_types ); i++) {
518 AsteroidTypeGroup grp = {
519 .name = strdup(at->name),
520 .types = array_create( AsteroidType* ),
521 .weights= array_create( double ),
522 .wtotal = 1.
523 };
524 array_push_back( &grp.types, at );
525 array_push_back( &grp.weights, 1. );
527 }
530
531 /* Check for name collisions. */
532 for (int i=0; i<array_size(asteroid_groups)-1; i++)
533 if (strcmp( asteroid_groups[i].name, asteroid_groups[i+1].name )==0)
534 WARN(_("Asteroid Type Groups with same name '%s'"),asteroid_groups[i].name);
535
536 return 0;
537}
538
545static int asttype_parse( AsteroidType *at, const char *file )
546{
547 xmlNodePtr parent, node;
548 xmlDocPtr doc;
549
550 /* Load the data. */
551 doc = xml_parsePhysFS( file );
552 if (doc == NULL)
553 return -1;
554
555 /* Get the root node. */
556 parent = doc->xmlChildrenNode;
557 if (!xml_isNode(parent,"asteroid")) {
558 WARN( _("Malformed '%s' file: missing root element 'asteroid'"), file);
559 return -1;
560 }
561
562 /* Set up the element. */
563 memset( at, 0, sizeof(AsteroidType) );
564 at->gfxs = array_create( glTexture* );
567 at->damage = 100;
568 at->penetration = 100.;
569 at->exp_radius = 50.;
570 at->alert_range = 7000.;
571
572 xmlr_attr_strd(parent,"name",at->name);
573 if (at->name == NULL)
574 WARN(_("Asteroid '%s' has invalid or no name"), file);
575
576 node = parent->xmlChildrenNode;
577 do {
578 /* Only handle nodes. */
579 xml_onlyNodes(node);
580
581 xmlr_strd( node, "scanned", at->scanned_msg );
582 xmlr_float( node, "armour_min", at->armour_min );
583 xmlr_float( node, "armour_max", at->armour_max );
584 xmlr_float( node, "absorb", at->absorb );
585 xmlr_float( node, "damage", at->damage );
586 xmlr_float( node, "disable", at->disable );
587 xmlr_float( node, "penetration", at->penetration );
588 xmlr_float( node, "exp_radius", at->exp_radius );
589 xmlr_float( node, "alert_range", at->alert_range );
590
591 if (xml_isNode(node,"gfx")) {
592 array_push_back( &at->gfxs, xml_parseTexture( node, SPOB_GFX_SPACE_PATH"asteroid/%s", 1, 1, OPENGL_TEX_MAPTRANS | OPENGL_TEX_MIPMAPS ) );
593 asteroid_loadPLG( at, xml_get(node) );
594 continue;
595 }
596 else if (xml_isNode(node,"commodity")) {
597 /* Check that name and quantity are defined. */
598 int namdef = 0;
599 AsteroidReward material;
600 memset( &material, 0, sizeof(material) );
601
602 xmlNodePtr cur = node->xmlChildrenNode;
603 do {
604 xml_onlyNodes(cur);
605
606 xmlr_int( cur, "quantity", material.quantity );
607 xmlr_int( cur, "rarity", material.rarity );
608
609 if (xml_isNode(cur,"name")) {
610 const char *str = xml_get(cur);
611 material.material = commodity_get( str );
612 if (material.material->gfx_space==NULL)
613 WARN(_("Asteroid Type '%s' has Commodity '%s' with no 'gfx_space'."),at->name,str);
614 namdef = 1;
615 continue;
616 }
617
618 WARN(_("Asteroid Type '%s' has unknown node '%s'"), at->name, cur->name);
619 } while (xml_nextNode(cur));
620
621 if (namdef==0 || material.quantity==0)
622 WARN(_("Asteroid Type '%s' has commodity that lacks name or quantity."), at->name);
623
624 array_push_back( &at->material, material );
625 continue;
626 }
627 WARN(_("Asteroid Type '%s' has unknown node '%s'"), at->name, node->name);
628 } while (xml_nextNode(node));
629
630 /* Clean up. */
631 xmlFreeDoc(doc);
632
633 /* Some post-process. */
634 at->absorb = CLAMP( 0., 1., at->absorb / 100. );
635 at->penetration = CLAMP( 0., 1., at->penetration / 100. );
636
637 /* Checks. */
638 if (at->armour_max < at->armour_min)
639 WARN(_("Asteroid Type '%s' has armour_max below armour_min"), at->name);
640
641#define MELEMENT(o,s) \
642if (o) WARN(_("Asteroid Type '%s' missing/invalid '%s' element"), at->name, s)
643 MELEMENT(at->scanned_msg==NULL,"scanned");
644 MELEMENT(array_size(at->gfxs)==0,"gfx");
645 MELEMENT(at->armour_min <= 0.,"armour_min");
646 MELEMENT(at->armour_max <= 0.,"armour_max");
647#undef MELEMENT
648
649 return 0;
650}
651
658static int asteroid_loadPLG( AsteroidType *temp, const char *buf )
659{
660 char file[PATH_MAX];
661 CollPoly *polygon;
662 xmlDocPtr doc;
663 xmlNodePtr node;
664
665 snprintf( file, sizeof(file), "%s%s.xml", ASTEROID_POLYGON_PATH, buf );
666
667 /* There is only one polygon per gfx, but it has to be added to all the other polygons */
668 /* associated to each gfx of current AsteroidType. */
669 /* In case it fails to load for some reason, its size will be set to 0. */
670 polygon = &array_grow( &temp->polygon );
671 polygon->npt = 0;
672
673 /* See if the file does exist. */
674 if (!PHYSFS_exists(file)) {
675 WARN(_("%s xml collision polygon does not exist!\n \
676 Please use the script 'polygon_from_sprite.py'\n \
677 This file can be found in Naev's artwork repo."), file);
678 return 0;
679 }
680
681 /* Load the XML. */
682 doc = xml_parsePhysFS( file );
683 if (doc == NULL)
684 return 0;
685
686 node = doc->xmlChildrenNode; /* First polygon node */
687 if (node == NULL) {
688 xmlFreeDoc(doc);
689 WARN(_("Malformed %s file: does not contain elements"), file);
690 return 0;
691 }
692
693 do { /* load the polygon data */
694 if (xml_isNode(node,"polygons")) {
695 xmlNodePtr cur = node->children;
696 do {
697 if (xml_isNode(cur,"polygon")) {
698 LoadPolygon( polygon, cur );
699 }
700 } while (xml_nextNode(cur));
701 }
702 } while (xml_nextNode(node));
703
704 xmlFreeDoc(doc);
705 return 0;
706}
707
715static int astgroup_parse( AsteroidTypeGroup *ag, const char *file )
716{
717 xmlNodePtr parent, node;
718 xmlDocPtr doc;
719
720 /* Load the data. */
721 doc = xml_parsePhysFS( file );
722 if (doc == NULL)
723 return -1;
724
725 /* Get the root node. */
726 parent = doc->xmlChildrenNode;
727 if (!xml_isNode(parent,"asteroid_group")) {
728 WARN( _("Malformed '%s' file: missing root element 'asteroid_group'"), file);
729 return -1;
730 }
731
732 /* Set up the element. */
733 memset( ag, 0, sizeof(AsteroidTypeGroup) );
735 ag->weights = array_create( double );
736
737 xmlr_attr_strd(parent,"name",ag->name);
738 if (ag->name == NULL)
739 WARN(_("Asteroid '%s' has invalid or no name"), file);
740
741 node = parent->xmlChildrenNode;
742 do {
743 /* Only handle nodes. */
744 xml_onlyNodes(node);
745
746 if (xml_isNode(node,"type")) {
747 double w;
748 xmlr_attr_float_def(node, "weight", w, 1.);
749 AsteroidType *at = asttype_getName( xml_get(node) );
750 array_push_back( &ag->types, at );
751 array_push_back( &ag->weights, w );
752 ag->wtotal += w;
753 continue;
754 }
755 WARN(_("Asteroid Type Group '%s' has unknown node '%s'"), ag->name, node->name);
756 } while (xml_nextNode(node));
757
758 /* Clean up. */
759 xmlFreeDoc(doc);
760
761 return 0;
762}
763
768{
769 double cx, cy;
770 cam_getPos( &cx, &cy );
771 cx -= SCREEN_W/2.;
772 cy -= SCREEN_H/2.;
773
774 /* Render the debris. */
775 for (int j=0; j<array_size(debris_stack); j++) {
776 Debris *d = &debris_stack[j];
777 if (d->height > 1.)
778 debris_renderSingle( d, cx, cy );
779 }
780}
781
786{
787 double cx, cy;
788 cam_getPos( &cx, &cy );
789 cx -= SCREEN_W/2.;
790 cy -= SCREEN_H/2.;
791
792 /* Render the asteroids & debris. */
793 for (int i=0; i<array_size(cur_system->asteroids); i++) {
794 AsteroidAnchor *ast = &cur_system->asteroids[i];
795 for (int j=0; j<ast->nb; j++)
797 }
798
799 /* Render the debris. */
800 for (int j=0; j<array_size(debris_stack); j++) {
801 Debris *d = &debris_stack[j];
802 if (d->height <= 1.)
803 debris_renderSingle( d, cx, cy );
804 }
805
806 /* Render gatherable stuff. */
808}
809
813static void asteroid_renderSingle( const Asteroid *a )
814{
815 double nx, ny;
816 const AsteroidType *at;
817 glColour col;
818 double progress;
819 const glColour darkcol = cGrey20;
820
821 /* Skip invisible asteroids */
822 if (a->state == ASTEROID_XX)
823 return;
824
825 progress = a->timer / a->timer_max;
826 switch (a->state) {
827 case ASTEROID_XX_TO_BG:
828 col = darkcol;
829 col.a = 1.-progress;
830 break;
831 case ASTEROID_XB:
832 case ASTEROID_BX:
833 col = darkcol;
834 break;
835 case ASTEROID_BG_TO_FG:
836 col_blend( &col, &darkcol, &cWhite, progress );
837 break;
838 case ASTEROID_FG:
839 col = cWhite;
840 break;
841 case ASTEROID_FG_TO_BG:
842 col_blend( &col, &cWhite, &darkcol, progress );
843 break;
844 case ASTEROID_BG_TO_XX:
845 col = darkcol;
846 col.a = progress;
847 break;
848
849 default:
850 break;
851 }
852
853 at = a->type;
854 gl_renderSpriteRotate( a->gfx, a->pos.x, a->pos.y, a->ang, 0, 0, &col );
855
856 /* Add the commodities if scanned. */
857 if (!a->scanned)
858 return;
859 col = cFontWhite;
860 col.a = a->scan_alpha;
861 gl_gameToScreenCoords( &nx, &ny, a->pos.x, a->pos.y );
862 gl_printRaw( &gl_smallFont, nx+a->gfx->sw/2, ny-gl_smallFont.h/2, &col, -1., _(at->scanned_msg) );
863 /*
864 for (int i=0; i<array_size(at->material); i++) {
865 AsteroidReward *mat = &at->material[i];
866 Commodity *com = mat->material;
867 if (com->gfx_space!=NULL)
868 gl_renderSprite( com->gfx_space, a->pos.x, a->pos.y-10.*i, 0, 0, NULL );
869 snprintf(c, sizeof(c), "x%i", mat->quantity);
870 gl_printRaw( &gl_smallFont, nx+10, ny-5-10.*i, &cFontWhite, -1., c );
871 }
872 */
873}
874
878static void debris_renderSingle( const Debris *d, double cx, double cy )
879{
880 const double scale = 0.5;
881 const glColour col = COL_ALPHA( cInert, d->alpha );
882
883 gl_renderSpriteScaleRotate( d->gfx, d->pos.x+cx, d->pos.y+cy, scale, scale, d->ang, 0, 0, &col );
884}
885
892{
893 free(ast->label);
894 free(ast->asteroids);
895 array_free(ast->groups);
896 array_free(ast->groupsw);
897}
898
902void asteroids_free (void)
903{
904 /* Free asteroid graphics. */
905 for (int i=0; i<array_size(asteroid_gfx); i++)
909
910 /* Free the asteroid types. */
911 for (int i=0; i<array_size(asteroid_types); i++) {
913 free(at->name);
914 free(at->scanned_msg);
915 array_free(at->material);
916 for (int j=0; j<array_size(at->gfxs); j++)
917 gl_freeTexture(at->gfxs[j]);
918 array_free(at->gfxs);
919
920 /* Free collision polygons. */
921 for (int j=0; j<array_size(at->polygon); j++) {
922 free(at->polygon[j].x);
923 free(at->polygon[j].y);
924 }
925 array_free(at->polygon);
926 }
928 asteroid_types = NULL;
929
930 /* Free the asteroid groups. */
931 for (int i=0; i<array_size(asteroid_groups); i++) {
933 free(ag->name);
934 array_free(ag->types);
935 array_free(ag->weights);
936 }
938 asteroid_groups = NULL;
939
940 /* Clean up debris. */
942 debris_stack = NULL;
943
944 /* Free the gatherable stack. */
946}
947
954int asteroids_inField( const vec2 *p )
955{
956 /* Always return -1 if in an exclusion zone */
957 for (int i=0; i<array_size(cur_system->astexclude); i++) {
958 AsteroidExclusion *e = &cur_system->astexclude[i];
959 if (vec2_dist2( p, &e->pos ) <= pow2(e->radius))
960 return -1;
961 }
962
963 /* Check if in asteroid field */
964 for (int i=0; i<array_size(cur_system->asteroids); i++) {
965 AsteroidAnchor *a = &cur_system->asteroids[i];
966 if (vec2_dist2( p, &a->pos ) <= pow2(a->radius))
967 return i;
968 }
969
970 return -1;
971}
972
979{
980 return asteroid_types;
981}
982
989AsteroidType *asttype_getName( const char *name )
990{
991 const AsteroidType q = { .name=(char*)name };
993 if (at == NULL)
994 WARN(_("Unknown Asteroid Type '%s'"),name);
995 return at;
996}
997
1004{
1005 return asteroid_groups;
1006}
1007
1015{
1016 const AsteroidTypeGroup q = { .name=(char*)name };
1018 if (ag == NULL)
1019 WARN(_("Unknown Asteroid Type Group '%s'"),name);
1020 return ag;
1021}
1022
1031void asteroid_hit( Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus )
1032{
1033 double darmour;
1034 double absorb = 1. - CLAMP( 0., 1., a->type->absorb - dmg->penetration );
1035 dtype_calcDamage( NULL, &darmour, absorb, NULL, dmg, NULL );
1036
1037 a->armour -= darmour;
1038 if (a->armour <= 0)
1039 asteroid_explode( a, max_rarity, mining_bonus );
1040}
1041
1049void asteroid_explode( Asteroid *a, int max_rarity, double mining_bonus )
1050{
1051 Damage dmg;
1052 char buf[16];
1053 double rad2;
1054 LuaAsteroid_t la;
1055 const AsteroidType *at = a->type;
1056 AsteroidAnchor *field = &cur_system->asteroids[a->parent];
1057 Pilot *const* pilot_stack = pilot_getAll();
1058
1059 /* Manage the explosion */
1060 dmg.type = dtype_get("explosion_splash");
1061 dmg.damage = at->damage;
1062 dmg.penetration = at->penetration; /* Full penetration. */
1063 dmg.disable = 0.;
1064 expl_explode( a->pos.x, a->pos.y, a->vel.x, a->vel.y,
1065 at->exp_radius, &dmg, NULL, EXPL_MODE_SHIP );
1066
1067 /* Play random explosion sound. */
1068 snprintf(buf, sizeof(buf), "explosion%d", RNG(0,2));
1069 sound_playPos( sound_get(buf), a->pos.x, a->pos.y, a->vel.x, a->vel.y );
1070
1071 /* Alert nearby pilots. */
1072 rad2 = pow2( at->alert_range );
1073 la.parent = a->parent;
1074 la.id = a->id;
1075 lua_pushasteroid( naevL, la );
1076 for (int i=0; i<array_size(pilot_stack); i++) {
1077 Pilot *p = pilot_stack[i];
1078
1079 if (vec2_dist2( &p->solid->pos, &a->pos ) > rad2)
1080 continue;
1081
1082 pilot_msg( NULL, p, "asteroid", -1 );
1083 }
1084 lua_pop(naevL,1);
1085
1086 /* Release commodity rewards. */
1087 if (max_rarity >= 0) {
1088 double prob, accum;
1089 int ndrops = 0;
1090 for (int i=0; i<array_size(at->material); i++) {
1091 AsteroidReward *mat = &at->material[i];
1092 if (mat->rarity > max_rarity)
1093 continue;
1094 ndrops++;
1095 }
1096 if (ndrops > 0) {
1097 double r = RNGF();
1098 prob = 1./(double)ndrops;
1099 accum = 0.;
1100 for (int i=0; i<array_size(at->material); i++) {
1101 AsteroidReward *mat = &at->material[i];
1102 if (mat->rarity > max_rarity)
1103 continue;
1104 accum += prob;
1105 if (r > accum)
1106 continue;
1107
1108 int nb = RNG(0, round((double)mat->quantity * mining_bonus));
1109 for (int j=0; j<nb; j++) {
1110 vec2 pos, vel;
1111 pos = a->pos;
1112 vel = a->vel;
1113 pos.x += (RNGF()*30.-15.);
1114 pos.y += (RNGF()*30.-15.);
1115 vel.x += (RNGF()*20.-10.);
1116 vel.y += (RNGF()*20.-10.);
1117 gatherable_init( mat->material, pos, vel, -1., RNG(1,5), 0 );
1118 }
1119 break;
1120 }
1121 }
1122 }
1123
1124 /* Remove the asteroid target to any pilot. */
1125 pilot_untargetAsteroid( a->parent, a->id );
1126
1127 /* Make it respawn elsewhere */
1128 asteroid_init( a, field );
1129 a->state = ASTEROID_BG_TO_XX;
1130 a->timer_max = a->timer = 0.5;
1131}
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:785
static int astgroup_parse(AsteroidTypeGroup *ag, const char *file)
Parses an asteroid type group from a file.
Definition: asteroid.c:715
void asteroids_computeInternals(AsteroidAnchor *a)
Updates internal alues of an asteroid field.
Definition: asteroid.c:407
static Debris * debris_stack
Definition: asteroid.c:44
static void asteroid_renderSingle(const Asteroid *a)
Renders an asteroid.
Definition: asteroid.c:813
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:902
void asteroid_hit(Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus)
Hits an asteroid.
Definition: asteroid.c:1031
const double DEBRIS_BUFFER
Definition: asteroid.c:40
int asteroids_load(void)
Loads the asteroids.
Definition: asteroid.c:429
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:878
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:989
static int astgroup_cmp(const void *p1, const void *p2)
Compares two asteroid type groups.
Definition: asteroid.c:466
const AsteroidTypeGroup * astgroup_getAll(void)
Gets all the asteroid type groups.
Definition: asteroid.c:1003
void asteroid_free(AsteroidAnchor *ast)
Frees an asteroid anchor.
Definition: asteroid.c:891
AsteroidTypeGroup * astgroup_getName(const char *name)
Gets an asteroid type group by name.
Definition: asteroid.c:1014
static int asttype_cmp(const void *p1, const void *p2)
Compares two asteroid types.
Definition: asteroid.c:455
static int asttype_parse(AsteroidType *at, const char *file)
Parses the XML of an asteroid type.
Definition: asteroid.c:545
void asteroid_explode(Asteroid *a, int max_rarity, double mining_bonus)
Makes an asteroid explode.
Definition: asteroid.c:1049
static int asttype_load(void)
Loads the asteroids types.
Definition: asteroid.c:479
static const double SCAN_FADE
Definition: asteroid.c:42
static void debris_init(Debris *deb)
Initializes a debris.
Definition: asteroid.c:382
void asteroids_init(void)
Initializes the system.
Definition: asteroid.c:238
void asteroids_renderOverlay(void)
Renders the system overlay.
Definition: asteroid.c:767
static int asteroid_init(Asteroid *ast, const AsteroidAnchor *field)
Initializes an asteroid.
Definition: asteroid.c:306
const AsteroidType * asttype_getAll(void)
Gets all the asteroid types.
Definition: asteroid.c:978
int asteroids_inField(const vec2 *p)
See if the position is in an asteroid field.
Definition: asteroid.c:954
void asteroids_update(double dt)
Controls fleet spawning.
Definition: asteroid.c:74
static int asteroid_loadPLG(AsteroidType *temp, const char *buf)
Loads the collision polygon for an asteroid type.
Definition: asteroid.c:658
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
void LoadPolygon(CollPoly *polygon, xmlNodePtr node)
Loads a polygon from an xml node.
Definition: collision.c:32
void col_blend(glColour *blend, const glColour *fg, const glColour *bg, float alpha)
Blends two colours.
Definition: colour.c:192
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:257
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition: damagetype.c:144
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:616
void gatherable_free(void)
Frees all the gatherables.
Definition: gatherable.c:99
int gatherable_init(const Commodity *com, vec2 pos, vec2 vel, double lifeleng, int qtt, unsigned int player_only)
Initializes a gatherable object.
Definition: gatherable.c:52
void gatherable_render(void)
Renders all the gatherables.
Definition: gatherable.c:107
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:366
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
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:570
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition: opengl_tex.c:755
void pilot_msg(Pilot *p, Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition: pilot.c:3941
static Pilot ** pilot_stack
Definition: pilot.c:57
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition: pilot.c:83
void pilot_untargetAsteroid(int anchor, int asteroid)
Loops over pilot stack to remove an asteroid as target.
Definition: pilot.c:2867
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:835
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition: sound.c:764
StarSystem * cur_system
Definition: space.c:105
int space_isSimulation(void)
returns whether we're just simulating.
Definition: space.c:1482
Represents an asteroid field anchor.
Definition: asteroid.h:100
double radius
Definition: asteroid.h:107
double density
Definition: asteroid.h:104
double maxspin
Definition: asteroid.h:113
char * label
Definition: asteroid.h:101
double * groupsw
Definition: asteroid.h:110
double thrust
Definition: asteroid.h:114
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:121
Represents a potential reward from the asteroid.
Definition: asteroid.h:38
Commodity * material
Definition: asteroid.h:39
Represents a group of asteroids.
Definition: asteroid.h:66
double * weights
Definition: asteroid.h:69
AsteroidType ** types
Definition: asteroid.h:68
Represents a type of asteroid.
Definition: asteroid.h:47
double exp_radius
Definition: asteroid.h:59
double penetration
Definition: asteroid.h:58
double alert_range
Definition: asteroid.h:60
double armour_max
Definition: asteroid.h:54
CollPoly * polygon
Definition: asteroid.h:51
double armour_min
Definition: asteroid.h:53
double damage
Definition: asteroid.h:56
char * name
Definition: asteroid.h:48
AsteroidReward * material
Definition: asteroid.h:52
double absorb
Definition: asteroid.h:55
char * scanned_msg
Definition: asteroid.h:49
double disable
Definition: asteroid.h:57
glTexture ** gfxs
Definition: asteroid.h:50
Represents a single asteroid.
Definition: asteroid.h:76
CollPoly * polygon
Definition: asteroid.h:83
int id
Definition: asteroid.h:78
double timer
Definition: asteroid.h:91
int state
Definition: asteroid.h:80
vec2 vel
Definition: asteroid.h:87
double timer_max
Definition: asteroid.h:92
int parent
Definition: asteroid.h:79
double armour
Definition: asteroid.h:84
const glTexture * gfx
Definition: asteroid.h:82
vec2 pos
Definition: asteroid.h:86
int scanned
Definition: asteroid.h:94
const AsteroidType * type
Definition: asteroid.h:81
double ang
Definition: asteroid.h:88
double spin
Definition: asteroid.h:89
Represents a polygon used for collision detection.
Definition: collision.h:13
float * x
Definition: collision.h:14
float * y
Definition: collision.h:15
int npt
Definition: collision.h:20
glTexture * gfx_space
Definition: commodity.h:54
Core damage that an outfit does.
Definition: outfit.h:111
int type
Definition: outfit.h:112
double disable
Definition: outfit.h:115
double penetration
Definition: outfit.h:113
double damage
Definition: outfit.h:114
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:210
int h
Definition: font.h:18
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33