Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Weapon.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: Weapon.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Weapon class
13 */
14 
15 #include "MemDebug.h"
16 #include "Weapon.h"
17 #include "Shot.h"
18 #include "Drone.h"
19 #include "Contact.h"
20 #include "Ship.h"
21 #include "Sim.h"
22 #include "SimEvent.h"
23 #include "Random.h"
24 
25 #include "NetGame.h"
26 #include "NetUtil.h"
27 
28 #include "Game.h"
29 #include "Solid.h"
30 
31 // +----------------------------------------------------------------------+
32 
33 Weapon::Weapon(WeaponDesign* d, int nmuz, Vec3* muzzles, double az, double el)
34 : System(WEAPON, d->type, d->name, d->value,
35 d->capacity, d->capacity, d->recharge_rate),
36 design(d), group(d->group), ammo(-1), ripple_count(0),
37 aim_azimuth((float) az), aim_elevation((float) el),
38 old_azimuth(0.0f), old_elevation(0.0f), aim_time(0),
39 enabled(true), refire(0.0f),
40 mass(d->carry_mass), resist(d->carry_resist),
41 guided(d->guided), shot_speed(d->speed),
42 active_barrel(0), locked(false), centered(false), firing(false), blocked(false),
43 index(0), target(0), subtarget(0), beams(0), orders(MANUAL),
44 control(SINGLE_FIRE), sweep(SWEEP_TIGHT), turret(0), turret_base(0)
45 {
46  ZeroMemory(visible_stores, sizeof(visible_stores));
47 
48  if (design->primary)
49  abrv = Game::GetText("sys.weapon.primary.abrv");
50  else
51  abrv = Game::GetText("sys.weapon.secondary.abrv");
52 
53  nbarrels = nmuz;
54 
55  if (nbarrels > MAX_BARRELS)
57 
58  if (nbarrels == 0 && design->nbarrels > 0) {
60 
61  for (int i = 0; i < nbarrels; i++)
63 
64  ammo = design->ammo * nbarrels;
65  }
66  else if (nbarrels == 1 && design->nstores > 0) {
68 
69  for (int i = 0; i < nbarrels; i++)
70  muzzle_pts[i] = rel_pts[i] = (muzzles[0] + design->attachments[i]);
71 
72  ammo = nbarrels;
73  }
74  else {
75  for (int i = 0; i < nbarrels; i++)
76  muzzle_pts[i] = rel_pts[i] = muzzles[i];
77 
78  ammo = design->ammo * nbarrels;
79  }
80 
81  if (design->syncro)
82  active_barrel = -1;
83 
84  emcon_power[0] = 0;
85  emcon_power[1] = 0;
86  emcon_power[2] = 100;
87 
91 
95 }
96 
97 // +----------------------------------------------------------------------+
98 
100 : System(w), design(w.design), ammo(-1), ripple_count(0),
101 enabled(true), refire(0.0f),
102 mass(w.mass), resist(w.resist),
103 aim_azimuth(w.aim_azimuth), aim_elevation(w.aim_elevation),
104 old_azimuth(0.0f), old_elevation(0.0f), aim_time(0),
105 guided(w.guided), shot_speed(w.shot_speed),
106 active_barrel(0), locked(false), centered(false), firing(false), blocked(false),
107 target(0), subtarget(0), beams(0), orders(MANUAL),
108 control(SINGLE_FIRE), sweep(SWEEP_TIGHT), group(w.group),
109 aim_az_max(w.aim_az_max), aim_az_min(w.aim_az_min), aim_az_rest(w.aim_az_rest),
110 aim_el_max(w.aim_el_max), aim_el_min(w.aim_el_min), aim_el_rest(w.aim_el_rest),
111 turret(0), turret_base(0)
112 {
113  Mount(w);
114  ZeroMemory(visible_stores, sizeof(visible_stores));
115 
116  nbarrels = w.nbarrels;
117 
118  for (int i = 0; i < nbarrels; i++) {
119  muzzle_pts[i] = rel_pts[i] = w.muzzle_pts[i];
120  }
121 
122  ammo = design->ammo * nbarrels;
123 
124  if (design->syncro)
125  active_barrel = -1;
126 
127  if (design->beam) {
128  beams = new(__FILE__,__LINE__) Shot* [nbarrels];
129  ZeroMemory(beams, sizeof(Shot*) * nbarrels);
130  }
131 
132  if (aim_az_rest >= 2*PI)
134 
135  if (aim_el_rest >= 2*PI)
137 }
138 
139 // +--------------------------------------------------------------------+
140 
142 {
143  if (beams) {
144  for (int i = 0; i < nbarrels; i++) {
145  if (beams[i]) {
146  Ignore(beams[i]);
147  delete beams[i];
148  beams[i] = 0;
149  }
150  }
151 
152  delete [] beams;
153  }
154 
157 
158  for (int i = 0; i < MAX_BARRELS; i++)
160 }
161 
162 // +--------------------------------------------------------------------+
163 
164 bool
166 {
167  return design->primary;
168 }
169 
170 bool
172 {
173  return design->drone;
174 }
175 
176 bool
178 {
179  return design->decoy_type != 0;
180 }
181 
182 bool
184 {
185  return design->probe != 0;
186 }
187 
188 bool
190 {
191  return !design->primary;
192 }
193 
194 bool
196 {
197  return design->beam;
198 }
199 
200 // +--------------------------------------------------------------------+
201 
202 Shot*
204 {
205  if (beams && i >= 0 && i < nbarrels)
206  return beams[i];
207 
208  return 0;
209 }
210 
211 // +--------------------------------------------------------------------+
212 
213 void
215 {
216  ship = s;
217 
218  if (design->turret_model) {
219  Solid* t = new(__FILE__,__LINE__) Solid;
221  turret = t;
222  }
223 
224  if (design->turret_base_model) {
225  Solid* t = new(__FILE__,__LINE__) Solid;
227  turret_base = t;
228  }
229 
230  if (!design->primary &&
232  ammo == nbarrels &&
233  design->shot_model != 0)
234  {
235  for (int i = 0; i < nbarrels; i++) {
236  Solid* s = new(__FILE__,__LINE__) Solid;
238 
239  visible_stores[i] = s;
240  }
241  }
242 }
243 
244 Solid*
246 {
247  return turret;
248 }
249 
250 Solid*
252 {
253  return turret_base;
254 }
255 
256 Solid*
258 {
259  if (i >= 0 && i < MAX_BARRELS)
260  return visible_stores[i];
261 
262  return 0;
263 }
264 
265 void
267 {
268  if (a >= 0) {
269  if (active_barrel >= 0 && design->visible_stores) {
270  while (a < ammo) {
271  if (active_barrel >= nbarrels)
272  active_barrel = 0;
273 
275  GRAPHIC_DESTROY(visible_stores[active_barrel]);
276  active_barrel++;
277  ammo--;
278  }
279  }
280  }
281 
282  ammo = a;
283  }
284 }
285 
286 // +--------------------------------------------------------------------+
287 
288 void
289 Weapon::ExecFrame(double seconds)
290 {
291  System::ExecFrame(seconds);
292 
293  if (refire > 0)
294  refire -= (float) seconds;
295 
296  locked = false;
297  centered = false;
298 
299  if (!ship)
300  return;
301 
302  if (orders == POINT_DEFENSE && enabled)
303  SelectTarget();
304 
305  if (beams && !target) {
306  for (int i = 0; i < nbarrels; i++) {
307  if (beams[i]) {
308  // aim beam straight:
309  Aim();
310  SetBeamPoints(false);
311  return;
312  }
313  }
314  }
315 
316  if (design->self_aiming) {
318  }
319  else if (turret) {
320  ZeroAim();
321  }
322 
323  if (ship->CheckFire())
324  return;
325 
326  // aim beam at target:
327  bool aim_beams = false;
328 
329  if (beams) {
330  for (int i = 0; i < nbarrels; i++) {
331  if (beams[i]) {
332  aim_beams = true;
333  SetBeamPoints(true);
334  break;
335  }
336  }
337  }
338 
339  if (!aim_beams) {
340  if (ripple_count > 0) {
341  if (Fire())
342  ripple_count--;
343  }
344 
345  else if (locked && !blocked) {
346  if (!ship->IsHostileTo(target))
347  return;
348 
349  if (orders == AUTO && centered) {
350  if (energy >= design->charge &&
351  (ammo < 0 || target && target->Integrity() >= 1) &&
353  Fire();
354  }
355 
356  else if (orders == POINT_DEFENSE) {
357  if (energy >= design->min_charge &&
358  (ammo < 0 || target && target->Integrity() >= 1) &&
360  Fire();
361  }
362  }
363  }
364 }
365 
366 // +----------------------------------------------------------------------+
367 
368 void
369 Weapon::Distribute(double delivered_energy, double seconds)
370 {
371  if (UsesWatts()) {
372  if (seconds < 0.01)
373  seconds = 0.01;
374 
375  // convert Joules to Watts:
376  energy = (float) (delivered_energy/seconds);
377  }
378 
379  else if (!Game::Paused()) {
380  energy += (float) (delivered_energy * 1.25);
381 
382  if (energy > capacity)
383  energy = capacity;
384 
385  else if (energy < 0)
386  energy = 0.0f;
387  }
388 }
389 
390 
391 // +--------------------------------------------------------------------+
392 
393 bool
395 {
396  if (obj == target) {
397  target = 0;
398  }
399 
400  else if (beams) {
401  for (int i = 0; i < nbarrels; i++)
402  if (obj == beams[i])
403  beams[i] = 0;
404  }
405 
406  return SimObserver::Update(obj);
407 }
408 
409 const char*
411 {
412  static char name[256];
413  sprintf_s(name, "Weapon %s", design->name.data());
414  return name;
415 }
416 
417 // +--------------------------------------------------------------------+
418 
419 void
421 {
422  if (o >= MANUAL && o <= POINT_DEFENSE)
423  orders = o;
424 }
425 
426 void
428 {
429  if (m >= SINGLE_FIRE && m <= SALVO_FIRE)
430  control = m;
431 }
432 
433 void
435 {
436  if (s >= SWEEP_NONE && s <= SWEEP_WIDE)
437  sweep = s;
438 }
439 
440 // +--------------------------------------------------------------------+
441 
442 bool
443 Weapon::CanTarget(DWORD classification) const
444 {
445  return (design->target_type & classification) ? true : false;
446 }
447 
448 // +--------------------------------------------------------------------+
449 
450 void
452 {
453  // check self targeting:
454  if (targ == (SimObject*) ship)
455  return;
456 
457  // check target class filter:
458  if (targ) {
459  switch (targ->Type()) {
460  case SimObject::SIM_SHIP: {
461  Ship* tgt_ship = (Ship*) targ;
462 
463  if ((tgt_ship->Class() & design->target_type) == 0)
464  return;
465  }
466  break;
467 
468  case SimObject::SIM_SHOT:
469  return;
470 
471  case SimObject::SIM_DRONE: {
472  if ((design->target_type & Ship::DRONE) == 0)
473  return;
474  }
475  break;
476 
477  default:
478  return;
479  }
480  }
481 
482  // if ok, target this object:
483  if (target != targ) {
484  target = targ;
485 
486  if (target)
487  Observe(target);
488  }
489 
490  subtarget = sub;
491 }
492 
493 // +--------------------------------------------------------------------+
494 
495 void
497 {
498  bool select_locked = false;
499  SimObject* targ = 0;
500  double dist = 1e9;
501  double az = 0;
502  double el = 0;
503 
504  if (ammo && enabled && (availability > crit_level)) {
505  ZeroAim();
506 
507  ListIter<Contact> contact = ship->ContactList();
508 
509  // lock onto any threatening shots first (if we can):
510  if (design->target_type & Ship::DRONE) {
511  while (++contact) {
512  Shot* c_shot = contact->GetShot();
513 
514  if (c_shot && contact->Threat(ship)) {
515 
516  // distance from self to target:
517  double distance = Point(c_shot->Location() - muzzle_pts[0]).length();
518 
519  if (distance > design->min_range &&
520  distance < design->max_range &&
521  distance < dist) {
522  // check aim basket:
523  select_locked = CanLockPoint(c_shot->Location(), az, el);
524 
525  if (select_locked) {
526  targ = c_shot;
527  dist = distance;
528  }
529  }
530  }
531  }
532  }
533 
534  // lock onto a threatening ship only if it is (much) closer:
535  dist *= 0.2;
536  contact.reset();
537  while (++contact) {
538  Ship* c_ship = contact->GetShip();
539 
540  if (!c_ship) continue;
541 
542  // can we lock onto this target?
543  if ((c_ship->IsRogue() || c_ship->GetIFF() > 0 && c_ship->GetIFF() != ship->GetIFF()) &&
544  (c_ship->Class() & design->target_type) &&
545  c_ship->Weapons().size() > 0) {
546  // distance from self to target:
547  double distance = Point(c_ship->Location() - muzzle_pts[0]).length();
548 
549  if (distance < design->max_range && distance < dist) {
550  // check aim basket:
551  select_locked = CanLockPoint(c_ship->Location(), az, el);
552 
553  if (select_locked) {
554  targ = c_ship;
555  dist = distance;
556  }
557  }
558  }
559  }
560  }
561 
562  if (!ammo || !enabled) {
563  SetTarget(0,0);
564  locked = false;
565  }
566 
567  else {
568  SetTarget(targ, 0);
569  }
570 }
571 
572 // +--------------------------------------------------------------------+
573 
574 int
576 {
577  if (ammo && enabled && (availability > crit_level)) {
578  firing = 0;
579  Aim();
580  }
581  else if (turret) {
582  ZeroAim();
583  }
584 
585  return locked;
586 }
587 
588 // +--------------------------------------------------------------------+
589 
590 Shot*
592 {
593  if (Game::Paused())
594  return 0;
595 
596  if (ship && ship->CheckFire())
597  return 0;
598 
599  if (ship->IsStarship() && target && !centered)
600  return 0;
601 
603  return 0;
604 
605  Shot* shot = 0;
606 
607  if (ammo && enabled &&
608  (refire <= 0) && (energy > design->min_charge) &&
609  (availability > crit_level)) {
610 
612 
613  NetGame* net_game = NetGame::GetInstance();
614  bool net_client = net_game ? net_game->IsClient() : false;
615 
616  // all barrels
617  if (active_barrel < 0) {
618  if (net_client || IsPrimary())
620 
621  if (!net_game || IsPrimary()) {
622  for (int i = 0; i < nbarrels; i++)
623  shot = FireBarrel(i);
624  }
625 
626  else if (net_game && net_game->IsServer() && IsMissile()) {
627  for (int i = 0; i < nbarrels; i++) {
628  shot = FireBarrel(i);
629  NetUtil::SendWepRelease(this, shot);
630  }
631  }
632  }
633 
634  // single barrel
635  else {
636  if (net_client || IsPrimary())
638 
639  if (!net_game || IsPrimary()) {
640  shot = FireBarrel(active_barrel++);
641  }
642 
643  else if (net_game && net_game->IsServer() && IsMissile()) {
644  shot = FireBarrel(active_barrel++);
645  NetUtil::SendWepRelease(this, shot);
646  }
647 
648  if (active_barrel >= nbarrels) {
649  active_barrel = 0;
651  }
652  }
653 
654  if (design->ripple_count > 0 && ripple_count <= 0)
656 
657  if (status != NOMINAL)
658  refire *= 2;
659  }
660 
661  return shot;
662 }
663 
664 // +--------------------------------------------------------------------+
665 
666 Shot*
668 {
669  Shot* shot = 0;
670 
671  if (!IsPrimary() || Game::Paused())
672  return shot;
673 
674  if (active_barrel < 0 || active_barrel >= nbarrels)
675  active_barrel = 0;
676 
677  target = tgt;
678  subtarget = sub;
679  aim_time = 0;
680 
681  for (int i = 0; i < count; i++) {
682  shot = FireBarrel(active_barrel++);
683 
684  if (active_barrel >= nbarrels) {
685  active_barrel = 0;
687  }
688  }
689 
690  if (target)
691  Observe(target);
692 
693  return shot;
694 }
695 
696 Shot*
697 Weapon::NetFireSecondary(SimObject* tgt, System* sub, DWORD objid)
698 {
699  Shot* shot = 0;
700 
701  if (IsPrimary() || Game::Paused())
702  return shot;
703 
704  if (active_barrel < 0 || active_barrel >= nbarrels)
705  active_barrel = 0;
706 
707  target = tgt;
708  subtarget = sub;
709  aim_time = 0;
710 
711  shot = FireBarrel(active_barrel++);
712 
713  if (active_barrel >= nbarrels) {
714  active_barrel = 0;
716  }
717 
718  if (shot)
719  shot->SetObjID(objid);
720 
721  if (target)
722  Observe(target);
723 
724  return shot;
725 }
726 
727 // +--------------------------------------------------------------------+
728 
729 Shot*
731 {
732  const Point& base_vel = ship->Velocity();
733  Shot* shot = 0;
734  SimRegion* region = ship->GetRegion();
735 
736  if (!region || n < 0 || n >= nbarrels || Game::Paused())
737  return 0;
738 
739  firing = 1;
740  Aim();
741 
742  Camera rail_cam;
743  rail_cam.Clone(aim_cam);
744 
745  Point shotpos = muzzle_pts[n];
746  if (design->length > 0)
747  shotpos = shotpos + aim_cam.vpn() * design->length;
748 
749  // guns may be slewed towards target:
750  if (design->primary) {
751  shot = CreateShot(shotpos, aim_cam, design, ship);
752 
753  if (shot) {
754  shot->SetVelocity(shot->Velocity() + base_vel);
755  }
756  }
757 
758  // missiles always launch in rail direction:
759  else {
760  // unless they are on a mobile launcher
761  if (turret && design->self_aiming) {
762  shot = CreateShot(shotpos, aim_cam, design, ship);
763  shot->SetVelocity(base_vel);
764  }
765  else {
766  shot = CreateShot(shotpos, rail_cam, design, ship);
767  if (shot /* && !turret */) {
768  Matrix orient = ship->Cam().Orientation();
769  if (aim_azimuth != 0) orient.Yaw(aim_azimuth);
770  if (aim_elevation != 0) orient.Pitch(aim_elevation);
771 
772  Point eject = design->eject * orient;
773  shot->SetVelocity(base_vel + eject);
774  }
775  }
776 
777  if (shot && visible_stores[n]) {
779  }
780  }
781 
782  if (shot) {
783  if (ammo > 0)
784  ammo--;
785 
786  if (guided && target)
787  shot->SeekTarget(target, subtarget);
788 
789  float shot_load;
790  if (energy > design->charge)
791  shot_load = design->charge;
792  else
793  shot_load = energy;
794 
795  energy -= shot_load;
796  shot->SetCharge(shot_load * availability);
797 
798  if (target && design->flak && !design->guided) {
799  double speed = shot->Velocity().length();
800  double range = (target->Location() - shot->Location()).length();
801 
802  if (range > design->min_range && range < design->max_range) {
803  shot->SetFuse(range / speed);
804  }
805  }
806 
807  region->InsertObject(shot);
808 
809  if (beams) {
810  beams[n] = shot;
811  Observe(beams[n]);
812 
813  // aim beam at target:
814  SetBeamPoints(true);
815  }
816 
817  if (ship) {
818  ShipStats* stats = ShipStats::Find(ship->Name());
819 
820  if (design->primary)
821  stats->AddGunShot();
822  else if (design->decoy_type == 0 && design->damage > 0)
823  stats->AddMissileShot();
824  }
825  }
826 
827  return shot;
828 }
829 
830 Shot*
831 Weapon::CreateShot(const Point& loc, const Camera& cam, WeaponDesign* dsn, const Ship* own)
832 {
833  if (dsn->drone)
834  return new(__FILE__,__LINE__) Drone(loc, cam, dsn, own);
835 
836  else
837  return new(__FILE__,__LINE__) Shot(loc, cam, dsn, own);
838 }
839 
840 // +--------------------------------------------------------------------+
841 
842 void
844 {
845  System::Orient(rep);
846 
847  const Matrix& orientation = rep->Cam().Orientation();
848  Point loc = rep->Location();
849 
850  // align graphics with camera:
851  if (turret) {
852  if (!design->self_aiming)
853  ZeroAim();
854 
855  const Matrix& aim = aim_cam.Orientation();
856 
858  turret->SetOrientation(aim);
859 
860  if (turret_base) {
861  Matrix base = orientation;
862 
863  if (design->turret_axis == 1) {
864  base.Pitch(aim_elevation);
865  base.Pitch(old_elevation);
866  }
867  else {
868  base.Yaw(aim_azimuth);
869  base.Yaw(old_azimuth);
870  }
871 
874  }
875 
876  for (int i = 0; i < nbarrels; i++) {
877  muzzle_pts[i] = (rel_pts[i] * aim) + mount_loc;
878 
879  if (visible_stores[i]) {
882  }
883  }
884  }
885  else {
886  for (int i = 0; i < nbarrels; i++) {
887  muzzle_pts[i] = (rel_pts[i] * orientation) + loc;
888 
889  if (visible_stores[i]) {
890  visible_stores[i]->SetOrientation(orientation);
892  }
893  }
894  }
895 }
896 
897 // +--------------------------------------------------------------------+
898 
899 void
901 {
902  for (int i = 0; i < nbarrels; i++) {
903  if (beams && beams[i]) {
904  Point from = muzzle_pts[i];
905  Point to;
906  double len = design->length;
907 
908  if (len < 1 || len > 1e7)
909  len = design->max_range;
910 
911  if (len < 1)
912  len = 3e5;
913 
914  else if (len > 1e7)
915  len = 1e7;
916 
917  // always use the aim cam, to enforce slew_rate
918  to = from + aim_cam.vpn() * len;
919 
920  beams[i]->SetBeamPoints(from, to);
921  }
922  }
923 }
924 
925 // +--------------------------------------------------------------------+
926 
927 void
929 {
930  locked = false;
931  centered = false;
932 
933  FindObjective();
934 
935  if (target) {
936  double az = 0;
937  double el = 0;
938 
939  locked = CanLockPoint(obj_w, az, el, &objective);
940 
941  double spread_az = design->spread_az;
942  double spread_el = design->spread_el;
943 
944  // beam sweep target:
945  if (design->beam) {
946  double factor = 0;
947  double az_phase = 0;
948  double el_phase = 0;
949 
950  if (target->Type() == SimObject::SIM_SHIP) {
951  Ship* s = (Ship*) target;
952 
953  if (s->IsStarship()) {
954  switch (sweep) {
955  default:
956  case SWEEP_NONE: factor = 0; break;
957  case SWEEP_TIGHT: factor = 1; break;
958  case SWEEP_WIDE: factor = 2; break;
959  }
960  }
961  }
962 
963  if (factor > 0) {
964  factor *= atan2(target->Radius(), (double) objective.z);
965 
966  for (int i = 0; i < nbarrels; i++) {
967  if (beams && beams[i]) {
968  az_phase = sin(beams[i]->Life() * 0.4 * PI);
969  el_phase = sin(beams[i]->Life() * 1.0 * PI);
970  break;
971  }
972  }
973 
974  az += factor * spread_az * az_phase;
975  el += factor * spread_el * el_phase * 0.25;
976  }
977  }
978 
979  else if (!design->beam) {
980  if (spread_az > 0)
981  az += Random(-spread_az, spread_az);
982 
983  if (spread_el > 0)
984  el += Random(-spread_el, spread_el);
985  }
986 
987  AimTurret(az, -el);
988 
989  // check range for guided weapons:
990  if (locked && guided) {
991  double range = objective.length();
992 
993  if (range > design->max_track)
994  locked = false;
995 
996  else if (range > design->max_range) {
997  if (firing) {
998  if (RandomChance(1,4)) // 1 in 4 chance of locking anyway
999  locked = false;
1000  }
1001  else {
1002  locked = false;
1003  }
1004  }
1005  }
1006 
1007  if (locked) {
1008  Point tloc = target->Location();
1009  tloc = Transform(tloc);
1010 
1011  if (tloc.z > 1) {
1012  az = atan2(fabs(tloc.x), tloc.z);
1013  el = atan2(fabs(tloc.y), tloc.z);
1014 
1015  double firing_cone = 10*DEGREES;
1016 
1017  if (orders == MANUAL)
1018  firing_cone = 30*DEGREES;
1019 
1020  if (az < firing_cone && el < firing_cone)
1021  centered = true;
1022  }
1023  }
1024  }
1025  else {
1027  }
1028 }
1029 
1030 void
1032 {
1033  ZeroAim();
1034 
1035  if (!target || !design->self_aiming) {
1036  objective = Point();
1037  return;
1038  }
1039 
1040  obj_w = target->Location();
1041 
1042  if (subtarget) {
1044  }
1045  else if (target->Type() == SimObject::SIM_SHIP) {
1046  Ship* tgt_ship = (Ship*) target;
1047 
1048  if (tgt_ship->IsGroundUnit())
1049  obj_w += Point(0,150,0);
1050  }
1051 
1052  if (!design->beam && shot_speed > 0) {
1053  // distance from self to target:
1054  double distance = Point(obj_w - muzzle_pts[0]).length();
1055 
1056  // TRUE shot speed is relative to ship speed:
1057  Point eff_shot_vel = ship->Velocity() + aim_cam.vpn() * shot_speed - target->Velocity();
1058  double eff_shot_speed = eff_shot_vel.length();
1059 
1060  // time to reach target:
1061  double time = distance / eff_shot_speed;
1062 
1063  // where the target will be when the shot reaches it:
1064  obj_w += (target->Velocity() - ship->Velocity()) * time +
1065  target->Acceleration() * 0.25 * time * time;
1066  }
1067 
1068  // transform into camera coords:
1070 }
1071 
1072 Point
1074 {
1075  Point result;
1076 
1077  Point obj_t = pt - aim_cam.Pos();
1078  result.x = obj_t * aim_cam.vrt();
1079  result.y = obj_t * aim_cam.vup();
1080  result.z = obj_t * aim_cam.vpn();
1081 
1082  return result;
1083 }
1084 
1085 bool
1086 Weapon::CanLockPoint(const Point& test, double& az, double& el, Point* obj)
1087 {
1088  Point pt = Transform(test);
1089  bool locked = true;
1090 
1091  // first compute az:
1092  if (fabs(pt.z) < 0.1) pt.z = 0.1;
1093  az = atan(pt.x / pt.z);
1094  if (pt.z < 0) az -= PI;
1095  if (az < -PI) az += 2*PI;
1096 
1097  // then, rotate target into az-coords to compute el:
1098  Camera tmp;
1099  tmp.Clone(aim_cam);
1100  aim_cam.Yaw(az);
1101  pt = Transform(test);
1102  aim_cam.Clone(tmp);
1103 
1104  if (fabs(pt.z) < 0.1) pt.z = 0.1;
1105  el = atan(pt.y / pt.z);
1106 
1107  if (obj) *obj = pt;
1108 
1109  // is the target in the basket?
1110  // clamp if necessary:
1111 
1112  if (az > aim_az_max) {
1113  az = aim_az_max;
1114  locked = false;
1115  }
1116  else if (az < aim_az_min) {
1117  az = aim_az_min;
1118  locked = false;
1119  }
1120 
1121  if (el > aim_el_max) {
1122  el = aim_el_max;
1123  locked = false;
1124  }
1125  else if (el < aim_el_min) {
1126  el = aim_el_min;
1127  locked = false;
1128  }
1129 
1130  if (IsDrone() && guided) {
1131  double firing_cone = 10*DEGREES;
1132 
1133  if (orders == MANUAL)
1134  firing_cone = 20*DEGREES;
1135 
1136  if (az < firing_cone && el < firing_cone)
1137  locked = true;
1138  }
1139 
1140  return locked;
1141 }
1142 
1143 // +--------------------------------------------------------------------+
1144 
1145 void
1146 Weapon::AimTurret(double az, double el)
1147 {
1148  double seconds = (Game::GameTime() - aim_time) / 1000.0;
1149 
1150  // don't let the weapon turn faster than turret slew rate:
1151  double max_turn = design->slew_rate * seconds;
1152 
1153  if (fabs(az-old_azimuth) > max_turn) {
1154  if (az > old_azimuth)
1155  az = old_azimuth + max_turn;
1156  else
1157  az = old_azimuth - max_turn;
1158  }
1159 
1160  if (fabs(el-old_elevation) > PI/2 * seconds) {
1161  if (el > old_elevation)
1162  el = old_elevation + max_turn;
1163  else
1164  el = old_elevation - max_turn;
1165  }
1166 
1167  aim_cam.Yaw(az);
1168  aim_cam.Pitch(el);
1169 
1170  old_azimuth = (float) az;
1171  old_elevation = (float) el;
1172 
1174 }
1175 
1176 void
1178 {
1179  aim_cam.Clone(ship->Cam());
1180  if (aim_azimuth != 0) aim_cam.Yaw(aim_azimuth);
1182 
1184 }