Skip to content

Commit 5e9dcae

Browse files
committed
new cover AI seems to be functional!
sectoids are scurrying well Conflicts: src/Savegame/BattleUnit.cpp
1 parent 09756ab commit 5e9dcae

13 files changed

Lines changed: 199 additions & 59 deletions

‎src/Battlescape/AggroBAIState.cpp‎

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ void AggroBAIState::exit()
160160
*/
161161
void AggroBAIState::think(BattleAction *action)
162162
{
163+
if (Options::getBool("traceAI")) { Log(LOG_INFO) << "AggroBAIState::think() #" << action->number; }
164+
163165
action->type = BA_RETHINK;
164166
action->actor = _unit;
165167
/* Aggro is mainly either shooting a target or running towards it (melee).
@@ -387,11 +389,19 @@ void AggroBAIState::think(BattleAction *action)
387389
if (aggression == 2 && number < 90)
388390
takeCover = false;
389391

390-
if (action->number == 2)
391-
{
392+
if (action->number >= 3)
393+
{
394+
if (_unit->_hidingForTurn)
395+
{
396+
// already tried to get to cover; stay still
397+
action->type = BA_NONE;
398+
action->TU = 0;
399+
return;
400+
}
401+
392402
takeCover = true; // always seek cover as last action
393403
action->reckless = true;
394-
}
404+
}
395405

396406
// we're using melee, so CHAAAAAAAARGE!!!!!
397407
if (_unit->getMainHandWeapon() && _unit->getMainHandWeapon()->getRules()->getBattleType() == BT_MELEE)
@@ -423,8 +433,8 @@ void AggroBAIState::think(BattleAction *action)
423433
_game->getPathfinding()->calculate(action->actor, checkPath, 0);
424434
int newDistance = _game->getTileEngine()->distance(action->actor->getPosition(), checkPath);
425435
bool valid = _game->getTileEngine()->validMeleeRange(checkPath, -1, action->actor->getArmor()->getSize(), action->actor->getHeight(), _aggroTarget);
426-
if (_game->getPathfinding()->getStartDirection() != -1 /* && valid &&
427-
newDistance < distance*/)
436+
if (_game->getPathfinding()->getStartDirection() != -1 && valid &&
437+
newDistance < distance)
428438
{
429439
// CHAAAAAAARGE!
430440
action->target = checkPath;
@@ -517,6 +527,7 @@ void AggroBAIState::think(BattleAction *action)
517527
// if there is no such tile, we run away from the target.
518528
bool sneak = Options::getBool("sneakyAI");
519529
action->type = BA_WALK;
530+
_unit->_hidingForTurn = true;
520531
int tries = 0;
521532
bool coverFound = false;
522533
int x_search_sign = RNG::generate(0, 1) ? 1 : -1; // randomize the direction of the search for lack of a better heuristic
@@ -533,32 +544,50 @@ void AggroBAIState::think(BattleAction *action)
533544
int score = -1000;
534545
Position bestTile(0, 0, 0);
535546
++_randomTileSearchAge;
547+
if (action->number > 1) action->desperate = true;
548+
549+
Tile *tile = 0;
550+
536551

537552
while (tries < 150 && !coverFound)
538553
{
539554
tries++;
540555
action->target = _unit->getPosition() + run; // start looking in a direction away from the enemy
556+
557+
if (!_game->getTile(action->target))
558+
{
559+
action->target = _unit->getPosition(); // cornered at the edge of the map perhaps?
560+
}
561+
541562
if (tries < 121)
542563
{
543564
// looking for cover
544565
action->target.x += _randomTileSearch[tries].x;
545566
action->target.y += _randomTileSearch[tries].y;
546-
if (action->target == _unit->getPosition() && unitsSpottingMe > 0)
567+
if (action->target == _unit->getPosition() && action->number == 1 && unitsSpottingMe > 0)
547568
{
548569
// don't even think about staying in the same spot. Move!
549570
action->target.x += RNG::generate(-20,20);
550571
action->target.y += RNG::generate(-20,20);
551572
}
552-
score = _game->getTileEngine()->visible(_aggroTarget, _game->getTile(action->target)) ? 0 : 100;
573+
//score = _game->getTileEngine()->visible(_aggroTarget, _game->getTile(action->target)) ? 0 : 100;
574+
score = 100; // no need for visible here, the TileEngine code will take care of it
553575
}
554576
else
555577
{
556-
if (tries == 121) Log(LOG_INFO) << _unit->getId() << " best score after systematic search was: " << bestTileScore;
557-
action->desperate = true;
558-
score = 130; // ruuuuuuun
559-
action->target = _unit->getPosition() + run*2;
560-
action->target.x += RNG::generate(-5,5);
561-
action->target.y += RNG::generate(-5,5);
578+
if (tries == 121)
579+
{
580+
action->reckless = true;
581+
if (Options::getBool("traceAI"))
582+
{
583+
Log(LOG_INFO) << _unit->getId() << " best score after systematic search was: " << bestTileScore;
584+
}
585+
}
586+
587+
score = 110; // ruuuuuuun
588+
action->target = _unit->getPosition() + run*3;
589+
action->target.x += RNG::generate(-10,10);
590+
action->target.y += RNG::generate(-10,10);
562591
action->target.z = _unit->getPosition().z + RNG::generate(-1,1);
563592
if (action->target.z < 0)
564593
{
@@ -571,39 +600,51 @@ void AggroBAIState::think(BattleAction *action)
571600
}
572601

573602
// THINK, DAMN YOU
574-
if (!_game->getPathfinding()->bresenhamPath(_aggroTarget->getPosition(), action->target, 0, false)) score += 25; // partial cover?
575-
_game->getPathfinding()->abortPath(); // clean up hypothetical path data
576-
if (sneak)
603+
tile = _game->getTile(action->target);
604+
if (!tile)
577605
{
578-
Tile *tile = _game->getTile(action->target);
579-
if (!tile) score = -10000;
580-
else if (tile->getVisible()) score -= 50;
581-
// XXX perhaps only psi-skilled aliens should cheat-sneak?
606+
score = -10000; // no you can't quit the battlefield by running off the map.
582607
}
583-
score += abs(action->target.x - _aggroTarget->getPosition().x);
584-
score += abs(action->target.y - _aggroTarget->getPosition().y);
585-
score += abs(action->target.z - _aggroTarget->getPosition().z)*2;
586-
587-
int nearestEnemy = 1000; // too far away to matter
588-
for (std::vector<BattleUnit*>::const_iterator i = _game->getUnits()->begin(); i != _game->getUnits()->end(); ++i)
608+
else
589609
{
590-
if ((*i)->getId() == _unit->getId()) continue; // woo, it's us
591-
if ((*i)->isOut()) continue; // derp
592-
int distSq = _game->getTileEngine()->distanceSq(action->target, (*i)->getPosition());
593-
if (distSq == 0) distSq = 1; // dont divide by 0
594-
if (distSq <= 100)
610+
if (sneak)
595611
{
596-
if ((*i)->getFaction() == FACTION_PLAYER)
597-
{
598-
if (distSq < nearestEnemy) nearestEnemy = distSq;
599-
} else
600-
{
601-
if (distSq <= 2) score += 5; // quick, hide behind the nearest civilian?
602-
if (distSq <= 9 && (*i)->getFaction() == FACTION_HOSTILE) score += 5; // strength in numbers?
603-
}
612+
if (tile->getVisible()) score -= 50;
613+
// XXX perhaps only psi-skilled aliens should cheat-sneak?
604614
}
615+
616+
_game->getTileEngine()->surveyXComThreatToTile(tile, action->target, _unit);
617+
618+
if (tile->_soldiersVisible && tile->_closestSoldierDSqr <= 100 && tile->_closestSoldierDSqr > 0) score -= (200/tile->_closestSoldierDSqr);
619+
//if (!tile->_soldiersVisible) { Log(LOG_WARNING) << "No soldiers visible? Really?"; }
620+
621+
if (!tile->_soldiersVisible)
622+
{
623+
score += dist*5; // hooray!
624+
} else
625+
{
626+
score -= tile->_soldiersVisible * 10;
627+
score += (dist > 9) ? 9 : dist;
628+
}
629+
630+
if (tile->_closestAlienDSqr < 25 && tile->_closestAlienDSqr > 4) score += 4; // strength in numbers but not in "grenade us!" huddles
631+
632+
_game->getPathfinding()->setUnit(_unit); // because we can't just pass this around as a paramater, can we... no, that would be too simple
633+
634+
if (tile->_soldiersVisible && _game->getPathfinding()->bresenhamPath(tile->_closestSoldierPos, action->target, 0, false)) score -= 50; // not even partial cover?
635+
_game->getPathfinding()->abortPath(); // clean up hypothetical path data
636+
637+
if (!_game->getPathfinding()->bresenhamPath(_aggroTarget->getPosition(), action->target, 0, false)) score += 4; // partial cover?
638+
_game->getPathfinding()->abortPath(); // clean up hypothetical path data
639+
640+
if (tile->getFire()) score -= 20;
641+
642+
if (tile->getSmoke()) score -= 2;
643+
644+
if (tile->getMapData(MapData::O_NORTHWALL) || tile->getMapData(MapData::O_WESTWALL)) score += 1; // hug the walls
645+
646+
if (_game->getTileEngine()->faceWindow(action->target) != -1) score -= 50; // a window is not cover.
605647
}
606-
if (nearestEnemy < 100) score -= (100-nearestEnemy);
607648

608649

609650
if (score > bestTileScore)
@@ -617,11 +658,15 @@ void AggroBAIState::think(BattleAction *action)
617658
bestTile = action->target;
618659
}
619660
_game->getPathfinding()->abortPath();
620-
if (bestTileScore > 130) coverFound = true; // good enough, gogogo; this value is hand-tuned and subjective :(
661+
if (bestTileScore > 120) coverFound = true; // good enough, gogogo; this value is hand-tuned and subjective :(
621662
}
622663
}
623-
Log(LOG_INFO) << _unit->getId() << " Taking cover with score " << bestTileScore << " after " << tries << " tries.";
624-
Log(LOG_INFO) << "Walking " << _game->getTileEngine()->distance(_unit->getPosition(), bestTile) << " squares or so.";
664+
if (Options::getBool("traceAI"))
665+
{
666+
Log(LOG_INFO) << _unit->getId() << " Taking cover with score " << bestTileScore << " after " << tries << " tries, at a tile spotted by " << ((tile=_game->getTile(bestTile)) ? tile->_soldiersVisible : -666) << ". Action #" << action->number;
667+
//Log(LOG_INFO) << "Walking " << _game->getTileEngine()->distance(_unit->getPosition(), bestTile) << " squares or so.";
668+
669+
}
625670
action->target = bestTile;
626671
}
627672
}

‎src/Battlescape/BattlescapeGame.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ void BattlescapeGame::handleAI(BattleUnit *unit)
189189
{
190190
_playedAggroSound = false;
191191
}
192+
if(_AIActionCounter == 1)
193+
{
194+
unit->_hidingForTurn = 0;
195+
}
192196
AggroBAIState *aggro = dynamic_cast<AggroBAIState*>(ai);
193197

194198
// psionic or blaster launcher units may attack remotely
@@ -471,7 +475,7 @@ void BattlescapeGame::checkForCasualties(BattleItem *murderweapon, BattleUnit *m
471475
{
472476
if ((*h)->getFaction() == FACTION_HOSTILE && !(*h)->isOut() && (*h) != victim)
473477
{
474-
int d = _save->getTileEngine()->distance(victim->getPosition(), (*h)->getPosition());
478+
int d = _save->getTileEngine()->distanceSq(victim->getPosition(), (*h)->getPosition());
475479
if (d < closest)
476480
{
477481
revenger = (*h);

‎src/Battlescape/BattlescapeGame.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class BattlescapeGame
153153
ResourcePack *getResourcePack();
154154
const Ruleset *getRuleset() const;
155155
/// this method evaluates the threats from XCom soldiers to tiles, for later use by AI
156-
void BattlescapeGame::resetSituationForAI();
156+
void resetSituationForAI();
157157
static bool _debugPlay; // nobody needs a getter for this. It should really be global.
158158
};
159159

‎src/Battlescape/Pathfinding.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ int Pathfinding::getTUCost(const Position &startPosition, int direction, Positio
228228
*endPosition += startPosition;
229229
bool fellDown = false;
230230
bool triedStairs = false;
231-
int size = _unit->getArmor()->getSize() - 1;
231+
int size = _unit ? (_unit->getArmor()->getSize() - 1) : 1;
232232
int cost = 0;
233233
int numberOfPartsGoingUp = 0;
234234
int numberOfPartsGoingDown = 0;

‎src/Battlescape/Pathfinding.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class Pathfinding
8383
bool validateUpDown(BattleUnit *bu, Position startPosition, const int direction);
8484
bool previewPath(bool bRemove = false);
8585
bool removePreview();
86+
/// Set _unit in order to abuse low-level pathfinding functions from outside the class
87+
void setUnit(BattleUnit *unit) { _unit = unit; };
8688
/// Get all reachable tiles, based on cost.
8789
std::vector<int> findReachable(BattleUnit *unit, int tuMax);
8890
};

‎src/Battlescape/PatrolBAIState.cpp‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "../Savegame/SavedBattleGame.h"
2626
#include "../Savegame/Node.h"
2727
#include "../Engine/RNG.h"
28+
#include "../Engine/Logger.h"
29+
#include "../Engine/Options.h"
2830
#include "../Ruleset/Armor.h"
2931
#include "../Savegame/Tile.h"
3032

@@ -127,6 +129,19 @@ void PatrolBAIState::think(BattleAction *action)
127129

128130
Node *node;
129131

132+
133+
if (Options::getBool("traceAI"))
134+
{
135+
Log(LOG_INFO) << "PatrolBAIState::think() #" << action->number;
136+
}
137+
138+
if (_unit->_hidingForTurn)
139+
{
140+
action->type = BA_NONE;
141+
action->TU = 0;
142+
return;
143+
}
144+
130145
if (_toNode != 0 && _unit->getPosition() == _toNode->getPosition())
131146
{
132147
// destination reached

0 commit comments

Comments
 (0)