ProtoBot
Loading...
Searching...
No Matches
DarkTemplar Class Reference

DarkTemplar Controls scouting behavior for a Protoss Dark Templar. Combines movement, target prioritization, and stealth-based harassment to pressure the enemy while scouting. More...

#include <DarkTemplar.h>

Public Member Functions

 DarkTemplar (ProtoBotCommander *commander, ScoutingManager *manager)
void onStart ()
 Initializes the dark templar scouting state.
void onFrame ()
 Main update loop executed every frame.
void onUnitDestroy (BWAPI::Unit unit)
 Handles cleanup when the dark templar is destroyed.
void setEnemyMain (const BWAPI::TilePosition &tp)
 Sets the enemy main base location and updates behavior state.
void assign (BWAPI::Unit unit)
 Assigns a unit as the dark templar scout.
void drawDebug () const
 Draws debug information for the dark templar scout.

Private Types

enum class  State {
  Idle , WaitEnemyMain , MoveToEnemyMain , ApproachNatural ,
  AttackEnemyMain , Done
}

Private Member Functions

void issueMove (BWAPI::Unit unit, const BWAPI::Position &p, bool force=false)
 Issues a movement command with cooldown control.
BWAPI::Unit pickTargetFor (BWAPI::Unit dt) const
 Selects the highest priority target inside the enemy base.
BWAPI::Unit getLockedTarget () const
 Returns the currently locked target, if it still exists.
BWAPI::Position getNaturalApproachPosition () const
 Returns the preferred approach position near the enemy natural.
BWAPI::Unit findApproachTarget (BWAPI::Unit dt) const
 Finds a nearby target to attack while approaching the enemy base.

Static Private Member Functions

static bool isDetectorBuildingType (BWAPI::UnitType type)
 Checks whether a unit type is considered a detector building.
static bool isDetectorBuilding (BWAPI::Unit unit)
static bool isWorker (BWAPI::Unit unit)
 Checks whether a unit is a worker.
static bool isBuilding (BWAPI::Unit unit)
 Checks whether a unit is a building that is not specifically a detector.

Private Attributes

ProtoBotCommandercommanderRef = nullptr
ScoutingManagermanager = nullptr
BWAPI::Unit darkTemplar = nullptr
int lockedTargetId = -1
std::optional< BWAPI::TilePosition > enemyMainTile
BWAPI::Position enemyMainPos = BWAPI::Positions::Invalid
State state = State::Idle
int lastMoveIssueFrame = 0

Static Private Attributes

static constexpr int kMoveCooldownFrames = 8
static constexpr int kAttackRangePx = 20 * 32
static constexpr int kArriveDistPx = 160
static constexpr int kMainPriorityRadiusPx = 14 * 32

Detailed Description

DarkTemplar Controls scouting behavior for a Protoss Dark Templar. Combines movement, target prioritization, and stealth-based harassment to pressure the enemy while scouting.

/// Uses a state-driven system to:

  • Move toward the enemy natural and main base
  • Prioritize valuable targets while approaching
  • Attack key structures and workers inside the enemy base
  • Maintain target focus through locked target selection

Definition at line 21 of file DarkTemplar.h.

Member Enumeration Documentation

◆ State

enum class DarkTemplar::State
strongprivate

Definition at line 39 of file DarkTemplar.h.

40 {
41 Idle,
42 WaitEnemyMain,
43 MoveToEnemyMain,
44 ApproachNatural,
45 AttackEnemyMain,
46 Done
47 };

Constructor & Destructor Documentation

◆ DarkTemplar()

DarkTemplar::DarkTemplar ( ProtoBotCommander * commander,
ScoutingManager * manager )
inlineexplicit

Definition at line 24 of file DarkTemplar.h.

25 : commanderRef(commander), manager(manager)
26 {
27 }

Member Function Documentation

◆ assign()

void DarkTemplar::assign ( BWAPI::Unit unit)

Assigns a unit as the dark templar scout.

Verifies the unit type and sets the initial movement state based on whether the enemy main is already known.

Parameters
unitUnit to assign as scout

Definition at line 50 of file DarkTemplar.cpp.

51{
52 if (!unit || !unit->exists())
53 {
54 return;
55 }
56
57 if (unit->getType() != BWAPI::UnitTypes::Protoss_Dark_Templar)
58 {
59 return;
60 }
61
62 darkTemplar = unit;
63
64 if (enemyMainPos.isValid())
65 {
66 state = State::MoveToEnemyMain;
67 }
68 else
69 {
70 state = State::WaitEnemyMain;
71 }
72}

◆ drawDebug()

void DarkTemplar::drawDebug ( ) const

Draws debug information for the dark templar scout.

Displays:

  • Current state
  • Locked target
  • Enemy main direction
  • Enemy natural direction

Definition at line 615 of file DarkTemplar.cpp.

616{
617 if (!darkTemplar || !darkTemplar->exists())
618 {
619 return;
620 }
621 const char* stateName = "Unknown";
622
623 switch (state)
624 {
625 case State::Idle:
626 stateName = "Idle";
627 break;
628 case State::WaitEnemyMain:
629 stateName = "WaitEnemyMain";
630 break;
631 case State::MoveToEnemyMain:
632 stateName = "MoveToEnemyMain";
633 break;
634 case State::ApproachNatural:
635 stateName = "ApproachNatural";
636 break;
637 case State::AttackEnemyMain:
638 stateName = "AttackEnemyMain";
639 break;
640 case State::Done:
641 stateName = "Done";
642 break;
643 default:
644 stateName = "Unknown";
645 break;
646 }
647
648
649 BWAPI::Position p = darkTemplar->getPosition();
650
651 BWAPI::Broodwar->drawCircleMap(p, 22, BWAPI::Colors::Purple, false);
652 BWAPI::Broodwar->drawTextMap(p.x - 30, p.y - 46, "\x05DT Scout");
653 BWAPI::Broodwar->drawTextMap(p.x - 30, p.y - 34, "\x11State: %s", stateName);
654
655 BWAPI::Unit target = getLockedTarget();
656 if (target && target->exists())
657 {
658 BWAPI::Broodwar->drawLineMap(p, target->getPosition(), BWAPI::Colors::Red);
659 BWAPI::Broodwar->drawCircleMap(target->getPosition(), 14, BWAPI::Colors::Red, false);
660 BWAPI::Broodwar->drawTextMap(
661 p.x - 30,
662 p.y - 22,
663 "\x08Target: %s",
664 target->getType().getName().c_str()
665 );
666 }
667 else
668 {
669 BWAPI::Broodwar->drawTextMap(p.x - 30, p.y - 22, "\x08Target: None");
670 }
671
672 if (enemyMainPos.isValid())
673 {
674 BWAPI::Broodwar->drawLineMap(p, enemyMainPos, BWAPI::Colors::Green);
675 }
676
677 if (manager)
678 {
679 auto nat = manager->getEnemyNatural();
680 if (nat.has_value())
681 {
682 BWAPI::Position natPos(nat.value());
683 BWAPI::Broodwar->drawLineMap(p, natPos, BWAPI::Colors::Orange);
684 }
685 }
686
687
688}
BWAPI::Unit getLockedTarget() const
Returns the currently locked target, if it still exists.

◆ findApproachTarget()

BWAPI::Unit DarkTemplar::findApproachTarget ( BWAPI::Unit dt) const
private

Finds a nearby target to attack while approaching the enemy base.

Used during movement states to opportunistically attack visible nearby targets without fully switching behavior.

Parameters
dtDark templar unit
Returns
Best nearby target, or nullptr if none found

Definition at line 410 of file DarkTemplar.cpp.

411{
412 if (!dt || !dt->exists())
413 {
414 return nullptr;
415 }
416
417 BWAPI::Unit best = nullptr;
418 int bestDist = INT_MAX;
419
420 for (BWAPI::Unit enemy : BWAPI::Broodwar->enemy()->getUnits())
421 {
422 if (!enemy || !enemy->exists())
423 {
424 continue;
425 }
426
427 if (!enemy->isVisible())
428 {
429 continue;
430 }
431
432 const int d = dt->getDistance(enemy);
433
434 // only grab things reasonably close during the walk-in
435 if (d > 8 * 32)
436 {
437 continue;
438 }
439
440 if (!isValidDTTarget(dt, enemy))
441 {
442 continue;
443 }
444
445 if (d < bestDist)
446 {
447 bestDist = d;
448 best = enemy;
449 }
450 }
451
452 return best;
453}

◆ getLockedTarget()

BWAPI::Unit DarkTemplar::getLockedTarget ( ) const
private

Returns the currently locked target, if it still exists.

Returns
Locked enemy unit, or nullptr if unavailable

Definition at line 352 of file DarkTemplar.cpp.

353{
354 if (lockedTargetId < 0)
355 {
356 return nullptr;
357 }
358
359 for (BWAPI::Unit enemy : BWAPI::Broodwar->enemy()->getUnits())
360 {
361 if (!enemy || !enemy->exists())
362 {
363 continue;
364 }
365
366 if (enemy->getID() == lockedTargetId)
367 {
368 return enemy;
369 }
370 }
371
372 return nullptr;
373}

◆ getNaturalApproachPosition()

BWAPI::Position DarkTemplar::getNaturalApproachPosition ( ) const
private

Returns the preferred approach position near the enemy natural.

Falls back to the enemy main if no natural location is known.

Returns
Approach position for the dark templar

Definition at line 382 of file DarkTemplar.cpp.

383{
384 if (manager)
385 {
386 auto nat = manager->getEnemyNatural();
387 if (nat.has_value())
388 {
389 return BWAPI::Position(nat.value());
390 }
391 }
392
393 if (enemyMainPos.isValid())
394 {
395 return enemyMainPos;
396 }
397
398 return BWAPI::Positions::Invalid;
399}

◆ isBuilding()

bool DarkTemplar::isBuilding ( BWAPI::Unit unit)
staticprivate

Checks whether a unit is a building that is not specifically a detector.

Parameters
unitUnit to check
Returns
True if the unit is a building

Definition at line 505 of file DarkTemplar.cpp.

506{
507 return unit && unit->exists() && unit->getType().isBuilding();
508}

◆ isDetectorBuilding()

bool DarkTemplar::isDetectorBuilding ( BWAPI::Unit unit)
staticprivate

Definition at line 471 of file DarkTemplar.cpp.

472{
473 if (!unit || !unit->exists())
474 {
475 return false;
476 }
477
478 const BWAPI::UnitType type = unit->getType();
479
480 if (isDetectorBuildingType(type))
481 {
482 return true;
483 }
484
485 return false;
486}
static bool isDetectorBuildingType(BWAPI::UnitType type)
Checks whether a unit type is considered a detector building.

◆ isDetectorBuildingType()

bool DarkTemplar::isDetectorBuildingType ( BWAPI::UnitType type)
staticprivate

Checks whether a unit type is considered a detector building.

Parameters
typeUnit type to check
Returns
True if the type is a detector building

Definition at line 461 of file DarkTemplar.cpp.

462{
463 return
464 type == BWAPI::UnitTypes::Terran_Missile_Turret ||
465 type == BWAPI::UnitTypes::Terran_Comsat_Station ||
466 type == BWAPI::UnitTypes::Protoss_Photon_Cannon ||
467 type == BWAPI::UnitTypes::Protoss_Observatory ||
468 type == BWAPI::UnitTypes::Zerg_Spore_Colony;
469}

◆ issueMove()

void DarkTemplar::issueMove ( BWAPI::Unit unit,
const BWAPI::Position & p,
bool force = false )
private

Issues a movement command with cooldown control.

Prevents command spam by throttling repeated move orders.

Parameters
unitUnit receiving the command
pTarget position
forceForce movement regardless of cooldown

Definition at line 326 of file DarkTemplar.cpp.

327{
328 if (!unit || !unit->exists() || !p.isValid())
329 {
330 return;
331 }
332
333 const int now = BWAPI::Broodwar->getFrameCount();
334
335 if (!force && (now - lastMoveIssueFrame) < kMoveCooldownFrames)
336 {
337 return;
338 }
339
340 if (force || !unit->isMoving() || unit->getDistance(p) > 48)
341 {
342 unit->move(p);
343 lastMoveIssueFrame = now;
344 }
345}

◆ isWorker()

bool DarkTemplar::isWorker ( BWAPI::Unit unit)
staticprivate

Checks whether a unit is a worker.

Parameters
unitUnit to check
Returns
True if the unit is a worker

Definition at line 494 of file DarkTemplar.cpp.

495{
496 return unit && unit->exists() && unit->getType().isWorker();
497}

◆ onFrame()

void DarkTemplar::onFrame ( )

Main update loop executed every frame.

Controls state transitions and behavior including:

  • Waiting for enemy main information
  • Moving toward the enemy main
  • Approaching through the enemy natural
  • Attacking targets inside the enemy base

Definition at line 127 of file DarkTemplar.cpp.

128{
129 BWAPI::Unit dt = darkTemplar;
130
131 if (state == State::Done || !dt || !dt->exists())
132 {
133 return;
134 }
135
136 if (!enemyMainPos.isValid() && commanderRef)
137 {
138 const auto& e = commanderRef->enemy();
139 if (e.main.has_value())
140 {
141 setEnemyMain(*e.main);
142 }
143 }
144
145 switch (state)
146 {
147 case State::Idle:
148 {
149 state = enemyMainPos.isValid() ? State::MoveToEnemyMain : State::WaitEnemyMain;
150 break;
151 }
152
153 case State::WaitEnemyMain:
154 {
155 if (enemyMainPos.isValid())
156 {
157 state = State::MoveToEnemyMain;
158 }
159 break;
160 }
161
162 case State::MoveToEnemyMain:
163 {
164 if (enemyMainPos.isValid() && dt->getDistance(enemyMainPos) <= kMainPriorityRadiusPx)
165 {
166 lockedTargetId = -1;
167 state = State::AttackEnemyMain;
168 break;
169 }
170
171 if (manager)
172 {
173 auto nat = manager->getEnemyNatural();
174 if (nat.has_value())
175 {
176 state = State::ApproachNatural;
177 break;
178 }
179 }
180
181 BWAPI::Unit target = getLockedTarget();
182
183 if (!target || !target->exists())
184 {
185 target = findApproachTarget(dt);
186
187 if (target && target->exists())
188 {
189 lockedTargetId = target->getID();
190 }
191 else
192 {
193 lockedTargetId = -1;
194 }
195 }
196
197 if (target && target->exists())
198 {
199 if (dt->getOrderTarget() != target)
200 {
201 dt->attack(target);
202 }
203 }
204 else if (dt->getDistance(enemyMainPos) > kArriveDistPx)
205 {
206 if (dt->getLastCommandFrame() < BWAPI::Broodwar->getFrameCount() - kMoveCooldownFrames)
207 {
208 dt->attack(enemyMainPos);
209 }
210 }
211 else
212 {
213 state = State::ApproachNatural;
214 }
215
216 break;
217 }
218
219 case State::ApproachNatural:
220 {
221 BWAPI::Position approachPos = getNaturalApproachPosition();
222
223 if (!approachPos.isValid())
224 {
225 if (enemyMainPos.isValid())
226 {
227 approachPos = enemyMainPos;
228 }
229 else
230 {
231 break;
232 }
233 }
234
235 const int dist = dt->getDistance(approachPos);
236
237 const int mainPriorityRadius = 10 * 32;
238
239 if (enemyMainPos.isValid() && dt->getDistance(enemyMainPos) <= mainPriorityRadius)
240 {
241 lockedTargetId = -1;
242 state = State::AttackEnemyMain;
243 break;
244 }
245
246 BWAPI::Unit target = getLockedTarget();
247
248 if (!target || !target->exists())
249 {
250 target = findApproachTarget(dt);
251
252 if (target && target->exists())
253 {
254 lockedTargetId = target->getID();
255 }
256 else
257 {
258 lockedTargetId = -1;
259 }
260 }
261
262 if (target && target->exists())
263 {
264 if (dt->getOrderTarget() != target)
265 {
266 dt->attack(target);
267 }
268 }
269 else if (dt->getLastCommandFrame() < BWAPI::Broodwar->getFrameCount() - kMoveCooldownFrames)
270 {
271 dt->attack(approachPos);
272 }
273
274 break;
275 }
276
277 case State::AttackEnemyMain:
278 {
279 BWAPI::Unit target = getLockedTarget();
280
281 if (!target || !target->exists())
282 {
283 target = pickTargetFor(dt);
284
285 if (target && target->exists())
286 {
287 lockedTargetId = target->getID();
288 }
289 else
290 {
291 lockedTargetId = -1;
292 }
293 }
294
295 if (target && target->exists())
296 {
297 if (dt->getOrderTarget() != target)
298 {
299 dt->attack(target);
300 }
301 }
302 else
303 {
304 issueMove(dt, enemyMainPos);
305 }
306 break;
307 }
308
309 case State::Done:
310 default:
311 {
312 break;
313 }
314 }
315}
BWAPI::Position getNaturalApproachPosition() const
Returns the preferred approach position near the enemy natural.
void setEnemyMain(const BWAPI::TilePosition &tp)
Sets the enemy main base location and updates behavior state.
BWAPI::Unit findApproachTarget(BWAPI::Unit dt) const
Finds a nearby target to attack while approaching the enemy base.
BWAPI::Unit pickTargetFor(BWAPI::Unit dt) const
Selects the highest priority target inside the enemy base.
void issueMove(BWAPI::Unit unit, const BWAPI::Position &p, bool force=false)
Issues a movement command with cooldown control.

◆ onStart()

void DarkTemplar::onStart ( )

Initializes the dark templar scouting state.

Resets movement timing and sets the initial state to Idle.

Definition at line 13 of file DarkTemplar.cpp.

14{
15 state = State::Idle;
16 lastMoveIssueFrame = 0;
17}

◆ onUnitDestroy()

void DarkTemplar::onUnitDestroy ( BWAPI::Unit unit)

Handles cleanup when the dark templar is destroyed.

Clears the tracked unit, resets locked target state, and marks the behavior as complete.

Parameters
unitDestroyed unit

Definition at line 82 of file DarkTemplar.cpp.

83{
84 if (!unit)
85 {
86 return;
87 }
88
89 if (darkTemplar == unit)
90 {
91 darkTemplar = nullptr;
92 lockedTargetId = -1;
93 state = State::Done;
94 }
95}

◆ pickTargetFor()

BWAPI::Unit DarkTemplar::pickTargetFor ( BWAPI::Unit dt) const
private

Selects the highest priority target inside the enemy base.

Priority order:

  • Detector buildings
  • Workers
  • Other buildings
Parameters
dtDark templar unit
Returns
Best target to attack, or nullptr if none found

Definition at line 521 of file DarkTemplar.cpp.

522{
523 if (!dt || !dt->exists())
524 {
525 return nullptr;
526 }
527
528 BWAPI::Unit bestDetector = nullptr;
529 int bestDetectorDist = INT_MAX;
530
531 BWAPI::Unit bestWorker = nullptr;
532 int bestWorkerDist = INT_MAX;
533
534 BWAPI::Unit bestBuilding = nullptr;
535 int bestBuildingDist = INT_MAX;
536
537 for (BWAPI::Unit enemy : BWAPI::Broodwar->enemy()->getUnits())
538 {
539
540 if (!enemy || !enemy->exists())
541 {
542 continue;
543 }
544
545 if (!isValidDTTarget(dt, enemy))
546 {
547 continue;
548 }
549
550 const int d = dt->getDistance(enemy);
551
552 if (d > kAttackRangePx)
553 {
554 continue;
555 }
556
557 if (isDetectorBuilding(enemy))
558 {
559 if (d < bestDetectorDist)
560 {
561 bestDetectorDist = d;
562 bestDetector = enemy;
563 }
564 continue;
565 }
566
567 if (isWorker(enemy))
568 {
569 if (d < bestWorkerDist)
570 {
571 bestWorkerDist = d;
572 bestWorker = enemy;
573 }
574 continue;
575 }
576
577 if (isBuilding(enemy))
578 {
579 if (d < bestBuildingDist)
580 {
581 bestBuildingDist = d;
582 bestBuilding = enemy;
583 }
584 }
585 }
586
587 if (bestDetector)
588 {
589 return bestDetector;
590 }
591
592 if (bestWorker)
593 {
594 return bestWorker;
595 }
596
597 if (bestBuilding)
598 {
599 return bestBuilding;
600 }
601
602 return nullptr;
603}
static bool isWorker(BWAPI::Unit unit)
Checks whether a unit is a worker.
static bool isBuilding(BWAPI::Unit unit)
Checks whether a unit is a building that is not specifically a detector.

◆ setEnemyMain()

void DarkTemplar::setEnemyMain ( const BWAPI::TilePosition & tp)

Sets the enemy main base location and updates behavior state.

If the dark templar is already assigned, transitions into movement toward the enemy main. Otherwise waits for assignment.

Parameters
tpEnemy main tile position

Definition at line 27 of file DarkTemplar.cpp.

28{
29 enemyMainTile = tp;
30 enemyMainPos = BWAPI::Position(tp);
31
32 if (darkTemplar && darkTemplar->exists())
33 {
34 state = State::MoveToEnemyMain;
35 }
36 else
37 {
38 state = State::WaitEnemyMain;
39 }
40}

Member Data Documentation

◆ commanderRef

ProtoBotCommander* DarkTemplar::commanderRef = nullptr
private

Definition at line 49 of file DarkTemplar.h.

◆ darkTemplar

BWAPI::Unit DarkTemplar::darkTemplar = nullptr
private

Definition at line 52 of file DarkTemplar.h.

◆ enemyMainPos

BWAPI::Position DarkTemplar::enemyMainPos = BWAPI::Positions::Invalid
private

Definition at line 56 of file DarkTemplar.h.

◆ enemyMainTile

std::optional<BWAPI::TilePosition> DarkTemplar::enemyMainTile
private

Definition at line 55 of file DarkTemplar.h.

◆ kArriveDistPx

int DarkTemplar::kArriveDistPx = 160
staticconstexprprivate

Definition at line 63 of file DarkTemplar.h.

◆ kAttackRangePx

int DarkTemplar::kAttackRangePx = 20 * 32
staticconstexprprivate

Definition at line 62 of file DarkTemplar.h.

◆ kMainPriorityRadiusPx

int DarkTemplar::kMainPriorityRadiusPx = 14 * 32
staticconstexprprivate

Definition at line 64 of file DarkTemplar.h.

◆ kMoveCooldownFrames

int DarkTemplar::kMoveCooldownFrames = 8
staticconstexprprivate

Definition at line 61 of file DarkTemplar.h.

◆ lastMoveIssueFrame

int DarkTemplar::lastMoveIssueFrame = 0
private

Definition at line 60 of file DarkTemplar.h.

◆ lockedTargetId

int DarkTemplar::lockedTargetId = -1
private

Definition at line 53 of file DarkTemplar.h.

◆ manager

ScoutingManager* DarkTemplar::manager = nullptr
private

Definition at line 50 of file DarkTemplar.h.

◆ state

State DarkTemplar::state = State::Idle
private

Definition at line 58 of file DarkTemplar.h.


The documentation for this class was generated from the following files: