naev 0.10.4
weapon.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
13#include <math.h>
14#include <stdlib.h>
15
16#include "naev.h"
19#include "weapon.h"
20
21#include "array.h"
22#include "ai.h"
23#include "camera.h"
24#include "collision.h"
25#include "explosion.h"
26#include "gui.h"
27#include "log.h"
28#include "nstring.h"
29#include "nlua_pilot.h"
30#include "nlua_vec2.h"
31#include "opengl.h"
32#include "pilot.h"
33#include "player.h"
34#include "rng.h"
35#include "spfx.h"
36
37#define weapon_isSmart(w) (w->think != NULL)
39/* Weapon status */
40typedef enum WeaponStatus_ {
47
48/* Weapon flags. */
49#define WEAPON_FLAG_DESTROYED 1
50#define weapon_isFlag(w,f) ((w)->flags & (f))
51#define weapon_setFlag(w,f) ((w)->flags |= (f))
52#define weapon_rmFlag(w,f) ((w)->flags &= ~(f))
53
59typedef struct Weapon_ {
60 unsigned int flags;
62 unsigned int ID;
64 int faction;
65 unsigned int parent;
66 unsigned int target;
67 const Outfit* outfit;
69 double real_vel;
70 double dam_mod;
72 int voice;
73 double timer2;
74 double paramf;
75 double life;
76 double timer;
77 double anim;
78 GLfloat r;
79 int sprite;
81 int lua_mem;
82 double falloff;
83 double strength;
84 int sx;
85 int sy;
88 /* position update and render */
89 void (*update)(struct Weapon_*, const double, WeaponLayer);
90 void (*think)(struct Weapon_*, const double);
93} Weapon;
94
95/* Weapon layers. */
96static Weapon** wbackLayer = NULL;
97static Weapon** wfrontLayer = NULL;
99/* Graphics. */
100static gl_vbo *weapon_vbo = NULL;
101static GLfloat *weapon_vboData = NULL;
102static size_t weapon_vboSize = 0;
104/* Internal stuff. */
105static unsigned int beam_idgen = 0;
107/*
108 * Prototypes
109 */
110/* Creation. */
111static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
112 const Pilot *pilot_target, const vec2 *pos, const vec2 *vel, double dir,
113 double swivel, double time );
114static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
115 const double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim );
116static void weapon_createAmmo( Weapon *w, const Outfit* outfit, double T,
117 const double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim );
118static Weapon* weapon_create( PilotOutfitSlot* po, double T,
119 const double dir, const vec2* pos, const vec2* vel,
120 const Pilot *parent, const unsigned int target, double time, int aim );
121static double weapon_computeTimes( double rdir, double rx, double ry, double dvx, double dvy, double pxv,
122 double vmin, double acc, double *tt );
123/* Updating. */
124static void weapon_render( Weapon* w, const double dt );
125static void weapons_updateLayer( const double dt, const WeaponLayer layer );
126static void weapon_update( Weapon* w, const double dt, WeaponLayer layer );
127static void weapon_sample_trail( Weapon* w );
128/* Destruction. */
129static void weapon_destroy( Weapon* w );
130static void weapon_free( Weapon* w );
131static void weapon_explodeLayer( WeaponLayer layer,
132 double x, double y, double radius,
133 const Pilot *parent, int mode );
134static void weapons_purgeLayer( Weapon** layer );
135/* Hitting. */
136static int weapon_checkCanHit( const Weapon* w, const Pilot *p );
137static void weapon_hit( Weapon* w, Pilot* p, vec2* pos );
138static void weapon_miss( Weapon *w );
139static void weapon_hitAst( Weapon* w, Asteroid* a, WeaponLayer layer, vec2* pos );
140static void weapon_hitBeam( Weapon* w, Pilot* p, WeaponLayer layer,
141 vec2 pos[2], const double dt );
142static void weapon_hitAstBeam( Weapon* w, Asteroid* a, WeaponLayer layer,
143 vec2 pos[2], const double dt );
144/* think */
145static void think_seeker( Weapon* w, const double dt );
146static void think_beam( Weapon* w, const double dt );
147/* externed */
148void weapon_minimap( const double res, const double w,
149 const double h, const RadarShape shape, double alpha );
150/* movement. */
151static void weapon_setThrust( Weapon *w, double thrust );
152static void weapon_setTurn( Weapon *w, double turn );
153
157void weapon_init (void)
158{
161}
162
172void weapon_minimap( const double res, const double w,
173 const double h, const RadarShape shape, double alpha )
174{
175 int rc, p;
176 const glColour *c;
177 GLsizei offset;
178 Pilot *par;
179
180 /* Get offset. */
181 p = 0;
182 offset = weapon_vboSize;
183
184 if (shape==RADAR_CIRCLE)
185 rc = (int)(w*w);
186 else
187 rc = 0;
188
189 /* Draw the points for weapons on all layers. */
190 for (int i=0; i<array_size(wbackLayer); i++) {
191 double x, y;
192 Weapon *wp = wbackLayer[i];
193
194 /* Make sure is in range. */
195 if (!pilot_inRange( player.p, wp->solid->pos.x, wp->solid->pos.y ))
196 continue;
197
198 /* Get radar position. */
199 x = (wp->solid->pos.x - player.p->solid->pos.x) / res;
200 y = (wp->solid->pos.y - player.p->solid->pos.y) / res;
201
202 /* Make sure in range. */
203 if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.))
204 continue;
205 if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc))
206 continue;
207
208 /* Choose colour based on if it'll hit player. */
209 if ((outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID)) ||
210 (wp->faction == FACTION_PLAYER))
211 c = &cNeutral;
212 else {
213 if (wp->target == PLAYER_ID)
214 c = &cHostile;
215 else {
216 par = pilot_get(wp->parent);
217 if ((par!=NULL) && pilot_isHostile(par))
218 c = &cHostile;
219 else
220 c = &cNeutral;
221 }
222 }
223
224 /* Set the colour. */
225 weapon_vboData[ offset + 4*p + 0 ] = c->r;
226 weapon_vboData[ offset + 4*p + 1 ] = c->g;
227 weapon_vboData[ offset + 4*p + 2 ] = c->b;
228 weapon_vboData[ offset + 4*p + 3 ] = alpha;
229
230 /* Put the pixel. */
231 weapon_vboData[ 2*p + 0 ] = x;
232 weapon_vboData[ 2*p + 1 ] = y;
233
234 /* "Add" pixel. */
235 p++;
236 }
237 for (int i=0; i<array_size(wfrontLayer); i++) {
238 double x, y;
239 Weapon *wp = wfrontLayer[i];
240
241 /* Make sure is in range. */
242 if (!pilot_inRange( player.p, wp->solid->pos.x, wp->solid->pos.y ))
243 continue;
244
245 /* Get radar position. */
246 x = (wp->solid->pos.x - player.p->solid->pos.x) / res;
247 y = (wp->solid->pos.y - player.p->solid->pos.y) / res;
248
249 /* Make sure in range. */
250 if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.))
251 continue;
252 if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc))
253 continue;
254
255 /* Choose colour based on if it'll hit player. */
256 if (outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID))
257 c = &cNeutral;
258 else if ((wp->target == PLAYER_ID && wp->target != wp->parent) ||
259 areEnemies(FACTION_PLAYER, wp->faction))
260 c = &cHostile;
261 else
262 c = &cNeutral;
263
264 /* Set the colour. */
265 weapon_vboData[ offset + 4*p + 0 ] = c->r;
266 weapon_vboData[ offset + 4*p + 1 ] = c->g;
267 weapon_vboData[ offset + 4*p + 2 ] = c->b;
268 weapon_vboData[ offset + 4*p + 3 ] = alpha;
269
270 /* Put the pixel. */
271 weapon_vboData[ 2*p + 0 ] = x;
272 weapon_vboData[ 2*p + 1 ] = y;
273
274 /* "Add" pixel. */
275 p++;
276 }
277
278 /* Only render with something to draw. */
279 if (p > 0) {
280 /* Upload data changes. */
281 gl_vboSubData( weapon_vbo, 0, sizeof(GLfloat) * 2*p, weapon_vboData );
282 gl_vboSubData( weapon_vbo, offset * sizeof(GLfloat),
283 sizeof(GLfloat) * 4*p, &weapon_vboData[offset] );
284
285 glUseProgram(shaders.points.program);
286 glEnableVertexAttribArray(shaders.points.vertex);
287 glEnableVertexAttribArray(shaders.points.vertex_color);
288 gl_uniformMat4(shaders.points.projection, &gl_view_matrix);
289 gl_vboActivateAttribOffset( weapon_vbo, shaders.points.vertex, 0, 2, GL_FLOAT, 0 );
290 gl_vboActivateAttribOffset( weapon_vbo, shaders.points.vertex_color, offset * sizeof(GLfloat), 4, GL_FLOAT, 0 );
291 glDrawArrays( GL_POINTS, 0, p );
292 glDisableVertexAttribArray(shaders.points.vertex);
293 glDisableVertexAttribArray(shaders.points.vertex_color);
294 glUseProgram(0);
295 gl_checkErr();
296 }
297}
298
302static void weapon_setThrust( Weapon *w, double thrust )
303{
304 w->solid->thrust = thrust;
305}
306
310static void weapon_setTurn( Weapon *w, double turn )
311{
312 w->solid->dir_vel = turn;
313}
314
321static void think_seeker( Weapon* w, const double dt )
322{
323 double diff;
324 Pilot *p;
325 vec2 v;
326 double t, turn_max, d, jc, speed_mod;
327
328 if (w->target == w->parent)
329 return; /* no self shooting */
330
331 p = pilot_get(w->target); /* no null pilot */
332 if (p==NULL) {
333 weapon_setThrust( w, 0. );
334 weapon_setTurn( w, 0. );
335 return;
336 }
337
338 //ewtrack = pilot_ewWeaponTrack( pilot_get(w->parent), p, w->outfit->u.lau.resist );
339
340 /* Handle by status. */
341 switch (w->status) {
342 case WEAPON_STATUS_LOCKING: /* Check to see if we can get a lock on. */
343 w->timer2 -= dt;
344 if (w->timer2 >= 0.)
345 weapon_setThrust( w, w->outfit->u.lau.thrust * w->outfit->mass );
346 else
347 w->status = WEAPON_STATUS_OK; /* Weapon locked on. */
348 /* Can't get jammed while locking on. */
349 break;
350
351 case WEAPON_STATUS_OK: /* Check to see if can get jammed */
352 jc = p->stats.jam_chance - w->outfit->u.lau.resist;
353 if (jc > 0.) {
354 /* Roll based on distance. */
355 d = vec2_dist( &p->solid->pos, &w->solid->pos );
356 if (d < w->r * p->ew_evasion) {
357 if (RNGF() < jc) {
358 double r = RNGF();
359 if (r < 0.3) {
360 w->timer = -1.; /* Should blow up. */
361 w->status = WEAPON_STATUS_JAMMED;
362 }
363 else if (r < 0.6) {
364 w->status = WEAPON_STATUS_JAMMED;
365 weapon_setTurn( w, w->outfit->u.lau.turn * ((RNGF()>0.5)?-1.0:1.0) );
366 }
367 else if (r < 0.8) {
368 w->status = WEAPON_STATUS_JAMMED;
369 weapon_setTurn( w, 0. );
370 weapon_setThrust( w, w->outfit->u.lau.thrust * w->outfit->mass );
371 }
372 else {
373 w->status = WEAPON_STATUS_JAMMED_SLOWED;
374 w->falloff = RNGF()*0.5;
375 }
376 break;
377 }
378 else
379 w->status = WEAPON_STATUS_UNJAMMED;
380 }
381 }
382 FALLTHROUGH;
383
384 case WEAPON_STATUS_JAMMED_SLOWED: /* Slowed down. */
385 case WEAPON_STATUS_UNJAMMED: /* Work as expected */
386 /* Smart seekers take into account ship velocity. */
387 if (w->outfit->u.lau.ai == AMMO_AI_SMART) {
388
389 /* Calculate time to reach target. */
390 vec2_cset( &v, p->solid->pos.x - w->solid->pos.x,
391 p->solid->pos.y - w->solid->pos.y );
392 t = vec2_odist( &v ) / w->outfit->u.lau.speed_max;
393
394 /* Calculate target's movement. */
395 vec2_cset( &v, v.x + t*(p->solid->vel.x - w->solid->vel.x),
396 v.y + t*(p->solid->vel.y - w->solid->vel.y) );
397
398 /* Get the angle now. */
399 diff = angle_diff(w->solid->dir, VANGLE(v) );
400 }
401 /* Other seekers are simplistic. */
402 else {
403 diff = angle_diff(w->solid->dir, /* Get angle to target pos */
404 vec2_angle(&w->solid->pos, &p->solid->pos));
405 }
406
407 /* Set turn. */
408 turn_max = w->outfit->u.lau.turn;// * ewtrack;
409 weapon_setTurn( w, CLAMP( -turn_max, turn_max,
410 10 * diff * w->outfit->u.lau.turn ));
411 break;
412
413 case WEAPON_STATUS_JAMMED: /* Continue doing whatever */
414 /* Do nothing, dir_vel should be set already if needed */
415 break;
416
417 default:
418 WARN(_("Unknown weapon status for '%s'"), w->outfit->name);
419 break;
420 }
421
422 /* Slow off based on falloff. */
423 speed_mod = (w->status==WEAPON_STATUS_JAMMED_SLOWED) ? w->falloff : 1.;
424
425 /* Limit speed here */
426 w->real_vel = MIN( speed_mod * w->outfit->u.lau.speed_max, w->real_vel + w->outfit->u.lau.thrust*dt );
427 vec2_pset( &w->solid->vel, /* ewtrack * */ w->real_vel, w->solid->dir );
428
429 /* Modulate max speed. */
430 //w->solid->speed_max = w->outfit->u.lau.speed * ewtrack;
431}
432
439static void think_beam( Weapon* w, const double dt )
440{
441 Pilot *p, *t;
442 AsteroidAnchor *field;
443 Asteroid *ast;
444 double diff, mod;
445 vec2 v;
446 PilotOutfitSlot *slot;
447 unsigned int turn_off;
448
449 slot = w->mount;
450
451 /* Get pilot, if pilot is dead beam is destroyed. */
452 p = pilot_get(w->parent);
453 if (p == NULL) {
454 w->timer = -1.; /* Hack to make it get destroyed next update. */
455 return;
456 }
457
458 /* Check if pilot has enough energy left to keep beam active. */
459 mod = (w->outfit->type == OUTFIT_TYPE_BEAM) ? p->stats.fwd_energy : p->stats.tur_energy;
460 p->energy -= mod * dt*w->outfit->u.bem.energy;
461 pilot_heatAddSlotTime( p, slot, dt );
462 if (p->energy < 0.) {
463 p->energy = 0.;
464 w->timer = -1;
465 return;
466 }
467
468 /* Get the targets. */
469 if (p->nav_asteroid != -1) {
470 field = &cur_system->asteroids[p->nav_anchor];
471 ast = &field->asteroids[p->nav_asteroid];
472 }
473 else
474 ast = NULL;
475 t = (w->target != w->parent) ? pilot_get(w->target) : NULL;
476
477 /* Check the beam is still in range. */
478 if (slot->inrange) {
479 turn_off = 1;
480 if (t != NULL) {
481 if (vec2_dist( &p->solid->pos, &t->solid->pos ) <= slot->outfit->u.bem.range)
482 turn_off = 0;
483 }
484 if (ast != NULL) {
485 if (vec2_dist( &p->solid->pos, &ast->pos ) <= slot->outfit->u.bem.range)
486 turn_off = 0;
487 }
488
489 /* Attempt to turn the beam off. */
490 if (turn_off) {
491 if (slot->outfit->u.bem.min_duration > 0.) {
492 slot->stimer = slot->outfit->u.bem.min_duration -
493 (slot->outfit->u.bem.duration - slot->timer);
494 if (slot->stimer > 0.)
495 turn_off = 0;
496 }
497 }
498 if (turn_off) {
499 w->timer = -1;
500 }
501 }
502
503 /* Use mount position. */
504 pilot_getMount( p, slot, &v );
505 w->solid->pos.x = p->solid->pos.x + v.x;
506 w->solid->pos.y = p->solid->pos.y + v.y;
507
508 /* Handle aiming at the target. */
509 switch (w->outfit->type) {
510 case OUTFIT_TYPE_BEAM:
511 if (w->outfit->u.bem.swivel > 0.)
512 w->solid->dir = weapon_aimTurret( w->outfit, p, t, &w->solid->pos, &p->solid->vel, p->solid->dir, w->outfit->u.bem.swivel, 0. );
513 else
514 w->solid->dir = p->solid->dir;
515 break;
516
517 case OUTFIT_TYPE_TURRET_BEAM:
518 /* If target is dead beam stops moving. Targeting
519 * self is invalid so in that case we ignore the target.
520 */
521 t = (w->target != w->parent) ? pilot_get(w->target) : NULL;
522 if (t == NULL) {
523 if (ast != NULL) {
524 diff = angle_diff(w->solid->dir, /* Get angle to target pos */
525 vec2_angle(&w->solid->pos, &ast->pos));
526 }
527 else
528 diff = angle_diff(w->solid->dir, p->solid->dir);
529 }
530 else
531 diff = angle_diff(w->solid->dir, /* Get angle to target pos */
532 vec2_angle(&w->solid->pos, &t->solid->pos));
533
534 weapon_setTurn( w, CLAMP( -w->outfit->u.bem.turn, w->outfit->u.bem.turn,
535 10 * diff * w->outfit->u.bem.turn ));
536 break;
537
538 default:
539 return;
540 }
541}
542
548void weapons_update( const double dt )
549{
550 /* When updating, just mark weapons for deletion. */
551 weapons_updateLayer(dt,WEAPON_LAYER_BG);
552 weapons_updateLayer(dt,WEAPON_LAYER_FG);
553
554 /* Actually purge and remove weapons. */
557}
558
565static void weapons_updateLayer( const double dt, const WeaponLayer layer )
566{
567 Weapon **wlayer;
568
569 /* Choose layer. */
570 switch (layer) {
571 case WEAPON_LAYER_BG:
572 wlayer = wbackLayer;
573 break;
574 case WEAPON_LAYER_FG:
575 wlayer = wfrontLayer;
576 break;
577
578 default:
579 WARN(_("Unknown weapon layer!"));
580 return;
581 }
582
583 for (int i=0; i<array_size(wlayer); i++) {
584 Weapon *w = wlayer[i];
585
586 /* Ignore destroyed wapons. */
587 if (weapon_isFlag(w, WEAPON_FLAG_DESTROYED))
588 continue;
589
590 /* Handle types. */
591 switch (w->outfit->type) {
592
593 /* most missiles behave the same */
594 case OUTFIT_TYPE_LAUNCHER:
595 case OUTFIT_TYPE_TURRET_LAUNCHER:
596 w->timer -= dt;
597 if (w->timer < 0.) {
598 int spfx = -1;
599 /* See if we need armour death sprite. */
600 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR))
601 spfx = outfit_spfxArmour(w->outfit);
602 /* See if we need shield death sprite. */
603 else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD))
604 spfx = outfit_spfxShield(w->outfit);
605 /* Add death sprite if needed. */
606 if (spfx != -1) {
607 int s;
608 spfx_add( spfx, w->solid->pos.x, w->solid->pos.y,
609 w->solid->vel.x, w->solid->vel.y,
610 SPFX_LAYER_MIDDLE ); /* presume middle. */
611 /* Add sound if explodes and has it. */
612 s = outfit_soundHit(w->outfit);
613 if (s != -1)
614 w->voice = sound_playPos(s,
615 w->solid->pos.x,
616 w->solid->pos.y,
617 w->solid->vel.x,
618 w->solid->vel.y);
619 }
620 weapon_miss(w);
621 break;
622 }
623 break;
624
625 case OUTFIT_TYPE_BOLT:
626 case OUTFIT_TYPE_TURRET_BOLT:
627 w->timer -= dt;
628 if (w->timer < 0.) {
629 int spfx = -1;
630 /* See if we need armour death sprite. */
631 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR))
632 spfx = outfit_spfxArmour(w->outfit);
633 /* See if we need shield death sprite. */
634 else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD))
635 spfx = outfit_spfxShield(w->outfit);
636 /* Add death sprite if needed. */
637 if (spfx != -1) {
638 int s;
639 spfx_add( spfx, w->solid->pos.x, w->solid->pos.y,
640 w->solid->vel.x, w->solid->vel.y,
641 SPFX_LAYER_MIDDLE ); /* presume middle. */
642 /* Add sound if explodes and has it. */
643 s = outfit_soundHit(w->outfit);
644 if (s != -1)
645 w->voice = sound_playPos(s,
646 w->solid->pos.x,
647 w->solid->pos.y,
648 w->solid->vel.x,
649 w->solid->vel.y);
650 }
651 weapon_miss(w);
652 break;
653 }
654 else if (w->timer < w->falloff)
655 w->strength = w->timer / w->falloff;
656 break;
657
658 /* Beam weapons handled a part. */
659 case OUTFIT_TYPE_BEAM:
660 case OUTFIT_TYPE_TURRET_BEAM:
661 /* Beams don't have inherent accuracy, so we use the
662 * heatAccuracyMod to modulate duration. */
663 w->timer -= dt / (1.-pilot_heatAccuracyMod(w->mount->heat_T));
664 if (w->timer < 0. || (w->outfit->u.bem.min_duration > 0. &&
665 w->mount->stimer < 0.)) {
666 Pilot *p = pilot_get(w->parent);
667 if (p != NULL)
668 pilot_stopBeam(p, w->mount);
669 weapon_miss(w);
670 break;
671 }
672 /* We use the explosion timer to tell when we have to create explosions. */
673 w->timer2 -= dt;
674 if (w->timer2 < 0.) {
675 if (w->timer2 < -1.)
676 w->timer2 = 0.100;
677 else
678 w->timer2 = -1.;
679 }
680 break;
681 default:
682 WARN(_("Weapon of type '%s' has no update implemented yet!"),
683 w->outfit->name);
684 break;
685 }
686
687 /* Only increment if weapon wasn't destroyed. */
688 if (!weapon_isFlag(w, WEAPON_FLAG_DESTROYED))
689 weapon_update(w,dt,layer);
690 }
691}
692
698static void weapons_purgeLayer( Weapon** layer )
699{
700 for (int i=0; i<array_size(layer); i++) {
701 if (weapon_isFlag(layer[i],WEAPON_FLAG_DESTROYED)) {
702 weapon_free(layer[i]);
703 array_erase( &layer, &layer[i], &layer[i+1] );
704 i--;
705 }
706 }
707}
708
715void weapons_render( const WeaponLayer layer, const double dt )
716{
717 Weapon** wlayer;
718
719 switch (layer) {
720 case WEAPON_LAYER_BG:
721 wlayer = wbackLayer;
722 break;
723 case WEAPON_LAYER_FG:
724 wlayer = wfrontLayer;
725 break;
726
727 default:
728 WARN(_("Unknown weapon layer!"));
729 return;
730 }
731
732 for (int i=0; i<array_size(wlayer); i++)
733 weapon_render( wlayer[i], dt );
734}
735
736static void weapon_renderBeam( Weapon* w, const double dt )
737{
738 double x, y, z;
739 mat4 projection;
740
741 /* Animation. */
742 w->anim += dt;
743
744 /* Load GLSL program */
745 glUseProgram(shaders.beam.program);
746
747 /* Zoom. */
748 z = cam_getZoom();
749
750 /* Position. */
751 gl_gameToScreenCoords( &x, &y, w->solid->pos.x, w->solid->pos.y );
752
753 projection = gl_view_matrix;
754 mat4_translate( &projection, x, y, 0. );
755 mat4_rotate2d( &projection, w->solid->dir );
756 mat4_scale( &projection, w->outfit->u.bem.range*z,w->outfit->u.bem.width * z, 1. );
757 mat4_translate( &projection, 0., -0.5, 0. );
758
759 /* Set the vertex. */
760 glEnableVertexAttribArray( shaders.beam.vertex );
761 gl_vboActivateAttribOffset( gl_squareVBO, shaders.beam.vertex,
762 0, 2, GL_FLOAT, 0 );
763
764 /* Set shader uniforms. */
765 gl_uniformMat4(shaders.beam.projection, &projection);
766 gl_uniformColor(shaders.beam.color, &w->outfit->u.bem.colour);
767 glUniform2f(shaders.beam.dimensions, w->outfit->u.bem.range, w->outfit->u.bem.width);
768 glUniform1f(shaders.beam.dt, w->anim);
769 glUniform1f(shaders.beam.r, w->r);
770
771 /* Set the subroutine. */
772 if (gl_has( OPENGL_SUBROUTINES ))
773 glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &w->outfit->u.bem.shader );
774
775 /* Draw. */
776 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
777
778 /* Clear state. */
779 glDisableVertexAttribArray( shaders.beam.vertex );
780 glUseProgram(0);
781
782 /* anything failed? */
783 gl_checkErr();
784}
785
792static void weapon_render( Weapon* w, const double dt )
793{
794 const glTexture *gfx;
795 double x, y, z, r, st;
796 glColour col, c = { .r=1., .g=1., .b=1. };
797
798 /* Don't render destroyed weapons. */
799 if (weapon_isFlag(w,WEAPON_FLAG_DESTROYED))
800 return;
801
802 switch (w->outfit->type) {
803 /* Weapons that use sprites. */
804 case OUTFIT_TYPE_LAUNCHER:
805 case OUTFIT_TYPE_TURRET_LAUNCHER:
806 if (w->status == WEAPON_STATUS_LOCKING) {
807 z = cam_getZoom();
808 gl_gameToScreenCoords( &x, &y, w->solid->pos.x, w->solid->pos.y );
809 gfx = outfit_gfx(w->outfit);
810 r = gfx->sw * z * 0.75; /* Assume square. */
811
812 st = 1. - w->timer2 / w->paramf;
813 col_blend( &col, &cYellow, &cRed, st );
814 col.a = 0.5;
815
816 glUseProgram( shaders.iflockon.program );
817 glUniform1f( shaders.iflockon.paramf, st );
818 gl_renderShader( x, y, r, r, r, &shaders.iflockon, &col, 1 );
819 }
820 FALLTHROUGH;
821 case OUTFIT_TYPE_BOLT:
822 case OUTFIT_TYPE_TURRET_BOLT:
823 gfx = outfit_gfx(w->outfit);
824
825 /* Alpha based on strength. */
826 c.a = w->strength;
827
828 /* Outfit spins around. */
829 if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_SPIN)) {
830 /* Check timer. */
831 w->anim -= dt;
832 if (w->anim < 0.) {
833 w->anim = outfit_spin(w->outfit);
834
835 /* Increment sprite. */
836 w->sprite++;
837 if (w->sprite >= gfx->sx*gfx->sy)
838 w->sprite = 0;
839 }
840
841 /* Render. */
842 if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
843 gl_renderSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
844 w->timer / w->life,
845 w->solid->pos.x, w->solid->pos.y,
846 w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
847 else
848 gl_renderSprite( gfx, w->solid->pos.x, w->solid->pos.y,
849 w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c );
850 }
851 /* Outfit faces direction. */
852 else {
853 if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end)
854 gl_renderSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end,
855 w->timer / w->life,
856 w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
857 else
858 gl_renderSprite( gfx, w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c );
859 }
860 break;
861
862 /* Beam weapons. */
863 case OUTFIT_TYPE_BEAM:
864 case OUTFIT_TYPE_TURRET_BEAM:
865 weapon_renderBeam(w, dt);
866 break;
867
868 default:
869 WARN(_("Weapon of type '%s' has no render implemented yet!"),
870 w->outfit->name);
871 break;
872 }
873}
874
882static int weapon_checkCanHit( const Weapon* w, const Pilot *p )
883{
884 Pilot *parent;
885
886 /* Can't hit invincible stuff. */
887 if (pilot_isFlag(p, PILOT_INVINCIBLE))
888 return 0;
889
890 /* Can't hit hidden stuff. */
891 if (pilot_isFlag(p, PILOT_HIDE))
892 return 0;
893
894 /* Must not be landing nor taking off. */
895 if (pilot_isFlag(p, PILOT_LANDING) ||
896 pilot_isFlag(p, PILOT_TAKEOFF))
897 return 0;
898
899 /* Go "through" dead pilots. */
900 if (pilot_isFlag(p, PILOT_DEAD))
901 return 0;
902
903 /* Player can not hit special pilots. */
904 if ((w->faction == FACTION_PLAYER) &&
905 pilot_isFlag(p, PILOT_INVINC_PLAYER))
906 return 0;
907
908 /* Always hit target. */
909 if (w->target == p->id)
910 return 1;
911
912 /* Can never hit same faction, unless explicitly targetted (see above). */
913 if (p->faction == w->faction)
914 return 0;
915
916 /* Player behaves differently. */
917 if (w->faction == FACTION_PLAYER) {
918
919 /* Always hit hostiles. */
920 if (pilot_isHostile(p))
921 return 1;
922
923 /* Miss rest - can be neutral/ally. */
924 else
925 return 0;
926 }
927
928 /* Let hostiles hit player. */
929 if (p->faction == FACTION_PLAYER) {
930 parent = pilot_get(w->parent);
931 if (parent != NULL) {
932 if (pilot_isHostile(parent))
933 return 1;
934 }
935 }
936
937 /* Hit non-allies. */
938 if (areEnemies(w->faction, p->faction))
939 return 1;
940
941 return 0;
942}
943
951static void weapon_update( Weapon* w, const double dt, WeaponLayer layer )
952{
953 int b, psx, psy, n;
954 unsigned int coll, usePoly, usePolyW = 1;
955 const glTexture *gfx;
956 const CollPoly *plg, *polygon;
957 vec2 crash[2];
958 Pilot *const* pilot_stack;
959 int isjammed;
960
961 gfx = NULL;
962 polygon = NULL;
964
965 /* Get the sprite direction to speed up calculations. */
966 b = outfit_isBeam(w->outfit);
967 if (!b) {
968 gfx = outfit_gfx(w->outfit);
969 gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
970 n = gfx->sx * w->sy + w->sx;
971 plg = outfit_plg(w->outfit);
972 polygon = &plg[n];
973
974 /* See if the outfit has a collision polygon. */
975 if (outfit_isBolt(w->outfit)) {
976 if (array_size(w->outfit->u.blt.polygon) == 0)
977 usePolyW = 0;
978 }
979 else if (outfit_isLauncher(w->outfit)) {
980 if (array_size(w->outfit->u.lau.polygon) == 0)
981 usePolyW = 0;
982 }
983 }
984 else {
985 Pilot *p = pilot_get( w->parent );
986 if (p != NULL) {
987 /* Beams need to update their properties online. */
988 if (w->outfit->type == OUTFIT_TYPE_BEAM) {
989 w->dam_mod = p->stats.fwd_damage;
990 w->dam_as_dis_mod = p->stats.fwd_dam_as_dis-1.;
991 }
992 else {
993 w->dam_mod = p->stats.tur_damage;
994 w->dam_as_dis_mod = p->stats.tur_dam_as_dis-1.;
995 }
996 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
997 }
998 }
999
1000 for (int i=0; i<array_size(pilot_stack); i++) {
1001 Pilot *p = pilot_stack[i];
1002
1003 /* Ignore pilots being deleted. */
1004 if (pilot_isFlag(p, PILOT_DELETE))
1005 continue;
1006
1007 if (w->parent == pilot_stack[i]->id)
1008 continue; /* pilot is self */
1009
1010 psx = pilot_stack[i]->tsx;
1011 psy = pilot_stack[i]->tsy;
1012
1013 /* See if the ship has a collision polygon. */
1014 usePoly = usePolyW;
1015 if (array_size(p->ship->polygon) == 0)
1016 usePoly = 0;
1017
1018 /* Beam weapons have special collisions. */
1019 if (b) {
1020 /* Check for collision. */
1021 if (weapon_checkCanHit(w,p)) {
1022 if (usePoly) {
1023 int k = p->ship->gfx_space->sx * psy + psx;
1024 coll = CollideLinePolygon( &w->solid->pos, w->solid->dir,
1025 w->outfit->u.bem.range, &p->ship->polygon[k],
1026 &p->solid->pos, crash);
1027 }
1028 else {
1029 coll = CollideLineSprite( &w->solid->pos, w->solid->dir,
1030 w->outfit->u.bem.range, p->ship->gfx_space, psx, psy,
1031 &p->solid->pos, crash);
1032 }
1033 if (coll)
1034 weapon_hitBeam( w, p, layer, crash, dt );
1035 /* No return because beam can still think, it's not
1036 * destroyed like the other weapons.*/
1037 }
1038 }
1039 /* smart weapons only collide with their target */
1040 else if (weapon_isSmart(w)) {
1041 isjammed = ((w->status == WEAPON_STATUS_JAMMED) || (w->status == WEAPON_STATUS_JAMMED_SLOWED));
1042 if ((((pilot_stack[i]->id == w->target) && !isjammed) || isjammed) &&
1043 weapon_checkCanHit(w,p) ) {
1044 if (usePoly) {
1045 int k = p->ship->gfx_space->sx * psy + psx;
1046 coll = CollidePolygon( &p->ship->polygon[k], &p->solid->pos,
1047 polygon, &w->solid->pos, &crash[0] );
1048 }
1049 else {
1050 coll = CollideSprite( gfx, w->sx, w->sy, &w->solid->pos,
1051 p->ship->gfx_space, psx, psy,
1052 &p->solid->pos, &crash[0] );
1053 }
1054 if (coll) {
1055 weapon_hit( w, p, &crash[0] );
1056 return; /* Weapon is destroyed. */
1057 }
1058 }
1059 }
1060 /* unguided weapons hit anything not of the same faction */
1061 else {
1062 if (weapon_checkCanHit(w,p)) {
1063 if (usePoly) {
1064 int k = p->ship->gfx_space->sx * psy + psx;
1065 coll = CollidePolygon( &p->ship->polygon[k], &p->solid->pos,
1066 polygon, &w->solid->pos, &crash[0] );
1067 }
1068 else {
1069 coll = CollideSprite( gfx, w->sx, w->sy, &w->solid->pos,
1070 p->ship->gfx_space, psx, psy,
1071 &p->solid->pos, &crash[0] );
1072 }
1073
1074 if (coll) {
1075 weapon_hit( w, p, &crash[0] );
1076 return; /* Weapon is destroyed. */
1077 }
1078 }
1079 }
1080 }
1081
1082 /* Collide with asteroids*/
1083 if (outfit_isLauncher(w->outfit) || outfit_isBolt(w->outfit)) {
1084 for (int i=0; i<array_size(cur_system->asteroids); i++) {
1085 AsteroidAnchor *ast = &cur_system->asteroids[i];
1086
1087 /* Early in-range check with the asteroid field. */
1088 if ( vec2_dist2( &w->solid->pos, &ast->pos ) >
1089 pow2( ast->radius + ast->margin + gfx->sw/2. ))
1090 continue;
1091
1092 for (int j=0; j<ast->nb; j++) {
1093 Asteroid *a = &ast->asteroids[j];
1094 if (a->state != ASTEROID_FG)
1095 continue;
1096
1097 /* In-range check with the actual asteroid. */
1098 /* This is advantageous because we are going to rotate the polygon afterwards. */
1099 if ( vec2_dist2( &w->solid->pos, &a->pos ) > pow2( gfx->sw/2. + a->gfx->sw/2. ) )
1100 continue;
1101
1102 /* See if the asteroid has a collision polygon. */
1103 usePoly = usePolyW;
1104 if (a->polygon->npt == 0)
1105 usePoly = 0;
1106
1107 if (usePoly) {
1108 CollPoly rpoly;
1109 RotatePolygon( &rpoly, a->polygon, (float) a->ang );
1110 coll = CollidePolygon( &rpoly, &a->pos,
1111 polygon, &w->solid->pos, &crash[0] );
1112 free(rpoly.x);
1113 free(rpoly.y);
1114 }
1115 else {
1116 coll = CollideSprite( gfx, w->sx, w->sy, &w->solid->pos,
1117 a->gfx, 0, 0, &a->pos, &crash[0] );
1118 }
1119
1120 if (coll) {
1121 weapon_hitAst( w, a, layer, &crash[0] );
1122 return; /* Weapon is destroyed. */
1123 }
1124 }
1125 }
1126 }
1127
1128 else if (b) { /* Beam */
1129 for (int i=0; i<array_size(cur_system->asteroids); i++) {
1130 AsteroidAnchor *ast = &cur_system->asteroids[i];
1131
1132 /* Early in-range check. */
1133 if (vec2_dist2( &w->solid->pos, &ast->pos ) >
1134 pow2( ast->radius + ast->margin + w->outfit->u.bem.range ))
1135 continue;
1136
1137 for (int j=0; j<ast->nb; j++) {
1138 Asteroid *a = &ast->asteroids[j];
1139 if (a->state != ASTEROID_FG)
1140 continue;
1141
1142 /* In-range check with the actual asteroid. */
1143 if ( vec2_dist2( &w->solid->pos, &a->pos ) > pow2( w->outfit->u.bem.range + a->gfx->sw/2. ) )
1144 continue;
1145
1146 /* See if the asteroid has a collision polygon. */
1147 usePoly = usePolyW;
1148 if (a->polygon->npt == 0)
1149 usePoly = 0;
1150
1151 if (usePoly) {
1152 CollPoly rpoly;
1153 RotatePolygon( &rpoly, a->polygon, (float) a->ang );
1154 coll = CollideLinePolygon( &w->solid->pos, w->solid->dir,
1155 w->outfit->u.bem.range,
1156 &rpoly, &a->pos, crash );
1157 free(rpoly.x);
1158 free(rpoly.y);
1159 }
1160 else {
1161 coll = CollideLineSprite( &w->solid->pos, w->solid->dir,
1162 w->outfit->u.bem.range,
1163 a->gfx, 0, 0, &a->pos, crash );
1164 }
1165
1166 if (coll) {
1167 weapon_hitAstBeam( w, a, layer, crash, dt );
1168 /* No return because beam can still think, it's not
1169 * destroyed like the other weapons.*/
1170 }
1171 }
1172 }
1173 }
1174
1175 /* smart weapons also get to think their next move */
1176 if (weapon_isSmart(w))
1177 (*w->think)(w,dt);
1178
1179 /* Update the solid position. */
1180 (*w->solid->update)(w->solid, dt);
1181
1182 /* Update the sound. */
1183 sound_updatePos(w->voice, w->solid->pos.x, w->solid->pos.y,
1184 w->solid->vel.x, w->solid->vel.y);
1185
1186 /* Update the trail. */
1187 if (w->trail != NULL)
1189}
1190
1195{
1196 double a, dx, dy;
1197 TrailMode mode;
1198
1200 return;
1201
1202 /* Compute the engine offset. */
1203 a = w->solid->dir;
1204 dx = w->outfit->u.lau.trail_x_offset * cos(a);
1205 dy = w->outfit->u.lau.trail_x_offset * sin(a);
1206
1207 /* Set the colour. */
1208 if ((w->outfit->u.lau.ai == AMMO_AI_UNGUIDED) ||
1209 w->solid->vel.x*w->solid->vel.x + w->solid->vel.y*w->solid->vel.y + 1.
1210 < w->solid->speed_max*w->solid->speed_max)
1211 mode = MODE_AFTERBURN;
1212 else if (w->solid->dir_vel != 0.)
1213 mode = MODE_GLOW;
1214 else
1215 mode = MODE_IDLE;
1216
1217 spfx_trail_sample( w->trail, w->solid->pos.x + dx, w->solid->pos.y + dy*M_SQRT1_2, mode, 0 );
1218}
1219
1227void weapon_hitAI( Pilot *p, const Pilot *shooter, double dmg )
1228{
1229 /* Must be a valid shooter. */
1230 if (shooter == NULL)
1231 return;
1232
1233 /* Only care about actual damage. */
1234 if (dmg <= 0.)
1235 return;
1236
1237 /* Must not be disabled. */
1238 if (pilot_isDisabled(p))
1239 return;
1240
1241 /* Must not be deleting. */
1242 if (pilot_isFlag(p, PILOT_DELETE) || pilot_isFlag(p, PILOT_DEAD) || pilot_isFlag( p, PILOT_HIDE ))
1243 return;
1244
1245 /* Player is handled differently. */
1246 if (shooter->faction == FACTION_PLAYER) {
1247 /* Increment damage done to by player. */
1248 p->player_damage += dmg / (p->shield_max + p->armour_max);
1249
1250 /* If damage is over threshold, inform pilot or if is targeted. */
1251 if ((p->player_damage > PILOT_HOSTILE_THRESHOLD) ||
1252 (shooter->target==p->id)) {
1253 /* Inform attacked. */
1255 ai_attacked( p, shooter->id, dmg );
1256 }
1257 }
1258 /* Otherwise just inform of being attacked. */
1259 else
1260 ai_attacked( p, shooter->id, dmg );
1261}
1262
1270static void weapon_hit( Weapon* w, Pilot* p, vec2* pos )
1271{
1272 Pilot *parent;
1273 int s, spfx;
1274 double damage;
1275 WeaponLayer spfx_layer;
1276 Damage dmg;
1277 const Damage *odmg;
1278
1279 /* Get general details. */
1280 odmg = outfit_damage( w->outfit );
1281 parent = pilot_get( w->parent );
1282 damage = w->dam_mod * w->strength * odmg->damage;
1283 dmg.damage = MAX( 0., damage * (1.-w->dam_as_dis_mod) );
1284 dmg.penetration = odmg->penetration;
1285 dmg.type = odmg->type;
1286 dmg.disable = MAX( 0., w->dam_mod * w->strength * odmg->disable + damage * w->dam_as_dis_mod );
1287
1288 /* Play sound if they have it. */
1289 s = outfit_soundHit(w->outfit);
1290 if (s != -1)
1291 w->voice = sound_playPos( s,
1292 w->solid->pos.x,
1293 w->solid->pos.y,
1294 w->solid->vel.x,
1295 w->solid->vel.y);
1296
1297 /* Have pilot take damage and get real damage done. */
1298 damage = pilot_hit( p, w->solid, parent, &dmg, w->outfit, w->lua_mem, 1 );
1299
1300 /* Get the layer. */
1301 spfx_layer = (p==player.p) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE;
1302 /* Choose spfx. */
1303 if (p->shield > 0.)
1304 spfx = outfit_spfxShield(w->outfit);
1305 else
1306 spfx = outfit_spfxArmour(w->outfit);
1307 /* Add sprite, layer depends on whether player shot or not. */
1308 spfx_add( spfx, pos->x, pos->y,
1309 VX(p->solid->vel), VY(p->solid->vel), spfx_layer );
1310
1311 /* Inform AI that it's been hit. */
1312 weapon_hitAI( p, parent, damage );
1313
1314 /* no need for the weapon particle anymore */
1315 weapon_destroy(w);
1316}
1317
1323static void weapon_miss( Weapon *w )
1324{
1325 /* On hit weapon effects. */
1326 if (w->outfit->lua_onmiss != LUA_NOREF) {
1327 Pilot *parent = pilot_get( w->parent );
1328
1329 lua_rawgeti(naevL, LUA_REGISTRYINDEX, w->lua_mem); /* mem */
1330 nlua_setenv(naevL, w->outfit->lua_env, "mem"); /* */
1331
1332 /* Set up the function: onmiss() */
1333 lua_rawgeti(naevL, LUA_REGISTRYINDEX, w->outfit->lua_onmiss); /* f */
1334 lua_pushpilot(naevL, (parent==NULL) ? 0 : parent->id);
1335 lua_pushvector(naevL, w->solid->pos);
1336 lua_pushvector(naevL, w->solid->vel);
1337 if (nlua_pcall( w->outfit->lua_env, 3, 0 )) { /* */
1338 WARN( _("Outfit '%s' -> '%s':\n%s"), w->outfit->name, "onmiss", lua_tostring(naevL,-1) );
1339 lua_pop(naevL, 1);
1340 }
1341 }
1342
1343 weapon_destroy(w);
1344}
1345
1354static void weapon_hitAst( Weapon* w, Asteroid* a, WeaponLayer layer, vec2* pos )
1355{
1356 int s, spfx;
1357 Damage dmg;
1358 const Damage *odmg;
1359 Pilot *parent;
1360 double mining_bonus;
1361
1362 /* Get general details. */
1363 odmg = outfit_damage( w->outfit );
1364 dmg.damage = MAX( 0., w->dam_mod * w->strength * odmg->damage );
1365 dmg.penetration = odmg->penetration;
1366 dmg.type = odmg->type;
1367 dmg.disable = odmg->disable;
1368
1369 /* Play sound if they have it. */
1370 s = outfit_soundHit(w->outfit);
1371 if (s != -1)
1372 w->voice = sound_playPos( s,
1373 w->solid->pos.x,
1374 w->solid->pos.y,
1375 w->solid->vel.x,
1376 w->solid->vel.y);
1377
1378 /* Add the spfx */
1379 spfx = outfit_spfxArmour(w->outfit);
1380 spfx_add( spfx, pos->x, pos->y,VX(a->vel), VY(a->vel), layer );
1381
1382 weapon_destroy(w);
1383
1384 parent = pilot_get( w->parent );
1385 mining_bonus = (parent != NULL) ? parent->stats.mining_bonus : 1.;
1386 asteroid_hit( a, &dmg, outfit_miningRarity(w->outfit), mining_bonus );
1387}
1388
1398static void weapon_hitBeam( Weapon* w, Pilot* p, WeaponLayer layer,
1399 vec2 pos[2], const double dt )
1400{
1401 (void) layer;
1402 Pilot *parent;
1403 int spfx;
1404 double damage;
1405 WeaponLayer spfx_layer;
1406 Damage dmg;
1407 const Damage *odmg;
1408
1409 /* Get general details. */
1410 odmg = outfit_damage( w->outfit );
1411 parent = pilot_get( w->parent );
1412 damage = w->dam_mod * w->strength * odmg->damage * dt ;
1413 dmg.damage = MAX( 0., damage * (1.-w->dam_as_dis_mod) );
1414 dmg.penetration = odmg->penetration;
1415 dmg.type = odmg->type;
1416 dmg.disable = MAX( 0., w->dam_mod * w->strength * odmg->disable * dt + damage * w->dam_as_dis_mod );
1417
1418 /* Have pilot take damage and get real damage done. */
1419 damage = pilot_hit( p, w->solid, parent, &dmg, w->outfit, w->lua_mem, 1 );
1420
1421 /* Add sprite, layer depends on whether player shot or not. */
1422 if (w->timer2 == -1.) {
1423 /* Get the layer. */
1424 spfx_layer = (p==player.p) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE;
1425
1426 /* Choose spfx. */
1427 if (p->shield > 0.)
1428 spfx = outfit_spfxShield(w->outfit);
1429 else
1430 spfx = outfit_spfxArmour(w->outfit);
1431
1432 /* Add graphic. */
1433 spfx_add( spfx, pos[0].x, pos[0].y,
1434 VX(p->solid->vel), VY(p->solid->vel), spfx_layer );
1435 spfx_add( spfx, pos[1].x, pos[1].y,
1436 VX(p->solid->vel), VY(p->solid->vel), spfx_layer );
1437 w->timer2 = -2.;
1438
1439 /* Inform AI that it's been hit, to not saturate ai Lua with messages. */
1440 weapon_hitAI( p, parent, damage );
1441 }
1442}
1443
1453static void weapon_hitAstBeam( Weapon* w, Asteroid* a, WeaponLayer layer,
1454 vec2 pos[2], const double dt )
1455{
1456 (void) layer;
1457 Damage dmg;
1458 const Damage *odmg;
1459 Pilot *parent;
1460 double mining_bonus;
1461
1462 /* Get general details. */
1463 odmg = outfit_damage( w->outfit );
1464 dmg.damage = MAX( 0., w->dam_mod * w->strength * odmg->damage * dt );
1465 dmg.penetration = odmg->penetration;
1466 dmg.type = odmg->type;
1467 dmg.disable = odmg->disable * dt;
1468
1469 parent = pilot_get( w->parent );
1470 mining_bonus = (parent != NULL) ? parent->stats.mining_bonus : 1.;
1471 asteroid_hit( a, &dmg, outfit_miningRarity(w->outfit), mining_bonus );
1472
1473 /* Add sprite. */
1474 if (w->timer2 == -1.) {
1475 int spfx = outfit_spfxArmour(w->outfit);
1476
1477 /* Add graphic. */
1478 spfx_add( spfx, pos[0].x, pos[0].y,
1479 VX(a->vel), VY(a->vel), SPFX_LAYER_MIDDLE );
1480 spfx_add( spfx, pos[1].x, pos[1].y,
1481 VX(a->vel), VY(a->vel), SPFX_LAYER_MIDDLE );
1482 w->timer2 = -2.;
1483 }
1484}
1485
1498static double weapon_aimTurret( const Outfit *outfit, const Pilot *parent,
1499 const Pilot *pilot_target, const vec2 *pos, const vec2 *vel, double dir,
1500 double swivel, double time )
1501{
1502 vec2 *target_pos, *target_vel;
1503 double rx, ry, x, y, t, lead, rdir, off;
1504
1505 if (pilot_target != NULL) {
1506 target_pos = &pilot_target->solid->pos;
1507 target_vel = &pilot_target->solid->vel;
1508 }
1509 else {
1510 if (parent->nav_asteroid < 0)
1511 return dir;
1512
1513 AsteroidAnchor *field = &cur_system->asteroids[parent->nav_anchor];
1514 Asteroid *ast = &field->asteroids[parent->nav_asteroid];
1515 target_pos = &ast->pos;
1516 target_vel = &ast->vel;
1517 }
1518
1519 /* Get the vector : shooter -> target */
1520 rx = target_pos->x - pos->x;
1521 ry = target_pos->y - pos->y;
1522
1523 /* Try to predict where the enemy will be. */
1524 t = time;
1525 if (t == INFINITY) /*Postprocess (t = INFINITY means target is not hittable)*/
1526 t = 0.;
1527
1528 /* Position is calculated on where it should be */
1529 x = (target_pos->x + target_vel->x*t) - (pos->x + vel->x*t);
1530 y = (target_pos->y + target_vel->y*t) - (pos->y + vel->y*t);
1531
1532 /* Compute both the angles we want. */
1533 if (pilot_target != NULL) {
1534 /* Lead angle is determined from ewarfare. */
1535 double trackmin = outfit_trackmin(outfit);
1536 double trackmax = outfit_trackmax(outfit);
1537 lead = pilot_ewWeaponTrack( parent, pilot_target, trackmin, trackmax );
1538 x = lead * x + (1.-lead) * rx;
1539 y = lead * y + (1.-lead) * ry;
1540 }
1541 else
1542 lead = 1.;
1543 rdir = ANGLE(x,y);
1544
1545 /* For unguided rockets: use a FD quasi-Newton algorithm to aim better. */
1546 if (outfit_isLauncher(outfit) && outfit->u.lau.thrust > 0.) {
1547 double vmin = outfit->u.lau.speed;
1548
1549 if (vmin > 0.) {
1550 /* Get various details. */
1551 double tt, ddir, dtdd, acc, pxv, ang, dvx, dvy;
1552 int niter = 5;
1553 acc = outfit->u.lau.thrust;
1554
1555 /* Get the relative velocity. */
1556 dvx = lead * (target_vel->x - vel->x);
1557 dvy = lead * (target_vel->y - vel->y);
1558
1559 /* Cross product between position and vel. */
1560 /* For having a better conditionning, ddir is adapted to the angular difference. */
1561 pxv = rx*dvy - ry*dvx;
1562 ang = atan2( pxv, rx*dvx+ry*dvy ); /* Angle between position and velocity. */
1563 if (fabs(ang + M_PI) < fabs(ang))
1564 ang += M_PI; /* Periodicity tricks. */
1565 else if (fabs(ang - M_PI) < fabs(ang))
1566 ang -= M_PI;
1567 ddir = -ang/1000.;
1568
1569 /* Iterate to correct the initial guess rdir. */
1570 /* We compute more precisely ta and tt. */
1571 /* (times for the ammo and the target to get to intersection point) */
1572 /* The aim is to nullify ta-tt. */
1573 if (fabs(ang) > 1e-7) { /* No need to iterate if it's already nearly aligned. */
1574 for (int i=0; i<niter; i++) {
1575 double d = weapon_computeTimes( rdir, rx, ry, dvx, dvy, pxv, vmin, acc, &tt );
1576 double dd = weapon_computeTimes( rdir+ddir, rx, ry, dvx, dvy, pxv, vmin, acc, &tt );
1577
1578 /* Manage an exception (tt<0), and regular stopping condition. */
1579 /* TODO: this stopping criterion is too restrictive. */
1580 /* (for example when pos and vel are nearly aligned). */
1581 if (tt < 0. || fabs(d) < 5.)
1582 break;
1583
1584 dtdd = (dd-d)/ddir; /* Derivative of the criterion wrt. rdir. */
1585 rdir = rdir - d/dtdd; /* Update. */
1586 }
1587 }
1588 }
1589 }
1590
1591 /* Calculate bounds. */
1592 off = angle_diff( rdir, dir );
1593 if (FABS(off) > swivel) {
1594 if (off > 0.)
1595 rdir = dir - swivel;
1596 else
1597 rdir = dir + swivel;
1598 }
1599
1600 return rdir;
1601}
1602
1616static double weapon_computeTimes( double rdir, double rx, double ry, double dvx, double dvy, double pxv,
1617 double vmin, double acc, double *tt )
1618{
1619 double l, dxv, dxp, ct, st, d;
1620
1621 /* Trigonometry. */
1622 ct = cos(rdir); st = sin(rdir);
1623
1624 /* Two extra cross products. */
1625 dxv = ct*dvy - st*dvx;
1626 dxp = ct*ry - st*rx;
1627
1628 /* Compute criterion. */
1629 *tt = -dxp/dxv; /* Time to interception for target. Because triangle aera. */
1630 l = pxv/dxv; /* Length to interception for shooter. Because triangle aera. */
1631 d = .5*acc*(*tt)*(*tt) + vmin*(*tt); /* Estimate how far the projectile went. */
1632
1633 return (d-l); /* Criterion is distance of projectile to intersection when target is there. */
1634}
1635
1649static void weapon_createBolt( Weapon *w, const Outfit* outfit, double T,
1650 const double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim )
1651{
1652 vec2 v;
1653 double mass, rdir, acc, m;
1654 Pilot *pilot_target;
1655 const glTexture *gfx;
1656
1657 /* Only difference is the direction of fire */
1658 if ((w->parent!=w->target) && (w->target != 0)) /* Must have valid target */
1659 pilot_target = pilot_get(w->target);
1660 else /* fire straight or at asteroid */
1661 pilot_target = NULL;
1662
1663 if (aim)
1664 rdir = weapon_aimTurret( outfit, parent, pilot_target, pos, vel, dir, outfit->u.blt.swivel, time );
1665 else
1666 rdir = dir;
1667
1668 /* Disperse as necessary. */
1669 if (outfit->u.blt.dispersion > 0.)
1670 rdir += RNG_1SIGMA() * outfit->u.blt.dispersion;
1671
1672 /* Calculate accuracy. */
1673 acc = HEAT_WORST_ACCURACY * pilot_heatAccuracyMod( T );
1674
1675 /* Stat modifiers. */
1676 if (outfit->type == OUTFIT_TYPE_TURRET_BOLT) {
1677 w->dam_mod *= parent->stats.tur_damage;
1678 /* dam_as_dis is computed as multiplier, must be corrected. */
1679 w->dam_as_dis_mod = parent->stats.tur_dam_as_dis-1.;
1680 }
1681 else {
1682 w->dam_mod *= parent->stats.fwd_damage;
1683 /* dam_as_dis is computed as multiplier, must be corrected. */
1684 w->dam_as_dis_mod = parent->stats.fwd_dam_as_dis-1.;
1685 }
1686 /* Clamping, but might not actually be necessary if weird things want to be done. */
1687 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
1688
1689 /* Calculate direction. */
1690 rdir += RNG_2SIGMA() * acc;
1691 if (rdir < 0.)
1692 rdir += 2.*M_PI;
1693 else if (rdir >= 2.*M_PI)
1694 rdir -= 2.*M_PI;
1695
1696 mass = 1.; /* Lasers are presumed to have unitary mass, just like the real world. */
1697 v = *vel;
1698 m = outfit->u.blt.speed;
1699 if (outfit->u.blt.speed_dispersion > 0.)
1700 m += RNG_1SIGMA() * outfit->u.blt.speed_dispersion;
1701 vec2_cadd( &v, m*cos(rdir), m*sin(rdir));
1702 w->timer = outfit->u.blt.range / outfit->u.blt.speed;
1703 w->falloff = w->timer - outfit->u.blt.falloff / outfit->u.blt.speed;
1704 w->solid = solid_create( mass, rdir, pos, &v, SOLID_UPDATE_EULER );
1705 w->voice = sound_playPos( w->outfit->u.blt.sound,
1706 w->solid->pos.x,
1707 w->solid->pos.y,
1708 w->solid->vel.x,
1709 w->solid->vel.y);
1710
1711 /* Set facing direction. */
1712 gfx = outfit_gfx( w->outfit );
1713 gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
1714}
1715
1729static void weapon_createAmmo( Weapon *w, const Outfit* outfit, double T,
1730 const double dir, const vec2* pos, const vec2* vel, const Pilot* parent, double time, int aim )
1731{
1732 (void) T;
1733 vec2 v;
1734 double mass, rdir, m;
1735 Pilot *pilot_target;
1736 const glTexture *gfx;
1737
1738 /* Only difference is the direction of fire */
1739 if ((w->parent!=w->target) && (w->target != 0)) /* Must have valid target */
1740 pilot_target = pilot_get(w->target);
1741 else /* fire straight or at asteroid */
1742 pilot_target = NULL;
1743
1744 if (aim) {
1745 if (outfit->type == OUTFIT_TYPE_TURRET_LAUNCHER)
1746 rdir = weapon_aimTurret( outfit, parent, pilot_target, pos, vel, dir, M_PI, time );
1747 else if (outfit->u.lau.swivel > 0.)
1748 rdir = weapon_aimTurret( outfit, parent, pilot_target, pos, vel, dir, outfit->u.lau.swivel, time );
1749 else
1750 rdir = dir;
1751 }
1752 else
1753 rdir = dir;
1754
1755 /* Disperse as necessary. */
1756 if (outfit->u.blt.dispersion > 0.)
1757 rdir += RNG_1SIGMA() * outfit->u.lau.dispersion;
1758
1759 /* Make sure angle is in range. */
1760 while (rdir < 0.)
1761 rdir += 2.*M_PI;
1762 while (rdir >= 2.*M_PI)
1763 rdir -= 2.*M_PI;
1764
1765 /* Launcher damage. */
1766 w->dam_mod *= parent->stats.launch_damage;
1767
1768 /* If thrust is 0. we assume it starts out at speed. */
1769 v = *vel;
1770 m = outfit->u.lau.speed;
1771 if (outfit->u.lau.speed_dispersion > 0.)
1772 m += RNG_1SIGMA() * outfit->u.lau.speed_dispersion;
1773 vec2_cadd( &v, m * cos(rdir), m * sin(rdir) );
1774 w->real_vel = VMOD(v);
1775
1776 /* Set up ammo details. */
1777 mass = w->outfit->mass;
1778 w->timer = w->outfit->u.lau.duration * parent->stats.launch_range;
1779 w->solid = solid_create( mass, rdir, pos, &v, SOLID_UPDATE_EULER );
1780 if (w->outfit->u.lau.thrust > 0.) {
1781 weapon_setThrust( w, w->outfit->u.lau.thrust * mass );
1782 /* Limit speed, we only relativize in the case it has thrust + initial speed. */
1783 w->solid->speed_max = w->outfit->u.lau.speed_max;
1784 if (w->outfit->u.lau.speed > 0.)
1785 w->solid->speed_max = -1; /* No limit. */
1786 }
1787
1788 /* Handle seekers. */
1789 if (w->outfit->u.lau.ai != AMMO_AI_UNGUIDED) {
1790 w->timer2 = outfit->u.lau.iflockon * parent->stats.launch_calibration;
1791 w->paramf = outfit->u.lau.iflockon * parent->stats.launch_calibration;
1792 w->status = (w->timer2 > 0.) ? WEAPON_STATUS_LOCKING : WEAPON_STATUS_OK;
1793
1794 w->think = think_seeker; /* AI is the same atm. */
1795 w->r = RNGF(); /* Used for jamming. */
1796
1797 /* If they are seeking a pilot, increment lockon counter. */
1798 if (pilot_target == NULL)
1799 pilot_target = pilot_get(w->target);
1800 if (pilot_target != NULL)
1801 pilot_target->lockons++;
1802 }
1803 else
1804 w->status = WEAPON_STATUS_OK;
1805
1806 /* Play sound. */
1807 w->voice = sound_playPos(w->outfit->u.lau.sound,
1808 w->solid->pos.x,
1809 w->solid->pos.y,
1810 w->solid->vel.x,
1811 w->solid->vel.y);
1812
1813 /* Set facing direction. */
1814 gfx = outfit_gfx( w->outfit );
1815 gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir );
1816
1817 /* Set up trails. */
1818 if (w->outfit->u.lau.trail_spec != NULL)
1819 w->trail = spfx_trail_create( w->outfit->u.lau.trail_spec );
1820}
1821
1836static Weapon* weapon_create( PilotOutfitSlot *po, double T,
1837 const double dir, const vec2* pos, const vec2* vel,
1838 const Pilot* parent, const unsigned int target, double time, int aim )
1839{
1840 double mass, rdir;
1841 Pilot *pilot_target;
1842 AsteroidAnchor *field;
1843 Asteroid *ast;
1844 Weapon* w;
1845 const Outfit *outfit = po->outfit;
1846
1847 /* Create basic features */
1848 w = calloc( 1, sizeof(Weapon) );
1849 w->dam_mod = 1.; /* Default of 100% damage. */
1850 w->dam_as_dis_mod = 0.; /* Default of 0% damage to disable. */
1851 w->faction = parent->faction; /* non-changeable */
1852 w->parent = parent->id; /* non-changeable */
1853 w->target = target; /* non-changeable */
1854 w->lua_mem = LUA_NOREF;
1855 if (po != NULL && po->lua_mem != LUA_NOREF) {
1856 lua_rawgeti(naevL, LUA_REGISTRYINDEX, po->lua_mem); /* mem */
1857 w->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX );
1858 }
1859 w->outfit = outfit; /* non-changeable */
1860 w->update = weapon_update;
1861 w->strength = 1.;
1862
1863 /* Inform the target. */
1864 if (!(outfit_isBeam(w->outfit))) {
1865 pilot_target = pilot_get(target);
1866 if (pilot_target != NULL)
1867 pilot_target->projectiles++;
1868 }
1869
1870 switch (outfit->type) {
1871
1872 /* Bolts treated together */
1873 case OUTFIT_TYPE_BOLT:
1874 case OUTFIT_TYPE_TURRET_BOLT:
1875 weapon_createBolt( w, outfit, T, dir, pos, vel, parent, time, aim );
1876 break;
1877
1878 /* Beam weapons are treated together. */
1879 case OUTFIT_TYPE_BEAM:
1880 case OUTFIT_TYPE_TURRET_BEAM:
1881 rdir = dir;
1882 if (aim && (outfit->type == OUTFIT_TYPE_TURRET_BEAM)) {
1883 pilot_target = pilot_get(target);
1884 if ((w->parent != w->target) && (pilot_target != NULL))
1885 rdir = vec2_angle(pos, &pilot_target->solid->pos);
1886 else if (parent->nav_asteroid >= 0) {
1887 field = &cur_system->asteroids[parent->nav_anchor];
1888 ast = &field->asteroids[parent->nav_asteroid];
1889 rdir = vec2_angle(pos, &ast->pos);
1890 }
1891 }
1892
1893 if (rdir < 0.)
1894 rdir += 2.*M_PI;
1895 else if (rdir >= 2.*M_PI)
1896 rdir -= 2.*M_PI;
1897 mass = 1.;
1898 w->r = RNGF(); /* Set unique value. */
1899 w->solid = solid_create( mass, rdir, pos, vel, SOLID_UPDATE_EULER );
1900 w->think = think_beam;
1901 w->timer = outfit->u.bem.duration;
1902 w->voice = sound_playPos( w->outfit->u.bem.sound,
1903 w->solid->pos.x,
1904 w->solid->pos.y,
1905 w->solid->vel.x,
1906 w->solid->vel.y);
1907
1908 if (outfit->type == OUTFIT_TYPE_BEAM) {
1909 w->dam_mod *= parent->stats.fwd_damage;
1910 w->dam_as_dis_mod = parent->stats.fwd_dam_as_dis-1.;
1911 }
1912 else {
1913 w->dam_mod *= parent->stats.tur_damage;
1914 w->dam_as_dis_mod = parent->stats.tur_dam_as_dis-1.;
1915 }
1916 w->dam_as_dis_mod = CLAMP( 0., 1., w->dam_as_dis_mod );
1917
1918 break;
1919
1920 /* Treat seekers together. */
1921 case OUTFIT_TYPE_LAUNCHER:
1922 case OUTFIT_TYPE_TURRET_LAUNCHER:
1923 weapon_createAmmo( w, outfit, T, dir, pos, vel, parent, time, aim );
1924 break;
1925
1926 /* just dump it where the player is */
1927 default:
1928 WARN(_("Weapon of type '%s' has no create implemented yet!"),
1929 w->outfit->name);
1930 w->solid = solid_create( 1., dir, pos, vel, SOLID_UPDATE_EULER );
1931 break;
1932 }
1933
1934 /* Set life to timer. */
1935 w->life = w->timer;
1936
1937 return w;
1938}
1939
1953void weapon_add( PilotOutfitSlot *po, const double T, const double dir,
1954 const vec2* pos, const vec2* vel,
1955 const Pilot *parent, unsigned int target, double time, int aim )
1956{
1957 WeaponLayer layer;
1958 Weapon *w, **m;
1959 size_t bufsize;
1960
1961 if (!outfit_isBolt(po->outfit) &&
1962 !outfit_isLauncher(po->outfit)) {
1963 ERR(_("Trying to create a Weapon from a non-Weapon type Outfit"));
1964 return;
1965 }
1966
1967 layer = (parent->id==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
1968 w = weapon_create( po, T, dir, pos, vel, parent, target, time, aim );
1969
1970 /* set the proper layer */
1971 switch (layer) {
1972 case WEAPON_LAYER_BG:
1973 m = &array_grow(&wbackLayer);
1974 break;
1975 case WEAPON_LAYER_FG:
1976 m = &array_grow(&wfrontLayer);
1977 break;
1978
1979 default:
1980 WARN(_("Unknown weapon layer!"));
1981 return;
1982 }
1983 *m = w;
1984
1985 /* Grow the vertex stuff if needed. */
1987 if (bufsize != weapon_vboSize) {
1988 GLsizei size;
1989 weapon_vboSize = bufsize;
1990 size = sizeof(GLfloat) * (2+4) * weapon_vboSize;
1991 weapon_vboData = realloc( weapon_vboData, size );
1992 if (weapon_vbo == NULL)
1993 weapon_vbo = gl_vboCreateStream( size, NULL );
1995 }
1996}
1997
2012unsigned int beam_start( PilotOutfitSlot *po,
2013 const double dir, const vec2* pos, const vec2* vel,
2014 const Pilot *parent, const unsigned int target, int aim )
2015{
2016 WeaponLayer layer;
2017 Weapon *w, **m;
2018 GLsizei size;
2019 size_t bufsize;
2020
2021 if (!outfit_isBeam(po->outfit)) {
2022 ERR(_("Trying to create a Beam Weapon from a non-beam outfit."));
2023 return -1;
2024 }
2025
2026 layer = (parent->id==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
2027 w = weapon_create( po, 0., dir, pos, vel, parent, target, 0., aim );
2028 w->ID = ++beam_idgen;
2029 w->mount = po;
2030 w->timer2 = 0.;
2031
2032 /* set the proper layer */
2033 switch (layer) {
2034 case WEAPON_LAYER_BG:
2035 m = &array_grow(&wbackLayer);
2036 break;
2037 case WEAPON_LAYER_FG:
2038 m = &array_grow(&wfrontLayer);
2039 break;
2040
2041 default:
2042 ERR(_("Invalid WEAPON_LAYER specified"));
2043 return -1;
2044 }
2045 *m = w;
2046
2047 /* Grow the vertex stuff if needed. */
2049 if (bufsize != weapon_vboSize) {
2050 weapon_vboSize = bufsize;
2051 size = sizeof(GLfloat) * (2+4) * weapon_vboSize;
2052 weapon_vboData = realloc( weapon_vboData, size );
2053 if (weapon_vbo == NULL)
2054 weapon_vbo = gl_vboCreateStream( size, NULL );
2056 }
2057
2058 return w->ID;
2059}
2060
2067void beam_end( const unsigned int parent, unsigned int beam )
2068{
2069 WeaponLayer layer;
2070 Weapon **curLayer;
2071
2072 layer = (parent==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG;
2073
2074 /* set the proper layer */
2075 switch (layer) {
2076 case WEAPON_LAYER_BG:
2077 curLayer = wbackLayer;
2078 break;
2079 case WEAPON_LAYER_FG:
2080 curLayer = wfrontLayer;
2081 break;
2082
2083 default:
2084 ERR(_("Invalid WEAPON_LAYER specified"));
2085 return;
2086 }
2087
2088#if DEBUGGING
2089 if (beam==0) {
2090 WARN(_("Trying to remove beam with ID 0!"));
2091 return;
2092 }
2093#endif /* DEBUGGING */
2094
2095 /* Now try to destroy the beam. */
2096 for (int i=0; i<array_size(curLayer); i++) {
2097 if (curLayer[i]->ID == beam) { /* Found it. */
2098 weapon_miss(curLayer[i]);
2099 break;
2100 }
2101 }
2102}
2103
2109static void weapon_destroy( Weapon* w )
2110{
2111 /* Just mark for removal. */
2112 weapon_setFlag( w, WEAPON_FLAG_DESTROYED );
2113}
2114
2120static void weapon_free( Weapon* w )
2121{
2122 Pilot *pilot_target = pilot_get( w->target );
2123
2124 /* Stop playing sound if beam weapon. */
2125 if (outfit_isBeam(w->outfit)) {
2126 sound_stop( w->voice );
2127 sound_playPos(w->outfit->u.bem.sound_off,
2128 w->solid->pos.x,
2129 w->solid->pos.y,
2130 w->solid->vel.x,
2131 w->solid->vel.y);
2132 }
2133 else {
2134 /* Decrement target lockons if needed */
2135 if (pilot_target != NULL) {
2136 pilot_target->projectiles--;
2137 if (outfit_isSeeker(w->outfit))
2138 pilot_target->lockons--;
2139 }
2140 }
2141
2142 /* Free the solid. */
2143 solid_free(w->solid);
2144
2145 /* Free the trail, if any. */
2146 spfx_trail_remove(w->trail);
2147
2148 /* Free the Lua ref, if any. */
2149 luaL_unref( naevL, LUA_REGISTRYINDEX, w->lua_mem );
2150
2151#ifdef DEBUGGING
2152 memset(w, 0, sizeof(Weapon));
2153#endif /* DEBUGGING */
2154
2155 free(w);
2156}
2157
2161void weapon_clear (void)
2162{
2163 /* Don't forget to stop the sounds. */
2164 for (int i=0; i < array_size(wbackLayer); i++) {
2165 sound_stop(wbackLayer[i]->voice);
2167 }
2169 for (int i=0; i < array_size(wfrontLayer); i++) {
2170 sound_stop(wfrontLayer[i]->voice);
2172 }
2174}
2175
2179void weapon_exit (void)
2180{
2181 weapon_clear();
2182
2183 /* Destroy front layer. */
2185
2186 /* Destroy back layer. */
2188
2189 /* Destroy VBO. */
2190 free( weapon_vboData );
2191 weapon_vboData = NULL;
2193 weapon_vbo = NULL;
2194}
2195
2199void weapon_explode( double x, double y, double radius,
2200 int dtype, double damage,
2201 const Pilot *parent, int mode )
2202{
2203 (void) dtype;
2204 (void) damage;
2205 weapon_explodeLayer( WEAPON_LAYER_FG, x, y, radius, parent, mode );
2206 weapon_explodeLayer( WEAPON_LAYER_BG, x, y, radius, parent, mode );
2207}
2208
2212static void weapon_explodeLayer( WeaponLayer layer,
2213 double x, double y, double radius,
2214 const Pilot *parent, int mode )
2215{
2216 (void)parent;
2217 Weapon **curLayer;
2218 double rad2;
2219
2220 /* set the proper layer */
2221 switch (layer) {
2222 case WEAPON_LAYER_BG:
2223 curLayer = wbackLayer;
2224 break;
2225 case WEAPON_LAYER_FG:
2226 curLayer = wfrontLayer;
2227 break;
2228
2229 default:
2230 ERR(_("Invalid WEAPON_LAYER specified"));
2231 return;
2232 }
2233
2234 rad2 = radius*radius;
2235
2236 /* Now try to destroy the weapons affected. */
2237 for (int i=0; i<array_size(curLayer); i++) {
2238 if (((mode & EXPL_MODE_MISSILE) && outfit_isLauncher(curLayer[i]->outfit)) ||
2239 ((mode & EXPL_MODE_BOLT) && outfit_isBolt(curLayer[i]->outfit))) {
2240
2241 double dist = pow2(curLayer[i]->solid->pos.x - x) +
2242 pow2(curLayer[i]->solid->pos.y - y);
2243
2244 if (dist < rad2)
2245 weapon_destroy(curLayer[i]);
2246 }
2247 }
2248}
void ai_attacked(Pilot *attacked, const unsigned int attacker, double dmg)
Triggers the attacked() function in the pilot's AI.
Definition: ai.c:840
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition: array.h:202
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition: array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition: array.h:194
#define array_reserved(array)
Returns number of elements reserved.
Definition: array.h:187
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
void asteroid_hit(Asteroid *a, const Damage *dmg, int max_rarity, double mining_bonus)
Hits an asteroid.
Definition: asteroid.c:1031
double cam_getZoom(void)
Gets the camera zoom.
Definition: camera.c:97
void RotatePolygon(CollPoly *rpolygon, CollPoly *ipolygon, float theta)
Rotates a polygon.
Definition: collision.c:325
int CollideLinePolygon(const vec2 *ap, double ad, double al, const CollPoly *bt, const vec2 *bp, vec2 crash[2])
Checks to see if a line collides with a polygon.
Definition: collision.c:650
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:501
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:97
int CollidePolygon(const CollPoly *at, const vec2 *ap, const CollPoly *bt, const vec2 *bp, vec2 *crash)
Checks whether or not two polygons collide. /!\ The function is not symmetric: the points of polygon ...
Definition: collision.c:254
void col_blend(glColour *blend, const glColour *fg, const glColour *bg, float alpha)
Blends two colours.
Definition: colour.c:192
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition: faction.c:1197
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition: mat4.c:99
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition: mat4.c:82
void mat4_rotate2d(mat4 *m, double angle)
Rotates an angle, in radians, around the z axis.
Definition: mat4.c:111
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition: naev.h:40
#define CLAMP(a, b, x)
Definition: naev.h:41
#define ABS(x)
Definition: naev.h:36
#define pow2(x)
Definition: naev.h:46
#define FABS(x)
Definition: naev.h:37
#define MAX(x, y)
Definition: naev.h:39
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition: nlua_pilot.c:495
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition: nlua_vec2.c:139
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_getSpriteFromDir(int *x, int *y, const glTexture *t, const double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition: opengl_tex.c:857
void gl_vboDestroy(gl_vbo *vbo)
Destroys a VBO.
Definition: opengl_vbo.c:248
gl_vbo * gl_vboCreateStream(GLsizei size, const void *data)
Creates a stream vbo.
Definition: opengl_vbo.c:147
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:228
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:100
void gl_vboSubData(gl_vbo *vbo, GLint offset, GLsizei size, const void *data)
Loads some data into the VBO.
Definition: opengl_vbo.c:132
double outfit_trackmin(const Outfit *o)
Gets the outfit's minimal tracking.
Definition: outfit.c:782
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition: outfit.c:488
int outfit_soundHit(const Outfit *o)
Gets the outfit's hit sound.
Definition: outfit.c:829
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition: outfit.c:498
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition: outfit.c:508
int outfit_miningRarity(const Outfit *o)
Gets the maximum rarity the outfit can mine up to.
Definition: outfit.c:806
int outfit_spfxShield(const Outfit *o)
Gets the outfit's sound effect.
Definition: outfit.c:636
const CollPoly * outfit_plg(const Outfit *o)
Gets the outfit's collision polygon.
Definition: outfit.c:615
const glTexture * outfit_gfx(const Outfit *o)
Gets the outfit's graphic effect.
Definition: outfit.c:605
double outfit_spin(const Outfit *o)
Gets the outfit's animation spin.
Definition: outfit.c:771
double outfit_trackmax(const Outfit *o)
Gets the outfit's minimal tracking.
Definition: outfit.c:794
int outfit_spfxArmour(const Outfit *o)
Gets the outfit's sound effect.
Definition: outfit.c:625
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition: outfit.c:647
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition: outfit.c:478
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition: pilot.c:651
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition: pilot.c:589
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition: pilot.c:1111
static Pilot ** pilot_stack
Definition: pilot.c:57
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition: pilot.c:83
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:1381
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:387
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:225
double pilot_heatAccuracyMod(double T)
Returns a 0:1 modifier representing accuracy (0. being normal).
Definition: pilot_heat.c:296
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:164
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
Definition: pilot_outfit.c:148
void pilot_stopBeam(Pilot *p, PilotOutfitSlot *w)
Stops a beam outfit and sets delay as appropriate.
Definition: pilot_weapon.c:839
Player_t player
Definition: player.c:73
static const double c[]
Definition: rng.c:264
static const double a[]
Definition: rng.c:247
static const double d[]
Definition: rng.c:273
static const double b[]
Definition: rng.c:256
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition: sound.c:835
void sound_stop(int voice)
Stops a voice from playing.
Definition: sound.c:1082
int sound_updatePos(int voice, double px, double py, double vx, double vy)
Updates the position of a voice.
Definition: sound.c:892
int space_isSimulationEffects(void)
returns whether or not we're simulating with effects.
Definition: space.c:1490
StarSystem * cur_system
Definition: space.c:105
void spfx_trail_sample(Trail_spfx *trail, double x, double y, TrailMode mode, int force)
Makes a trail grow.
Definition: spfx.c:743
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition: spfx.c:779
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:462
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition: spfx.c:675
Represents an asteroid field anchor.
Definition: asteroid.h:100
double margin
Definition: asteroid.h:115
double radius
Definition: asteroid.h:107
Asteroid * asteroids
Definition: asteroid.h:105
Represents a single asteroid.
Definition: asteroid.h:76
vec2 vel
Definition: asteroid.h:87
vec2 pos
Definition: asteroid.h:86
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:111
int type
Definition: outfit.h:112
double disable
Definition: outfit.h:115
double penetration
Definition: outfit.h:113
double damage
Definition: outfit.h:114
double duration
Definition: outfit.h:158
double min_duration
Definition: outfit.h:159
double range
Definition: outfit.h:162
double dispersion
Definition: outfit.h:133
double falloff
Definition: outfit.h:125
double range
Definition: outfit.h:124
double swivel
Definition: outfit.h:132
double speed
Definition: outfit.h:123
double speed_dispersion
Definition: outfit.h:134
double iflockon
Definition: outfit.h:194
double dispersion
Definition: outfit.h:199
double speed_dispersion
Definition: outfit.h:200
A ship outfit, depends radically on the type.
Definition: outfit.h:304
OutfitLauncherData lau
Definition: outfit.h:373
OutfitBeamData bem
Definition: outfit.h:372
OutfitBoltData blt
Definition: outfit.h:371
OutfitType type
Definition: outfit.h:369
union Outfit::@22 u
Stores an outfit the pilot has.
Definition: pilot.h:108
double stimer
Definition: pilot.h:124
double timer
Definition: pilot.h:125
unsigned int inrange
Definition: pilot.h:130
const Outfit * outfit
Definition: pilot.h:112
The representation of an in-game pilot.
Definition: pilot.h:210
Solid * solid
Definition: pilot.h:220
ShipStats stats
Definition: pilot.h:286
unsigned int id
Definition: pilot.h:211
int projectiles
Definition: pilot.h:367
int tsy
Definition: pilot.h:225
int tsx
Definition: pilot.h:224
int lockons
Definition: pilot.h:366
int nav_anchor
Definition: pilot.h:338
int faction
Definition: pilot.h:215
unsigned int target
Definition: pilot.h:334
int nav_asteroid
Definition: pilot.h:339
Pilot * p
Definition: player.h:101
double launch_damage
Definition: shipstats.h:259
double fwd_dam_as_dis
Definition: shipstats.h:278
double tur_dam_as_dis
Definition: shipstats.h:286
double tur_damage
Definition: shipstats.h:282
double fwd_damage
Definition: shipstats.h:275
double mining_bonus
Definition: shipstats.h:301
double launch_calibration
Definition: shipstats.h:262
double launch_range
Definition: shipstats.h:258
Represents a solid in the game.
Definition: physics.h:17
vec2 vel
Definition: physics.h:21
vec2 pos
Definition: physics.h:22
A trail generated by a ship or an ammo.
Definition: spfx.h:64
In-game representation of a weapon.
Definition: weapon.c:59
int sx
Definition: weapon.c:84
Trail_spfx * trail
Definition: weapon.c:86
int sy
Definition: weapon.c:85
PilotOutfitSlot * mount
Definition: weapon.c:80
int voice
Definition: weapon.c:72
double paramf
Definition: weapon.c:74
Solid * solid
Definition: weapon.c:61
double timer2
Definition: weapon.c:73
unsigned int ID
Definition: weapon.c:62
double life
Definition: weapon.c:75
int faction
Definition: weapon.c:64
double dam_as_dis_mod
Definition: weapon.c:71
const Outfit * outfit
Definition: weapon.c:67
double real_vel
Definition: weapon.c:69
unsigned int target
Definition: weapon.c:66
double anim
Definition: weapon.c:77
WeaponStatus status
Definition: weapon.c:92
double dam_mod
Definition: weapon.c:70
unsigned int flags
Definition: weapon.c:60
unsigned int parent
Definition: weapon.c:65
int lua_mem
Definition: weapon.c:81
GLfloat r
Definition: weapon.c:78
double strength
Definition: weapon.c:83
int sprite
Definition: weapon.c:79
double timer
Definition: weapon.c:76
double falloff
Definition: weapon.c:82
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34
double sw
Definition: opengl_tex.h:44
double sx
Definition: opengl_tex.h:42
double sy
Definition: opengl_tex.h:43
Definition: mat4.h:10
Definition: msgcat.c:199
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33
void weapon_add(PilotOutfitSlot *po, const double T, const double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, unsigned int target, double time, int aim)
Creates a new weapon.
Definition: weapon.c:1953
static void weapon_render(Weapon *w, const double dt)
Renders an individual weapon.
Definition: weapon.c:792
void beam_end(const unsigned int parent, unsigned int beam)
Ends a beam weapon.
Definition: weapon.c:2067
static void weapon_sample_trail(Weapon *w)
Updates the animated trail for a weapon.
Definition: weapon.c:1194
static int weapon_checkCanHit(const Weapon *w, const Pilot *p)
Checks to see if the weapon can hit the pilot.
Definition: weapon.c:882
void weapons_render(const WeaponLayer layer, const double dt)
Renders all the weapons in a layer.
Definition: weapon.c:715
static void weapon_destroy(Weapon *w)
Destroys a weapon.
Definition: weapon.c:2109
static gl_vbo * weapon_vbo
Definition: weapon.c:100
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:1616
static Weapon * weapon_create(PilotOutfitSlot *po, double T, const double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const unsigned int target, double time, int aim)
Creates a new weapon.
Definition: weapon.c:1836
static void weapon_hitAstBeam(Weapon *w, Asteroid *a, WeaponLayer layer, vec2 pos[2], const double dt)
Weapon hit an asteroid.
Definition: weapon.c:1453
static void weapon_update(Weapon *w, const double dt, WeaponLayer layer)
Updates an individual weapon.
Definition: weapon.c:951
static void weapons_updateLayer(const double dt, const WeaponLayer layer)
Updates all the weapons in the layer.
Definition: weapon.c:565
static void weapon_setTurn(Weapon *w, double turn)
Sets the weapon's turn.
Definition: weapon.c:310
void weapon_clear(void)
Clears all the weapons, does NOT free the layers.
Definition: weapon.c:2161
void weapons_update(const double dt)
Updates all the weapon layers.
Definition: weapon.c:548
void weapon_minimap(const double res, const double w, const double h, const RadarShape shape, double alpha)
Draws the minimap weapons (used in player.c).
Definition: weapon.c:172
void weapon_explode(double x, double y, double radius, int dtype, double damage, const Pilot *parent, int mode)
Clears possible exploded weapons.
Definition: weapon.c:2199
#define weapon_isSmart(w)
Definition: weapon.c:37
static void weapon_setThrust(Weapon *w, double thrust)
Sets the weapon's thrust.
Definition: weapon.c:302
void weapon_hitAI(Pilot *p, const Pilot *shooter, double dmg)
Informs the AI if needed that it's been hit.
Definition: weapon.c:1227
static GLfloat * weapon_vboData
Definition: weapon.c:101
static void weapon_hit(Weapon *w, Pilot *p, vec2 *pos)
Weapon hit the pilot.
Definition: weapon.c:1270
unsigned int beam_start(PilotOutfitSlot *po, const double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const unsigned int target, int aim)
Starts a beam weapon.
Definition: weapon.c:2012
static void weapon_hitBeam(Weapon *w, Pilot *p, WeaponLayer layer, vec2 pos[2], const double dt)
Weapon hit the pilot.
Definition: weapon.c:1398
static void think_seeker(Weapon *w, const double dt)
The AI of seeker missiles.
Definition: weapon.c:321
static void weapon_hitAst(Weapon *w, Asteroid *a, WeaponLayer layer, vec2 *pos)
Weapon hit an asteroid.
Definition: weapon.c:1354
static double weapon_aimTurret(const Outfit *outfit, const Pilot *parent, const Pilot *pilot_target, const vec2 *pos, const vec2 *vel, double dir, double swivel, double time)
Gets the aim position of a turret weapon.
Definition: weapon.c:1498
static void weapon_free(Weapon *w)
Frees the weapon.
Definition: weapon.c:2120
static void weapon_miss(Weapon *w)
Weapon missed and is due to be destroyed.
Definition: weapon.c:1323
void weapon_exit(void)
Destroys all the weapons and frees it all.
Definition: weapon.c:2179
static unsigned int beam_idgen
Definition: weapon.c:105
static void weapons_purgeLayer(Weapon **layer)
Purges weapons marked for deletion.
Definition: weapon.c:698
void weapon_init(void)
Initializes the weapon stuff.
Definition: weapon.c:157
static size_t weapon_vboSize
Definition: weapon.c:102
static void weapon_createAmmo(Weapon *w, const Outfit *outfit, double T, const 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:1729
static void weapon_createBolt(Weapon *w, const Outfit *outfit, double T, const 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:1649
static void weapon_explodeLayer(WeaponLayer layer, double x, double y, double radius, const Pilot *parent, int mode)
Explodes all the things on a layer.
Definition: weapon.c:2212
static void think_beam(Weapon *w, const double dt)
The pseudo-ai of the beam weapons.
Definition: weapon.c:439
static Weapon ** wbackLayer
Definition: weapon.c:96
WeaponStatus
Definition: weapon.c:40
@ WEAPON_STATUS_LOCKING
Definition: weapon.c:41
@ WEAPON_STATUS_UNJAMMED
Definition: weapon.c:43
@ WEAPON_STATUS_JAMMED
Definition: weapon.c:44
@ WEAPON_STATUS_OK
Definition: weapon.c:42
@ WEAPON_STATUS_JAMMED_SLOWED
Definition: weapon.c:45
static Weapon ** wfrontLayer
Definition: weapon.c:97