Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
RadioHandler.cpp
Go to the documentation of this file.
1 /* Project Starshatter 5.0
2  Destroyer Studios LLC
3  Copyright © 1997-2007. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: RadioHandler.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Radio message handler class implementation
13 */
14 
15 #include "MemDebug.h"
16 #include "RadioHandler.h"
17 #include "RadioMessage.h"
18 #include "RadioTraffic.h"
19 #include "Instruction.h"
20 
21 #include "Contact.h"
22 #include "Element.h"
23 #include "Mission.h"
24 #include "Ship.h"
25 #include "ShipDesign.h"
26 #include "Sim.h"
27 #include "StarSystem.h"
28 #include "Power.h"
29 #include "Drive.h"
30 #include "Shield.h"
31 #include "Hangar.h"
32 #include "FlightDeck.h"
33 #include "WeaponGroup.h"
34 #include "SteerAI.h"
35 
36 #include "Text.h"
37 #include "Game.h"
38 
39 // +----------------------------------------------------------------------+
40 
42 { }
43 
45 { }
46 
47 // +----------------------------------------------------------------------+
48 
49 bool
51 {
52  if (!s || !msg || !msg->Sender())
53  return false;
54 
55  if (s->Class() >= Ship::FARCASTER && s->Class() <= Ship::C3I)
56  return false;
57 
58  if (msg->Sender()->IsRogue()) {
59  Ship* sender = (Ship*) msg->Sender(); // cast-away const
60  RadioMessage* nak = new(__FILE__,__LINE__) RadioMessage(sender, s, RadioMessage::NACK);
62  return false;
63  }
64 
65  bool respond = (s != msg->Sender());
66 
67  // SPECIAL CASE:
68  // skip navpoint must be processed by elem leader,
69  // even if the elem leader sent the message:
70 
71  if (msg->Action() == RadioMessage::SKIP_NAVPOINT && !respond)
72  ProcessMessageAction(msg, s);
73 
74  if (!ProcessMessageOrders(msg, s))
75  respond = respond && ProcessMessageAction(msg, s);
76 
77  return respond;
78 }
79 
80 // +----------------------------------------------------------------------+
81 
82 bool
84 {
85  bool result = false;
86 
87  switch (action) {
88  default:
89  case RadioMessage::NONE:
90  case RadioMessage::ACK:
91  case RadioMessage::NACK: result = false; break;
92 
93  // target mgt:
97  case RadioMessage::IDENTIFY: result = true; break;
98 
99  // combat mgt:
102  case RadioMessage::FORM_UP: result = true; break;
103 
106  case RadioMessage::LAUNCH_PROBE: result = false; break;
107 
108  // formation mgt:
112  case RadioMessage::GO_TRAIL: result = true; break;
113 
114  // mission mgt:
115  case RadioMessage::MOVE_PATROL: result = true; break;
116  case RadioMessage::SKIP_NAVPOINT: result = false; break;
117  case RadioMessage::RESUME_MISSION: result = true; break;
118 
119  case RadioMessage::RTB:
122  case RadioMessage::FARCAST_TO: result = true; break;
123 
124  // sensor mgt:
127  case RadioMessage::GO_EMCON3: result = true; break;
128 
129  // support:
132  case RadioMessage::PICTURE: result = false; break;
133 
134  // traffic control:
139  case RadioMessage::CALL_WAVE_OFF: result = false; break;
140  }
141 
142  return result;
143 }
144 
145 // +----------------------------------------------------------------------+
146 
147 bool
149 {
150  Instruction* instruction = ship->GetRadioOrders();
151  int action = 0;
152 
153  if (msg && msg->Action() == RadioMessage::RESUME_MISSION) {
154  instruction->SetAction(RadioMessage::NONE);
155  instruction->SetFormation(-1);
156  instruction->SetWeaponsFree(true);
157  if (instruction->GetTarget()) {
158  instruction->ClearTarget();
159  ship->DropTarget();
160  }
161  return true;
162  }
163 
164  if (msg && IsOrder(msg->Action())) {
165  int posture_only = false;
166 
167  action = msg->Action();
168 
169  if (action == RadioMessage::FORM_UP)
170  action = RadioMessage::WEP_HOLD;
171 
172  // target orders => drop current target:
173  if (action >= RadioMessage::ATTACK &&
174  action <= RadioMessage::COVER_ME ||
175  action == RadioMessage::WEP_HOLD ||
176  action >= RadioMessage::DOCK_WITH &&
177  action <= RadioMessage::FARCAST_TO) {
178 
179  if (ship != msg->Sender())
180  ship->DropTarget();
181 
182  Director* dir = ship->GetDirector();
183  if (dir && dir->Type() >= SteerAI::SEEKER && dir->Type() <= SteerAI::GROUND) {
184  SteerAI* ai = (SteerAI*) dir;
185  ai->SetTarget(0);
186  }
187 
188  // farcast and quantum jump radio messages:
189  if (action >= RadioMessage::QUANTUM_TO) {
190  Sim* sim = Sim::GetSim();
191 
192  if (sim) {
193  SimRegion* rgn = sim->FindRegion(msg->Info());
194 
195  if (rgn) {
196  instruction->SetAction(action);
197  instruction->SetLocation(Point(0,0,0));
198  instruction->SetRegion(rgn);
199  instruction->SetFarcast(action == RadioMessage::FARCAST_TO);
200  instruction->SetWeaponsFree(false);
201  return true;
202  }
203  }
204  }
205  }
206 
207  // formation orders => set formation:
208  if (action >= RadioMessage::GO_DIAMOND &&
209  action <= RadioMessage::GO_TRAIL) {
210 
211  switch (action) {
213  case RadioMessage::GO_SPREAD: instruction->SetFormation(Instruction::SPREAD); break;
214  case RadioMessage::GO_BOX: instruction->SetFormation(Instruction::BOX); break;
215  case RadioMessage::GO_TRAIL: instruction->SetFormation(Instruction::TRAIL); break;
216  }
217 
218  posture_only = true;
219  }
220 
221  // emcon orders => set emcon:
222  if (action >= RadioMessage::GO_EMCON1 &&
223  action <= RadioMessage::GO_EMCON3) {
224 
225  switch (msg->Action()) {
226  case RadioMessage::GO_EMCON1: instruction->SetEMCON(1); break;
227  case RadioMessage::GO_EMCON2: instruction->SetEMCON(2); break;
228  case RadioMessage::GO_EMCON3: instruction->SetEMCON(3); break;
229  }
230 
231  posture_only = true;
232  }
233 
234  if (!posture_only) {
235  instruction->SetAction(action);
236  instruction->ClearTarget();
237 
238  if (msg->TargetList().size() > 0) {
239  SimObject* msg_tgt = msg->TargetList().at(0);
240  instruction->SetTarget(msg_tgt);
241  instruction->SetLocation(msg_tgt->Location());
242  }
243 
244  else if (action == RadioMessage::COVER_ME) {
245  instruction->SetTarget((Ship*) msg->Sender());
246  instruction->SetLocation(msg->Sender()->Location());
247  }
248 
249  else if (action == RadioMessage::MOVE_PATROL) {
250  instruction->SetLocation(msg->Location());
251  }
252 
253  // handle element engagement:
254  if (action == RadioMessage::ATTACK && msg->TargetList().size() > 0) {
255  Element* elem = msg->DestinationElem();
256 
257  if (!elem && msg->DestinationShip())
258  elem = msg->DestinationShip()->GetElement();
259 
260  if (elem) {
261  SimObject* msg_tgt = msg->TargetList().at(0);
262  if (msg_tgt && msg_tgt->Type() == SimObject::SIM_SHIP) {
263  Element* tgt = ((Ship*) msg_tgt)->GetElement();
264  elem->SetAssignment(tgt);
265 
266  if (msg->TargetList().size() > 1)
267  instruction->SetTarget(tgt->Name().data());
268  else
269  instruction->SetTarget(msg_tgt);
270  }
271  else {
272  elem->ResumeAssignment();
273  }
274  }
275  }
276 
277  else if (action == RadioMessage::RESUME_MISSION) {
278  Element* elem = msg->DestinationElem();
279 
280  if (!elem && msg->DestinationShip())
281  elem = msg->DestinationShip()->GetElement();
282 
283  if (elem) {
284  elem->ResumeAssignment();
285  }
286  }
287  }
288 
289  instruction->SetWeaponsFree(action <= RadioMessage::WEP_FREE);
290  return true;
291  }
292 
293  return false;
294 }
295 
296 // +----------------------------------------------------------------------+
297 
298 bool
300 {
301  if (!msg) return false;
302 
303  if (msg->Action() == RadioMessage::CALL_INBOUND)
304  return Inbound(msg, ship);
305 
306  if (msg->Action() == RadioMessage::CALL_FINALS)
307  return true; // acknowledge
308 
310  return Picture(msg, ship);
311 
313  return Support(msg, ship);
314 
315  if (msg->Action() == RadioMessage::SKIP_NAVPOINT)
316  return SkipNavpoint(msg, ship);
317 
318  if (msg->Action() == RadioMessage::LAUNCH_PROBE)
319  return LaunchProbe(msg, ship);
320 
321  return false;
322 }
323 
324 bool
326 {
327  // Find next Instruction:
328  Instruction* navpt = ship->GetNextNavPoint();
329  int elem_index = ship->GetElementIndex();
330 
331  if (navpt && elem_index < 2) {
332  ship->SetNavptStatus(navpt, Instruction::SKIPPED);
333  }
334 
335  return true;
336 }
337 
338 bool
340 {
341  if (ship && ship->GetProbeLauncher()) {
342  ship->LaunchProbe();
343  return ship->GetProbe() != 0;
344  }
345 
346  return false;
347 }
348 
349 bool
351 {
352  Ship* inbound = (Ship*) msg->Sender();
353  Hangar* hangar = ship->GetHangar();
354  FlightDeck* deck = 0;
355  int squadron = -1;
356  int slot = -1;
357  bool same_rgn = false;
358 
359  if (inbound && inbound->GetRegion() == ship->GetRegion())
360  same_rgn = true;
361 
362  // is the sender already inbound to us?
363  if (inbound->GetInbound() &&
364  inbound->GetInbound()->GetDeck() &&
365  inbound->GetInbound()->GetDeck()->GetCarrier() == ship) {
366  InboundSlot* islot = inbound->GetInbound();
367  deck = islot->GetDeck();
368  squadron = islot->Squadron();
369  slot = islot->Index();
370  }
371 
372  // otherwise, find space for sender:
373  else {
374  if (hangar && same_rgn) {
375  if (hangar->FindSlot(inbound, squadron, slot)) {
376  int shortest_queue = 1000;
377 
378  for (int i = 0; i < ship->NumFlightDecks(); i++) {
379  FlightDeck* d = ship->GetFlightDeck(i);
380  if (d->IsRecoveryDeck()) {
381  int nwaiting = d->GetRecoveryQueue().size();
382 
383  if (nwaiting < shortest_queue) {
384  deck = d;
385  shortest_queue = nwaiting;
386  }
387  }
388  }
389  }
390  }
391  }
392 
393  // if no space (or not a carrier!) wave sender off:
394  if (!deck || !same_rgn || squadron < 0 || slot < 0) {
395  RadioMessage* wave_off = new(__FILE__,__LINE__) RadioMessage(inbound, ship, RadioMessage::NACK);
396  if (!hangar)
397  wave_off->SetInfo(Game::GetText("RadioHandler.no-hangar"));
398 
399  else if (!same_rgn) {
400  char info[256];
401  sprintf_s(info, Game::GetText("RadioHandler.too-far-away").data(), ship->GetRegion()->Name());
402  wave_off->SetInfo(info);
403  }
404 
405  else
406  wave_off->SetInfo(Game::GetText("RadioHandler.all-full"));
407 
408  RadioTraffic::Transmit(wave_off);
409  return false;
410  }
411 
412  // put sender in recovery queue, if not already there:
413  InboundSlot* inbound_slot = inbound->GetInbound();
414  int sequence = 0;
415 
416  if (!inbound_slot) {
417  inbound_slot = new(__FILE__,__LINE__) InboundSlot(inbound, deck, squadron, slot);
418  sequence = deck->Inbound(inbound_slot);
419  }
420  else {
421  sequence = inbound_slot->Index();
422  }
423 
424  // inform sender of status:
425  RadioMessage* approach = new(__FILE__,__LINE__) RadioMessage(inbound, ship, RadioMessage::CALL_APPROACH);
426 
427  if (inbound_slot->Cleared()) {
428  char info[256];
429  sprintf_s(info, Game::GetText("RadioHandler.cleared").data(), deck->Name());
430  approach->SetInfo(info);
431  }
432  else if (sequence) {
433  char info[256];
434  sprintf_s(info, Game::GetText("RadioHandler.sequenced").data(), sequence, deck->Name());
435  approach->SetInfo(info);
436  }
437 
438  RadioTraffic::Transmit(approach);
439 
440  return false;
441 }
442 
443 bool
445 {
446  if (!ship) return false;
447 
448  // try to find some enemy fighters in the area:
449  Ship* tgt = 0;
450  double range = 1e9;
451 
452  ListIter<Contact> iter = ship->ContactList();
453  while (++iter) {
454  Contact* c = iter.value();
455  int iff = c->GetIFF(ship);
456  Ship* s = c->GetShip();
457 
458  if (s && s->IsDropship() && s->IsHostileTo(ship)) {
459  double s_range = Point(msg->Sender()->Location() - s->Location()).length();
460  if (!tgt || s_range < range) {
461  tgt = s;
462  range = s_range;
463  }
464  }
465  }
466 
467  // found some:
468  if (tgt) {
469  Element* sender = msg->Sender()->GetElement();
470  Element* tgt_elem = tgt->GetElement();
471  RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::ATTACK);
472 
473  if (tgt_elem) {
474  for (int i = 1; i <= tgt_elem->NumShips(); i++)
475  response->AddTarget(tgt_elem->GetShip(i));
476  }
477  else {
478  response->AddTarget(tgt);
479  }
480 
481  RadioTraffic::Transmit(response);
482  }
483 
484  // nobody worth killin':
485  else {
486  Ship* sender = (Ship*) msg->Sender(); // cast-away const
487  RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::PICTURE);
488  RadioTraffic::Transmit(response);
489  }
490 
491  return false;
492 }
493 
494 bool
496 {
497  if (!ship) return false;
498 
499  // try to find some fighters with time on their hands...
500  Element* help = 0;
501  Element* cmdr = ship->GetElement();
502  Element* baby = msg->Sender()->GetElement();
503  SimRegion* rgn = msg->Sender()->GetRegion();
504 
505  for (int i = 0; i < rgn->Ships().size(); i++) {
506  Ship* s = rgn->Ships().at(i);
507  Element* e = s->GetElement();
508 
509  if (e && s->IsDropship() &&
510  e->Type() == Mission::PATROL &&
511  e != baby &&
512  cmdr->CanCommand(e) &&
514  help = e;
515  break;
516  }
517  }
518 
519  // found some:
520  if (help) {
521  RadioMessage* escort = new(__FILE__,__LINE__) RadioMessage(help, ship, RadioMessage::ESCORT);
522  escort->TargetList().append(msg->Sender());
523  RadioTraffic::Transmit(escort);
524 
525  Text ok = Game::GetText("RadioHandler.help-enroute");
526  Ship* sender = (Ship*) msg->Sender(); // cast-away const
527  RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::ACK);
528  response->SetInfo(ok);
529  RadioTraffic::Transmit(response);
530  }
531 
532  // no help in sight:
533  else {
534  Text nope = Game::GetText("RadioHandler.no-help-for-you");
535  Ship* sender = (Ship*) msg->Sender(); // cast-away const
536  RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::NACK);
537  response->SetInfo(nope);
538  RadioTraffic::Transmit(response);
539  }
540 
541  return false;
542 }
543 
544 // +----------------------------------------------------------------------+
545 
546 void
548 {
549  if (s && msg && msg->Sender() && msg->Action()) {
550  if (msg->Action() >= RadioMessage::ACK && msg->Action() <= RadioMessage::NACK)
551  return; // nothing to say here
552 
553  Ship* sender = (Ship*) msg->Sender(); // cast-away const
554  RadioMessage* ack = new(__FILE__,__LINE__) RadioMessage(sender, s, RadioMessage::ACK);
556  }
557 }
558