naev 0.11.5
weapon.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
13#include <math.h>
14#include <stdlib.h>
15
16#include "naev.h"
19#include "weapon.h"
20
21#include "array.h"
22#include "ai.h"
23#include "camera.h"
24#include "collision.h"
25#include "explosion.h"
26#include "gui.h"
27#include "log.h"
28#include "nstring.h"
29#include "nlua_pilot.h"
30#include "nlua_vec2.h"
31#include "nlua_outfit.h"
32#include "opengl.h"
33#include "pilot.h"
34#include "player.h"
35#include "rng.h"
36#include "spfx.h"
37#include "intlist.h"
38
42typedef struct WeaponCollision_ {
43 const Weapon *w;
44 const OutfitGFX *gfx;
45 int beam;
46 double range;
47 double beamrange;
51
55typedef struct WeaponHit_ {
56 TargetType type; /* Class of object hit. */
57 union {
58 Pilot *plt; /* Hit a pilot. */
59 Asteroid *ast; /* Hit an asteroid. */
60 Weapon *wpn; /* Hit a weapon. */
61 } u;
62 const vec2 *pos; /* Location of the hit, can be 2d array in the case of beams. */
63} WeaponHit;
64
65/* Weapon layers. */
66static Weapon* weapon_stack = NULL;
68/* Graphics. */
69static gl_vbo *weapon_vbo = NULL;
70static GLfloat *weapon_vboData = NULL;
71static size_t weapon_vboSize = 0;
73/* Internal stuff. */
74static unsigned int weapon_idgen = 0;
75static int qt_init = 0;
80/*
81 * Prototypes
82 */
83/* Creation. */
84static void weapon_updateVBO (void);
85static double weapon_aimTurretAngle( const Outfit *outfit, const Pilot *parent,
86 const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time );
87static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
88 const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time );
89static double weapon_aimTurretStatic( const vec2 *target_pos, const vec2 *pos, double dir, double swivel );
90static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
91 double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim );
92static void weapon_createAmmo( Weapon *w, const Outfit* outfit, double T,
93 double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim );
94static int weapon_create( Weapon *w, PilotOutfitSlot* po, const Outfit *ref,
95 double T, double dir, const vec2* pos, const vec2* vel,
96 const Pilot* parent, const Target *target, double time, int aim );
97static double weapon_computeTimes( double rdir, double rx, double ry, double dvx, double dvy, double pxv,
98 double vmin, double acc, double *tt );
99/* Updating. */
100static void weapon_render( Weapon* w, double dt );
101static void weapon_updateCollide( Weapon* w, double dt );
102static void weapon_update( Weapon* w, double dt );
103static void weapon_sample_trail( Weapon* w );
104/* Destruction. */
105static void weapon_destroy( Weapon* w );
106static void weapon_free( Weapon* w );
107/* Hitting. */
108static int weapon_checkCanHit( const Weapon* w, const Pilot *p );
109static void weapon_damage( Weapon *w, const Damage *dmg );
110static void weapon_hit( Weapon *w, const WeaponHit *hit );
111static void weapon_hitBeam( Weapon* w, const WeaponHit *hit, double dt );
112static void weapon_miss( Weapon *w );
113static int weapon_testCollision( const WeaponCollision *wc, const glTexture *ctex,
114 int csx, int csy, const Solid *csol, const CollPoly *cpol, double cradius, vec2 crash[2] );
115/* think */
116static void think_seeker( Weapon* w, double dt );
117static void think_beam( Weapon* w, double dt );
118/* externed */
119void weapon_minimap( double res, double w,
120 double h, const RadarShape shape, double alpha );
121/* movement. */
122static void weapon_setAccel( Weapon *w, double accel );
123static void weapon_setTurn( Weapon *w, double turn );
124
128void weapon_init (void)
129{
131 il_create( &weapon_qtquery, 1 );
132 il_create( &weapon_qtexp, 1 );
133}
134
139{
140 return weapon_stack;
141}
142
146static void weapon_updateVBO (void)
147{
148 size_t bufsize = array_reserved(weapon_stack);
149 if (bufsize != weapon_vboSize) {
150 GLsizei size;
151 weapon_vboSize = bufsize;
152 size = sizeof(GLfloat) * (2+4) * weapon_vboSize;
153 weapon_vboData = realloc( weapon_vboData, size );
154 if (weapon_vbo == NULL)
155 weapon_vbo = gl_vboCreateStream( size, NULL );
157 }
158}
159
163static int weapon_cmp( const void *ptr1, const void *ptr2 )
164{
165 const Weapon *w1, *w2;
166 w1 = (const Weapon*) ptr1;
167 w2 = (const Weapon*) ptr2;
168 return w1->id - w2->id;
169}
170
174Weapon *weapon_getID( unsigned int id )
175{
176 const Weapon wid = { .id = id };
177 Weapon *w = bsearch( &wid, weapon_stack, array_size(weapon_stack), sizeof(Weapon), weapon_cmp );
178 if ((w==NULL) || weapon_isFlag(w,WEAPON_FLAG_DESTROYED))
179 return NULL;
180 return w;
181}
182
187{
188 double r = cur_system->radius * 1.1;
189 if (qt_init)
190 qt_destroy( &weapon_quadtree );
191 qt_create( &weapon_quadtree, -r, -r, r, r, 4, 6 ); /* TODO tune parameters. */
192 qt_init = 1;
193}
194
204void weapon_minimap( double res, double w,
205 double h, const RadarShape shape, double alpha )
206{
207 int rc, p;
208 GLsizei offset;
209
210 /* Get offset. */
211 p = 0;
212 offset = weapon_vboSize;
213
214 if (shape==RADAR_CIRCLE)
215 rc = (int)(w*w);
216 else
217 rc = 0;
218
219 /* Draw the points for weapons on all layers. */
220 /* TODO potentially do quadtree look-up. Not sure if worth it given that only
221 * weapons with health are currently added to the quadtree. */
222 for (int i=0; i<array_size(weapon_stack); i++) {
223 double x, y;
224 const glColour *c;
225 Weapon *wp = &weapon_stack[i];
226 int isplayer;
227
228 /* Make sure is in range. */
229 if (!pilot_inRange( player.p, wp->solid.pos.x, wp->solid.pos.y ))
230 continue;
231
232 /* Get radar position. */
233 x = (wp->solid.pos.x - player.p->solid.pos.x) / res;
234 y = (wp->solid.pos.y - player.p->solid.pos.y) / res;
235
236 /* Make sure in range. */
237 if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.))
238 continue;
239 if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc))
240 continue;
241
242 /* Choose colour based on if it'll hit player. */
243 isplayer = ((wp->target.type==TARGET_PILOT) && (wp->target.u.id==PLAYER_ID));
244 if ((outfit_isSeeker(wp->outfit) && !isplayer) || (wp->faction == FACTION_PLAYER))
245 c = &cNeutral;
246 else {
247 if (isplayer)
248 c = &cHostile;
249 else {
250 const Pilot *par = pilot_get(wp->parent);
251 if ((par!=NULL) && pilot_isHostile(par))
252 c = &cHostile;
253 else
254 c = &cNeutral;
255 }
256 }
257
258 /* Set the colour. */
259 weapon_vboData[ offset + 4*p + 0 ] = c->r;
260 weapon_vboData[ offset + 4*p + 1 ] = c->g;
261
262 weapon_vboData[ offset + 4*p + 2 ] = c->b;
263 weapon_vboData[ offset + 4*p + 3 ] = alpha;
264
265 /* Put the pixel. */
266 weapon_vboData[ 2*p + 0 ] = x;
267 weapon_vboData[ 2*p + 1 ] = y;
268
269 /* "Add" pixel. */
270 p++;
271 }
272
273 /* Only render with something to draw. */
274 if (p > 0) {
275 /* Upload data changes. */
276 gl_vboSubData( weapon_vbo, 0, sizeof(GLfloat) * 2*p, weapon_vboData );
277 gl_vboSubData( weapon_vbo, offset * sizeof(GLfloat),
278 sizeof(GLfloat) * 4*p, &weapon_vboData[offset] );
279
280 glUseProgram(shaders.points.program);
281 glEnableVertexAttribArray(shaders.points.vertex);
282 glEnableVertexAttribArray(shaders.points.vertex_colour);
283 gl_uniformMat4(shaders.points.projection, &gl_view_matrix);
284 gl_vboActivateAttribOffset( weapon_vbo, shaders.points.vertex, 0, 2, GL_FLOAT, 0 );
285 gl_vboActivateAttribOffset( weapon_vbo, shaders.points.vertex_colour, offset * sizeof(GLfloat), 4, GL_FLOAT, 0 );
286 glDrawArrays( GL_POINTS, 0, p );
287 glDisableVertexAttribArray(shaders.points.vertex);
288 glDisableVertexAttribArray(shaders.points.vertex_colour);
289 glUseProgram(0);
290 gl_checkErr();
291 }
292}
293
297static void weapon_setAccel( Weapon *w, double accel )
298{
299 w->solid.accel = accel;
300}
301
305static void weapon_setTurn( Weapon *w, double turn )
306{
307 w->solid.dir_vel = turn;
308}
309
316static void think_seeker( Weapon* w, double dt )
317{
318 const Pilot *p;
319 vec2 v;
320 double turn_max, jc, speed_mod;
321
322 if (w->target.type != TARGET_PILOT)
323 return; /* Ignore no targets. */
324
325 p = pilot_get(w->target.u.id); /* No null pilot */
326 if (p==NULL) {
327 weapon_setAccel( w, 0. );
328 weapon_setTurn( w, 0. );
329 return;
330 }
331
332 //ewtrack = pilot_ewWeaponTrack( pilot_get(w->parent), p, w->outfit->u.lau.resist );
333
334 /* Handle by status. */
335 switch (w->status) {
336 case WEAPON_STATUS_LOCKING: /* Check to see if we can get a lock on. */
337 w->timer2 -= dt;
338 if (w->timer2 >= 0.)
339 weapon_setAccel( w, w->outfit->u.lau.accel );
340 else
341 w->status = WEAPON_STATUS_OK; /* Weapon locked on. */
342 /* Can't get jammed while locking on. */
343 break;
344
345 case WEAPON_STATUS_OK: /* Check to see if can get jammed */
346 jc = p->stats.jam_chance - w->outfit->u.lau.resist;
347 if (jc > 0.) {
348 /* Roll based on distance. */
349 double d = vec2_dist( &p->solid.pos, &w->solid.pos );
350 if (d < w->r * p->ew_signature) {
351 if (RNGF() < jc) {
352 double r = RNGF();
353 if (r < 0.3) {
354 w->timer = -1.; /* Should blow up. */
355 w->status = WEAPON_STATUS_JAMMED;
356 }
357 else if (r < 0.6) {
358 w->status = WEAPON_STATUS_JAMMED;
359 weapon_setTurn( w, w->outfit->u.lau.turn * ((RNGF()>0.5)?-1.0:1.0) );
360 }
361 else if (r < 0.8) {
362 w->status = WEAPON_STATUS_JAMMED;
363 weapon_setTurn( w, 0. );
364 weapon_setAccel( w, w->outfit->u.lau.accel );
365 }
366 else {
367 w->status = WEAPON_STATUS_JAMMED_SLOWED;
368 w->falloff = RNGF()*0.5;
369 }
370 break;
371 }
372 else
373 w->status = WEAPON_STATUS_UNJAMMED;
374 }
375 }
376 FALLTHROUGH;
377
378 case WEAPON_STATUS_JAMMED_SLOWED: /* Slowed down. */
379 case WEAPON_STATUS_UNJAMMED: /* Work as expected */
380 turn_max = w->outfit->u.lau.turn;// * ewtrack;
381
382 /* Smart seekers take into account ship velocity. */
383 if (w->outfit->u.lau.ai == AMMO_AI_SMART) {
384 /*
385 The control interval is short enough compared to the maximum turn rate,
386 so we can use a bang-bang control.
387 */
388 vec2_csetmin( &v, p->solid.pos.x - w->solid.pos.x,
389 p->solid.pos.y - w->solid.pos.y );
390
391#define QUADRATURE(ref, v) ((v).x * (-(ref).y) + (v).y * (ref).x)
392 if (vec2_dot(&v, &w->solid.vel) < 0) {
393 /*
394 The target's behind the weapon.
395 Make U-turn.
396 */
397 if (QUADRATURE(w->solid.vel, v) > 0)
398 weapon_setTurn( w, turn_max );
399 else
400 weapon_setTurn( w, -turn_max );
401 }
402 else {
403 vec2 r_vel;
404 vec2_csetmin( &r_vel, p->solid.vel.x - w->solid.vel.x,
405 p->solid.vel.y - w->solid.vel.y);
406 if (vec2_dot(&r_vel, &w->solid.vel) > 0) {
407 /*
408 The target is going away.
409 Run parallel to the target.
410 */
411 if (QUADRATURE(w->solid.vel, p->solid.vel) > 0)
412 weapon_setTurn( w, turn_max );
413 else
414 weapon_setTurn( w, -turn_max );
415 }
416 else {
417 /*
418 Match the horizontal speed of the missile to the target's.
419 (cf. Proportional navigation)
420 It assumes that the approaching speed is a positive number.
421 */
422 if (QUADRATURE(r_vel, v) < 0)
423 weapon_setTurn( w, turn_max );
424 else
425 weapon_setTurn( w, -turn_max );
426 }
427 }
428#undef QUADRATURE
429 }
430 /* Other seekers are simplistic. */
431 else {
432 double diff = angle_diff(w->solid.dir, /* Get angle to target pos */
433 vec2_angle(&w->solid.pos, &p->solid.pos));
434 weapon_setTurn( w, CLAMP( -turn_max, turn_max,
435 10 * diff * w->outfit->u.lau.turn ));
436 }
437 break;
438
439 case WEAPON_STATUS_JAMMED: /* Continue doing whatever */
440 /* Do nothing, dir_vel should be set already if needed */
441 break;
442
443 default:
444 WARN(_("Unknown weapon status for '%s'"), w->outfit->name);
445 break;
446 }
447
448 /* Slow off based on falloff. */
449 speed_mod = (w->status==WEAPON_STATUS_JAMMED_SLOWED) ? w->falloff : 1.;
450
451 /* Limit speed here */
452 w->real_vel = MIN( speed_mod * w->outfit->u.lau.speed_max, w->real_vel + w->outfit->u.lau.accel*dt );
453 vec2_pset( &w->solid.vel, /* ewtrack * */ w->real_vel, w->solid.dir );
454
455 /* Modulate max speed. */
456 //w->solid.speed_max = w->outfit->u.lau.speed * ewtrack;
457}
458
465static void think_beam( Weapon* w, double dt )
466{
467 Pilot *p, *t;
468 Asteroid *ast;
469 double diff, mod;
470 vec2 v;
471 PilotOutfitSlot *slot;
472 unsigned int turn_off;
473
474 /* Get pilot, if pilot is dead beam is destroyed. */
475 p = pilot_get(w->parent);
476 if (p == NULL) {
477 w->timer = -1.; /* Hack to make it get destroyed next update. */
478 return;
479 }
480 slot = w->mount;
481 dt *= p->stats.time_speedup; /* Have to consider time speedup here. */
482
483 /* Check if pilot has enough energy left to keep beam active. */
484 mod = (w->outfit->type == OUTFIT_TYPE_BEAM) ? p->stats.fwd_energy : p->stats.tur_energy;
485 p->energy -= mod * dt*w->outfit->u.bem.energy;
486 pilot_heatAddSlotTime( p, slot, dt );
487 if (p->energy < 0.) {
488 p->energy = 0.;
489 w->timer = -1;
490 return;
491 }
492
493 /* Get the targets. */
494 t = NULL;
495 ast = NULL;
496 switch (w->target.type) {
497 case TARGET_PILOT:
498 t = pilot_get( w->target.u.id );
499 break;
500 case TARGET_ASTEROID:
501 {
502 const AsteroidAnchor *field = &cur_system->asteroids[ w->target.u.ast.anchor ];
503 ast = &field->asteroids[ w->target.u.ast.asteroid ];
504 }
505 break;
506 default:
507 turn_off = 1;
508 break;
509 }
510
511 /* Check the beam is still in range. */
512 if (slot->inrange) {
513 turn_off = 1;
514 if (t != NULL) {
515 if (vec2_dist( &p->solid.pos, &t->solid.pos ) <= slot->outfit->u.bem.range)
516 turn_off = 0;
517 }
518 if (ast != NULL) {
519 if (vec2_dist( &p->solid.pos, &ast->sol.pos ) <= slot->outfit->u.bem.range)
520 turn_off = 0;
521 }
522
523 /* Attempt to turn the beam off. */
524 if (turn_off) {
525 if (slot->outfit->u.bem.min_duration > 0.) {
526 slot->stimer = slot->outfit->u.bem.min_duration -
527 (slot->outfit->u.bem.duration - slot->timer);
528 if (slot->stimer > 0.)
529 turn_off = 0;
530 }
531 }
532 if (turn_off) {
533 w->timer = -1;
534 }
535 }
536
537 /* Use mount position. */
538 pilot_getMount( p, slot, &v );
539 w->solid.pos.x = p->solid.pos.x + v.x;
540 w->solid.pos.y = p->solid.pos.y + v.y;
541
542 /* Handle aiming at the target. */
543 switch (w->outfit->type) {
544 case OUTFIT_TYPE_BEAM:
545 if (w->outfit->u.bem.swivel > 0.)
546 w->solid.dir = weapon_aimTurret( w->outfit, p, &w->target, &w->solid.pos, &p->solid.vel, p->solid.dir, 0. );
547 else
548 w->solid.dir = p->solid.dir;
549 break;
550
551 case OUTFIT_TYPE_TURRET_BEAM:
552 if (!weapon_isFlag(w,WEAPON_FLAG_AIM) && pilot_isPlayer(p) && (SDL_ShowCursor(SDL_QUERY)==SDL_ENABLE)) {
553 vec2 tv;
555 diff = angle_diff(w->solid.dir, /* Get angle to target pos */
556 vec2_angle( &w->solid.pos, &tv ) );
557 }
558 /* If target is dead beam stops moving. Targeting
559 * self is invalid so in that case we ignore the target.
560 */
561 else if (t == NULL) {
562 if (ast != NULL) {
563 diff = angle_diff(w->solid.dir, /* Get angle to target pos */
564 vec2_angle( &w->solid.pos, &ast->sol.pos ));
565 }
566 else
567 diff = angle_diff(w->solid.dir, p->solid.dir);
568 }
569 else
570 diff = angle_diff(w->solid.dir, /* Get angle to target pos */
571 vec2_angle(&w->solid.pos, &t->solid.pos));
572
573 weapon_setTurn( w, p->stats.time_speedup*CLAMP( -w->outfit->u.bem.turn, w->outfit->u.bem.turn,
574 10. * diff * w->outfit->u.bem.turn ));
575 break;
576
577 default:
578 return;
579 }
580}
581
586{
587 /* Clear quadtree. */
588 qt_clear( &weapon_quadtree );
589
590 /* Actually purge and remove weapons. */
591 for (int i=array_size(weapon_stack)-1; i>=0; i--) {
592 Weapon *w = &weapon_stack[i];
593 if (!weapon_isFlag(w,WEAPON_FLAG_DESTROYED))
594 continue;
595 weapon_free( w );
597 }
598
599 /* Do a second pass to add the quadtree elements. */
600 for (int i=0; i<array_size(weapon_stack); i++) {
601 const Weapon *w = &weapon_stack[i];
602 int x,y, px,py, w2,h2;
603 const OutfitGFX *gfx;
604 double range;
605
606 if (!weapon_isFlag(w,WEAPON_FLAG_HITTABLE))
607 continue;
608
609 gfx = outfit_gfx(w->outfit);
610 if (gfx->tex != NULL)
611 range = gfx->size;
612 else
613 range = gfx->col_size;
614
615 /* Determine quadtree location, and insert. */
616 x = round(w->solid.pos.x);
617 y = round(w->solid.pos.y);
618 px = round(w->solid.pre.x);
619 py = round(w->solid.pre.y);
620 w2 = ceil(range * 0.5);
621 h2 = ceil(range * 0.5);
622 qt_insert( &weapon_quadtree, i, MIN(x,px)-w2, MIN(y,py)-h2, MAX(x,px)+w2, MAX(y,py)+h2 );
623 }
624}
625
629void weapons_updateCollide( double dt )
630{
631 for (int i=0; i<array_size(weapon_stack); i++) {
632 Weapon *w = &weapon_stack[i];
633
634 /* Ignore destroyed wapons. */
635 if (weapon_isFlag(w, WEAPON_FLAG_DESTROYED))
636 continue;
637
638 /* Handle types. */
639 switch (w->outfit->type) {
640
641 /* most missiles behave the same */
642 case OUTFIT_TYPE_LAUNCHER:
643 case OUTFIT_TYPE_TURRET_LAUNCHER:
644 w->timer -= dt;
645 if (w->timer < 0.)
646 weapon_miss(w);
647 break;
648
649 case OUTFIT_TYPE_BOLT:
650 case OUTFIT_TYPE_TURRET_BOLT:
651 w->timer -= dt;
652 if (w->timer < 0.) {
653 weapon_miss(w);
654 break;
655 }
656 else if (w->timer < w->falloff)
657 w->strength = w->timer / w->falloff * w->strength_base;
658 break;
659
660 /* Beam weapons handled a part. */
661 case OUTFIT_TYPE_BEAM:
662 case OUTFIT_TYPE_TURRET_BEAM:
663 /* Beams don't have inherent accuracy, so we use the
664 * heatAccuracyMod to modulate duration. */
665 w->timer -= dt / (1.-pilot_heatAccuracyMod(w->mount->heat_T));
666 if (w->timer < 0. || (w->outfit->u.bem.min_duration > 0. &&
667 w->mount->stimer < 0.)) {
668 const Pilot *p = pilot_get(w->parent);
669 if (p != NULL)
670 pilot_stopBeam(p, w->mount);
671 weapon_miss(w);
672 break;
673 }
674 /* We use the explosion timer to tell when we have to create explosions. */
675 w->timer2 -= dt;
676 if (w->timer2 < -1.)
677 w->timer2 = 0.100;
678 break;
679 default:
680 WARN(_("Weapon of type '%s' has no update implemented yet!"),
681 w->outfit->name);
682 break;
683 }
684
685 /* Only increment if weapon wasn't destroyed. */
686 if (!weapon_isFlag(w, WEAPON_FLAG_DESTROYED))
687 weapon_updateCollide( w, dt );
688 }
689}
690
696void weapons_update( double dt )
697{
698 for (int i=0; i<array_size(weapon_stack); i++) {
699 Weapon *w = &weapon_stack[i];
700 /* Only increment if weapon wasn't destroyed. */
701 if (!weapon_isFlag(w, WEAPON_FLAG_DESTROYED))
702 weapon_update( w, dt );
703 }
704}
705
712void weapons_render( const WeaponLayer layer, double dt )
713{
714 for (int i=0; i<array_size(weapon_stack); i++) {
715 Weapon *w = &weapon_stack[i];
716 if (w->layer==layer)
717 weapon_render( w, dt );
718 }
719}
720
721static void weapon_renderBeam( Weapon* w, double dt )
722{
723 double x, y, z;
724 mat4 projection;
725
726 /* Animation. */
727 w->anim += dt;
728
729 /* Load GLSL program */
730 glUseProgram(shaders.beam.program);
731
732 /* Zoom. */
733 z = cam_getZoom();
734
735 /* Position. */
736 gl_gameToScreenCoords( &x, &y, w->solid.pos.x, w->solid.pos.y );
737
738 projection = gl_view_matrix;
739 mat4_translate( &projection, x, y, 0. );
740 mat4_rotate2d( &projection, w->solid.dir );
741 mat4_scale( &projection, w->outfit->u.bem.range*z,w->outfit->u.bem.width * z, 1. );
742 mat4_translate( &projection, 0., -0.5, 0. );
743
744 /* Set the vertex. */
745 glEnableVertexAttribArray( shaders.beam.vertex );
746 gl_vboActivateAttribOffset( gl_squareVBO, shaders.beam.vertex,
747 0, 2, GL_FLOAT, 0 );
748
749 /* Set shader uniforms. */
750 gl_uniformMat4(shaders.beam.projection, &projection);
751 gl_uniformColour(shaders.beam.colour, &w->outfit->u.bem.colour);
752 glUniform2f(shaders.beam.dimensions, w->outfit->u.bem.range, w->outfit->u.bem.width);
753 glUniform1f(shaders.beam.dt, w->anim);
754 glUniform1f(shaders.beam.r, w->r);
755
756 /* Set the subroutine. */
757 if (gl_has( OPENGL_SUBROUTINES ))
758 glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &w->outfit->u.bem.shader );
759
760 /* Draw. */
761 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
762
763 /* Clear state. */
764 glDisableVertexAttribArray( shaders.beam.vertex );
765 glUseProgram(0);
766
767 /* anything failed? */
768 gl_checkErr();
769}
770
777static void weapon_render( Weapon* w, double dt )
778{
779 const OutfitGFX *gfx;
780 double x, y;
781 glColour col, c = { .r=1., .g=1., .b=1. };
782
783 /* Don't render destroyed weapons. */
784 if (weapon_isFlag(w,WEAPON_FLAG_DESTROYED))
785 return;
786
787 switch (w->outfit->type) {
788 /* Weapons that use sprites. */
789 case OUTFIT_TYPE_LAUNCHER:
790 case OUTFIT_TYPE_TURRET_LAUNCHER:
791 if (w->status == WEAPON_STATUS_LOCKING) {
792 double st, r, z;
793 z = cam_getZoom();
794 gl_gameToScreenCoords( &x, &y, w->solid.pos.x, w->solid.pos.y );
795 r = w->outfit->u.lau.gfx.size * z * 0.75; /* Assume square. */
796
797 st = 1. - w->timer2 / w->paramf;
798 col_blend( &col, &cYellow, &cRed, st );
799 col.a = 0.5;
800
801 glUseProgram( shaders.iflockon.program );
802 glUniform1f( shaders.iflockon.paramf, st );
803 gl_renderShader( x, y, r, r, r, &shaders.iflockon, &col, 1 );
804 }
805 FALLTHROUGH;
806 case OUTFIT_TYPE_BOLT:
807 case OUTFIT_TYPE_TURRET_BOLT:
808 gfx = outfit_gfx(w->outfit);
809
810 /* Alpha based on strength. */
811 c.a = MIN( 1., w->strength );
812
813 /* Outfit spins around. */
814 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_SPIN)) {
815
816 /* Render. */
817 if (gfx->tex != NULL) {
818 const glTexture *tex = gfx->tex;
819
820 /* Check timer. */
821 w->anim -= dt;
822 if (w->anim < 0.) {
823 w->anim = outfit_spin(w->outfit);
824
825 /* Increment sprite. */
826 w->sprite++;
827 if (w->sprite >= tex->sx*tex->sy)
828 w->sprite = 0;
829 }
830
831 if (gfx->tex_end != NULL)
833 w->timer / w->life,
834 w->solid.pos.x, w->solid.pos.y,
835 w->sprite % (int)tex->sx, w->sprite / (int)tex->sx, &c );
836 else
837 gl_renderSprite( tex, w->solid.pos.x, w->solid.pos.y,
838 w->sprite % (int)tex->sx, w->sprite / (int)tex->sx, &c );
839 }
840 }
841 /* Outfit faces direction. */
842 else {
843 /* Render. */
844 if (gfx->tex != NULL) {
845 const glTexture *tex = gfx->tex;
846 if (gfx->tex_end != NULL)
848 w->timer / w->life,
849 w->solid.pos.x, w->solid.pos.y, w->sx, w->sy, &c );
850 else
851 gl_renderSprite( tex, w->solid.pos.x, w->solid.pos.y, w->sx, w->sy, &c );
852 }
853 else {
854 double r, z;
855
856 /* Translate coords. */
857 z = cam_getZoom();
858 gl_gameToScreenCoords( &x, &y, w->solid.pos.x, w->solid.pos.y );
859
860 /* Scaled sprite dimensions. */
861 r = gfx->size*z;
862
863 /* Check if inbounds */
864 if ((x < -r) || (x > SCREEN_W+r) ||
865 (y < -r) || (y > SCREEN_H+r))
866 return;
867
868 mat4 projection = gl_view_matrix;
869 mat4_translate( &projection, x, y, 0. );
870 mat4_rotate2d( &projection, w->solid.dir );
871 mat4_scale( &projection, r, r, 1. );
872
873 glUseProgram( gfx->program );
874 glUniform2f( gfx->dimensions, r, r );
875 glUniform1f( gfx->u_r, w->r );
876 glUniform1f( gfx->u_time, w->life-w->timer );
877 glUniform1f( gfx->u_fade, MIN( 1., w->strength ) );
878 gl_uniformMat4( gfx->projection, &projection );
879
880 glEnableVertexAttribArray( gfx->vertex );
881 gl_vboActivateAttribOffset( gl_circleVBO, gfx->vertex, 0, 2, GL_FLOAT, 0 );
882
883 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
884
885 glDisableVertexAttribArray(gfx->vertex);
886 glUseProgram(0);
887 gl_checkErr();
888 }
889 }
890 break;
891
892 /* Beam weapons. */
893 case OUTFIT_TYPE_BEAM:
894 case OUTFIT_TYPE_TURRET_BEAM:
895 weapon_renderBeam(w, dt);
896 break;
897
898 default:
899 WARN(_("Weapon of type '%s' has no render implemented yet!"),
900 w->outfit->name);
901 break;
902 }
903}
904
912static int weapon_checkCanHit( const Weapon* w, const Pilot *p )
913{
914 /* Can't hit invincible stuff. */
915 if (pilot_isFlag(p, PILOT_INVINCIBLE))
916 return 0;
917
918 /* Can't hit hidden stuff. */
919 if (pilot_isFlag(p, PILOT_HIDE))
920 return 0;
921
922 /* Must not be landing nor taking off. */
923 if (pilot_isFlag(p, PILOT_LANDING) ||
924 pilot_isFlag(p, PILOT_TAKEOFF))
925 return 0;
926
927 /* Go "through" dead pilots. */
928 if (pilot_isFlag(p, PILOT_DEAD))
929 return 0;
930
931 /* Player can not hit special pilots. */
932 if ((w->faction == FACTION_PLAYER) &&
933 pilot_isFlag(p, PILOT_INVINC_PLAYER))
934 return 0;
935
936 /* Always hit target. */
937 if ((w->target.type==TARGET_PILOT) && (w->target.u.id==p->id))
938 return 1;
939
940 /* Can never hit same faction, unless explicitly targetted (see above). */
941 if (p->faction == w->faction)
942 return 0;
943
944 /* Player behaves differently. */
945 if (w->faction == FACTION_PLAYER) {
946
947 /* Always hit hostiles. */
948 if (pilot_isHostile(p))
949 return 1;
950
951 /* Miss rest - can be neutral/ally. */
952 else
953 return 0;
954 }
955
956 /* Let hostiles hit player. */
957 if (p->faction == FACTION_PLAYER) {
958 const Pilot *parent = pilot_get(w->parent);
959 if (parent != NULL) {
960 if (pilot_isHostile(parent))
961 return 1;
962 }
963 }
964
965 /* Hit non-allies. */
966 if (areEnemies(w->faction, p->faction))
967 return 1;
968
969 return 0;
970}
971
985static int weapon_testCollision( const WeaponCollision *wc, const glTexture *ctex,
986 int csx, int csy, const Solid *csol, const CollPoly *cpol, double cradius, vec2 crash[2] )
987{
988 const Weapon *w = wc->w;
989 vec2 wipos, cipos; /* Interpolated positions. */
990 const vec2 *wpos, *cpos;
991
992 /* Default to the real positions. */
993 wpos = &w->solid.pos;
994 cpos = &csol->pos;
995
996 /* Correct position if possible.
997 * This is basically looking at both line segments from pos+vel*t and then
998 * writing the distance. The resulting equation can be minimized to find the
999 * time of the closest intersection. */
1000 if (!wc->explosion && !wc->beam) {
1001 /* Don't consider the real velocity, just the linear interpolation of the
1002 * previous positions. Thus time becomes a [0,1] value. */
1003 double vx1 = w->solid.pre.x - w->solid.pos.x;
1004 double vy1 = w->solid.pre.y - w->solid.pos.y;
1005 double vx2 = csol->pre.x - csol->pos.x;
1006 double vy2 = csol->pre.y - csol->pos.y;
1007 double b = vx1 - vx2;
1008 double d = vy1 - vy2;
1009
1010 /* Make sure we have to do a correction. */
1011 if ((fabs(b)>1e-5) || (fabs(d)>1e-5)) {
1012 double a = w->solid.pos.x - csol->pos.x;
1013 double c = w->solid.pos.y - csol->pos.y;
1014 double t = CLAMP( 0., 1., -(a*b+c*d) / (b*b+d*d) );
1015 /* Now we can update the position to the minimum. */
1016 wipos.x = w->solid.pos.x + t*vx1;
1017 wipos.y = w->solid.pos.y + t*vy1;
1018 cipos.x = csol->pos.x + t*vx2;
1019 cipos.y = csol->pos.y + t*vy2;
1020 wpos = &wipos;
1021 cpos = &cipos;
1022 }
1023 }
1024
1025 if (wc->beam) {
1026 int ret, pll;
1027 /* Set up variables so we can use the equations from CollideLineLine as is.
1028 * Main idea is to just do a line-line collision*/
1029 double s1x = csol->pos.x;
1030 double s1y = csol->pos.y;
1031 double e1x = csol->pre.x;
1032 double e1y = csol->pre.y;
1033 double s2x = w->solid.pos.x;
1034 double s2y = w->solid.pos.y;
1035 double e2x = w->solid.pos.x + cos(w->solid.dir) * wc->beamrange;
1036 double e2y = w->solid.pos.y + sin(w->solid.dir) * wc->beamrange;
1037
1038 /* Find intersection position. */
1039 double u_b = (e2y - s2y) * (e1x - s1x) - (e2x - s2x) * (e1y - s1y);
1040
1041 /* Only handle case not coincident or parallel. */
1042 if (fabs(u_b) > 1e-5) {
1043 double ua_t = (e2x - s2x) * (s1y - s2y) - (e2y - s2y) * (s1x - s2x);
1044
1045 /* Interested in closest point only on the csol line. */
1046 double ua = CLAMP( 0., 1., ua_t / u_b );
1047
1048 /* Nearest point on the line segment. */
1049 cipos.x = s1x + ua * (e1x-s1x);
1050 cipos.y = s1y + ua * (e1y-s1y);
1051 cpos = &cipos;
1052 pll = 0;
1053 }
1054 else
1055 pll = 1;
1056
1057 /* Now we can look at the collision at the corrected point. */
1058 if (cpol!=NULL) {
1059 int k = ctex->sx * csy + csx;
1060 ret = CollideLinePolygon( &w->solid.pos, w->solid.dir,
1061 wc->beamrange, &cpol[k], cpos, crash);
1062 }
1063 else if (ctex!=NULL) {
1064 ret = CollideLineSprite( &w->solid.pos, w->solid.dir,
1065 wc->beamrange, ctex, csx, csy, cpos, crash);
1066 }
1067 else {
1068 const vec2 endpoint = { .x=e2x, .y=e2y };
1069 ret = CollideLineCircle( &w->solid.pos, &endpoint, cpos, cradius, crash );
1070 }
1071 if (ret > 0)
1072 return ret;
1073 /* Case non-parallel lines (or 0 length lines), we just compute the nearest point. */
1074 else if (!pll) {
1075 double ub_t = (e1x - s1x) * (s1y - s2y) - (e1y - s1y) * (s1x - s2x);
1076 double ub = CLAMP( 0., 1., ub_t / u_b );
1077 wipos.x = s2x + ub * (e2x-s2x);
1078 wipos.y = s2y + ub * (e2y-s2y);
1079 wpos = &wipos;
1080 }
1081 /* Case parallel or 0 length lines, computer nearest point on beam to position. */
1082 else {
1083 double vx1 = s1x-s2x;
1084 double vy1 = s1y-s2y;
1085 double vx2 = e2x-s2x;
1086 double vy2 = e2y-s2y;
1087 double t = (vx1*vx2 + vy1*vy2) / (pow2(vx2)+pow2(vy2));
1088 t = CLAMP( 0., 1., t );
1089 wipos.x = s2x + t * vx2;
1090 wipos.y = s2y + t * vy2;
1091 wpos = &wipos;
1092 }
1093 /* Purpose fallthrough. If beam doesn't intersect with lines, we try a spherical collision.
1094 * This works because wc.range should be set up for a spherical collision using the beam width
1095 * and can use the code below. */
1096 }
1097
1098 /* Try to do polygon first. */
1099 if (cpol != NULL) {
1100 int k = ctex->sx * csy + csx;
1101 /* Case full polygon on polygon collision. */
1102 if (wc->polygon!=NULL)
1103 return CollidePolygon( &cpol[k], cpos, wc->polygon, wpos, crash );
1104 /* GFX on polygon. */
1105 else if ((wc->gfx!=NULL) && (wc->gfx->tex != NULL))
1106 return CollideSpritePolygon( &cpol[k], cpos, wc->gfx->tex, w->sx, w->sy, wpos, crash );
1107 /* Circle on polygon. */
1108 else
1109 return CollideCirclePolygon( wpos, wc->range, &cpol[k], cpos, crash );
1110 }
1111 /* Try to do texture next. */
1112 else if (ctex != NULL) {
1113 /* GFX on polygon. */
1114 if (wc->polygon!=NULL)
1115 return CollideSpritePolygon( wc->polygon, wpos, ctex, csx, csy, cpos, crash );
1116 /* Case texture on texture collision. */
1117 else if ((wc->gfx!=NULL) && (wc->gfx->tex!=NULL))
1118 return CollideSprite( wc->gfx->tex, w->sx, w->sy, wpos,
1119 ctex, csx, csy, cpos, crash );
1120 /* Case no polygon and circle collision. */
1121 else
1122 return CollideCircleSprite( wpos, wc->range, ctex, csx, csy, cpos, crash );
1123 }
1124 /* Finally radius only. */
1125 else {
1126 /* GFX on polygon. */
1127 if (wc->polygon!=NULL)
1128 return CollideSpritePolygon( wc->polygon, wpos, ctex, csx, csy, cpos, crash );
1129 /* Case texture on texture collision. */
1130 else if ((wc->gfx!=NULL) && (wc->gfx->tex!=NULL))
1131 return CollideCircleSprite( cpos, cradius, wc->gfx->tex, w->sx, w->sy, wpos, crash );
1132 /* Trivial circle on circle case. */
1133 else
1134 return CollideCircleCircle( wpos, wc->range, cpos, cradius, crash );
1135 }
1136}
1137
1144static void weapon_updateCollide( Weapon* w, double dt )
1145{
1146 vec2 crash[2];
1147 WeaponCollision wc;
1148 Pilot *const* pilot_stack = pilot_getAll();
1149 int x1, y1, x2, y2;
1150
1151 /* Get the sprite direction to speed up calculations. */
1152 wc.explosion = 0;
1153 wc.w = w;
1154 wc.beam = outfit_isBeam(w->outfit);
1155 if (!wc.beam) {
1156 int x, y, w2, h2, px, py;
1157 wc.gfx = outfit_gfx(w->outfit);
1158 if (wc.gfx->tex != NULL) {
1159 const CollPoly *plg = outfit_plg(w->outfit);
1160 if (plg!=NULL) {
1161 int n;
1162 gl_getSpriteFromDir( &w->sx, &w->sy, wc.gfx->tex, w->solid.dir );
1163 n = wc.gfx->tex->sx * w->sy + w->sx;
1164 wc.polygon = &plg[n];
1165 }
1166 else
1167 wc.polygon = NULL;
1168 wc.range = wc.gfx->size; /* Range is set to size in this case. */
1169 }
1170 else {
1171 wc.polygon = NULL;
1172 wc.range = wc.gfx->col_size;
1173 }
1174 wc.beamrange = 0.;
1175
1176 /* Determine quadtree location. */
1177 x = round(w->solid.pos.x);
1178 y = round(w->solid.pos.y);
1179 px = x+round(w->solid.pre.x);
1180 py = y+round(w->solid.pre.y);
1181 w2 = ceil(wc.range * 0.5);
1182 h2 = ceil(wc.range * 0.5);
1183 x1 = MIN(x,px)-w2;
1184 y1 = MIN(y,py)-h2;
1185 x2 = MAX(x,px)+w2;
1186 y2 = MAX(y,py)+h2;
1187 }
1188 else {
1189 Pilot *p = pilot_get( w->parent );
1190 /* Beams have to update properties as necessary. */
1191 if (p != NULL) {
1192 /* Beams need to update their properties online. */
1193 if (w->outfit->type == OUTFIT_TYPE_BEAM) {
1194 w->dam_mod = p->stats.fwd_damage;
1195 w->dam_as_dis_mod = p->stats.fwd_dam_as_dis-1.;
1196 }
1197 else {
1198 w->dam_mod = p->stats.tur_damage;
1199 w->dam_as_dis_mod = p->stats.tur_dam_as_dis-1.;
1200 }
1201 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
1202 }
1203 wc.gfx = NULL;
1204 wc.polygon = NULL;
1205 wc.range = w->outfit->u.bem.width*0.5; /* Set beam range. */
1206 wc.beamrange = w->outfit->u.bem.range; /* Set beam range. */
1207
1208 /* Determine quadtree location. */
1209 x1 = round(w->solid.pos.x);
1210 y1 = round(w->solid.pos.y);
1211 x2 = x1 + ceil( w->outfit->u.bem.range * cos(w->solid.dir) );
1212 y2 = y1 + ceil( w->outfit->u.bem.range * sin(w->solid.dir) );
1213 if (x1 > x2) {
1214 int t = x1;
1215 x1 = x2;
1216 x2 = t;
1217 }
1218 if (y1 > y2) {
1219 int t = y1;
1220 y1 = y2;
1221 y2 = t;
1222 }
1223 }
1224
1225 /* Get colliding pilots. */
1226 if (!outfit_isProp(w->outfit,OUTFIT_PROP_WEAP_MISS_SHIPS)) {
1227 pilot_collideQueryIL( &weapon_qtquery, x1, y1, x2, y2 );
1228 for (int i=0; i<il_size(&weapon_qtquery); i++) {
1229 Pilot *p = pilot_stack[ il_get( &weapon_qtquery, i, 0 ) ];
1230 WeaponHit hit;
1231
1232 /* Ignore pilots being deleted. */
1233 if (pilot_isFlag(p, PILOT_DELETE))
1234 continue;
1235
1236 /* Ignore if parent is self. */
1237 if (w->parent==p->id)
1238 continue; /* pilot is self */
1239
1240 /* Smart weapons only collide with their target */
1241 if (weapon_isSmart(w)) {
1242 int isjammed = ((w->status == WEAPON_STATUS_JAMMED) || (w->status == WEAPON_STATUS_JAMMED_SLOWED));
1243 if (!isjammed && (w->target.type==TARGET_PILOT) && (p->id != w->target.u.id))
1244 continue;
1245 }
1246
1247 /* Check if only hit target. */
1248 if (weapon_isFlag(w,WEAPON_FLAG_ONLYHITTARGET)) {
1249 if ((w->target.type==TARGET_PILOT) && (p->id != w->target.u.id))
1250 continue;
1251 }
1252
1253 /* Check to see if it can hit. */
1254 if (!weapon_checkCanHit(w,p))
1255 continue;
1256
1257 /* Test if hit. */
1258 if (!weapon_testCollision( &wc, p->ship->gfx_space, p->tsx, p->tsy,
1259 &p->solid, p->ship->polygon, 0., crash ))
1260 continue;
1261
1262 /* Handle the hit. */
1263 hit.type = TARGET_PILOT;
1264 hit.u.plt = p;
1265 hit.pos = crash;
1266 if (wc.beam)
1267 weapon_hitBeam( w, &hit, dt );
1268 /* No return because beam can still think, it's not
1269 * destroyed like the other weapons.*/
1270 else {
1271 weapon_hit( w, &hit );
1272 return; /* Weapon is destroyed. */
1273 }
1274 }
1275 }
1276
1277 /* Collide with asteroids. */
1278 if (!outfit_isProp(w->outfit,OUTFIT_PROP_WEAP_MISS_ASTEROIDS)) {
1279 for (int i=0; i<array_size(cur_system->asteroids); i++) {
1281
1282 /* Early in-range check with the asteroid field.
1283 * Since range for beam weapons is set to width, we have to use the max. */
1284 if (vec2_dist2( &w->solid.pos, &ast->pos ) >
1285 pow2( ast->radius + ast->margin + MAX(wc.range, wc.beamrange) ))
1286 continue;
1287
1288 /* Quadtree collisions. */
1289 asteroid_collideQueryIL( ast, &weapon_qtquery, x1, y1, x2, y2 );
1290 for (int j=0; j<il_size(&weapon_qtquery); j++) {
1291 Asteroid *a = &ast->asteroids[ il_get( &weapon_qtquery, j, 0 ) ];
1292 int coll;
1293 WeaponHit hit;
1294
1295 if (a->state != ASTEROID_FG)
1296 continue;
1297
1298 if (a->polygon->npt!=0) {
1299 CollPoly rpoly;
1300 RotatePolygon( &rpoly, a->polygon, (float) a->ang );
1301 coll = weapon_testCollision( &wc, a->gfx, 0, 0, &a->sol, &rpoly, 0., crash );
1302 free(rpoly.x);
1303 free(rpoly.y);
1304 }
1305 else
1306 coll = weapon_testCollision( &wc, a->gfx, 0, 0, &a->sol, NULL, 0., crash );
1307
1308 /* Missed. */
1309 if (!coll)
1310 continue;
1311
1312 /* Handle the hit. */
1313 hit.type = TARGET_ASTEROID;
1314 hit.u.ast = a;
1315 hit.pos = crash;
1316 if (wc.beam)
1317 weapon_hitBeam( w, &hit, dt );
1318 else {
1319 weapon_hit( w, &hit );
1320 return; /* Weapon is destroyed. */
1321 }
1322 }
1323 }
1324 }
1325
1326 /* Finally do a point defense test. */
1327 if (outfit_isProp( w->outfit, OUTFIT_PROP_WEAP_POINTDEFENSE )) {
1328 qt_query( &weapon_quadtree, &weapon_qtquery, x1, y1, x2, y2 );
1329 for (int i=0; i<il_size(&weapon_qtquery); i++) {
1330 Weapon *whit = &weapon_stack[ il_get( &weapon_qtquery, i, 0 ) ];
1331 WeaponCollision wchit;
1332 int coll;
1333 WeaponHit hit;
1334
1335 /* We can only hit ammo weapons, so no beams. */
1336 wchit.w = whit;
1337 wchit.explosion= 0;
1338 wchit.beam = 0;
1339 wchit.gfx = outfit_gfx(w->outfit);
1340 if (wchit.gfx->tex != NULL) {
1341 gl_getSpriteFromDir( &whit->sx, &whit->sy, wchit.gfx->tex, w->solid.dir );
1342 wchit.polygon = outfit_plg(w->outfit);
1343 wchit.range = wchit.gfx->size; /* Range is set to size in this case. */
1344 }
1345 else {
1346 wchit.polygon = NULL;
1347 wchit.range = wchit.gfx->col_size;
1348 }
1349
1350 /* Do the real collision test. */
1351 coll = weapon_testCollision( &wc, wchit.gfx->tex, whit->sx, whit->sy, &whit->solid, wchit.polygon, wchit.range, crash );
1352 if (!coll)
1353 continue;
1354
1355 /* Handle the hit. */
1356 hit.type = TARGET_WEAPON;
1357 hit.u.wpn = whit;
1358 hit.pos = crash;
1359 if (wc.beam)
1360 weapon_hitBeam( w, &hit, dt );
1361 else {
1362 weapon_hit( w, &hit );
1363 return; /* Weapon is destroyed. */
1364 }
1365 }
1366 }
1367}
1368
1375static void weapon_update( Weapon* w, double dt )
1376{
1377 /* Smart weapons also get to think their next move */
1378 if (w->think!=NULL)
1379 (*w->think)( w, dt );
1380
1381 /* Update the solid position. */
1382 (*w->solid.update)( &w->solid, dt );
1383
1384 /* Update the sound. */
1385 sound_updatePos(w->voice, w->solid.pos.x, w->solid.pos.y,
1386 w->solid.vel.x, w->solid.vel.y);
1387
1388 /* Update the trail. */
1389 if (w->trail != NULL)
1391}
1392
1397{
1398 double a, dx, dy;
1399 TrailMode mode;
1400
1401 if (!space_needsEffects())
1402 return;
1403
1404 /* Compute the engine offset. */
1405 a = w->solid.dir;
1406 dx = w->outfit->u.lau.trail_x_offset * cos(a);
1407 dy = w->outfit->u.lau.trail_x_offset * sin(a);
1408
1409 /* Set the colour. */
1410 if ((w->outfit->u.lau.ai == AMMO_AI_UNGUIDED) ||
1411 w->solid.vel.x*w->solid.vel.x + w->solid.vel.y*w->solid.vel.y + 1.
1412 < w->solid.speed_max*w->solid.speed_max)
1413 mode = MODE_AFTERBURN;
1414 else if (w->solid.dir_vel != 0.)
1415 mode = MODE_GLOW;
1416 else
1417 mode = MODE_IDLE;
1418
1419 spfx_trail_sample( w->trail, w->solid.pos.x + dx, w->solid.pos.y + dy*M_SQRT1_2, mode, 0 );
1420}
1421
1429void weapon_hitAI( Pilot *p, const Pilot *shooter, double dmg )
1430{
1431 /* Must be a valid shooter. */
1432 if (shooter == NULL)
1433 return;
1434
1435 /* Only care about actual damage. */
1436 if (dmg <= 0.)
1437 return;
1438
1439 /* Must not be disabled. */
1440 if (pilot_isDisabled(p))
1441 return;
1442
1443 /* Must not be deleting. */
1444 if (pilot_isFlag(p, PILOT_DELETE) || pilot_isFlag(p, PILOT_DEAD) || pilot_isFlag( p, PILOT_HIDE ))
1445 return;
1446
1447 /* Player is handled differently. */
1448 if (shooter->faction == FACTION_PLAYER) {
1449 /* Increment damage done to by player. */
1450 p->player_damage += dmg / (p->shield_max + p->armour_max);
1451
1452 /* If damage is over threshold, inform pilot or if is targeted. */
1453 if ((p->player_damage > PILOT_HOSTILE_THRESHOLD) ||
1454 (shooter->target==p->id)) {
1455 /* Inform attacked. */
1457 ai_attacked( p, shooter->id, dmg );
1458 }
1459 }
1460 /* Otherwise just inform of being attacked. */
1461 else
1462 ai_attacked( p, shooter->id, dmg );
1463}
1464
1472static void weapon_hitExplode( Weapon *w, const Damage *dmg, double radius )
1473{
1474 int x, y, r, x1, y1, x2, y2;
1475 Pilot *parent = pilot_get( w->parent );
1476 WeaponCollision wc;
1477
1478 /* Circle explosion. */
1479 wc.w = w;
1480 wc.gfx = NULL;
1481 wc.beam = 0;
1482 wc.range = radius;
1483 wc.polygon = NULL;
1484 wc.explosion = 1;
1485
1486 /* Set up coordinates. */
1487 x = round(w->solid.pos.x);
1488 y = round(w->solid.pos.y);
1489 r = ceil(radius);
1490 x1 = x-r;
1491 y1 = y-r;
1492 x2 = x+r;
1493 y2 = y+r;
1494
1495 /* Test pilots. */
1496 if (!outfit_isProp(w->outfit,OUTFIT_PROP_WEAP_MISS_SHIPS)) {
1497 Pilot *const* pilot_stack = pilot_getAll();
1498 pilot_collideQueryIL( &weapon_qtexp, x1, y1, x2, y2 );
1499 for (int i=0; i<il_size(&weapon_qtexp); i++) {
1500 vec2 crash[2];
1501 double damage;
1502 Pilot *p = pilot_stack[ il_get( &weapon_qtexp, i, 0 ) ];
1503
1504 /* Ignore pilots being deleted. */
1505 if (pilot_isFlag(p, PILOT_DELETE))
1506 continue;
1507
1508 if (!outfit_isProp( w->outfit, OUTFIT_PROP_WEAP_FRIENDLYFIRE )) {
1509 /* Ignore if parent is self. */
1510 if (w->parent == p->id)
1511 continue; /* pilot is self */
1512
1513 /* Check to see if it can hit. */
1514 if (!weapon_checkCanHit( w, p ))
1515 continue;
1516 }
1517
1518 /* Test if hit. */
1519 if (!weapon_testCollision( &wc, p->ship->gfx_space, p->tsx, p->tsy,
1520 &p->solid, p->ship->polygon, 0., crash ))
1521 continue;
1522
1523 /* Have pilot take damage and get real damage done. */
1524 damage = pilot_hit( p, &w->solid, parent, dmg, w->outfit, w->lua_mem, 1 );
1525 /* Inform AI that it's been hit. */
1526 weapon_hitAI( p, parent, damage );
1527 }
1528 }
1529
1530 /* Test asteroids. */
1531 if (!outfit_isProp(w->outfit,OUTFIT_PROP_WEAP_MISS_ASTEROIDS)) {
1532 double mining_bonus = (parent != NULL) ? parent->stats.mining_bonus : 1.;
1533 int mining_rarity = outfit_miningRarity( w->outfit );
1534 for (int i=0; i<array_size(cur_system->asteroids); i++) {
1536
1537 /* Early in-range check with the asteroid field. */
1538 if (vec2_dist2( &w->solid.pos, &ast->pos ) >
1539 pow2( ast->radius + ast->margin + wc.range ))
1540 continue;
1541
1542 /* Quadtree collisions. */
1543 asteroid_collideQueryIL( ast, &weapon_qtquery, x1, y1, x2, y2 );
1544 for (int j=0; j<il_size(&weapon_qtquery); j++) {
1545 Asteroid *a = &ast->asteroids[ il_get( &weapon_qtquery, j, 0 ) ];
1546 vec2 crash[2];
1547 int coll;
1548 if (a->state != ASTEROID_FG)
1549 continue;
1550
1551 if (a->polygon->npt!=0) {
1552 CollPoly rpoly;
1553 RotatePolygon( &rpoly, a->polygon, (float) a->ang );
1554 coll = weapon_testCollision( &wc, a->gfx, 0, 0, &a->sol, &rpoly, 0., crash );
1555 free(rpoly.x);
1556 free(rpoly.y);
1557 }
1558 else
1559 coll = weapon_testCollision( &wc, a->gfx, 0, 0, &a->sol, NULL, 0., crash );
1560
1561 /* Missed. */
1562 if (!coll)
1563 continue;
1564
1565 asteroid_hit( a, dmg, mining_rarity, mining_bonus );
1566 }
1567 }
1568 }
1569
1570 /* Finally do a point defense test. */
1571 if (outfit_isProp( w->outfit, OUTFIT_PROP_WEAP_POINTDEFENSE )) {
1572 qt_query( &weapon_quadtree, &weapon_qtquery, x1, y1, x2, y2 );
1573 for (int i=0; i<il_size(&weapon_qtquery); i++) {
1574 Weapon *whit = &weapon_stack[ il_get( &weapon_qtquery, i, 0 ) ];
1575 WeaponCollision wchit;
1576 vec2 crash[2];
1577 int coll;
1578
1579 wchit.gfx = outfit_gfx(w->outfit);
1580 if (wchit.gfx->tex != NULL) {
1581 const CollPoly *plg = outfit_plg(w->outfit);
1582 if (plg!=NULL) {
1583 int n;
1584 gl_getSpriteFromDir( &w->sx, &w->sy, wchit.gfx->tex, w->solid.dir );
1585 n = wchit.gfx->tex->sx * w->sy + w->sx;
1586 wchit.polygon = &plg[n];
1587 }
1588 else
1589 wchit.polygon = NULL;
1590 wchit.range = wchit.gfx->size; /* Range is set to size in this case. */
1591 }
1592 else {
1593 wchit.polygon = NULL;
1594 wchit.range = wchit.gfx->col_size;
1595 }
1596
1597 /* Actually test the collision. */
1598 coll = weapon_testCollision( &wc, wchit.gfx->tex, whit->sx, whit->sy, &whit->solid, wchit.polygon, wchit.range, crash );
1599 if (!coll)
1600 continue;
1601
1602 /* Handle the hit. */
1603 weapon_damage( whit, dmg );
1604 }
1605 }
1606}
1607
1614static void weapon_hit( Weapon *w, const WeaponHit *hit )
1615{
1616 int s;
1617 double damage, radius;
1618 Damage dmg;
1619 const Damage *odmg;
1620
1621 /* Get general details. */
1622 odmg = outfit_damage( w->outfit );
1623 damage = w->dam_mod * w->strength * odmg->damage;
1624 radius = outfit_radius( w->outfit );
1625 dmg.damage = MAX( 0., damage * (1.-w->dam_as_dis_mod) );
1626 dmg.penetration = odmg->penetration;
1627 dmg.type = odmg->type;
1628 dmg.disable = MAX( 0., w->dam_mod * w->strength * odmg->disable + damage * w->dam_as_dis_mod );
1629
1630 /* Play sound if they have it. */
1631 s = outfit_soundHit(w->outfit);
1632 if (s != -1)
1633 w->voice = sound_playPos( s,
1634 w->solid.pos.x, w->solid.pos.y,
1635 w->solid.vel.x, w->solid.vel.y );
1636
1637 /* Explosive weapons blow up and hit everything in range. */
1638 if (radius > 0.) {
1639 weapon_hitExplode( w, &dmg, radius );
1640 weapon_destroy(w);
1641 return;
1642 }
1643
1644 if (hit->type==TARGET_PILOT) {
1645 Pilot *ptarget = hit->u.plt;
1646 const Pilot *parent = pilot_get( w->parent );
1647 int spfx;
1648
1649 /* Have pilot take damage and get real damage done. */
1650 double realdmg = pilot_hit( ptarget, &w->solid, parent, &dmg, w->outfit, w->lua_mem, 1 );
1651 /* Inform AI that it's been hit. */
1652 weapon_hitAI( ptarget, parent, realdmg );
1653
1654 /* Choose spfx. */
1655 if (ptarget->shield > 0.)
1656 spfx = outfit_spfxShield(w->outfit);
1657 else
1658 spfx = outfit_spfxArmour(w->outfit);
1659 /* Add sprite, layer depends on whether player shot or not. */
1660 spfx_add( spfx, hit->pos->x, hit->pos->y,
1661 VX(ptarget->solid.vel), VY(ptarget->solid.vel),
1662 pilot_isPlayer(ptarget) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE );
1663 }
1664 else if (hit->type==TARGET_ASTEROID) {
1665 Asteroid *ast = hit->u.ast;
1666 Pilot *parent = pilot_get( w->parent );
1667 double mining_bonus = (parent != NULL) ? parent->stats.mining_bonus : 1.;
1668 int spfx = outfit_spfxArmour(w->outfit);
1669 spfx_add( spfx, hit->pos->x, hit->pos->y,
1670 VX(ast->sol.vel), VY(ast->sol.vel), SPFX_LAYER_BACK );
1671 asteroid_hit( ast, &dmg, outfit_miningRarity(w->outfit), mining_bonus );
1672 }
1673 else if (hit->type==TARGET_WEAPON) {
1674 Weapon *wpn = hit->u.wpn;
1675 int spfx = outfit_spfxArmour(w->outfit);
1676 spfx_add( spfx, hit->pos->x, hit->pos->y,
1677 VX(wpn->solid.vel), VY(wpn->solid.vel),
1678 (wpn->layer==WEAPON_LAYER_FG) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE );
1679 weapon_damage( wpn, &dmg );
1680 }
1681
1682 /* no need for the weapon particle anymore */
1683 weapon_destroy(w);
1684}
1685
1691static void weapon_miss( Weapon *w )
1692{
1693 int spfx = -1;
1694
1695 /* See if we need armour death sprite. */
1696 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR))
1697 spfx = outfit_spfxArmour(w->outfit);
1698 /* See if we need shield death sprite. */
1699 else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD))
1700 spfx = outfit_spfxShield(w->outfit);
1701
1702 /* Add death sprite if needed. */
1703 if (spfx != -1) {
1704 int s;
1705 spfx_add( spfx, w->solid.pos.x, w->solid.pos.y,
1706 w->solid.vel.x, w->solid.vel.y,
1707 (w->layer==WEAPON_LAYER_FG) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE );
1708 /* Add sound if explodes and has it. */
1709 s = outfit_soundHit(w->outfit);
1710 if (s != -1)
1711 w->voice = sound_playPos(s,
1712 w->solid.pos.x,
1713 w->solid.pos.y,
1714 w->solid.vel.x,
1715 w->solid.vel.y);
1716 }
1717
1718 /* On hit weapon effects. */
1719 if (w->outfit->lua_onmiss != LUA_NOREF) {
1720 const Pilot *parent = pilot_get( w->parent );
1721
1722 lua_rawgeti(naevL, LUA_REGISTRYINDEX, w->lua_mem); /* mem */
1723 nlua_setenv(naevL, w->outfit->lua_env, "mem"); /* */
1724
1725 /* Set up the function: onmiss() */
1726 lua_rawgeti(naevL, LUA_REGISTRYINDEX, w->outfit->lua_onmiss); /* f */
1727 lua_pushpilot(naevL, (parent==NULL) ? 0 : parent->id);
1728 lua_pushvector(naevL, w->solid.pos);
1729 lua_pushvector(naevL, w->solid.vel);
1730 lua_pushoutfit(naevL, w->outfit);
1731 if (nlua_pcall( w->outfit->lua_env, 4, 0 )) { /* */
1732 WARN( _("Outfit '%s' -> '%s':\n%s"), w->outfit->name, "onmiss", lua_tostring(naevL,-1) );
1733 lua_pop(naevL, 1);
1734 }
1735 }
1736
1737 /* Explodes when it misses. */
1738 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_MISS_EXPLODE)) {
1739 double damage, radius;
1740 Damage dmg;
1741 const Damage *odmg;
1742
1743 /* Get general details. */
1744 odmg = outfit_damage( w->outfit );
1745 damage = w->dam_mod * w->strength * odmg->damage;
1746 radius = outfit_radius( w->outfit );
1747 dmg.damage = MAX( 0., damage * (1.-w->dam_as_dis_mod) );
1748 dmg.penetration = odmg->penetration;
1749 dmg.type = odmg->type;
1750 dmg.disable = MAX( 0., w->dam_mod * w->strength * odmg->disable + damage * w->dam_as_dis_mod );
1751
1752 weapon_hitExplode( w, &dmg, radius );
1753 }
1754
1755 weapon_destroy(w);
1756}
1757
1764static void weapon_damage( Weapon *w, const Damage *dmg )
1765{
1766 assert( outfit_isLauncher(w->outfit) );
1767
1768 double damage_armour;
1769 double absorb = 1. - CLAMP( 0., 1., w->outfit->u.lau.dmg_absorb - dmg->penetration );
1770
1771 dtype_calcDamage( NULL, &damage_armour, absorb, NULL, dmg, NULL );
1772 w->armour -= damage_armour + dmg->disable;
1773
1774 /* Still alive so nothing really happens. */
1775 if (w->armour > 0.)
1776 return;
1777
1778 /* Bye bye. */
1779 weapon_destroy( w );
1780}
1781
1789static void weapon_hitBeam( Weapon *w, const WeaponHit *hit, double dt )
1790{
1791 Pilot *parent;
1792 double damage, firerate, mod;
1793 Damage dmg;
1794 const Damage *odmg;
1795
1796 /* Get general details. */
1797 odmg = outfit_damage( w->outfit );
1798 parent = pilot_get( w->parent );
1799 if (w->outfit->type == OUTFIT_TYPE_TURRET_BEAM)
1800 firerate = parent->stats.tur_firerate;
1801 else
1802 firerate = parent->stats.fwd_firerate;
1803 mod = w->dam_mod * w->strength * firerate * parent->stats.time_speedup * dt;
1804 damage = odmg->damage * mod;
1805 dmg.damage = MAX( 0., damage * (1.-w->dam_as_dis_mod) );
1806 dmg.penetration = odmg->penetration;
1807 dmg.type = odmg->type;
1808 dmg.disable = MAX( 0., odmg->disable * mod + damage * w->dam_as_dis_mod );
1809
1810 if (hit->type==TARGET_PILOT) {
1811 Pilot *p = hit->u.plt;
1812
1813 /* Have pilot take damage and get real damage done. */
1814 double realdmg = pilot_hit( p, &w->solid, parent, &dmg, w->outfit, w->lua_mem, 1 );
1815
1816 /* Add sprite, layer depends on whether player shot or not. */
1817 if (w->timer2 <= 0.) {
1818 int spfx;
1819 /* Get the layer. */
1820 WeaponLayer spfx_layer = (w->layer==WEAPON_LAYER_FG) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE;
1821
1822 /* Choose spfx. */
1823 if (p->shield > 0.)
1824 spfx = outfit_spfxShield(w->outfit);
1825 else
1826 spfx = outfit_spfxArmour(w->outfit);
1827
1828 /* Add graphic. */
1829 spfx_add( spfx, hit->pos[0].x, hit->pos[0].y,
1830 VX(p->solid.vel), VY(p->solid.vel), spfx_layer );
1831 spfx_add( spfx, hit->pos[1].x, hit->pos[1].y,
1832 VX(p->solid.vel), VY(p->solid.vel), spfx_layer );
1833 w->timer2 = -2.;
1834
1835 /* Inform AI that it's been hit, to not saturate ai Lua with messages. */
1836 weapon_hitAI( p, parent, realdmg );
1837 }
1838 }
1839 else if (hit->type==TARGET_ASTEROID) {
1840 Asteroid *a = hit->u.ast;
1841 double mining_bonus = (parent != NULL) ? parent->stats.mining_bonus : 1.;
1842 asteroid_hit( a, &dmg, outfit_miningRarity(w->outfit), mining_bonus );
1843
1844 /* Add sprite. */
1845 if (w->timer2 <= 0.) {
1846 int spfx = outfit_spfxArmour(w->outfit);
1847
1848 /* Add graphic. */
1849 spfx_add( spfx, hit->pos[0].x, hit->pos[0].y,
1850 VX(a->sol.vel), VY(a->sol.vel), SPFX_LAYER_MIDDLE );
1851 spfx_add( spfx, hit->pos[1].x, hit->pos[1].y,
1852 VX(a->sol.vel), VY(a->sol.vel), SPFX_LAYER_MIDDLE );
1853 w->timer2 = -2.;
1854 }
1855 }
1856 else if (hit->type==TARGET_WEAPON) {
1857 Weapon *wpn = hit->u.wpn;
1858 weapon_damage( wpn, &dmg );
1859
1860 /* Add sprite. */
1861 if (w->timer2 <= 0.) {
1862 int spfx = outfit_spfxArmour(w->outfit);
1863 /* Get the layer. */
1864 WeaponLayer spfx_layer = (w->layer==WEAPON_LAYER_FG) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE;
1865
1866 /* Add graphic. */
1867 spfx_add( spfx, hit->pos[0].x, hit->pos[0].y,
1868 VX(wpn->solid.vel), VY(wpn->solid.vel), spfx_layer );
1869 spfx_add( spfx, hit->pos[1].x, hit->pos[1].y,
1870 VX(wpn->solid.vel), VY(wpn->solid.vel), spfx_layer );
1871 w->timer2 = -2.;
1872 }
1873 }
1874}
1875
1888int weapon_inArc( const Outfit *o, const Pilot *parent, const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time )
1889{
1890 if (outfit_isTurret(o))
1891 return 1;
1892 else if ((o->type==OUTFIT_TYPE_LAUNCHER) && (o->u.lau.ai > 0)) {
1893 /* TODO reduce code duplication here. */
1894 const vec2 *target_pos;
1895 double x, y, ang, off;
1896 switch (target->type) {
1897 case TARGET_PILOT:
1898 {
1899 const Pilot *pilot_target = pilot_get( target->u.id );
1900 if (pilot_target==NULL)
1901 return dir;
1902 target_pos = &pilot_target->solid.pos;
1903 }
1904 break;
1905
1906 case TARGET_ASTEROID:
1907 {
1908 const AsteroidAnchor *field = &cur_system->asteroids[ target->u.ast.anchor ];
1909 const Asteroid *ast = &field->asteroids[ target->u.ast.asteroid ];
1910 target_pos = &ast->sol.pos;
1911 }
1912 break;
1913
1914 case TARGET_WEAPON:
1915 {
1916 const Weapon *wtarget = weapon_getID( target->u.id );
1917 if (wtarget==NULL)
1918 return dir;
1919 target_pos = &wtarget->solid.pos;
1920 }
1921 break;
1922
1923 //case TARGET_NONE:
1924 default:
1925 return dir;
1926 }
1927 x = target_pos->x - pos->x;
1928 y = target_pos->y - pos->y;
1929 ang = ANGLE( x, y );
1930 off = angle_diff( ang, dir );
1931 if (FABS(off) <= o->u.lau.arc)
1932 return 1;
1933 return 0;
1934 }
1935 else {
1936 double swivel = outfit_swivel(o);
1937 double rdir = weapon_aimTurretAngle( o, parent, target, pos, vel, dir, time );
1938 double off = angle_diff( rdir, dir );
1939 if (FABS(off) <= swivel)
1940 return 1;
1941 return 0;
1942 }
1943}
1944
1948static double weapon_aimTurretAngle( const Outfit *outfit, const Pilot *parent,
1949 const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time )
1950{
1951 const Pilot *pilot_target = NULL;
1952 const vec2 *target_pos, *target_vel;
1953 double rx, ry, x, y, t, lead, rdir;
1954
1955 switch (target->type) {
1956 case TARGET_PILOT:
1957 pilot_target = pilot_get( target->u.id );
1958 if (pilot_target==NULL)
1959 return dir;
1960 target_pos = &pilot_target->solid.pos;
1961 target_vel = &pilot_target->solid.vel;
1962 break;
1963
1964 case TARGET_ASTEROID:
1965 {
1966 const AsteroidAnchor *field = &cur_system->asteroids[ target->u.ast.anchor ];
1967 const Asteroid *ast = &field->asteroids[ target->u.ast.asteroid ];
1968 target_pos = &ast->sol.pos;
1969 target_vel = &ast->sol.vel;
1970 }
1971 break;
1972
1973 case TARGET_WEAPON:
1974 {
1975 const Weapon *wtarget = weapon_getID( target->u.id );
1976 if (wtarget==NULL)
1977 return dir;
1978 target_pos = &wtarget->solid.pos;
1979 target_vel = &wtarget->solid.vel;
1980 }
1981 break;
1982
1983 //case TARGET_NONE:
1984 default:
1985 return dir;
1986 }
1987
1988 /* Get the vector : shooter -> target */
1989 rx = target_pos->x - pos->x;
1990 ry = target_pos->y - pos->y;
1991
1992 /* Try to predict where the enemy will be. */
1993 t = time;
1994 if (t == INFINITY) /* Postprocess (t = INFINITY means target is not hittable) */
1995 t = 0.;
1996
1997 double t_parent = t;
1998 /* Launch the missiles in the estimated direction of the target. */
1999 if (outfit_isLauncher(outfit) && outfit->u.lau.ai != AMMO_AI_UNGUIDED)
2000 t_parent = 0.;
2001
2002 /* Position is calculated on where it should be */
2003 x = (target_pos->x + target_vel->x*t) - (pos->x + vel->x*t_parent);
2004 y = (target_pos->y + target_vel->y*t) - (pos->y + vel->y*t_parent);
2005
2006 /* Compute both the angles we want. */
2007 if (pilot_target != NULL) {
2008 /* Lead angle is determined from ewarfare. */
2009 double trackmin = outfit_trackmin(outfit);
2010 double trackmax = outfit_trackmax(outfit);
2011 lead = pilot_ewWeaponTrack( parent, pilot_target, trackmin, trackmax );
2012 x = lead * x + (1.-lead) * rx;
2013 y = lead * y + (1.-lead) * ry;
2014 }
2015 else
2016 lead = 1.;
2017 rdir = ANGLE(x,y);
2018
2019 /* For unguided rockets: use a FD quasi-Newton algorithm to aim better. */
2020 if (outfit_isLauncher(outfit) && outfit->u.lau.accel > 0.) {
2021 double vmin = outfit->u.lau.speed;
2022
2023 if (vmin > 0.) {
2024 /* Get various details. */
2025 double tt, ddir, acc, pxv, ang, dvx, dvy;
2026 acc = outfit->u.lau.accel;
2027
2028 /* Get the relative velocity. */
2029 dvx = lead * (target_vel->x - vel->x);
2030 dvy = lead * (target_vel->y - vel->y);
2031
2032 /* Cross product between position and vel. */
2033 /* For having a better conditionning, ddir is adapted to the angular difference. */
2034 pxv = rx*dvy - ry*dvx;
2035 ang = atan2( pxv, rx*dvx+ry*dvy ); /* Angle between position and velocity. */
2036 if (fabs(ang + M_PI) < fabs(ang))
2037 ang += M_PI; /* Periodicity tricks. */
2038 else if (fabs(ang - M_PI) < fabs(ang))
2039 ang -= M_PI;
2040 ddir = -ang/1000.;
2041
2042 /* Iterate to correct the initial guess rdir. */
2043 /* We compute more precisely ta and tt. */
2044 /* (times for the ammo and the target to get to intersection point) */
2045 /* The aim is to nullify ta-tt. */
2046 if (fabs(ang) > 1e-7) { /* No need to iterate if it's already nearly aligned. */
2047 int niter = 5;
2048 for (int i=0; i<niter; i++) {
2049 double dtdd;
2050 double d = weapon_computeTimes( rdir, rx, ry, dvx, dvy, pxv, vmin, acc, &tt );
2051 double dd = weapon_computeTimes( rdir+ddir, rx, ry, dvx, dvy, pxv, vmin, acc, &tt );
2052
2053 /* Manage an exception (tt<0), and regular stopping condition. */
2054 /* TODO: this stopping criterion is too restrictive. */
2055 /* (for example when pos and vel are nearly aligned). */
2056 if (tt < 0. || fabs(d) < 5.)
2057 break;
2058
2059 dtdd = (dd-d)/ddir; /* Derivative of the criterion wrt. rdir. */
2060 rdir = rdir - d/dtdd; /* Update. */
2061 }
2062 }
2063 }
2064 }
2065 return rdir;
2066}
2067
2080static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
2081 const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time )
2082{
2083 double rdir, off;
2084 double swivel = outfit_swivel(outfit);
2085
2086 /* No swivel is trivial case. */
2087 if (swivel <= 0.)
2088 return dir;
2089
2090 /* Get the angle. */
2091 rdir = weapon_aimTurretAngle( outfit, parent, target, pos, vel, dir, time );
2092
2093 /* Calculate bounds. */
2094 off = angle_diff( rdir, dir );
2095 if (FABS(off) > swivel) {
2096 if (off > 0.)
2097 rdir = dir - swivel;
2098 else
2099 rdir = dir + swivel;
2100 }
2101
2102 return rdir;
2103}
2104
2113static double weapon_aimTurretStatic( const vec2 *target_pos, const vec2 *pos, double dir, double swivel )
2114{
2115 double rx, ry, rdir, off;
2116 /* Get the vector : shooter -> target */
2117 rx = target_pos->x - pos->x;
2118 ry = target_pos->y - pos->y;
2119 rdir = ANGLE(rx,ry);
2120
2121 /* Calculate bounds. */
2122 off = angle_diff( rdir, dir );
2123 if (FABS(off) > swivel) {
2124 if (off > 0.)
2125 rdir = dir - swivel;
2126 else
2127 rdir = dir + swivel;
2128 }
2129
2130 return rdir;
2131}
2132
2146static double weapon_computeTimes( double rdir, double rx, double ry, double dvx, double dvy, double pxv,
2147 double vmin, double acc, double *tt )
2148{
2149 double l, dxv, dxp, ct, st, d;
2150
2151 /* Trigonometry. */
2152 ct = cos(rdir); st = sin(rdir);
2153
2154 /* Two extra cross products. */
2155 dxv = ct*dvy - st*dvx;
2156 dxp = ct*ry - st*rx;
2157
2158 /* Compute criterion. */
2159 *tt = -dxp/dxv; /* Time to interception for target. Because triangle aera. */
2160 l = pxv/dxv; /* Length to interception for shooter. Because triangle aera. */
2161 d = .5*acc*(*tt)*(*tt) + vmin*(*tt); /* Estimate how far the projectile went. */
2162
2163 return (d-l); /* Criterion is distance of projectile to intersection when target is there. */
2164}
2165
2179static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
2180 double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim )
2181{
2182 vec2 v;
2183 double mass, rdir, acc, m;
2184 const OutfitGFX *gfx;
2185
2186 if (aim)
2187 rdir = weapon_aimTurret( outfit, parent, &w->target, pos, vel, dir, time );
2188 else {
2189 if (pilot_isPlayer(parent) && (SDL_ShowCursor(SDL_QUERY)==SDL_ENABLE)) {
2190 vec2 tv;
2192 rdir = weapon_aimTurretStatic( &tv, pos, dir, outfit->u.blt.swivel );
2193 }
2194 else
2195 rdir = dir;
2196 }
2197
2198 /* Disperse as necessary. */
2199 if (outfit->u.blt.dispersion > 0.)
2200 rdir += RNG_1SIGMA() * outfit->u.blt.dispersion;
2201
2202 /* Calculate accuracy. */
2203 acc = HEAT_WORST_ACCURACY * pilot_heatAccuracyMod( T );
2204
2205 /* Stat modifiers. */
2206 if (outfit->type == OUTFIT_TYPE_TURRET_BOLT) {
2207 w->dam_mod *= parent->stats.tur_damage;
2208 /* dam_as_dis is computed as multiplier, must be corrected. */
2209 w->dam_as_dis_mod = parent->stats.tur_dam_as_dis-1.;
2210 }
2211 else {
2212 w->dam_mod *= parent->stats.fwd_damage;
2213 /* dam_as_dis is computed as multiplier, must be corrected. */
2214 w->dam_as_dis_mod = parent->stats.fwd_dam_as_dis-1.;
2215 }
2216 /* Clamping, but might not actually be necessary if weird things want to be done. */
2217 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
2218
2219 /* Calculate direction. */
2220 rdir += RNG_2SIGMA() * acc;
2221 if (rdir < 0.)
2222 rdir += 2.*M_PI;
2223 else if (rdir >= 2.*M_PI)
2224 rdir -= 2.*M_PI;
2225
2226 mass = 1.; /* Lasers are presumed to have unitary mass, just like the real world. */
2227 v = *vel;
2228 m = outfit->u.blt.speed;
2229 if (outfit->u.blt.speed_dispersion > 0.)
2230 m += RNG_1SIGMA() * outfit->u.blt.speed_dispersion;
2231 vec2_cadd( &v, m*cos(rdir), m*sin(rdir));
2232 w->timer = outfit->u.blt.range / outfit->u.blt.speed;
2233 w->falloff = w->timer - outfit->u.blt.falloff / outfit->u.blt.speed;
2234 solid_init( &w->solid, mass, rdir, pos, &v, SOLID_UPDATE_EULER );
2235 w->voice = sound_playPos( w->outfit->u.blt.sound,
2236 w->solid.pos.x, w->solid.pos.y,
2237 w->solid.vel.x, w->solid.vel.y );
2238
2239 /* Set facing direction. */
2240 gfx = outfit_gfx( w->outfit );
2241 if (gfx->tex != NULL)
2242 gl_getSpriteFromDir( &w->sx, &w->sy, gfx->tex, w->solid.dir );
2243}
2244
2258static void weapon_createAmmo( Weapon *w, const Outfit* outfit, double T,
2259 double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim )
2260{
2261 (void) T;
2262 vec2 v;
2263 double mass, rdir, m;
2264 const OutfitGFX *gfx;
2265
2266 if (aim)
2267 rdir = weapon_aimTurret( outfit, parent, &w->target, pos, vel, dir, time );
2268 else {
2269 if (pilot_isPlayer(parent) && (SDL_ShowCursor(SDL_QUERY)==SDL_ENABLE)) {
2270 vec2 tv;
2272 rdir = weapon_aimTurretStatic( &tv, pos, dir, outfit->u.blt.swivel );
2273 }
2274 else
2275 rdir = dir;
2276 }
2277
2278 /* Disperse as necessary. */
2279 if (outfit->u.blt.dispersion > 0.)
2280 rdir += RNG_1SIGMA() * outfit->u.lau.dispersion;
2281
2282 /* Make sure angle is in range. */
2283 while (rdir < 0.)
2284 rdir += 2.*M_PI;
2285 while (rdir >= 2.*M_PI)
2286 rdir -= 2.*M_PI;
2287
2288 /* Launcher damage. */
2289 w->dam_mod *= parent->stats.launch_damage;
2290
2291 /* If accel is 0. we assume it starts out at speed. */
2292 v = *vel;
2293 m = outfit->u.lau.speed;
2294 if (outfit->u.lau.speed_dispersion > 0.)
2295 m += RNG_1SIGMA() * outfit->u.lau.speed_dispersion;
2296 vec2_cadd( &v, m * cos(rdir), m * sin(rdir) );
2297 w->real_vel = VMOD(v);
2298
2299 /* Set up ammo details. */
2300 mass = w->outfit->u.lau.ammo_mass;
2301 w->timer = w->outfit->u.lau.duration * parent->stats.launch_range;
2302 solid_init( &w->solid, mass, rdir, pos, &v, SOLID_UPDATE_EULER );
2303 if (w->outfit->u.lau.accel > 0.) {
2304 weapon_setAccel( w, w->outfit->u.lau.accel );
2305 /* Limit speed, we only relativize in the case it has accel + initial speed. */
2306 w->solid.speed_max = w->outfit->u.lau.speed_max;
2307 if (w->outfit->u.lau.speed > 0.)
2308 w->solid.speed_max = -1; /* No limit. */
2309 }
2310
2311 /* Handle health if necessary. */
2312 if (w->outfit->u.lau.armour > 0.) {
2313 w->armour = w->outfit->u.lau.armour;
2314 weapon_setFlag( w, WEAPON_FLAG_HITTABLE );
2315 }
2316
2317 /* Handle seekers. */
2318 if (w->outfit->u.lau.ai != AMMO_AI_UNGUIDED) {
2319 w->timer2 = outfit->u.lau.iflockon * parent->stats.launch_calibration;
2320 w->paramf = outfit->u.lau.iflockon * parent->stats.launch_calibration;
2321 w->status = (w->timer2 > 0.) ? WEAPON_STATUS_LOCKING : WEAPON_STATUS_OK;
2322
2323 w->think = think_seeker; /* AI is the same atm. */
2324 w->r = RNGF(); /* Used for jamming. */
2325
2326 /* If they are seeking a pilot, increment lockon counter. */
2327 if (w->target.type == TARGET_PILOT) {
2328 Pilot *pilot_target = pilot_get( w->target.u.id );
2329 if (pilot_target != NULL)
2330 pilot_target->lockons++;
2331 }
2332 }
2333 else
2334 w->status = WEAPON_STATUS_OK;
2335
2336 /* Play sound. */
2337 w->voice = sound_playPos( w->outfit->u.lau.sound,
2338 w->solid.pos.x, w->solid.pos.y,
2339 w->solid.vel.x, w->solid.vel.y );
2340
2341 /* Set facing direction. */
2342 gfx = outfit_gfx( w->outfit );
2343 if (gfx->tex != NULL)
2344 gl_getSpriteFromDir( &w->sx, &w->sy, gfx->tex, w->solid.dir );
2345
2346 /* Set up trails. */
2347 if (w->outfit->u.lau.trail_spec != NULL)
2348 w->trail = spfx_trail_create( w->outfit->u.lau.trail_spec );
2349}
2350
2367static int weapon_create( Weapon *w, PilotOutfitSlot* po, const Outfit *ref,
2368 double T, double dir, const vec2* pos, const vec2* vel,
2369 const Pilot* parent, const Target *target, double time, int aim )
2370{
2371 double mass, rdir;
2372 const Outfit *outfit = ((ref==NULL) && (po!=NULL)) ? po->outfit : ref;
2373
2374 /* Create basic features */
2375 memset( w, 0, sizeof(Weapon) );
2376 w->id = ++weapon_idgen;
2377 w->layer = (parent->id==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
2378 w->mount = po;
2379 w->dam_mod = 1.; /* Default of 100% damage. */
2380 w->dam_as_dis_mod = 0.; /* Default of 0% damage to disable. */
2381 w->faction = parent->faction; /* non-changeable */
2382 w->parent = parent->id; /* non-changeable */
2383 memcpy( &w->target, target, sizeof(Target) ); /* non-changeable */
2384 w->lua_mem = LUA_NOREF;
2385 if (po != NULL && po->lua_mem != LUA_NOREF) {
2386 lua_rawgeti(naevL, LUA_REGISTRYINDEX, po->lua_mem); /* mem */
2387 w->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX );
2388 }
2389 w->outfit = outfit; /* non-changeable */
2390 w->strength = 1.;
2391 w->strength_base = 1.;
2392 w->r = RNGF(); /* Set unique value. */
2393 /* Set flags. */
2394 if (outfit_isProp(outfit,OUTFIT_PROP_WEAP_ONLYHITTARGET))
2395 weapon_setFlag(w,WEAPON_FLAG_ONLYHITTARGET);
2396
2397 /* Inform the target. */
2398 if (!(outfit_isBeam(w->outfit)) && (w->target.type==TARGET_PILOT)) {
2399 Pilot *pilot_target = pilot_get( w->target.u.id );
2400 if (pilot_target != NULL)
2401 pilot_target->projectiles++;
2402 }
2403
2404 switch (outfit->type) {
2405
2406 /* Bolts treated together */
2407 case OUTFIT_TYPE_BOLT:
2408 case OUTFIT_TYPE_TURRET_BOLT:
2409 weapon_createBolt( w, outfit, T, dir, pos, vel, parent, time, aim );
2410 break;
2411
2412 /* Beam weapons are treated together. */
2413 case OUTFIT_TYPE_BEAM:
2414 case OUTFIT_TYPE_TURRET_BEAM:
2415 rdir = dir;
2416 if (outfit->type==OUTFIT_TYPE_TURRET_BEAM) {
2417 if (aim) {
2418 AsteroidAnchor *field;
2419 Asteroid *ast;
2420 Weapon *wtarget;
2421 weapon_setFlag(w, WEAPON_FLAG_AIM);
2422 switch (w->target.type) {
2423 case TARGET_NONE:
2424 break;
2425
2426 case TARGET_PILOT:
2427 if (w->parent != w->target.u.id) {
2428 Pilot *pilot_target = pilot_get( w->target.u.id );
2429 rdir = vec2_angle(pos, &pilot_target->solid.pos);
2430 }
2431 break;
2432
2433 case TARGET_ASTEROID:
2434 field = &cur_system->asteroids[ w->target.u.ast.anchor ];
2435 ast = &field->asteroids[ w->target.u.ast.asteroid ];
2436 rdir = vec2_angle(pos, &ast->sol.pos);
2437 break;
2438
2439 case TARGET_WEAPON:
2440 wtarget = weapon_getID( w->target.u.id );
2441 if (wtarget != NULL)
2442 rdir = vec2_angle( pos, &wtarget->solid.pos );
2443 break;
2444 }
2445 }
2446 else if (pilot_isPlayer(parent) && (SDL_ShowCursor(SDL_QUERY)==SDL_ENABLE)) {
2447 vec2 tv;
2449 rdir = vec2_angle( pos, &tv );
2450 }
2451 }
2452
2453 if (rdir < 0.)
2454 rdir += 2.*M_PI;
2455 else if (rdir >= 2.*M_PI)
2456 rdir -= 2.*M_PI;
2457
2458 mass = 1.;
2459 solid_init( &w->solid, mass, rdir, pos, vel, SOLID_UPDATE_EULER );
2460 w->think = think_beam;
2461 w->timer = outfit->u.bem.duration;
2462 w->voice = sound_playPos( w->outfit->u.bem.sound,
2463 w->solid.pos.x, w->solid.pos.y,
2464 w->solid.vel.x, w->solid.vel.y );
2465
2466 if (outfit->type == OUTFIT_TYPE_BEAM) {
2467 w->dam_mod *= parent->stats.fwd_damage;
2468 w->dam_as_dis_mod = parent->stats.fwd_dam_as_dis-1.;
2469 }
2470 else {
2471 w->dam_mod *= parent->stats.tur_damage;
2472 w->dam_as_dis_mod = parent->stats.tur_dam_as_dis-1.;
2473 }
2474 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
2475
2476 break;
2477
2478 /* Treat seekers together. */
2479 case OUTFIT_TYPE_LAUNCHER:
2480 case OUTFIT_TYPE_TURRET_LAUNCHER:
2481 weapon_createAmmo( w, outfit, T, dir, pos, vel, parent, time, aim );
2482 break;
2483
2484 /* just dump it where the player is */
2485 default:
2486 WARN(_("Weapon of type '%s' has no create implemented yet!"),
2487 w->outfit->name);
2488 solid_init( &w->solid, 1., dir, pos, vel, SOLID_UPDATE_EULER );
2489 break;
2490 }
2491
2492 /* Set life to timer. */
2493 w->life = w->timer;
2494
2495 return 0;
2496}
2497
2512 double dir, const vec2* pos, const vec2* vel,
2513 const Pilot *parent, const Target *target, double time, int aim )
2514{
2515 Weapon *w;
2516 double T = po->heat_T;
2517
2518#if DEBUGGING
2519 const Outfit *o = (ref==NULL) ? po->outfit : ref;
2520 if (!outfit_isBolt(o) &&
2521 !outfit_isLauncher(o)) {
2522 ERR(_("Trying to create a Weapon from a non-Weapon type Outfit"));
2523 return 0;
2524 }
2525#endif /* DEBUGGING */
2526
2527 w = &array_grow(&weapon_stack);
2528 weapon_create( w, po, ref, T, dir, pos, vel, parent, target, time, aim );
2529
2530 /* Grow the vertex stuff if needed. */
2532 return w;
2533}
2534
2538double weapon_targetFlyTime( const Outfit *o, const Pilot *p, const Target *t )
2539{
2540 switch (t->type) {
2541 case TARGET_NONE:
2542 return HUGE_VAL;
2543 case TARGET_PILOT:
2544 {
2545 const Pilot *pt = pilot_get( t->u.id );
2546 if (pt==NULL)
2547 return HUGE_VAL;
2548 return pilot_weapFlyTime( o, p, &pt->solid.pos, &pt->solid.vel );
2549 }
2550 break;
2551 case TARGET_WEAPON:
2552 {
2553 const Weapon *w = weapon_getID( t->u.id );
2554 if (w==NULL)
2555 return HUGE_VAL;
2556 return pilot_weapFlyTime( o, p, &w->solid.pos, &w->solid.vel );
2557 }
2558 break;
2559 case TARGET_ASTEROID:
2560 {
2561 const AsteroidAnchor *field = &cur_system->asteroids[t->u.ast.anchor];
2562 const Asteroid *ast = &field->asteroids[t->u.ast.asteroid];
2563 return pilot_weapFlyTime( o, p, &ast->sol.pos, &ast->sol.vel );
2564 }
2565 break;
2566 }
2567 return HUGE_VAL;
2568}
2569
2584unsigned int beam_start( PilotOutfitSlot *po,
2585 double dir, const vec2* pos, const vec2* vel,
2586 const Pilot *parent, const Target *target, int aim )
2587{
2588 Weapon *w;
2589
2590 if (!outfit_isBeam(po->outfit)) {
2591 ERR(_("Trying to create a Beam Weapon from a non-beam outfit."));
2592 return -1;
2593 }
2594
2595 w = &array_grow(&weapon_stack);
2596 weapon_create( w, po, NULL, 0., dir, pos, vel, parent, target, 0., aim );
2597
2598 /* Grow the vertex stuff if needed. */
2600 return w->id;
2601}
2602
2608void beam_end( unsigned int beam )
2609{
2610#if DEBUGGING
2611 if (beam==0) {
2612 WARN(_("Trying to remove beam with ID 0!"));
2613 return;
2614 }
2615#endif /* DEBUGGING */
2616
2617 /* Now try to destroy the beam. */
2618 for (int i=0; i<array_size(weapon_stack); i++) {
2619 Weapon *w = &weapon_stack[i];
2620 if (w->id == beam) { /* Found it. */
2621 weapon_miss( w );
2622 break;
2623 }
2624 }
2625}
2626
2632static void weapon_destroy( Weapon* w )
2633{
2634 /* Just mark for removal. */
2635 weapon_setFlag( w, WEAPON_FLAG_DESTROYED );
2636}
2637
2643static void weapon_free( Weapon* w )
2644{
2645 /* Stop playing sound if beam weapon. */
2646 if (outfit_isBeam(w->outfit)) {
2647 sound_stop( w->voice );
2648 sound_playPos(w->outfit->u.bem.sound_off,
2649 w->solid.pos.x, w->solid.pos.y,
2650 w->solid.vel.x, w->solid.vel.y );
2651 }
2652 else if (w->target.type==TARGET_PILOT) {
2653 Pilot *pilot_target = pilot_get( w->target.u.id );
2654
2655 /* Decrement target lockons if needed */
2656 if (pilot_target != NULL) {
2657 pilot_target->projectiles--;
2658 if (outfit_isSeeker(w->outfit))
2659 pilot_target->lockons--;
2660 }
2661 }
2662
2663 /* Free the solid. */
2664 //solid_free(w->solid);
2665
2666 /* Free the trail, if any. */
2667 spfx_trail_remove(w->trail);
2668
2669 /* Free the Lua ref, if any. */
2670 luaL_unref( naevL, LUA_REGISTRYINDEX, w->lua_mem );
2671
2672#ifdef DEBUGGING
2673 memset(w, 0, sizeof(Weapon));
2674#endif /* DEBUGGING */
2675}
2676
2680void weapon_clear (void)
2681{
2682 /* Don't forget to stop the sounds. */
2683 for (int i=0; i < array_size(weapon_stack); i++) {
2684 Weapon *w = &weapon_stack[i];
2685 sound_stop( w->voice );
2686 weapon_free( w );
2687 }
2689 /* We can restart the idgen. */
2690 weapon_idgen = 0; /* May mess up Lua stuff... */
2691}
2692
2696void weapon_exit (void)
2697{
2698 weapon_clear();
2699
2700 /* Destroy weapon stack. */
2702
2703 /* Destroy VBO. */
2704 free( weapon_vboData );
2705 weapon_vboData = NULL;
2707 weapon_vbo = NULL;
2708
2709 /* Clean up the queries. */
2710 qt_destroy( &weapon_quadtree );
2711 il_destroy( &weapon_qtquery );
2712 il_destroy( &weapon_qtexp );
2713}
2714
2715const IntList *weapon_collideQuery( int x1, int y1, int x2, int y2 )
2716{
2717 qt_query( &weapon_quadtree, &weapon_qtquery, x1, y1, x2, y2 );
2718 return &weapon_qtquery;
2719}
2720
2721void weapon_collideQueryIL( IntList *il, int x1, int y1, int x2, int y2 )
2722{
2723 qt_query( &weapon_quadtree, il, x1, y1, x2, y2 );
2724}
void ai_attacked(Pilot *attacked, const unsigned int attacker, double dmg)
Triggers the attacked() function in the pilot's AI.
Definition ai.c:911
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:202
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:119
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:194
#define array_reserved(array)
Returns number of elements reserved.
Definition array.h:187
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void asteroid_hit(Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus)
Hits an asteroid.
Definition asteroid.c:1074
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:97
int CollideCircleCircle(const vec2 *p1, double r1, const vec2 *p2, double r2, vec2 crash[2])
Computes the collision between two circles.
Definition collision.c:1054
void RotatePolygon(CollPoly *rpolygon, CollPoly *ipolygon, float theta)
Rotates a polygon.
Definition collision.c:332
int CollideLineCircle(const vec2 *p1, const vec2 *p2, const vec2 *cc, double cr, vec2 crash[2])
Checks to see if a line collides with a circle.
Definition collision.c:952
int CollideLinePolygon(const vec2 *ap, double ad, double al, const CollPoly *bt, const vec2 *bp, vec2 crash[2])
Checks to see if a line collides with a polygon.
Definition collision.c:657
int CollideLineSprite(const vec2 *ap, double ad, double al, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 crash[2])
Checks to see if a line collides with a sprite.
Definition collision.c:508
int CollideCircleSprite(const vec2 *ap, double ar, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 *crash)
Checks whether or not a sprite collides with a polygon.
Definition collision.c:868
int CollideSprite(const glTexture *at, const int asx, const int asy, const vec2 *ap, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 *crash)
Checks whether or not two sprites collide.
Definition collision.c:103
int CollideCirclePolygon(const vec2 *ap, double ar, const CollPoly *bt, const vec2 *bp, vec2 crash[2])
Checks to see if a circle collides with a polygon.
Definition collision.c:790
int CollideSpritePolygon(const CollPoly *at, const vec2 *ap, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 *crash)
Checks whether or not a sprite collides with a polygon.
Definition collision.c:185
int CollidePolygon(const CollPoly *at, const vec2 *ap, const CollPoly *bt, const vec2 *bp, vec2 *crash)
Checks whether or not two polygons collide. /!\ The function is not symmetric: the points of polygon ...
Definition collision.c:260
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:276
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1227
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition mat4.c:99
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition mat4.c:82
void mat4_rotate2d(mat4 *m, double angle)
Rotates an angle, in radians, around the z axis.
Definition mat4.c:111
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 ABS(x)
Definition naev.h:36
#define pow2(x)
Definition naev.h:46
#define FABS(x)
Definition naev.h:37
#define MAX(x, y)
Definition naev.h:39
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:563
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition nlua_vec2.c:145
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_renderSpriteInterpolate(const glTexture *sa, const glTexture *sb, double inter, double bx, double by, int sx, int sy, const glColour *c)
Blits a sprite interpolating, position is relative to the player.
void gl_renderSprite(const glTexture *sprite, double bx, double by, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player.
void gl_screenToGameCoords(double *nx, double *ny, int bx, int by)
Converts screen coordinates to in-game coordinates.
void gl_getSpriteFromDir(int *x, int *y, const glTexture *t, const double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:967
void gl_vboDestroy(gl_vbo *vbo)
Destroys a VBO.
Definition opengl_vbo.c:246
gl_vbo * gl_vboCreateStream(GLsizei size, const void *data)
Creates a stream vbo.
Definition opengl_vbo.c:145
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition opengl_vbo.c:226
void gl_vboData(gl_vbo *vbo, GLsizei size, const void *data)
Reloads new data or grows the size of the vbo.
Definition opengl_vbo.c:98
void gl_vboSubData(gl_vbo *vbo, GLint offset, GLsizei size, const void *data)
Loads some data into the VBO.
Definition opengl_vbo.c:130
double outfit_trackmin(const Outfit *o)
Gets the outfit's minimal tracking.
Definition outfit.c:848
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition outfit.c:554
int outfit_soundHit(const Outfit *o)
Gets the outfit's hit sound.
Definition outfit.c:895
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:564
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition outfit.c:574
int outfit_miningRarity(const Outfit *o)
Gets the maximum rarity the outfit can mine up to.
Definition outfit.c:872
double outfit_radius(const Outfit *o)
Gets the outfit's explosion radius.
Definition outfit.c:724
int outfit_spfxShield(const Outfit *o)
Gets the outfit's sound effect.
Definition outfit.c:702
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition outfit.c:587
const CollPoly * outfit_plg(const Outfit *o)
Gets the outfit's collision polygon.
Definition outfit.c:681
const OutfitGFX * outfit_gfx(const Outfit *o)
Gets the outfit's graphic effect.
Definition outfit.c:671
double outfit_spin(const Outfit *o)
Gets the outfit's animation spin.
Definition outfit.c:837
double outfit_trackmax(const Outfit *o)
Gets the outfit's minimal tracking.
Definition outfit.c:860
int outfit_spfxArmour(const Outfit *o)
Gets the outfit's sound effect.
Definition outfit.c:691
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition outfit.c:713
double outfit_swivel(const Outfit *o)
Gets the swivel of an outfit.
Definition outfit.c:825
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition outfit.c:544
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition pilot.c:678
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition pilot.c:620
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition pilot.c:1066
static Pilot ** pilot_stack
Definition pilot.c:61
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:94
double pilot_hit(Pilot *p, const Solid *w, const Pilot *pshooter, const Damage *dmg, const Outfit *outfit, int lua_mem, int reset)
Damages the pilot.
Definition pilot.c:1338
double pilot_ewWeaponTrack(const Pilot *p, const Pilot *t, double trackmin, double trackmax)
Calculates the weapon lead (1. is 100%, 0. is 0%)..
Definition pilot_ew.c:389
int pilot_inRange(const Pilot *p, double x, double y)
Check to see if a position is in range of the pilot.
Definition pilot_ew.c:221
double pilot_heatAccuracyMod(double T)
Returns a 0:1 modifier representing accuracy (0. being normal).
Definition pilot_heat.c:277
void pilot_heatAddSlotTime(const Pilot *p, PilotOutfitSlot *o, double dt)
Adds heat to an outfit slot over a period of time.
Definition pilot_heat.c:152
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
void pilot_stopBeam(const Pilot *p, PilotOutfitSlot *w)
Stops a beam outfit and sets delay as appropriate.
double pilot_weapFlyTime(const Outfit *o, const Pilot *parent, const vec2 *pos, const vec2 *vel)
Computes an estimation of ammo flying time.
Player_t player
Definition player.c:74
static const double c[]
Definition rng.c:264
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
void sound_stop(int voice)
Stops a voice from playing.
Definition sound.c:1074
int sound_updatePos(int voice, double px, double py, double vx, double vy)
Updates the position of a voice.
Definition sound.c:884
StarSystem * cur_system
Definition space.c:106
int space_needsEffects(void)
returns whether or not we're simulating with effects.
Definition space.c:1538
void spfx_trail_sample(Trail_spfx *trail, double x, double y, TrailMode mode, int force)
Makes a trail grow.
Definition spfx.c:756
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition spfx.c:792
void spfx_add(int effect, const double px, const double py, const double vx, const double vy, int layer)
Creates a new special effect.
Definition spfx.c:475
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition spfx.c:688
Represents an asteroid field anchor.
Definition asteroid.h:100
double margin
Definition asteroid.h:115
double radius
Definition asteroid.h:107
Asteroid * asteroids
Definition asteroid.h:105
Represents a single asteroid.
Definition asteroid.h:77
Solid sol
Definition asteroid.h:87
Represents a polygon used for collision detection.
Definition collision.h:13
float * x
Definition collision.h:14
float * y
Definition collision.h:15
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
double duration
Definition outfit.h:182
double min_duration
Definition outfit.h:183
double range
Definition outfit.h:186
double dispersion
Definition outfit.h:162
double falloff
Definition outfit.h:152
double range
Definition outfit.h:151
double swivel
Definition outfit.h:161
double speed
Definition outfit.h:150
double speed_dispersion
Definition outfit.h:163
double col_size
Definition outfit.h:132
glTexture * tex_end
Definition outfit.h:121
GLuint u_r
Definition outfit.h:128
GLuint program
Definition outfit.h:124
GLuint dimensions
Definition outfit.h:127
GLuint projection
Definition outfit.h:126
GLuint u_time
Definition outfit.h:129
glTexture * tex
Definition outfit.h:120
GLuint u_fade
Definition outfit.h:130
GLuint vertex
Definition outfit.h:125
double size
Definition outfit.h:131
double dispersion
Definition outfit.h:223
double speed_dispersion
Definition outfit.h:224
OutfitAmmoAI ai
Definition outfit.h:231
A ship outfit, depends radically on the type.
Definition outfit.h:328
OutfitLauncherData lau
Definition outfit.h:406
OutfitBeamData bem
Definition outfit.h:405
OutfitBoltData blt
Definition outfit.h:404
OutfitType type
Definition outfit.h:402
union Outfit::@12 u
Stores an outfit the pilot has.
Definition pilot.h:108
double stimer
Definition pilot.h:124
double heat_T
Definition pilot.h:117
double timer
Definition pilot.h:125
unsigned int inrange
Definition pilot.h:130
const Outfit * outfit
Definition pilot.h:112
The representation of an in-game pilot.
Definition pilot.h:217
ShipStats stats
Definition pilot.h:294
double shield
Definition pilot.h:253
unsigned int id
Definition pilot.h:218
int projectiles
Definition pilot.h:379
int lockons
Definition pilot.h:378
int faction
Definition pilot.h:222
Solid solid
Definition pilot.h:227
unsigned int target
Definition pilot.h:342
Pilot * p
Definition player.h:101
double mousex
Definition player.h:108
double mousey
Definition player.h:109
double time_speedup
Definition shipstats.h:309
double launch_damage
Definition shipstats.h:259
double fwd_dam_as_dis
Definition shipstats.h:278
double tur_dam_as_dis
Definition shipstats.h:286
double tur_damage
Definition shipstats.h:282
double fwd_damage
Definition shipstats.h:275
double tur_firerate
Definition shipstats.h:284
double mining_bonus
Definition shipstats.h:301
double launch_calibration
Definition shipstats.h:262
double launch_range
Definition shipstats.h:258
double fwd_firerate
Definition shipstats.h:276
Represents a solid in the game.
Definition physics.h:44
vec2 vel
Definition physics.h:48
vec2 pre
Definition physics.h:50
vec2 pos
Definition physics.h:49
Represents a weapon target.
Definition target.h:19
Struct useful for generalization of weapno collisions.
Definition weapon.c:42
double beamrange
Definition weapon.c:47
double range
Definition weapon.c:46
const Weapon * w
Definition weapon.c:43
const OutfitGFX * gfx
Definition weapon.c:44
const CollPoly * polygon
Definition weapon.c:48
Represents a weapon hitting something.
Definition weapon.c:55
In-game representation of a weapon.
Definition weapon.h:47
int sx
Definition weapon.h:74
unsigned int id
Definition weapon.h:51
int sy
Definition weapon.h:75
int faction
Definition weapon.h:53
WeaponLayer layer
Definition weapon.h:48
const Outfit * outfit
Definition weapon.h:56
unsigned int parent
Definition weapon.h:54
GLfloat r
Definition weapon.h:67
Target target
Definition weapon.h:55
Solid solid
Definition weapon.h:50
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:36
double sx
Definition opengl_tex.h:44
double sy
Definition opengl_tex.h:45
Definition mat4.h:10
Definition msgcat.c:199
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33
static void weapon_hitBeam(Weapon *w, const WeaponHit *hit, double dt)
A beam weapon hit something.
Definition weapon.c:1789
void weapons_update(double dt)
Updates all the weapons.
Definition weapon.c:696
static void weapon_updateVBO(void)
Checks to see if we have to update the VBO size.
Definition weapon.c:146
double weapon_targetFlyTime(const Outfit *o, const Pilot *p, const Target *t)
Gets the fly time for a weapon target.
Definition weapon.c:2538
static void weapon_sample_trail(Weapon *w)
Updates the animated trail for a weapon.
Definition weapon.c:1396
unsigned int beam_start(PilotOutfitSlot *po, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const Target *target, int aim)
Starts a beam weapon.
Definition weapon.c:2584
static int weapon_checkCanHit(const Weapon *w, const Pilot *p)
Checks to see if the weapon can hit the pilot.
Definition weapon.c:912
static unsigned int weapon_idgen
Definition weapon.c:74
Weapon * weapon_add(PilotOutfitSlot *po, const Outfit *ref, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const Target *target, double time, int aim)
Creates a new weapon.
Definition weapon.c:2511
static void weapon_destroy(Weapon *w)
Destroys a weapon.
Definition weapon.c:2632
static gl_vbo * weapon_vbo
Definition weapon.c:69
static void weapon_render(Weapon *w, double dt)
Renders an individual weapon.
Definition weapon.c:777
static double weapon_computeTimes(double rdir, double rx, double ry, double dvx, double dvy, double pxv, double vmin, double acc, double *tt)
Computes precisely interception times for propelled weapons (rockets).
Definition weapon.c:2146
static double weapon_aimTurretStatic(const vec2 *target_pos, const vec2 *pos, double dir, double swivel)
Gets the aim position of a turret weapon.
Definition weapon.c:2113
static void weapon_createBolt(Weapon *w, const Outfit *outfit, double T, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, double time, int aim)
Creates the bolt specific properties of a weapon.
Definition weapon.c:2179
Weapon * weapon_getID(unsigned int id)
Gets a weapon by ID.
Definition weapon.c:174
static double weapon_aimTurretAngle(const Outfit *outfit, const Pilot *parent, const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time)
Gets the aim direction of a turret weapon.
Definition weapon.c:1948
static void weapon_hit(Weapon *w, const WeaponHit *hit)
A bolt/launcher weapon hit something.
Definition weapon.c:1614
static void weapon_updateCollide(Weapon *w, double dt)
Updates an individual weapon.
Definition weapon.c:1144
static void weapon_setTurn(Weapon *w, double turn)
Sets the weapon's turn.
Definition weapon.c:305
static IntList weapon_qtquery
Definition weapon.c:77
static void weapon_setAccel(Weapon *w, double accel)
Sets the weapon's accel.
Definition weapon.c:297
void weapons_updatePurge(void)
Purges unnecessary weapons.
Definition weapon.c:585
void weapon_clear(void)
Clears all the weapons, does NOT free the layers.
Definition weapon.c:2680
static void weapon_createAmmo(Weapon *w, const Outfit *outfit, double T, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, double time, int aim)
Creates the ammo specific properties of a weapon.
Definition weapon.c:2258
static void weapon_damage(Weapon *w, const Damage *dmg)
Applies damage to a weapon.
Definition weapon.c:1764
void weapons_updateCollide(double dt)
Handles weapon collisions.
Definition weapon.c:629
static void weapon_update(Weapon *w, double dt)
Updates an individual weapon.
Definition weapon.c:1375
static int weapon_testCollision(const WeaponCollision *wc, const glTexture *ctex, int csx, int csy, const Solid *csol, const CollPoly *cpol, double cradius, vec2 crash[2])
Tests to see if a weapon collides with a ship.
Definition weapon.c:985
static Quadtree weapon_quadtree
Definition weapon.c:76
void weapon_hitAI(Pilot *p, const Pilot *shooter, double dmg)
Informs the AI if needed that it's been hit.
Definition weapon.c:1429
static GLfloat * weapon_vboData
Definition weapon.c:70
int weapon_inArc(const Outfit *o, const Pilot *parent, const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time)
Gets the aim position of a turret weapon.
Definition weapon.c:1888
void weapon_minimap(double res, double w, double h, const RadarShape shape, double alpha)
Draws the minimap weapons (used in player.c).
Definition weapon.c:204
Weapon * weapon_getStack(void)
Gets the weapon stack. Do not manipulate directly.
Definition weapon.c:138
static double weapon_aimTurret(const Outfit *outfit, const Pilot *parent, const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time)
Gets the aim position of a turret weapon.
Definition weapon.c:2080
static void weapon_free(Weapon *w)
Frees the weapon.
Definition weapon.c:2643
static void weapon_miss(Weapon *w)
Weapon missed and is due to be destroyed.
Definition weapon.c:1691
void weapon_exit(void)
Destroys all the weapons and frees it all.
Definition weapon.c:2696
static int weapon_cmp(const void *ptr1, const void *ptr2)
Compare id (for use with bsearch)
Definition weapon.c:163
static void think_beam(Weapon *w, double dt)
The pseudo-ai of the beam weapons.
Definition weapon.c:465
static void weapon_hitExplode(Weapon *w, const Damage *dmg, double radius)
A weapon hit something and decided to explode.
Definition weapon.c:1472
static int qt_init
Definition weapon.c:75
static IntList weapon_qtexp
Definition weapon.c:78
void weapons_render(const WeaponLayer layer, double dt)
Renders all the weapons in a layer.
Definition weapon.c:712
void weapon_init(void)
Initializes the weapon stuff.
Definition weapon.c:128
static size_t weapon_vboSize
Definition weapon.c:71
static Weapon * weapon_stack
Definition weapon.c:66
static void think_seeker(Weapon *w, double dt)
The AI of seeker missiles.
Definition weapon.c:316
static int weapon_create(Weapon *w, PilotOutfitSlot *po, const Outfit *ref, double T, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const Target *target, double time, int aim)
Creates a new weapon.
Definition weapon.c:2367
void weapon_newSystem(void)
Sets up collision stuff for a new system.
Definition weapon.c:186
void beam_end(unsigned int beam)
Ends a beam weapon.
Definition weapon.c:2608