Skip to content

Commit

Permalink
Add AI visibility handling logic and fix some bugs
Browse files Browse the repository at this point in the history
Add mechanism for AI to keep track of what plots other AI players can see.
Now instead of cheating by checking what another player can see directly, it will now base its knowledge on known enemy terrory and unit placements.
Currently does not handle the case where an enemy unit moves during your turn (retreating). Shouldn't have a big impact overall.

Fix AI sometimes not clearing their route caches if they only have one city.

Fix AI never building improvements near their own capital if they are at war with a civ with a spy there (part of new visibility mechanism).

Possibly fixes AI mistakenly cancelling sneak operations based on bad information.

Fix AI never building lower level roads even when it's supposed to.

Fix vassals removing their master's roads.

Masters will now build routes through vassals' lands when needed.

Fix AI being less prone to remove roads when losing gold, rather than the other way round.

Minor cleanup regarding road building and removing. Should fix potential Iroquis and Songhai issues.

Fix incorrect planned road maintenance cost calculation.

AI will no longer build villages on roads that are about to be removed.

AI can build routes through minor civs even if they don't have a quest for it.

AI will now not avoid building routes through their vassals' lands.

Routes between non-capital cities also benefit villages and towns, update pathfinding to take this into account.

Up route removal priority a little bit.

Remove unused variables.

Include Towns in route pathfinding logic.

Clean up some route logic, always build profitable roads, even if we are losing gold.

Don't build railroads for city state quests.
  • Loading branch information
KungCheops authored and RecursiveVision committed Dec 20, 2023
1 parent ca2e5f6 commit d8e4a29
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 135 deletions.
2 changes: 1 addition & 1 deletion CvGameCoreDLL_Expansion2/CvAIOperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,7 @@ bool CvAIOperationMilitary::CheckTransitionToNextStage()
int nVisible = 0;
for (CvUnit* pUnit = pThisArmy->GetFirstUnit(); pUnit; pUnit = pThisArmy->GetNextUnit(pUnit))
{
if (pUnit->plot()->isVisible( GET_PLAYER(m_eEnemy).getTeam() ))
if (GET_PLAYER(pUnit->getOwner()).GetTacticalAI()->IsVisibleToPlayer(pUnit->plot(), GET_PLAYER(m_eEnemy).getTeam()))
nVisible++;
}

Expand Down
48 changes: 26 additions & 22 deletions CvGameCoreDLL_Expansion2/CvAStar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2184,7 +2184,7 @@ int CityConnectionWaterValid(const CvAStarNode* parent, const CvAStarNode* node,

// --------------------------------------------------------------------------------
/// Prefer building routes that can have villages.
int BuildRouteVillageBonus(CvPlayer* pPlayer, CvPlot* pPlot, RouteTypes eRouteType, CvBuilderTaskingAI* eBuilderTaskingAi)
static int BuildRouteVillageBonus(CvPlayer* pPlayer, CvPlot* pPlot, RouteTypes eRouteType, CvBuilderTaskingAI* eBuilderTaskingAi)
{
// If we're not the owner of this plot, bail out
if (!pPlot->isOwned() || pPlot->getOwner() != pPlayer->GetID())
Expand Down Expand Up @@ -2239,9 +2239,6 @@ int BuildRouteVillageBonus(CvPlayer* pPlayer, CvPlot* pPlot, RouteTypes eRouteTy
if (pkImprovementInfo == NULL)
continue;

if (pkImprovementInfo->IsCreatedByGreatPerson())
continue;

for (int iYield = 0; iYield < NUM_YIELD_TYPES; iYield++)
{
YieldTypes eYield = (YieldTypes)iYield;
Expand Down Expand Up @@ -2289,10 +2286,10 @@ int BuildRouteCost(const CvAStarNode* /*parent*/, const CvAStarNode* node, const
}

//too dangerous, might be severed any time
if (pPlot->getOwner() == NO_PLAYER && pPlot->IsAdjacentOwnedByTeamOtherThan(pPlayer->getTeam()))
if (pPlot->getOwner() == NO_PLAYER && pPlot->IsAdjacentOwnedByTeamOtherThan(pPlayer->getTeam(), false, false, true, true))
iCost *= 3;

if (data.bIsForCapital)
if (data.bBenefitsVillages)
iCost -= BuildRouteVillageBonus(pPlayer, pPlot, eRouteType, eBuilderTaskingAi);

if (iCost < 0)
Expand All @@ -2301,26 +2298,38 @@ int BuildRouteCost(const CvAStarNode* /*parent*/, const CvAStarNode* node, const
return iCost;
}

bool IsSafeForRoute(CvPlot* pPlot, CvPlayer* ePlayer)
static bool IsSafeForRoute(CvPlot* pPlot, CvPlayer* pPlayer)
{
TeamTypes ePlotTeam = pPlot->getTeam();
TeamTypes ePlayerTeam = pPlayer->getTeam();
PlayerTypes ePlotOwner = pPlot->getOwner();

// Our plots and surrounding plots are safe
if (pPlot->getTeam() == ePlayer->getTeam() || pPlot->isAdjacentTeam(ePlayer->getTeam(), false))
if (ePlotTeam == ePlayerTeam || pPlot->isAdjacentTeam(pPlayer->getTeam(), false))
{
return true;
}

// Our vassal's plots and surrounding plots are safe
if (GET_TEAM(ePlotTeam).IsVassal(ePlayerTeam) || pPlot->isAdjacentOwnedByVassal(ePlayerTeam, false))
{
return true;
}

// City state plots and surrounding plots are safe
if (pPlot->isOwned() && GET_PLAYER(pPlot->getOwner()).isMinorCiv() && !GET_PLAYER(pPlot->getOwner()).GetMinorCivAI()->IsAtWarWithPlayersTeam(ePlayer->GetID()))
if (ePlotOwner != NO_PLAYER && GET_PLAYER(ePlotOwner).isMinorCiv() && !GET_PLAYER(ePlotOwner).GetMinorCivAI()->IsAtWarWithPlayersTeam(pPlayer->GetID()))
{
return true;
}
CvPlot** aPlotsToCheck = GC.getMap().getNeighborsUnchecked(pPlot);
PlayerTypes eAdjacentOwner;
for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
CvPlot* pAdjacentPlot = aPlotsToCheck[iI];
if (pAdjacentPlot != NULL)
{
if (pAdjacentPlot->isOwned() && GET_PLAYER(pAdjacentPlot->getOwner()).isMinorCiv() && !GET_PLAYER(pAdjacentPlot->getOwner()).GetMinorCivAI()->IsAtWarWithPlayersTeam(ePlayer->GetID()))
eAdjacentOwner = pAdjacentPlot->getOwner();
if (eAdjacentOwner != NO_PLAYER && GET_PLAYER(eAdjacentOwner).isMinorCiv() && !GET_PLAYER(eAdjacentOwner).GetMinorCivAI()->IsAtWarWithPlayersTeam(pPlayer->GetID()))
{
return true;
}
Expand Down Expand Up @@ -2357,7 +2366,7 @@ int BuildRouteValid(const CvAStarNode* parent, const CvAStarNode* node, const SP
return FALSE;

PlayerTypes ePlotOwnerPlayer = pNewPlot->getOwner();
if(ePlotOwnerPlayer != NO_PLAYER && pNewPlot->getTeam() != thisPlayer.getTeam())
if(ePlotOwnerPlayer != NO_PLAYER && pNewPlot->getTeam() != thisPlayer.getTeam() && !GET_TEAM(pNewPlot->getTeam()).IsVassal(thisPlayer.getTeam()))
{
PlayerTypes eMajorPlayer = NO_PLAYER;
PlayerTypes eMinorPlayer = NO_PLAYER;
Expand All @@ -2376,11 +2385,6 @@ int BuildRouteValid(const CvAStarNode* parent, const CvAStarNode* node, const SP
{
return FALSE;
}

if(!GET_PLAYER(eMinorPlayer).GetMinorCivAI()->IsActiveQuestForPlayer(eMajorPlayer, MINOR_CIV_QUEST_ROUTE))
{
return FALSE;
}
}

//Free routes from traits are always safe
Expand Down Expand Up @@ -3715,7 +3719,7 @@ SPathFinderUserData::SPathFinderUserData(const CvUnit* pUnit, int _iFlags, int _
iMaxNormalizedDistance = INT_MAX;
iMinMovesLeft = 0;
iStartMoves = pUnit ? pUnit->getMoves() : 0;
bIsForCapital = false;
bBenefitsVillages = false;
}

// ---------------------------------------------------------------------------
Expand All @@ -3733,7 +3737,7 @@ SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathTy
iMaxNormalizedDistance = INT_MAX;
iMinMovesLeft = 0;
iStartMoves = 0;
bIsForCapital = false;
bBenefitsVillages = false;
}

// ---------------------------------------------------------------------------
Expand All @@ -3751,7 +3755,7 @@ SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathTy
iMaxNormalizedDistance = INT_MAX;
iMinMovesLeft = 0;
iStartMoves = 0;
bIsForCapital = false;
bBenefitsVillages = false;
}

// ---------------------------------------------------------------------------
Expand All @@ -3769,12 +3773,12 @@ SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathTy
iMaxNormalizedDistance = INT_MAX;
iMinMovesLeft = 0;
iStartMoves = 0;
bIsForCapital = false;
bBenefitsVillages = false;
}

// ---------------------------------------------------------------------------
//convenience constructor
SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathType, BuildTypes _eBuildType, RouteTypes _eRouteType, bool _bIsForCapital)
SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathType, BuildTypes _eBuildType, RouteTypes _eRouteType, bool _bBenefitsVillages)
{
ePathType = _ePathType;
iFlags = 0;
Expand All @@ -3787,7 +3791,7 @@ SPathFinderUserData::SPathFinderUserData(PlayerTypes _ePlayer, PathType _ePathTy
iMaxNormalizedDistance = INT_MAX;
iMinMovesLeft = 0;
iStartMoves = 0;
bIsForCapital = _bIsForCapital;
bBenefitsVillages = _bBenefitsVillages;
}

CvPlot * SPath::get(int i) const
Expand Down
6 changes: 3 additions & 3 deletions CvGameCoreDLL_Expansion2/CvAStarNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ class CvAStarNode
//-------------------------------------------------------------------------------------------------
struct SPathFinderUserData
{
SPathFinderUserData() : ePathType(PT_GENERIC_SAME_AREA), iFlags(0), ePlayer(NO_PLAYER), eEnemy(NO_PLAYER), iUnitID(0), eBuildType(NO_BUILD), eRouteType(NO_ROUTE), bIsForCapital(false), iMaxTurns(INT_MAX), iMaxNormalizedDistance(INT_MAX), iMinMovesLeft(0), iStartMoves(60) {}
SPathFinderUserData() : ePathType(PT_GENERIC_SAME_AREA), iFlags(0), ePlayer(NO_PLAYER), eEnemy(NO_PLAYER), iUnitID(0), eBuildType(NO_BUILD), eRouteType(NO_ROUTE), bBenefitsVillages(false), iMaxTurns(INT_MAX), iMaxNormalizedDistance(INT_MAX), iMinMovesLeft(0), iStartMoves(60) {}
SPathFinderUserData(const CvUnit* pUnit, int iFlags=0, int iMaxTurns=INT_MAX); // PT_AIR_REBASE (special case, set after construction)
SPathFinderUserData(PlayerTypes ePlayer, PathType ePathType); // PT_TRADE_WATER, PT_TRADE_LAND, PT_LANDMASS_CONNECTION, PT_CITY_CONNECTION_WATER
SPathFinderUserData(PlayerTypes ePlayer, PathType ePathType, int iMaxTurns); // PT_AREA_CONNECTION (iMaxTurns is simple vs complex check (0/1)), PT_CITY_INFLUENCE
SPathFinderUserData(PlayerTypes ePlayer, PathType ePathType, PlayerTypes eEnemy, int iMaxTurns); // PT_GENERIC_SAME_AREA, PT_GENERIC_SAME_AREA_WIDE, PT_ARMY_LAND, PT_ARMY_WATER, PT_ARMY_MIXED
SPathFinderUserData(PlayerTypes ePlayer, PathType ePathType, BuildTypes eBuildType, RouteTypes eRouteType, bool bIsForCapital); // PT_BUILD_ROUTE, PT_BUILD_ROUTE_MIXED, PT_CITY_CONNECTION_LAND, PT_CITY_CONNECTION_MIXED
SPathFinderUserData(PlayerTypes ePlayer, PathType ePathType, BuildTypes eBuildType, RouteTypes eRouteType, bool bBenefitsVillages); // PT_BUILD_ROUTE, PT_BUILD_ROUTE_MIXED, PT_CITY_CONNECTION_LAND, PT_CITY_CONNECTION_MIXED

//do not compare max turns and max cost ...
bool operator==(const SPathFinderUserData& rhs) const
Expand All @@ -151,7 +151,7 @@ struct SPathFinderUserData
PathType ePathType;
RouteTypes eRouteType;
BuildTypes eBuildType; //BuildType related to the RouteType
bool bIsForCapital;
bool bBenefitsVillages;
int iFlags; //see CvUnit::MOVEFLAG*
PlayerTypes ePlayer; //optional
PlayerTypes eEnemy;
Expand Down
Loading

0 comments on commit d8e4a29

Please sign in to comment.