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