1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "WaitCommandsAI.h"
5 #include "SelectedUnitsHandler.h"
6 #include "GameHelper.h"
7 #include "GlobalUnsynced.h"
8 #include "UI/CommandColors.h"
9 #include "UI/CursorIcons.h"
10 #include "Rendering/LineDrawer.h"
11 #include "Sim/Misc/QuadField.h"
12 #include "Sim/Misc/GlobalConstants.h"
13 #include "Sim/Misc/GlobalSynced.h"
14 #include "Sim/Misc/TeamHandler.h"
15 #include "Sim/Units/Unit.h"
16 #include "Sim/Units/UnitHandler.h"
17 #include "Sim/Units/CommandAI/CommandQueue.h"
18 #include "Sim/Units/CommandAI/CommandAI.h"
19 #include "Sim/Units/CommandAI/FactoryCAI.h"
20 #include "Sim/Units/UnitTypes/Factory.h"
21 #include "System/Object.h"
22 #include "System/Util.h"
23 #include "System/creg/STL_Map.h"
24 #include "System/creg/STL_List.h"
25 #include "System/creg/STL_Set.h"
26 #include <assert.h>
27
28 CWaitCommandsAI waitCommandsAI;
29
30
31 static const int maxNetDelay = 30; // in seconds
32
33 static const int updatePeriod = 3; // in GAME_SPEED, 100 ms
34
35
36 CR_BIND(CWaitCommandsAI, )
37 CR_REG_METADATA(CWaitCommandsAI, (
38 CR_MEMBER(waitMap),
39 CR_MEMBER(unackedMap)
40 ))
41
CR_BIND_DERIVED_INTERFACE(CWaitCommandsAI::Wait,CObject)42 CR_BIND_DERIVED_INTERFACE(CWaitCommandsAI::Wait, CObject)
43 CR_REG_METADATA_SUB(CWaitCommandsAI,Wait, (
44 CR_MEMBER(code),
45 CR_MEMBER(key),
46 CR_MEMBER(valid),
47 CR_MEMBER(deadTime),
48 CR_POSTLOAD(PostLoad)
49 ))
50
51 CR_BIND_DERIVED(CWaitCommandsAI::TimeWait, CWaitCommandsAI::Wait, (1,0))
52 CR_REG_METADATA_SUB(CWaitCommandsAI,TimeWait , (
53 CR_MEMBER(unit),
54 CR_MEMBER(enabled),
55 CR_MEMBER(duration),
56 CR_MEMBER(endFrame),
57 CR_MEMBER(factory)
58 ))
59
60 CR_BIND_DERIVED(CWaitCommandsAI::DeathWait, CWaitCommandsAI::Wait, (Command()))
61 CR_REG_METADATA_SUB(CWaitCommandsAI,DeathWait , (
62 CR_MEMBER(waitUnits),
63 CR_MEMBER(deathUnits),
64 CR_MEMBER(unitPos)
65 ))
66
67 CR_BIND_DERIVED(CWaitCommandsAI::SquadWait, CWaitCommandsAI::Wait, (Command()))
68 CR_REG_METADATA_SUB(CWaitCommandsAI,SquadWait , (
69 CR_MEMBER(squadCount),
70 CR_MEMBER(buildUnits),
71 CR_MEMBER(waitUnits),
72 CR_MEMBER(stateText)
73 ))
74
75 CR_BIND_DERIVED(CWaitCommandsAI::GatherWait, CWaitCommandsAI::Wait, (Command()))
76 CR_REG_METADATA_SUB(CWaitCommandsAI,GatherWait , (
77 CR_MEMBER(waitUnits)
78 ))
79
80 /******************************************************************************/
81 /******************************************************************************/
82
83 CWaitCommandsAI::CWaitCommandsAI()
84 {
85 assert(sizeof(float) == sizeof(KeyType));
86 }
87
88
~CWaitCommandsAI()89 CWaitCommandsAI::~CWaitCommandsAI()
90 {
91 WaitMap::iterator it;
92 for (it = waitMap.begin(); it != waitMap.end(); ++it) {
93 delete it->second;
94 }
95 for (it = unackedMap.begin(); it != unackedMap.end(); ++it) {
96 delete it->second;
97 }
98 }
99
100
Update()101 void CWaitCommandsAI::Update()
102 {
103 // if ((gs->frameNum % GAME_SPEED) == 0) printf("Waits: %i\n", waitMap.size()); // FIXME
104
105 // limit the updates
106 if ((gs->frameNum % updatePeriod) != 0) {
107 return;
108 }
109
110 // update the waits
111 WaitMap::iterator it;
112 it = waitMap.begin();
113 while (it != waitMap.end()) {
114 // grab an incremented iterator in case the active iterator deletes itself
115 WaitMap::iterator tmp = it;
116 ++tmp;
117 it->second->Update();
118 it = tmp;
119 }
120
121 // delete old unacknowledged waits
122 const spring_time nowTime = spring_gettime();
123 it = unackedMap.begin();
124 while (it != unackedMap.end()) {
125 WaitMap::iterator tmp = it;
126 ++tmp;
127 Wait* wait = it->second;
128 if (wait->GetDeadTime() < nowTime) {
129 delete wait;
130 unackedMap.erase(it);
131 }
132 it = tmp;
133 }
134 }
135
136
DrawCommands() const137 void CWaitCommandsAI::DrawCommands() const
138 {
139 WaitMap::const_iterator it;
140 for (it = waitMap.begin(); it != waitMap.end(); ++it) {
141 it->second->Draw();
142 }
143 }
144
145
AddTimeWait(const Command & cmd)146 void CWaitCommandsAI::AddTimeWait(const Command& cmd)
147 {
148 // save the current selection
149 const CUnitSet tmpSet = selectedUnitsHandler.selectedUnits;
150 CUnitSet::const_iterator it;
151 for (it = tmpSet.begin(); it != tmpSet.end(); ++it) {
152 InsertWaitObject(TimeWait::New(cmd, *it));
153 }
154 // restore the selection
155 selectedUnitsHandler.ClearSelected();
156 for (it = tmpSet.begin(); it != tmpSet.end(); ++it) {
157 selectedUnitsHandler.AddUnit(*it);
158 }
159 }
160
161
AddDeathWait(const Command & cmd)162 void CWaitCommandsAI::AddDeathWait(const Command& cmd)
163 {
164 InsertWaitObject(DeathWait::New(cmd));
165 }
166
167
AddSquadWait(const Command & cmd)168 void CWaitCommandsAI::AddSquadWait(const Command& cmd)
169 {
170 InsertWaitObject(SquadWait::New(cmd));
171 }
172
173
AddGatherWait(const Command & cmd)174 void CWaitCommandsAI::AddGatherWait(const Command& cmd)
175 {
176 InsertWaitObject(GatherWait::New(cmd));
177 }
178
179
AcknowledgeCommand(const Command & cmd)180 void CWaitCommandsAI::AcknowledgeCommand(const Command& cmd)
181 {
182 if ((cmd.GetID() != CMD_WAIT) || (cmd.params.size() != 2)) {
183 return;
184 }
185 const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]);
186 WaitMap::iterator it = unackedMap.find(key);
187 if (it != unackedMap.end()) {
188 Wait* wait = it->second;
189 if (wait->GetCode() != cmd.params[0]) {
190 return; // code mismatch
191 }
192 // move into the acknowledged pool
193 unackedMap.erase(key);
194 waitMap[key] = wait;
195 }
196 }
197
198
AddLocalUnit(CUnit * unit,const CUnit * builder)199 void CWaitCommandsAI::AddLocalUnit(CUnit* unit, const CUnit* builder)
200 {
201 // NOTE: the wait keys will link the right units to
202 // the correct player (for multi-player teams)
203 if ((unit->team != gu->myTeam) || waitMap.empty()) {
204 return;
205 }
206
207 const CCommandQueue& dq = unit->commandAI->commandQue;
208 CCommandQueue::const_iterator qit;
209 for (qit = dq.begin(); qit != dq.end(); ++qit) {
210 const Command& cmd = *qit;
211 if ((cmd.GetID() != CMD_WAIT) || (cmd.params.size() != 2)) {
212 continue;
213 }
214
215 const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]);
216 WaitMap::iterator wit = waitMap.find(key);
217 if (wit == waitMap.end()) {
218 continue;
219 }
220
221 Wait* wait = wit->second;
222 if (cmd.params[0] != wait->GetCode()) {
223 continue;
224 }
225
226 const float code = cmd.params[0];
227 if (code != CMD_WAITCODE_TIMEWAIT) {
228 wait->AddUnit(unit);
229 }
230 else {
231 // add a unit-specific TimeWait
232 // (straight into the waitMap, no net ack required)
233 const int duration = static_cast<TimeWait*>(wait)->GetDuration();
234 TimeWait* tw = TimeWait::New(duration, unit);
235 if (tw != NULL) {
236 if (waitMap.find(tw->GetKey()) != waitMap.end()) {
237 delete tw;
238 } else {
239 waitMap[tw->GetKey()] = tw;
240 // should not affect the sync state
241 const_cast<Command&>(cmd).params[1] =
242 Wait::GetFloatFromKey(tw->GetKey());
243 }
244 }
245 }
246 }
247 }
248
249
RemoveWaitCommand(CUnit * unit,const Command & cmd)250 void CWaitCommandsAI::RemoveWaitCommand(CUnit* unit, const Command& cmd)
251 {
252 if ((cmd.params.size() != 2) ||
253 (unit->team != gu->myTeam)) {
254 return;
255 }
256 const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]);
257 WaitMap::iterator it = waitMap.find(key);
258 if (it != waitMap.end()) {
259 it->second->RemoveUnit(unit);
260 }
261 }
262
263
ClearUnitQueue(CUnit * unit,const CCommandQueue & queue)264 void CWaitCommandsAI::ClearUnitQueue(CUnit* unit, const CCommandQueue& queue)
265 {
266 if ((unit->team != gu->myTeam) || waitMap.empty()) {
267 return;
268 }
269 CCommandQueue::const_iterator qit;
270 for (qit = queue.begin(); qit != queue.end(); ++qit) {
271 const Command& cmd = *qit;
272 if ((cmd.GetID() == CMD_WAIT) && (cmd.params.size() == 2)) {
273 const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]);
274 WaitMap::iterator wit = waitMap.find(key);
275 if (wit != waitMap.end()) {
276 wit->second->RemoveUnit(unit);
277 }
278 }
279 }
280 }
281
282
InsertWaitObject(Wait * wait)283 bool CWaitCommandsAI::InsertWaitObject(Wait* wait)
284 {
285 if (wait == NULL) {
286 return false;
287 }
288 if (unackedMap.find(wait->GetKey()) != unackedMap.end()) {
289 return false;
290 }
291 unackedMap[wait->GetKey()] = wait;
292 return true;
293 }
294
295
RemoveWaitObject(Wait * wait)296 void CWaitCommandsAI::RemoveWaitObject(Wait* wait)
297 {
298 if (waitMap.erase(wait->GetKey()))
299 return;
300 if (unackedMap.erase(wait->GetKey()))
301 return;
302 }
303
304
AddIcon(const Command & cmd,const float3 & pos) const305 void CWaitCommandsAI::AddIcon(const Command& cmd, const float3& pos) const
306 {
307 if (cmd.params.size() != 2) {
308 lineDrawer.DrawIconAtLastPos(CMD_WAIT);
309 return;
310 }
311
312 const float code = cmd.params[0];
313 const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]);
314 WaitMap::const_iterator it = waitMap.find(key);
315
316 if (it == waitMap.end()) {
317 lineDrawer.DrawIconAtLastPos(CMD_WAIT);
318 }
319 else if (code == CMD_WAITCODE_TIMEWAIT) {
320 lineDrawer.DrawIconAtLastPos(CMD_TIMEWAIT);
321 cursorIcons.AddIconText(it->second->GetStateText(), pos);
322 }
323 else if (code == CMD_WAITCODE_SQUADWAIT) {
324 lineDrawer.DrawIconAtLastPos(CMD_SQUADWAIT);
325 cursorIcons.AddIconText(it->second->GetStateText(), pos);
326 }
327 else if (code == CMD_WAITCODE_DEATHWAIT) {
328 lineDrawer.DrawIconAtLastPos(CMD_DEATHWAIT);
329 it->second->AddUnitPosition(pos);
330 }
331 else if (code == CMD_WAITCODE_GATHERWAIT) {
332 lineDrawer.DrawIconAtLastPos(CMD_GATHERWAIT);
333 }
334 else {
335 lineDrawer.DrawIconAtLastPos(CMD_WAIT);
336 }
337 }
338
339
340 /******************************************************************************/
341 //
342 // Wait Base Class
343 //
344
345 CWaitCommandsAI::KeyType CWaitCommandsAI::Wait::keySource = 0;
346
347 const string CWaitCommandsAI::Wait::noText = "";
348
349
350 // static
GetNewKey()351 CWaitCommandsAI::KeyType CWaitCommandsAI::Wait::GetNewKey()
352 {
353 keySource = (0xffffff00 & keySource) |
354 (0x000000ff & gu->myPlayerNum);
355 keySource += 0x00000100;
356 return keySource;
357 }
358
359
360 // static
GetKeyFromFloat(float f)361 CWaitCommandsAI::KeyType CWaitCommandsAI::Wait::GetKeyFromFloat(float f)
362 {
363 return *((KeyType*)&f);
364 }
365
366
PostLoad()367 void CWaitCommandsAI::Wait::PostLoad()
368 {
369 deadTime = spring_gettime() + spring_secs(maxNetDelay);
370 }
371
372 // static
GetFloatFromKey(KeyType k)373 float CWaitCommandsAI::Wait::GetFloatFromKey(KeyType k)
374 {
375 return *((float*)&k);
376 }
377
378
Wait(float _code)379 CWaitCommandsAI::Wait::Wait(float _code)
380 : code(_code),
381 key(0),
382 valid(false),
383 deadTime(spring_gettime() + spring_secs(maxNetDelay))
384 {
385 }
386
387
~Wait()388 CWaitCommandsAI::Wait::~Wait()
389 {
390 waitCommandsAI.RemoveWaitObject(this);
391 }
392
393
394 CWaitCommandsAI::Wait::WaitState
GetWaitState(const CUnit * unit) const395 CWaitCommandsAI::Wait::GetWaitState(const CUnit* unit) const
396 {
397 const CCommandQueue& dq = unit->commandAI->commandQue;
398 if (dq.empty()) {
399 return Missing;
400 }
401 const Command& cmd = dq.front();
402 if ((cmd.GetID() == CMD_WAIT) && (cmd.params.size() == 2) &&
403 (cmd.params[0] == code) &&
404 (GetKeyFromFloat(cmd.params[1]) == key)) {
405 return Active;
406 }
407
408 CCommandQueue::const_iterator it = dq.begin();
409 ++it;
410 for ( ; it != dq.end(); ++it) {
411 const Command& qcmd = *it;
412 if ((qcmd.GetID() == CMD_WAIT) && (qcmd.params.size() == 2) &&
413 (qcmd.params[0] == code) &&
414 (GetKeyFromFloat(qcmd.params[1]) == key)) {
415 return Queued;
416 }
417 }
418 return Missing;
419 }
420
421
IsWaitingOn(const CUnit * unit) const422 bool CWaitCommandsAI::Wait::IsWaitingOn(const CUnit* unit) const
423 {
424 const CCommandQueue& dq = unit->commandAI->commandQue;
425 if (dq.empty()) {
426 return false;
427 }
428 const Command& cmd = dq.front();
429 if ((cmd.GetID() == CMD_WAIT) && (cmd.params.size() == 2) &&
430 (cmd.params[0] == code) &&
431 (GetKeyFromFloat(cmd.params[1]) == key)) {
432 return true;
433 }
434 return false;
435 }
436
437
SendCommand(const Command & cmd,const CUnitSet & unitSet)438 void CWaitCommandsAI::Wait::SendCommand(const Command& cmd,
439 const CUnitSet& unitSet)
440 {
441 if (unitSet.empty()) {
442 return;
443 }
444
445 const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
446 if (unitSet == selUnits) {
447 selectedUnitsHandler.GiveCommand(cmd, false);
448 return;
449 }
450
451 CUnitSet tmpSet = selUnits;
452 CUnitSet::const_iterator it;
453
454 selectedUnitsHandler.ClearSelected();
455 for (it = unitSet.begin(); it != unitSet.end(); ++it) {
456 selectedUnitsHandler.AddUnit(*it);
457 }
458
459 selectedUnitsHandler.GiveCommand(cmd, false);
460
461 selectedUnitsHandler.ClearSelected();
462 for (it = tmpSet.begin(); it != tmpSet.end(); ++it) {
463 selectedUnitsHandler.AddUnit(*it);
464 }
465 }
466
467
SendWaitCommand(const CUnitSet & unitSet)468 void CWaitCommandsAI::Wait::SendWaitCommand(const CUnitSet& unitSet)
469 {
470 Command waitCmd(CMD_WAIT);
471 SendCommand(waitCmd, unitSet);
472 }
473
474
RemoveUnitFromSet(CUnitSet::iterator it,CUnitSet & unitSet)475 CUnitSet::iterator CWaitCommandsAI::Wait::RemoveUnitFromSet(CUnitSet::iterator it, CUnitSet& unitSet)
476 {
477 CUnitSet::iterator tmp = it;
478 ++tmp;
479 unitSet.erase(it);
480 return tmp;
481 }
482
483
484 /******************************************************************************/
485 //
486 // TimeWait
487 //
488
489 CWaitCommandsAI::TimeWait*
New(const Command & cmd,CUnit * unit)490 CWaitCommandsAI::TimeWait::New(const Command& cmd, CUnit* unit)
491 {
492 TimeWait* tw = new TimeWait(cmd, unit);
493 if (!tw->valid) {
494 delete tw;
495 return NULL;
496 }
497 return tw;
498 }
499
500
501 CWaitCommandsAI::TimeWait*
New(int duration,CUnit * unit)502 CWaitCommandsAI::TimeWait::New(int duration, CUnit* unit)
503 {
504 TimeWait* tw = new TimeWait(duration, unit);
505 if (!tw->valid) {
506 delete tw;
507 return NULL;
508 }
509 return tw;
510 }
511
512
TimeWait(const Command & cmd,CUnit * _unit)513 CWaitCommandsAI::TimeWait::TimeWait(const Command& cmd, CUnit* _unit)
514 : Wait(CMD_WAITCODE_TIMEWAIT)
515 {
516 if (cmd.params.size() != 1) {
517 return;
518 }
519
520 valid = true;
521 key = GetNewKey();
522
523 unit = _unit;
524 enabled = false;
525 endFrame = 0;
526 duration = GAME_SPEED * (int)cmd.params[0];
527 factory = (dynamic_cast<CFactory*>(unit) != NULL);
528
529 Command waitCmd(CMD_WAIT, cmd.options, code);
530 waitCmd.PushParam(GetFloatFromKey(key));
531
532 selectedUnitsHandler.ClearSelected();
533 selectedUnitsHandler.AddUnit(unit);
534 selectedUnitsHandler.GiveCommand(waitCmd);
535
536 AddDeathDependence(unit, DEPENDENCE_WAITCMD);
537
538 return;
539 }
540
541
TimeWait(int _duration,CUnit * _unit)542 CWaitCommandsAI::TimeWait::TimeWait(int _duration, CUnit* _unit)
543 : Wait(CMD_WAITCODE_TIMEWAIT)
544 {
545 valid = true;
546 key = GetNewKey();
547
548 unit = _unit;
549 enabled = false;
550 endFrame = 0;
551 duration = _duration;
552 factory = false;
553
554 AddDeathDependence(unit, DEPENDENCE_WAITCMD);
555 }
556
557
~TimeWait()558 CWaitCommandsAI::TimeWait::~TimeWait()
559 {
560 // do nothing
561 }
562
563
DependentDied(CObject * object)564 void CWaitCommandsAI::TimeWait::DependentDied(CObject* object)
565 {
566 unit = NULL;
567 }
568
569
AddUnit(CUnit * unit)570 void CWaitCommandsAI::TimeWait::AddUnit(CUnit* unit)
571 {
572 // do nothing
573 }
574
575
RemoveUnit(CUnit * _unit)576 void CWaitCommandsAI::TimeWait::RemoveUnit(CUnit* _unit)
577 {
578 if (_unit == unit) {
579 delete this;
580 return;
581 }
582 }
583
584
Update()585 void CWaitCommandsAI::TimeWait::Update()
586 {
587 if (unit == NULL) {
588 delete this;
589 return;
590 }
591
592 WaitState state = GetWaitState(unit);
593
594 if (state == Active) {
595 if (!enabled) {
596 enabled = true;
597 endFrame = (gs->frameNum + duration);
598 }
599 else {
600 if (endFrame <= gs->frameNum) {
601 CUnitSet smallSet;
602 smallSet.insert(unit);
603 SendWaitCommand(smallSet);
604 if (!factory) {
605 delete this;
606 return;
607 } else {
608 enabled = false;
609 }
610 }
611 }
612 }
613 else if (state == Queued) {
614 return;
615 }
616 else if (state == Missing) {
617 if (!factory) { // FIXME
618 delete this;
619 return;
620 }
621 }
622 }
623
624
GetStateText() const625 const string& CWaitCommandsAI::TimeWait::GetStateText() const
626 {
627 static char buf[32];
628 if (enabled) {
629 const int remaining =
630 1 + (std::max(0, (endFrame - gs->frameNum - 1)) / GAME_SPEED);
631 SNPRINTF(buf, sizeof(buf), "%i", remaining);
632 } else {
633 SNPRINTF(buf, sizeof(buf), "%i", duration / GAME_SPEED);
634 }
635 static string text;
636 text = buf;
637 return text;
638 }
639
640
Draw() const641 void CWaitCommandsAI::TimeWait::Draw() const
642 {
643 // do nothing
644 }
645
646
647 /******************************************************************************/
648 //
649 // DeathWait
650 //
651
652 CWaitCommandsAI::DeathWait*
New(const Command & cmd)653 CWaitCommandsAI::DeathWait::New(const Command& cmd)
654 {
655 DeathWait* dw = new DeathWait(cmd);
656 if (!dw->valid) {
657 delete dw;
658 return NULL;
659 }
660 return dw;
661 }
662
663
DeathWait(const Command & cmd)664 CWaitCommandsAI::DeathWait::DeathWait(const Command& cmd)
665 : Wait(CMD_WAITCODE_DEATHWAIT)
666 {
667 const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
668
669 if (cmd.params.size() == 1) {
670 const int unitID = (int)cmd.params[0];
671 if ((unitID < 0) || (static_cast<size_t>(unitID) >= unitHandler->MaxUnits())) {
672 return;
673 }
674 CUnit* unit = unitHandler->units[unitID];
675 if (unit == NULL) {
676 return;
677 }
678 if (selUnits.find(unit) != selUnits.end()) {
679 return;
680 }
681 deathUnits.insert(unit);
682 }
683 else if (cmd.params.size() == 6) {
684 const float3& pos0 = cmd.GetPos(0);
685 const float3& pos1 = cmd.GetPos(3);
686 CUnitSet tmpSet;
687 SelectAreaUnits(pos0, pos1, tmpSet, false);
688 CUnitSet::iterator it;
689 for (it = tmpSet.begin(); it != tmpSet.end(); ++it) {
690 if (selUnits.find(*it) == selUnits.end()) {
691 deathUnits.insert(*it);
692 }
693 }
694 if (deathUnits.empty()) {
695 return;
696 }
697 }
698 else {
699 return; // unknown param config
700 }
701
702 valid = true;
703 key = GetNewKey();
704
705 waitUnits = selUnits;
706
707 Command waitCmd(CMD_WAIT, cmd.options, code);
708 waitCmd.PushParam(GetFloatFromKey(key));
709 selectedUnitsHandler.GiveCommand(waitCmd);
710
711 CUnitSet::iterator it;
712 for (it = waitUnits.begin(); it != waitUnits.end(); ++it) {
713 AddDeathDependence((CObject*)(*it), DEPENDENCE_WAITCMD);
714 }
715 for (it = deathUnits.begin(); it != deathUnits.end(); ++it) {
716 AddDeathDependence((CObject*)(*it), DEPENDENCE_WAITCMD);
717 }
718
719 return;
720 }
721
722
~DeathWait()723 CWaitCommandsAI::DeathWait::~DeathWait()
724 {
725 // do nothing
726 }
727
728
DependentDied(CObject * object)729 void CWaitCommandsAI::DeathWait::DependentDied(CObject* object)
730 {
731 waitUnits.erase(static_cast<CUnit*>(object));
732
733 if (waitUnits.empty())
734 return;
735
736 deathUnits.erase(static_cast<CUnit*>(object));
737 }
738
739
AddUnit(CUnit * unit)740 void CWaitCommandsAI::DeathWait::AddUnit(CUnit* unit)
741 {
742 if (waitUnits.insert(unit).second)
743 AddDeathDependence(unit, DEPENDENCE_WAITCMD);
744 }
745
746
RemoveUnit(CUnit * unit)747 void CWaitCommandsAI::DeathWait::RemoveUnit(CUnit* unit)
748 {
749 if (waitUnits.erase(unit))
750 DeleteDeathDependence(unit, DEPENDENCE_WAITCMD);
751 }
752
753
Update()754 void CWaitCommandsAI::DeathWait::Update()
755 {
756 if (waitUnits.empty()) {
757 delete this;
758 return;
759 }
760
761 unitPos.clear();
762
763 if (!deathUnits.empty()) {
764 return; // more must die
765 }
766
767 CUnitSet unblockSet;
768 CUnitSet::iterator it = waitUnits.begin();
769 while (it != waitUnits.end()) {
770 WaitState state = GetWaitState(*it);
771 if (state == Active) {
772 unblockSet.insert(*it);
773 DeleteDeathDependence(*it, DEPENDENCE_WAITCMD);
774 it = RemoveUnitFromSet(it, waitUnits);
775 continue;
776 }
777 else if (state == Queued) {
778 // do nothing
779 }
780 else if (state == Missing) {
781 DeleteDeathDependence(*it, DEPENDENCE_WAITCMD);
782 it = RemoveUnitFromSet(it, waitUnits);
783 continue;
784 }
785 ++it;
786 }
787 SendWaitCommand(unblockSet);
788 if (waitUnits.empty()) {
789 delete this;
790 return;
791 }
792 }
793
794
Draw() const795 void CWaitCommandsAI::DeathWait::Draw() const
796 {
797 if (unitPos.empty()) {
798 return;
799 }
800
801 float3 midPos(0.0f, 0.0f, 0.0f);
802 for (size_t i = 0; i < unitPos.size(); i++) {
803 midPos += unitPos.at(i);
804 }
805 midPos /= (float)unitPos.size();
806
807 cursorIcons.AddIcon(CMD_DEATHWAIT, midPos);
808
809 for (size_t i = 0; i < unitPos.size(); i++) {
810 lineDrawer.StartPath(unitPos.at(i), cmdColors.start);
811 lineDrawer.DrawLine(midPos, cmdColors.deathWait);
812 lineDrawer.FinishPath();
813 }
814
815 CUnitSet::const_iterator it;
816 for (it = deathUnits.begin(); it != deathUnits.end(); ++it) {
817 const CUnit* unit = *it;
818 if (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_INRADAR)) {
819 cursorIcons.AddIcon(CMD_SELFD, unit->midPos);
820 lineDrawer.StartPath(midPos, cmdColors.start);
821 lineDrawer.DrawLine(unit->midPos, cmdColors.deathWait);
822 lineDrawer.FinishPath();
823 }
824 }
825 }
826
827
AddUnitPosition(const float3 & pos)828 void CWaitCommandsAI::DeathWait::AddUnitPosition(const float3& pos)
829 {
830 unitPos.push_back(pos);
831 }
832
833
SelectAreaUnits(const float3 & pos0,const float3 & pos1,CUnitSet & units,bool enemies)834 void CWaitCommandsAI::DeathWait::SelectAreaUnits(
835 const float3& pos0, const float3& pos1, CUnitSet& units, bool enemies)
836 {
837 units.clear();
838
839 const float3 mins(std::min(pos0.x, pos1.x), 0.0f, std::min(pos0.z, pos1.z));
840 const float3 maxs(std::max(pos0.x, pos1.x), 0.0f, std::max(pos0.z, pos1.z));
841
842 const std::vector<CUnit*> &tmpUnits = quadField->GetUnitsExact(mins, maxs);
843
844 const int count = (int)tmpUnits.size();
845 for (int i = 0; i < count; i++) {
846 CUnit* unit = tmpUnits[i];
847 if (enemies && teamHandler->Ally(unit->allyteam, gu->myAllyTeam)) {
848 continue;
849 }
850 if (!(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_INRADAR))) {
851 continue;
852 }
853 units.insert(unit);
854 }
855 }
856
857
858 /******************************************************************************/
859 //
860 // SquadWait
861 //
862
863 CWaitCommandsAI::SquadWait*
New(const Command & cmd)864 CWaitCommandsAI::SquadWait::New(const Command& cmd)
865 {
866 SquadWait* sw = new SquadWait(cmd);
867 if (!sw->valid) {
868 delete sw;
869 return NULL;
870 }
871 return sw;
872 }
873
874
SquadWait(const Command & cmd)875 CWaitCommandsAI::SquadWait::SquadWait(const Command& cmd)
876 : Wait(CMD_WAITCODE_SQUADWAIT)
877 {
878 if (cmd.params.size() != 1) {
879 return;
880 }
881
882 squadCount = (int)cmd.params[0];
883 if (squadCount < 2) {
884 return;
885 }
886
887 const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
888 CUnitSet::const_iterator it;
889 for (it = selUnits.begin(); it != selUnits.end(); ++it) {
890 CUnit* unit = *it;
891 if (dynamic_cast<CFactory*>(unit)) {
892 buildUnits.insert(unit);
893 } else {
894 waitUnits.insert(unit);
895 }
896 }
897 if (buildUnits.empty() && ((int)waitUnits.size() < squadCount)) {
898 return;
899 }
900
901 valid = true;
902 key = GetNewKey();
903
904 Command waitCmd(CMD_WAIT, cmd.options, code);
905 waitCmd.PushParam(GetFloatFromKey(key));
906
907 SendCommand(waitCmd, buildUnits);
908 SendCommand(waitCmd, waitUnits);
909
910 for (it = buildUnits.begin(); it != buildUnits.end(); ++it) {
911 AddDeathDependence((CObject*)(*it), DEPENDENCE_WAITCMD);
912 }
913 for (it = waitUnits.begin(); it != waitUnits.end(); ++it) {
914 AddDeathDependence((CObject*)(*it), DEPENDENCE_WAITCMD);
915 }
916
917 UpdateText();
918
919 return;
920 }
921
922
~SquadWait()923 CWaitCommandsAI::SquadWait::~SquadWait()
924 {
925 // do nothing
926 }
927
928
DependentDied(CObject * object)929 void CWaitCommandsAI::SquadWait::DependentDied(CObject* object)
930 {
931 buildUnits.erase(static_cast<CUnit*>(object));
932 waitUnits.erase(static_cast<CUnit*>(object));
933 }
934
935
AddUnit(CUnit * unit)936 void CWaitCommandsAI::SquadWait::AddUnit(CUnit* unit)
937 {
938 if (waitUnits.insert(unit).second)
939 AddDeathDependence(unit, DEPENDENCE_WAITCMD);
940 }
941
942
RemoveUnit(CUnit * unit)943 void CWaitCommandsAI::SquadWait::RemoveUnit(CUnit* unit)
944 {
945 if (buildUnits.erase(unit))
946 DeleteDeathDependence(unit, DEPENDENCE_WAITCMD);
947 if (waitUnits.erase(unit))
948 DeleteDeathDependence(unit, DEPENDENCE_WAITCMD);
949 }
950
951
Update()952 void CWaitCommandsAI::SquadWait::Update()
953 {
954 if (buildUnits.empty() && ((int)waitUnits.size() < squadCount)) {
955 // FIXME -- unblock remaining waitUnits ?
956 delete this;
957 return;
958 }
959
960 if ((int)waitUnits.size() >= squadCount) {
961 CUnitSet unblockSet;
962 CUnitSet::iterator it = waitUnits.begin();
963 while (it != waitUnits.end()) {
964 WaitState state = GetWaitState(*it);
965 if (state == Active) {
966 unblockSet.insert(*it);
967 if ((int)unblockSet.size() >= squadCount) {
968 break; // we've got our squad
969 }
970 }
971 else if (state == Queued) {
972 // do nothing
973 }
974 else if (state == Missing) {
975 DeleteDeathDependence(*it, DEPENDENCE_WAITCMD);
976 it = RemoveUnitFromSet(it, waitUnits);
977 continue;
978 }
979 ++it;
980 }
981
982 if ((int)unblockSet.size() >= squadCount) {
983 // FIXME -- rebuild the order queue so
984 // that formations are created?
985 SendWaitCommand(unblockSet);
986 for (it = unblockSet.begin(); it != unblockSet.end(); ++it) {
987 if (waitUnits.erase(*it))
988 DeleteDeathDependence(*it, DEPENDENCE_WAITCMD);
989 }
990 }
991 }
992
993 UpdateText();
994 // FIXME -- clean builders
995 }
996
997
UpdateText()998 void CWaitCommandsAI::SquadWait::UpdateText()
999 {
1000 static char buf[64];
1001 SNPRINTF(buf, sizeof(buf), "%i/%i", (int)waitUnits.size(), squadCount);
1002 stateText = buf;
1003 }
1004
1005
Draw() const1006 void CWaitCommandsAI::SquadWait::Draw() const
1007 {
1008 // do nothing
1009 }
1010
1011
1012 /******************************************************************************/
1013 //
1014 // GatherWait
1015 //
1016
1017 CWaitCommandsAI::GatherWait*
New(const Command & cmd)1018 CWaitCommandsAI::GatherWait::New(const Command& cmd)
1019 {
1020 GatherWait* gw = new GatherWait(cmd);
1021 if (!gw->valid) {
1022 delete gw;
1023 return NULL;
1024 }
1025 return gw;
1026 }
1027
1028
GatherWait(const Command & cmd)1029 CWaitCommandsAI::GatherWait::GatherWait(const Command& cmd)
1030 : Wait(CMD_WAITCODE_GATHERWAIT)
1031 {
1032 if (!cmd.params.empty()) {
1033 return;
1034 }
1035
1036 // only add valid units
1037 const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
1038 CUnitSet::const_iterator sit;
1039 for (sit = selUnits.begin(); sit != selUnits.end(); ++sit) {
1040 CUnit* unit = *sit;
1041 const UnitDef* ud = unit->unitDef;
1042 if (ud->canmove && (dynamic_cast<CFactory*>(unit) == NULL)) {
1043 waitUnits.insert(unit);
1044 }
1045 }
1046
1047 if (waitUnits.size() < 2) {
1048 return; // one man does not a gathering make
1049 }
1050
1051 valid = true;
1052 key = GetNewKey();
1053
1054 Command waitCmd(CMD_WAIT, SHIFT_KEY, code);
1055 waitCmd.PushParam(GetFloatFromKey(key));
1056 selectedUnitsHandler.GiveCommand(waitCmd, true);
1057
1058 CUnitSet::iterator wit;
1059 for (wit = waitUnits.begin(); wit != waitUnits.end(); ++wit) {
1060 AddDeathDependence((CObject*)(*wit), DEPENDENCE_WAITCMD);
1061 }
1062
1063 return;
1064 }
1065
1066
~GatherWait()1067 CWaitCommandsAI::GatherWait::~GatherWait()
1068 {
1069 // do nothing
1070 }
1071
1072
DependentDied(CObject * object)1073 void CWaitCommandsAI::GatherWait::DependentDied(CObject* object)
1074 {
1075 waitUnits.erase(static_cast<CUnit*>(object));
1076 }
1077
1078
AddUnit(CUnit * unit)1079 void CWaitCommandsAI::GatherWait::AddUnit(CUnit* unit)
1080 {
1081 // do nothing
1082 }
1083
1084
RemoveUnit(CUnit * unit)1085 void CWaitCommandsAI::GatherWait::RemoveUnit(CUnit* unit)
1086 {
1087 if (waitUnits.erase(unit))
1088 DeleteDeathDependence(unit, DEPENDENCE_WAITCMD);
1089 }
1090
1091
Update()1092 void CWaitCommandsAI::GatherWait::Update()
1093 {
1094 if (waitUnits.size() < 2) {
1095 delete this;
1096 return;
1097 }
1098
1099 CUnitSet::iterator it = waitUnits.begin();
1100 while (it != waitUnits.end()) {
1101 WaitState state = GetWaitState(*it);
1102 if (state == Active) {
1103 // do nothing
1104 }
1105 else if(state == Queued) {
1106 return;
1107 }
1108 else if (state == Missing) {
1109 DeleteDeathDependence(*it, DEPENDENCE_WAITCMD);
1110 it = RemoveUnitFromSet(it, waitUnits);
1111 if (waitUnits.empty()) {
1112 delete this;
1113 return;
1114 }
1115 continue;
1116 }
1117 ++it;
1118 }
1119
1120 // all units are actively waiting on this command, unblock them and die
1121 SendWaitCommand(waitUnits);
1122 delete this;
1123 }
1124
1125
1126 /******************************************************************************/
1127