Fixed base bugs

* Fixed issue where commander would build siege turrets too far from the siege target
* Improved commander response to requests for turrets
* Nav reachabilities and paths are now regenerated after all nav mesh modifications are updated
This commit is contained in:
RGreenlees 2024-06-24 14:23:45 +01:00 committed by pierow
parent 2842aba0bb
commit 733ad6eb04
10 changed files with 327 additions and 93 deletions

View file

@ -3166,21 +3166,29 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
}
}
if (NextRequest->RequestType == BUILD_SIEGE)
if (NextRequest->RequestType == BUILD_SIEGE || NextRequest->RequestType == BUILD_TURRET)
{
DeployableSearchFilter TFFilter;
TFFilter.DeployableTeam = CommanderTeam;
TFFilter.DeployableTypes = STRUCTURE_MARINE_ADVTURRETFACTORY;
TFFilter.DeployableTypes = (NextRequest->RequestType == BUILD_SIEGE) ? STRUCTURE_MARINE_ADVTURRETFACTORY : STRUCTURE_MARINE_TURRETFACTORY;
TFFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
TFFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
TFFilter.MaxSearchRadius = BALANCE_VAR(kTurretFactoryBuildDistance);
bool bHasChair = AITAC_DeployableExistsAtLocation(ProjectedDeployLocation, &TFFilter);
bool bHasTF = AITAC_DeployableExistsAtLocation(ProjectedDeployLocation, &TFFilter);
if (!bHasChair)
if (!bHasTF)
{
char msg[128];
sprintf(msg, "There isn't a an advanced armory there, %s. Ask again next to one", STRING(Requestor->v.netname));
if (NextRequest->RequestType == BUILD_SIEGE)
{
sprintf(msg, "There isn't an advanced turret factory in range, %s. Ask again near one", STRING(Requestor->v.netname));
}
else
{
sprintf(msg, "There isn't a completed turret factory in range, %s. Ask again near one", STRING(Requestor->v.netname));
}
BotSay(pBot, true, 0.5f, msg);
NextRequest->bResponded = true;
return false;
@ -3826,16 +3834,42 @@ void AICOMM_UpdateSiegeBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base)
bool bStillStuffToSiege = AITAC_DeployableExistsAtLocation(Base->BaseLocation, &EnemyStuffFilter);
const AvHAIHiveDefinition* NearestHive = AITAC_GetHiveNearestLocation(Base->BaseLocation);
const AvHAIHiveDefinition* SiegeHive = nullptr;
if (bStillStuffToSiege || (NearestHive && NearestHive->Status != HIVE_STATUS_UNBUILT && vDist2DSq(Base->BaseLocation, NearestHive->Location) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
if (vDist2DSq(Base->BaseLocation, NearestHive->Location) < sqrf(BALANCE_VAR(kSiegeTurretRange)))
{
SiegeHive = NearestHive;
}
if (bStillStuffToSiege || (SiegeHive && SiegeHive->Status != HIVE_STATUS_UNBUILT))
{
if (SiegeHive && SiegeHive->Status != HIVE_STATUS_UNBUILT)
{
Base->SiegeTarget = SiegeHive->Location;
}
else
{
// Basically, find the centroid (average location) of all siege structures, that will be our target
vector<AvHAIBuildableStructure> NearbyEnemyStuff = AITAC_FindAllDeployables(Base->BaseLocation, &EnemyStuffFilter);
Vector Centroid = ZERO_VECTOR;
for (auto it = NearbyEnemyStuff.begin(); it != NearbyEnemyStuff.end(); it++)
{
Centroid = Centroid + it->Location;
}
Centroid = Centroid / NearbyEnemyStuff.size();
Base->SiegeTarget = Centroid;
}
Base->bRecycleBase = false;
Base->bIsActive = true;
return;
}
else
{
if (!NearestHive || vDist2DSq(Base->BaseLocation, NearestHive->Location) > sqrf(BALANCE_VAR(kSiegeTurretRange)))
if (!SiegeHive)
{
Base->bRecycleBase = false;
Base->bIsActive = false;
@ -3845,7 +3879,7 @@ void AICOMM_UpdateSiegeBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base)
{
for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++)
{
if (it->BaseType == MARINE_BASE_OUTPOST && vDist2DSq(NearestHive->FloorLocation, it->BaseLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
if (it->BaseType == MARINE_BASE_OUTPOST && vDist2DSq(SiegeHive->FloorLocation, it->BaseLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
{
if (it->bIsBaseEstablished)
{
@ -3862,8 +3896,9 @@ void AICOMM_UpdateSiegeBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base)
}
}
Base->bRecycleBase = false;
Base->bIsActive = true;
Base->SiegeTarget = ZERO_VECTOR;
Base->bRecycleBase = true;
Base->bIsActive = false;
}
void AICOMM_DeployBases(AvHAIPlayer* pBot)
@ -4036,13 +4071,14 @@ void AICOMM_DeployBases(AvHAIPlayer* pBot)
if (!bHasSiege)
{
vector<AvHPlayer*> PotentialBuilders = AITAC_GetAllPlayersOfTeamInArea(BotTeam, ThisHive->FloorLocation, BALANCE_VAR(kSiegeTurretRange), false, nullptr, AVH_USER3_COMMANDER_PLAYER);
vector<AvHPlayer*> PotentialBuilders = AITAC_GetAllPlayersOfTeamInArea(BotTeam, ThisHive->Location, BALANCE_VAR(kSiegeTurretRange), false, nullptr, AVH_USER3_COMMANDER_PLAYER);
for (auto playerIt = PotentialBuilders.begin(); playerIt != PotentialBuilders.end(); playerIt++)
{
AvHPlayer* ThisPlayer = (*playerIt);
if (vDist2DSq(ThisPlayer->pev->origin, ThisHive->FloorLocation) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
if (vDist2DSq(ThisPlayer->pev->origin, ThisHive->Location) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
{
// If this player isn't right inside the hive and hasn't been spotted by an enemy, then they're eligible to start building a siege base
if (!(ThisPlayer->pev->iuser4 & MASK_VIS_SIGHTED))
{
DeployableSearchFilter EnemyStuffFilter;
@ -4052,7 +4088,7 @@ void AICOMM_DeployBases(AvHAIPlayer* pBot)
EnemyStuffFilter.DeployableTypes = (STRUCTURE_MARINE_COMMCHAIR | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE | STRUCTURE_ALIEN_OFFENCECHAMBER);
EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
if (!AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuffFilter))
if (!AITAC_DeployableExistsAtLocation(ThisPlayer->pev->origin, &EnemyStuffFilter))
{
AICOMM_AddNewBase(pBot, ThisPlayer->pev->origin, MARINE_BASE_SIEGE);
}
@ -5078,6 +5114,15 @@ bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
if (pBot->Player->GetResources() < BALANCE_VAR(kSentryCost)) { return true; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_TURRET, TurretFactory.Location, BALANCE_VAR(kTurretFactoryBuildDistance));
if (!vIsZero(BuildLocation))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, BaseToBuildOut);
if (bSuccess) { return true; }
}
int NumAttempts = 0;
while (NumAttempts < 5)
@ -5114,7 +5159,7 @@ bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
if (pBot->Player->GetResources() < BALANCE_VAR(kArmsLabCost)) { return true; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_ARMSLAB, CommChair.Location, UTIL_MetresToGoldSrcUnits(15.0f));
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_ARMSLAB, CommChair.Location, UTIL_MetresToGoldSrcUnits(20.0f));
if (!vIsZero(BuildLocation))
{
@ -5155,7 +5200,7 @@ bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
if (pBot->Player->GetResources() < BALANCE_VAR(kObservatoryCost)) { return true; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_OBSERVATORY, CommChair.Location, UTIL_MetresToGoldSrcUnits(15.0f));
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_OBSERVATORY, CommChair.Location, UTIL_MetresToGoldSrcUnits(20.0f));
if (!vIsZero(BuildLocation))
{
@ -5198,7 +5243,7 @@ bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
if (pBot->Player->GetResources() < BALANCE_VAR(kPhaseGateCost)) { return true; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_PHASEGATE, CommChair.Location, UTIL_MetresToGoldSrcUnits(15.0f));
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_PHASEGATE, CommChair.Location, UTIL_MetresToGoldSrcUnits(20.0f));
if (!vIsZero(BuildLocation))
{
@ -5250,7 +5295,7 @@ bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
if (pBot->Player->GetResources() < BALANCE_VAR(kPrototypeLabCost)) { return true; }
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_PROTOTYPELAB, CommChair.Location, UTIL_MetresToGoldSrcUnits(15.0f));
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_PROTOTYPELAB, CommChair.Location, UTIL_MetresToGoldSrcUnits(20.0f));
if (!vIsZero(BuildLocation))
{
@ -5481,7 +5526,6 @@ bool AICOMM_BuildOutSiege(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
default:
break;
}
}
if (NumIncomplete > 0)
@ -5524,9 +5568,48 @@ bool AICOMM_BuildOutSiege(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
if (pBot->Player->GetResources() < ResourcesRequired) { return true; }
if (StructureToDeploy == STRUCTURE_MARINE_TURRETFACTORY)
{
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(StructureToDeploy, BaseToBuildOut->BaseLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (!vIsZero(BuildLocation) && (vIsZero(BaseToBuildOut->SiegeTarget) || vDist2DSq(BuildLocation, BaseToBuildOut->SiegeTarget) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, BuildLocation, BaseToBuildOut);
if (bSuccess) { return true; }
}
int NumAttempts = 0;
while (NumAttempts < 5)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), BaseToBuildOut->BaseLocation, UTIL_MetresToGoldSrcUnits(2.0f + NumAttempts));
if (!vIsZero(BuildLocation) && (vIsZero(BaseToBuildOut->SiegeTarget) || vDist2DSq(BuildLocation, BaseToBuildOut->SiegeTarget) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, BuildLocation, BaseToBuildOut);
if (bSuccess) { return true; }
}
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), BaseToBuildOut->BaseLocation, UTIL_MetresToGoldSrcUnits(2.0f + NumAttempts));
if (!vIsZero(BuildLocation) && (vIsZero(BaseToBuildOut->SiegeTarget) || vDist2DSq(BuildLocation, BaseToBuildOut->SiegeTarget) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, BuildLocation, BaseToBuildOut);
if (bSuccess) { return true; }
}
NumAttempts++;
}
return (pBot->Player->GetResources() <= BALANCE_VAR(kTurretFactoryCost) * 2);
}
if (StructureToDeploy == STRUCTURE_MARINE_SIEGETURRET)
{
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(StructureToDeploy, BaseToBuildOut->BaseLocation, UTIL_MetresToGoldSrcUnits(10.0f));
Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(StructureToDeploy, TurretFactory.Location, BALANCE_VAR(kTurretFactoryBuildDistance));
if (!vIsZero(BuildLocation))
{
@ -5541,7 +5624,7 @@ bool AICOMM_BuildOutSiege(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
{
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), TurretFactory.Location, (BALANCE_VAR(kTurretFactoryBuildDistance) * 0.4f));
if (!vIsZero(BuildLocation))
if (!vIsZero(BuildLocation) && (vIsZero(BaseToBuildOut->SiegeTarget) || vDist2DSq(BuildLocation, BaseToBuildOut->SiegeTarget) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, BuildLocation, BaseToBuildOut);
@ -5550,7 +5633,7 @@ bool AICOMM_BuildOutSiege(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut)
BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TurretFactory.Location, (BALANCE_VAR(kTurretFactoryBuildDistance) * 0.6f));
if (!vIsZero(BuildLocation))
if (!vIsZero(BuildLocation) && (vIsZero(BaseToBuildOut->SiegeTarget) || vDist2DSq(BuildLocation, BaseToBuildOut->SiegeTarget) < sqrf(BALANCE_VAR(kSiegeTurretRange))))
{
bool bSuccess = AICOMM_AddStructureToBase(pBot, StructureToDeploy, BuildLocation, BaseToBuildOut);

View file

@ -488,6 +488,7 @@ typedef struct _AI_MARINE_BASE
AvHTeamNumber BaseTeam = TEAM_IND;
MarineBaseType BaseType = MARINE_BASE_OUTPOST; // The purpose of the base. Determines what structures the commander will place
Vector BaseLocation = ZERO_VECTOR; // Where the base should be located. The base will be grown around this location
Vector SiegeTarget = ZERO_VECTOR; // For siege bases, this is where the siege base wants to blast stuff
vector<int> PlacedStructures; // Which structures are part of this base.
int NumBuilders = 0; // How many potential builders are there, able to construct stuff?
int NumEnemies = 0; // How many enemies are in and around the base?

View file

@ -350,9 +350,16 @@ void AIDEBUG_DrawPath(edict_t* OutputPlayer, vector<bot_path_node>& path, float
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
if (FNullEnt(pEntity) || pEntity->free)
{
MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
}
else
{
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
}
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
@ -378,12 +385,18 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end)
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
int timeTenthSeconds = (int)floorf(drawTimeSeconds * 10.0f);
timeTenthSeconds = fmaxf(timeTenthSeconds, 1);
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
if (FNullEnt(pEntity) || pEntity->free)
{
MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
}
else
{
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
}
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
@ -409,12 +422,18 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSec
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds, int r, int g, int b)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
int timeTenthSeconds = (int)ceilf(drawTimeSeconds * 10.0f);
timeTenthSeconds = fmaxf(timeTenthSeconds, 1);
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
if (FNullEnt(pEntity) || pEntity->free)
{
MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
}
else
{
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
}
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
@ -440,9 +459,15 @@ void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSec
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int b)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
if (FNullEnt(pEntity) || pEntity->free)
{
MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
}
else
{
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
}
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
@ -638,11 +663,64 @@ void UTIL_LocalizeText(const char* InputText, string& OutputText)
}
char* UTIL_StructTypeToChar(const AvHAIDeployableStructureType StructureType)
{
switch (StructureType)
{
case STRUCTURE_MARINE_RESTOWER:
return "RT";
case STRUCTURE_MARINE_INFANTRYPORTAL:
return "IP";
case STRUCTURE_MARINE_TURRETFACTORY:
return "TF";
case STRUCTURE_MARINE_ADVTURRETFACTORY:
return "Adv TF";
case STRUCTURE_MARINE_ARMOURY:
return "Armoury";
case STRUCTURE_MARINE_ADVARMOURY:
return "Adv Armoury";
case STRUCTURE_MARINE_ARMSLAB:
return "Armslab";
case STRUCTURE_MARINE_PROTOTYPELAB:
return "ProtoLab";
case STRUCTURE_MARINE_OBSERVATORY:
return "Obs";
case STRUCTURE_MARINE_PHASEGATE:
return "PG";
case STRUCTURE_MARINE_TURRET:
return "Sentry";
case STRUCTURE_MARINE_SIEGETURRET:
return "Siege T";
case STRUCTURE_MARINE_COMMCHAIR:
return "CC";
case STRUCTURE_MARINE_DEPLOYEDMINE:
return "Mine";
case STRUCTURE_ALIEN_HIVE:
return "Hive";
case STRUCTURE_ALIEN_RESTOWER:
return "RT";
case STRUCTURE_ALIEN_DEFENCECHAMBER:
return "DC";
case STRUCTURE_ALIEN_SENSORYCHAMBER:
return "SC";
case STRUCTURE_ALIEN_MOVEMENTCHAMBER:
return "MC";
case STRUCTURE_ALIEN_OFFENCECHAMBER:
return "OC";
default:
return "None";
}
return "None";
}
char* UTIL_TaskTypeToChar(const BotTaskType TaskType)
{
switch (TaskType)
{
case TASK_ATTACK:
return "Attack";
case TASK_BUILD:
return "Build";

View file

@ -60,6 +60,7 @@ void UTIL_ClearLocalizations();
void UTIL_LocalizeText(const char* InputText, string& OutputText);
char* UTIL_TaskTypeToChar(const BotTaskType TaskType);
char* UTIL_StructTypeToChar(const AvHAIDeployableStructureType StructureType);
char* UTIL_BotRoleToChar(const AvHAIBotRole Role);

View file

@ -479,7 +479,7 @@ void AIDEBUG_DrawOffMeshConnections(float DrawTime)
bool UTIL_UpdateTileCache()
{
bTileCacheUpToDate = true;
bool bNewTileCacheUpToDate = true;
for (int i = 0; i < MAX_NAV_MESHES; i++)
{
@ -487,10 +487,17 @@ bool UTIL_UpdateTileCache()
{
bool bUpToDate;
NavMeshes[i].tileCache->update(0.0f, NavMeshes[i].navMesh, &bUpToDate);
if (!bUpToDate) { bTileCacheUpToDate = false; }
if (i != BUILDING_NAV_MESH && !bUpToDate) { bNewTileCacheUpToDate = false; }
}
}
if (!bTileCacheUpToDate && bNewTileCacheUpToDate)
{
bNavMeshModified = true;
}
bTileCacheUpToDate = bNewTileCacheUpToDate;
return bTileCacheUpToDate;
}
@ -604,11 +611,6 @@ unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector L
NavMeshes[NavMeshIndex].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef);
ObstacleNum = (unsigned int)ObsRef;
if (ObstacleNum > 0 && NavMeshIndex != BUILDING_NAV_MESH)
{
bNavMeshModified = true;
}
}
return ObstacleNum;
@ -670,14 +672,7 @@ void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure)
if (ObstacleToRemove)
{
dtStatus RemovalStatus = NavMeshes[NavMeshIndex].tileCache->removeObstacle((dtObstacleRef)it->ObstacleRef);
if (dtStatusSucceed(RemovalStatus))
{
bNavMeshModified = true;
}
}
}
it = Structure->Obstacles.erase(it);
@ -700,11 +695,6 @@ void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Heigh
NavMeshes[i].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef);
ObstacleRefArray[i] = (unsigned int)ObsRef;
if ((unsigned int)ObsRef > 0)
{
bNavMeshModified = true;
}
}
}
}
@ -724,12 +714,6 @@ unsigned int UTIL_AddTemporaryBoxObstacle(const Vector bMin, const Vector bMax,
NavMeshes[i].tileCache->addBoxObstacle(bMinf, bMaxf, area, &ObsRef);
ObstacleNum = (unsigned int)ObsRef;
if (area == DT_TILECACHE_NULL_AREA || area == DT_TILECACHE_WELD_AREA)
{
bNavMeshModified = true;
}
}
}
@ -746,11 +730,6 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef)
{
const dtTileCacheObstacle* ObstacleToRemove = NavMeshes[i].tileCache->getObstacleByRef((dtObstacleRef)ObstacleRef);
if (ObstacleToRemove && (ObstacleToRemove->cylinder.area == DT_TILECACHE_NULL_AREA || ObstacleToRemove->cylinder.area == DT_TILECACHE_WELD_AREA))
{
bNavMeshModified = true;
}
NavMeshes[i].tileCache->removeObstacle((dtObstacleRef)ObstacleRef);
}
}
@ -765,11 +744,6 @@ void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs)
{
const dtTileCacheObstacle* ObstacleToRemove = NavMeshes[i].tileCache->getObstacleByRef((dtObstacleRef)ObstacleRefs[i]);
if (ObstacleToRemove && (ObstacleToRemove->cylinder.area == DT_TILECACHE_NULL_AREA || ObstacleToRemove->cylinder.area == DT_TILECACHE_WELD_AREA))
{
bNavMeshModified = true;
}
NavMeshes[i].tileCache->removeObstacle((dtObstacleRef)ObstacleRefs[i]);
}
@ -6823,6 +6797,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
if (!vIsZero(BotNavInfo->LastNavMeshPosition))
{
MoveDirectlyTo(pBot, BotNavInfo->LastNavMeshPosition);
UTIL_DrawLine(nullptr, pBot->Edict->v.origin, BotNavInfo->LastNavMeshPosition, 255, 0, 0);
if (vDist2DSq(pBot->CurrentFloorPosition, BotNavInfo->LastNavMeshPosition) < sqrf(8.0f))
{
@ -6846,6 +6821,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
if (!vIsZero(BotNavInfo->UnstuckMoveLocation))
{
MoveDirectlyTo(pBot, BotNavInfo->UnstuckMoveLocation);
UTIL_DrawLine(nullptr, pBot->Edict->v.origin, BotNavInfo->UnstuckMoveLocation, 255, 255, 0);
return false;
}
}
@ -6853,6 +6829,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move
else
{
MoveDirectlyTo(pBot, Destination);
UTIL_DrawLine(nullptr, pBot->Edict->v.origin, BotNavInfo->UnstuckMoveLocation, 0, 128, 0);
return false;
}
@ -8838,8 +8815,6 @@ void UTIL_ModifyOffMeshConnectionFlag(AvHAIOffMeshConnection* Connection, const
NavMeshes[i].tileCache->modifyOffMeshConnection(Connection->ConnectionRefs[i], NewFlag);
}
}
bNavMeshModified = true;
}
void UTIL_UpdateDoors(bool bInitial)
@ -9584,8 +9559,6 @@ void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char are
RemoveConnectionDef->ConnectionRefs[i] = (unsigned int)ref;
}
bNavMeshModified = true;
}
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef)
@ -9596,8 +9569,6 @@ void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef)
RemoveConnectionDef->ConnectionRefs[i] = 0;
}
bNavMeshModified = true;
}
const nav_profile GetBaseNavProfile(const int index)

View file

@ -1887,21 +1887,24 @@ void EndBotFrame(AvHAIPlayer* pBot)
void CustomThink(AvHAIPlayer* pBot)
{
if (IsPlayerAlien(pBot->Edict)) { return; }
if (!IsPlayerAlien(pBot->Edict)) { return; }
if (!IsPlayerCommander(pBot->Edict))
int Enemy = BotGetNextEnemyTarget(pBot);
if (Enemy > -1)
{
BotProgressTakeCommandTask(pBot);
return;
AlienCombatThink(pBot);
}
for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++)
else
{
if (it->bCanBeBuiltOut)
edict_t* CommChair = AITAC_GetCommChair(AIMGR_GetEnemyTeam(pBot->Player->GetTeam()));
if (!FNullEnt(CommChair))
{
if (AICOMM_BuildOutBase(pBot, &(*it))) { return; }
MoveTo(pBot, CommChair->v.origin, MOVESTYLE_NORMAL);
}
}
}
void DroneThink(AvHAIPlayer* pBot)
@ -2059,7 +2062,17 @@ void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor)
}
TrackingInfo->AwarenessOfPlayer = 1.0f;
TrackingInfo->bHasLOS = true;
Vector VisiblePoint = GetVisiblePointOnPlayerFromObserver(pBot->Edict, aggressor);
if (!vIsZero(VisiblePoint))
{
TrackingInfo->bHasLOS = true;
TrackingInfo->bEnemyHasLOS = true;
TrackingInfo->LastVisibleTime = gpGlobals->time;
}
}
}
@ -6348,7 +6361,7 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
// If we're already capping a node, are at the node and there is an unfinished tower on there, then finish the job and don't move on yet
if (Task->TaskType == TASK_CAP_RESNODE)
{
const AvHAIResourceNode* ResNodeIndex = AITAC_GetNearestResourceNodeToLocation(Task->TaskLocation);
const AvHAIResourceNode* ResNodeIndex = AITAC_GetResourceNodeFromEdict(Task->TaskTarget);
if (ResNodeIndex && ResNodeIndex->OwningTeam != BotTeam)
{
@ -6358,7 +6371,7 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
else
{
if (!FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
if (IsPlayerGorge(pBot->Edict) && !FNullEnt(ResNodeIndex->ActiveTowerEntity) && !UTIL_StructureIsFullyBuilt(ResNodeIndex->ActiveTowerEntity))
{
if (vDist2DSq(pBot->Edict->v.origin, ResNodeIndex->Location) < sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
{
@ -6380,7 +6393,7 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (IsPlayerLerk(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict))
{
bCanPlaceTower = pBot->Player->GetResources() >= 75 && AITAC_GetTeamResNodeOwnership(BotTeam, true) >= 0.5f;
bCanPlaceTower = pBot->Player->GetResources() >= 75 && AITAC_GetTeamResNodeOwnership(BotTeam, true) < 0.5f;
}
// If we have enough resources to cap a node, then find an empty one we can slap one down in

View file

@ -1313,6 +1313,64 @@ void AIMGR_SetDebugAIPlayer(edict_t* SpectatingPlayer, edict_t* AIPlayer)
DebugBots[PlayerIndex] = nullptr;
}
void AIDEBUG_DisplayTeamGoals()
{
AvHTeamNumber TeamANumber = AIMGR_GetTeamANumber();
AvHTeamNumber TeamBNumber = AIMGR_GetTeamBNumber();
vector<AvHAIPlayer*> Team1Players = AIMGR_GetAIPlayersOnTeam(TeamANumber);
vector<AvHAIPlayer*> Team2Players = AIMGR_GetAIPlayersOnTeam(TeamBNumber);
char buf[511];
char interbuf[164];
sprintf(buf, "Team A (%s)\n\n", (AIMGR_GetTeamType(TeamANumber) == AVH_CLASS_TYPE_MARINE) ? "Marines" : "Aliens");
for (auto it = Team1Players.begin(); it != Team1Players.end(); it++)
{
AvHAIPlayer* ThisPlayer = (*it);
if (!ThisPlayer->CurrentTask || !IsPlayerActiveInGame(ThisPlayer->Edict)) { continue; }
if (!FNullEnt(ThisPlayer->CurrentTask->TaskTarget))
{
sprintf(interbuf, "%s: %s (%s)\n", STRING(ThisPlayer->Player->pev->netname), UTIL_TaskTypeToChar(ThisPlayer->CurrentTask->TaskType), UTIL_StructTypeToChar(UTIL_IUSER3ToStructureType(ThisPlayer->CurrentTask->TaskTarget->v.iuser3)));
}
else
{
sprintf(interbuf, "%s: %s\n", STRING(ThisPlayer->Player->pev->netname), UTIL_TaskTypeToChar(ThisPlayer->CurrentTask->TaskType));
}
strcat(buf, interbuf);
}
UTIL_DrawHUDText(INDEXENT(1), 0, 0.1, 0.1f, 255, 255, 255, buf);
sprintf(buf, "Team B (%s)\n\n", (AIMGR_GetTeamType(TeamBNumber) == AVH_CLASS_TYPE_MARINE) ? "Marines" : "Aliens");
for (auto it = Team2Players.begin(); it != Team2Players.end(); it++)
{
AvHAIPlayer* ThisPlayer = (*it);
if (!ThisPlayer->CurrentTask || !IsPlayerActiveInGame(ThisPlayer->Edict)) { continue; }
if (!FNullEnt(ThisPlayer->CurrentTask->TaskTarget))
{
sprintf(interbuf, "%s: %s (%s)\n", STRING(ThisPlayer->Player->pev->netname), UTIL_TaskTypeToChar(ThisPlayer->CurrentTask->TaskType), UTIL_StructTypeToChar(UTIL_IUSER3ToStructureType(ThisPlayer->CurrentTask->TaskTarget->v.iuser3)));
}
else
{
sprintf(interbuf, "%s: %s\n", STRING(ThisPlayer->Player->pev->netname), UTIL_TaskTypeToChar(ThisPlayer->CurrentTask->TaskType));
}
strcat(buf, interbuf);
}
UTIL_DrawHUDText(INDEXENT(1), 1, 0.6, 0.1f, 255, 255, 255, buf);
}
#endif
void AIMGR_ReceiveCommanderRequest(AvHTeamNumber Team, edict_t* Requestor, const char* Request)

View file

@ -137,4 +137,6 @@ void AIMGR_ProcessPendingSounds();
void AIMGR_SetFrameDelta(float NewValue);
float AIMGR_GetFrameDelta();
void AIDEBUG_DisplayTeamGoals();
#endif

View file

@ -3154,7 +3154,6 @@ bool UTIL_ShouldStructureCollide(AvHAIDeployableStructureType StructureType)
{
case STRUCTURE_MARINE_INFANTRYPORTAL:
case STRUCTURE_MARINE_PHASEGATE:
case STRUCTURE_MARINE_TURRET:
case STRUCTURE_MARINE_DEPLOYEDMINE:
return false;
default:

View file

@ -875,12 +875,32 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
if (bActiveHiveWithoutTechExists) { return true; }
int NumMissing = 0;
AvHAIDeployableStructureType MissingStructure = AITAC_GetNextMissingUpgradeChamberForTeam(BotTeam, NumMissing);
if (MissingStructure != STRUCTURE_NONE) { return true; }
Vector ReinforceLocation = Task->TaskTarget->v.origin;
float SearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
if (IsEdictHive(Task->TaskTarget))
{
AvHAIHiveDefinition* HiveToReinforce = AITAC_GetHiveFromEdict(Task->TaskTarget);
if (HiveToReinforce)
{
ReinforceLocation = HiveToReinforce->FloorLocation;
}
SearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
}
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTypes = SEARCH_ALL_ALIEN_STRUCTURES;
StructureFilter.MaxSearchRadius = (IsEdictHive(Task->TaskTarget)) ? UTIL_MetresToGoldSrcUnits(10.0f) : UTIL_MetresToGoldSrcUnits(5.0f);
StructureFilter.DeployableTeam = BotTeam;
StructureFilter.MaxSearchRadius = SearchRadius * 1.25f;
StructureFilter.DeployableTeam = BotTeam;
vector<AvHAIBuildableStructure> AllNearbyStructures = AITAC_FindAllDeployables(Task->TaskTarget->v.origin, &StructureFilter);
vector<AvHAIBuildableStructure> AllNearbyStructures = AITAC_FindAllDeployables(ReinforceLocation, &StructureFilter);
bool bUnfinishedStructureExists = false;
int NumOffenceChambers = 0;
@ -916,11 +936,11 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
// Task is still valid if we have any missing structures, or we're a gorge at the target site and there is an incomplete structure that we can finish off
if (NumOffenceChambers < 2
if (NumOffenceChambers < 3
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_DEFENSE_CHAMBER) && NumDefenceChambers < 2)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_MOVEMENT_CHAMBER) && NumMovementChambers < 1)
|| (AITAC_TeamHiveWithTechExists(BotTeam, ALIEN_BUILD_SENSORY_CHAMBER) && NumSensoryChambers < 1)
|| (IsPlayerGorge(pBot->Edict) && bUnfinishedStructureExists && vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) <= sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
|| (IsPlayerGorge(pBot->Edict) && bUnfinishedStructureExists && vDist2DSq(pBot->Edict->v.origin, ReinforceLocation) <= sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
) { return true; }
// Otherwise, are there any enemy structures lying around we could clear out?
@ -934,9 +954,9 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas
EnemyStuff.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam);
EnemyStuff.ReachabilityTeam = BotTeam;
EnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
EnemyStuff.MaxSearchRadius = SearchRadius;
return AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &EnemyStuff);
return AITAC_DeployableExistsAtLocation(ReinforceLocation, &EnemyStuff);
}
bool AITASK_IsAlienSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -1388,7 +1408,7 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
DeployableSearchFilter StructureFilter;
StructureFilter.DeployableTeam = BotTeam;
StructureFilter.MaxSearchRadius = SearchRadius;
StructureFilter.MaxSearchRadius = SearchRadius * 1.25f;
StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
int NumOCs = AITAC_GetNumDeployablesNearLocation(ReinforceLocation, &StructureFilter);
@ -1443,6 +1463,14 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
}
if (NextStructure == STRUCTURE_NONE)
{
int NumMissing = 0;
AvHAIDeployableStructureType MissingStructure = AITAC_GetNextMissingUpgradeChamberForTeam(BotTeam, NumMissing);
NextStructure = MissingStructure;
}
if (NextStructure != STRUCTURE_NONE)
{
if (vIsZero(Task->TaskLocation))