2021-05-15 20:56:31 +02:00
/*
* ExecuteHeroChain . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "ExecuteHeroChain.h"
2021-05-16 14:39:38 +02:00
# include "../AIGateway.h"
2021-05-15 20:56:31 +02:00
# include "../Engine/Nullkiller.h"
2022-09-26 20:01:07 +02:00
namespace NKAI
{
2021-05-15 20:56:31 +02:00
using namespace Goals ;
ExecuteHeroChain : : ExecuteHeroChain ( const AIPath & path , const CGObjectInstance * obj )
2021-05-16 13:56:27 +02:00
: ElementarGoal ( Goals : : EXECUTE_HERO_CHAIN ) , chainPath ( path ) , closestWayRatio ( 1 )
2021-05-15 20:56:31 +02:00
{
hero = path . targetHero ;
2021-05-15 20:57:27 +02:00
tile = path . targetTile ( ) ;
if ( obj )
{
objid = obj - > id . getNum ( ) ;
targetName = obj - > getObjectName ( ) + tile . toString ( ) ;
}
else
{
targetName = " tile " + tile . toString ( ) ;
}
2021-05-15 20:56:31 +02:00
}
bool ExecuteHeroChain : : operator = = ( const ExecuteHeroChain & other ) const
{
2021-05-16 13:38:53 +02:00
return tile = = other . tile
& & chainPath . targetHero = = other . chainPath . targetHero
& & chainPath . nodes . size ( ) = = other . chainPath . nodes . size ( )
& & chainPath . chainMask = = other . chainPath . chainMask ;
2021-05-15 20:56:31 +02:00
}
2021-05-16 14:39:38 +02:00
void ExecuteHeroChain : : accept ( AIGateway * ai )
2021-05-15 20:56:31 +02:00
{
2021-05-15 20:57:27 +02:00
logAi - > debug ( " Executing hero chain towards %s. Path %s " , targetName , chainPath . toString ( ) ) ;
2021-05-15 20:56:31 +02:00
2021-05-16 13:38:26 +02:00
ai - > nullkiller - > setActive ( chainPath . targetHero , tile ) ;
2023-02-28 09:07:59 +02:00
ai - > nullkiller - > setTargetObject ( objid ) ;
2021-05-16 13:38:26 +02:00
2023-06-04 15:02:02 +02:00
auto targetObject = ai - > myCb - > getObj ( static_cast < ObjectInstanceID > ( objid ) , false ) ;
if ( chainPath . turn ( ) = = 0 & & targetObject & & targetObject - > ID = = Obj : : TOWN )
{
auto relations = ai - > myCb - > getPlayerRelations ( ai - > playerID , targetObject - > getOwner ( ) ) ;
if ( relations = = PlayerRelations : : ENEMIES )
{
ai - > nullkiller - > armyFormation - > rearrangeArmyForSiege (
dynamic_cast < const CGTownInstance * > ( targetObject ) ,
chainPath . targetHero ) ;
}
}
2021-05-15 20:56:31 +02:00
std : : set < int > blockedIndexes ;
for ( int i = chainPath . nodes . size ( ) - 1 ; i > = 0 ; i - - )
{
auto & node = chainPath . nodes [ i ] ;
2019-12-15 15:47:21 +02:00
const CGHeroInstance * hero = node . targetHero ;
HeroPtr heroPtr = hero ;
2021-05-15 20:56:31 +02:00
2021-05-16 14:39:38 +02:00
if ( node . parentIndex > = i )
{
logAi - > error ( " Invalid parentIndex while executing node " + node . coord . toString ( ) ) ;
}
2021-05-15 20:56:31 +02:00
if ( vstd : : contains ( blockedIndexes , i ) )
{
blockedIndexes . insert ( node . parentIndex ) ;
2019-12-15 15:47:21 +02:00
ai - > nullkiller - > lockHero ( hero , HeroLockedReason : : HERO_CHAIN ) ;
2021-05-15 20:56:31 +02:00
continue ;
}
2023-01-02 13:27:03 +02:00
logAi - > debug ( " Executing chain node %d. Moving hero %s to %s " , i , hero - > getNameTranslated ( ) , node . coord . toString ( ) ) ;
2021-05-15 20:56:31 +02:00
try
{
2023-06-21 19:38:26 +02:00
if ( hero - > movementPointsRemaining ( ) > 0 )
2021-05-15 21:02:27 +02:00
{
2019-12-15 15:47:21 +02:00
ai - > nullkiller - > setActive ( hero , node . coord ) ;
2021-05-15 20:56:31 +02:00
2021-05-15 21:02:57 +02:00
if ( node . specialAction )
{
2021-05-16 13:38:53 +02:00
if ( node . actionIsBlocked )
2021-05-16 13:07:54 +02:00
{
2019-12-15 15:47:21 +02:00
throw cannotFulfillGoalException ( " Path is nondeterministic. " ) ;
2021-05-16 13:07:54 +02:00
}
2019-12-15 15:47:21 +02:00
2021-05-16 13:38:53 +02:00
node . specialAction - > execute ( hero ) ;
2019-12-15 15:47:21 +02:00
if ( ! heroPtr . validAndSet ( ) )
2021-05-16 13:19:44 +02:00
{
2019-12-15 15:47:21 +02:00
logAi - > error ( " Hero %s was lost trying to execute special action. Exit hero chain. " , heroPtr . name ) ;
2021-05-16 13:19:44 +02:00
return ;
}
2021-05-15 21:02:57 +02:00
}
2021-05-16 13:55:33 +02:00
if ( node . turns = = 0 & & node . coord ! = hero - > visitablePos ( ) )
2021-05-16 13:09:49 +02:00
{
2019-12-15 15:47:21 +02:00
auto targetNode = cb - > getPathsInfo ( hero ) - > getPathInfo ( node . coord ) ;
2021-05-16 13:09:49 +02:00
2023-06-21 14:38:57 +02:00
if ( targetNode - > accessible = = EPathAccessibility : : NOT_SET
| | targetNode - > accessible = = EPathAccessibility : : BLOCKED
| | targetNode - > accessible = = EPathAccessibility : : FLYABLE
2021-05-16 13:19:44 +02:00
| | targetNode - > turns ! = 0 )
2021-05-16 13:09:49 +02:00
{
logAi - > error (
2022-09-14 16:23:13 +02:00
" Unable to complete chain. Expected hero %s to arrive to %s in 0 turns but he cannot do this " ,
2023-01-02 13:27:03 +02:00
hero - > getNameTranslated ( ) ,
2021-05-16 13:13:48 +02:00
node . coord . toString ( ) ) ;
2021-05-16 13:09:49 +02:00
return ;
}
}
2023-06-21 19:38:26 +02:00
if ( hero - > movementPointsRemaining ( ) )
2021-05-16 13:13:40 +02:00
{
2021-05-16 13:13:48 +02:00
try
2021-05-16 13:13:40 +02:00
{
2021-05-16 13:38:26 +02:00
if ( moveHeroToTile ( hero , node . coord ) )
{
continue ;
}
2021-05-16 13:13:48 +02:00
}
2022-12-07 18:05:47 +02:00
catch ( const cannotFulfillGoalException & )
2021-05-16 13:13:48 +02:00
{
2019-12-15 15:47:21 +02:00
if ( ! heroPtr . validAndSet ( ) )
2021-05-16 13:19:44 +02:00
{
2019-12-15 15:47:21 +02:00
logAi - > error ( " Hero %s was lost. Exit hero chain. " , heroPtr . name ) ;
2021-05-16 13:19:44 +02:00
return ;
}
2023-06-21 19:38:26 +02:00
if ( hero - > movementPointsRemaining ( ) > 0 )
2021-05-16 13:13:40 +02:00
{
2021-05-16 13:13:48 +02:00
CGPath path ;
2019-12-15 15:47:21 +02:00
bool isOk = cb - > getPathsInfo ( hero ) - > getPath ( path , node . coord ) ;
2021-05-16 13:13:48 +02:00
if ( isOk & & path . nodes . back ( ) . turns > 0 )
{
2023-06-21 19:38:26 +02:00
logAi - > warn ( " Hero %s has %d mp which is not enough to continue his way towards %s. " , hero - > getNameTranslated ( ) , hero - > movementPointsRemaining ( ) , node . coord . toString ( ) ) ;
2021-05-16 13:13:40 +02:00
2019-12-15 15:47:21 +02:00
ai - > nullkiller - > lockHero ( hero , HeroLockedReason : : HERO_CHAIN ) ;
2021-05-16 13:13:48 +02:00
return ;
}
2021-05-16 13:13:40 +02:00
}
2021-05-16 13:13:48 +02:00
throw ;
}
2021-05-16 13:13:40 +02:00
}
2021-05-15 21:02:27 +02:00
}
2021-05-15 20:57:27 +02:00
2021-05-16 13:19:07 +02:00
if ( node . coord = = hero - > visitablePos ( ) )
continue ;
2021-05-16 13:09:49 +02:00
if ( node . turns = = 0 )
{
logAi - > error (
2023-03-06 13:19:49 +02:00
" Unable to complete chain. Expected hero %s to arive to %s but he is at %s " ,
2023-01-02 13:27:03 +02:00
hero - > getNameTranslated ( ) ,
2021-05-16 13:09:49 +02:00
node . coord . toString ( ) ,
hero - > visitablePos ( ) . toString ( ) ) ;
return ;
}
2021-05-16 13:13:56 +02:00
2023-03-06 13:19:49 +02:00
// no exception means we were not able to reach the tile
2019-12-15 15:47:21 +02:00
ai - > nullkiller - > lockHero ( hero , HeroLockedReason : : HERO_CHAIN ) ;
2021-05-15 20:56:31 +02:00
blockedIndexes . insert ( node . parentIndex ) ;
}
2022-12-07 18:05:47 +02:00
catch ( const goalFulfilledException & )
2021-05-15 20:56:31 +02:00
{
2019-12-15 15:47:21 +02:00
if ( ! heroPtr . validAndSet ( ) )
2021-05-15 20:56:31 +02:00
{
2023-03-06 13:19:49 +02:00
logAi - > debug ( " Hero %s was killed while attempting to reach %s " , heroPtr . name , node . coord . toString ( ) ) ;
2021-05-15 20:56:31 +02:00
return ;
}
}
}
}
2021-05-16 13:38:26 +02:00
std : : string ExecuteHeroChain : : toString ( ) const
2021-05-15 20:56:31 +02:00
{
2023-01-02 13:27:03 +02:00
return " ExecuteHeroChain " + targetName + " by " + chainPath . targetHero - > getNameTranslated ( ) ;
2021-05-16 13:38:26 +02:00
}
bool ExecuteHeroChain : : moveHeroToTile ( const CGHeroInstance * hero , const int3 & tile )
{
2021-05-16 13:38:53 +02:00
if ( tile = = hero - > visitablePos ( ) & & cb - > getVisitableObjs ( hero - > visitablePos ( ) ) . size ( ) < 2 )
2021-05-16 13:38:26 +02:00
{
2023-01-02 13:27:03 +02:00
logAi - > warn ( " Why do I want to move hero %s to tile %s? Already standing on that tile! " , hero - > getNameTranslated ( ) , tile . toString ( ) ) ;
2021-05-16 13:38:26 +02:00
return true ;
}
return ai - > moveHeroToTile ( tile , hero ) ;
2022-09-26 20:01:07 +02:00
}
}