Fix ladder and wall climbing

This commit is contained in:
RGreenlees 2024-02-26 23:06:02 +00:00 committed by pierow
parent 3add6092d9
commit 2fef7b83ff
11 changed files with 546 additions and 373 deletions

View file

@ -410,6 +410,7 @@ typedef enum
typedef enum
{
MOVE_TASK_NONE = 0,
MOVE_TASK_MOVE,
MOVE_TASK_USE,
MOVE_TASK_BREAK,
MOVE_TASK_TOUCH,

View file

@ -853,6 +853,23 @@ bool LoadNavMesh(const char* mapname)
fread(&def, sizeof(dtOffMeshConnection), 1, savedFile);
unsigned char area = def.area;
if (def.flags & SAMPLE_POLYFLAGS_WALLCLIMB)
{
area = SAMPLE_POLYAREA_WALLCLIMB;
}
if (def.flags & SAMPLE_POLYFLAGS_LADDER)
{
area = SAMPLE_POLYAREA_LADDER;
}
if (def.flags & SAMPLE_POLYFLAGS_LIFT)
{
area = SAMPLE_POLYAREA_LIFT;
}
AvHAIOffMeshConnection NewMapConnection;
NewMapConnection.ConnectionFlags = def.flags;
NewMapConnection.DefaultConnectionFlags = def.flags;
@ -864,7 +881,7 @@ bool LoadNavMesh(const char* mapname)
{
dtOffMeshConnectionRef ref = 0;
NavMeshes[ii].tileCache->addOffMeshConnection(&def.pos[0], &def.pos[3], 10.0f, def.area, def.flags, def.bBiDir, &ref);
NavMeshes[ii].tileCache->addOffMeshConnection(&def.pos[0], &def.pos[3], 10.0f, area, def.flags, def.bBiDir, &ref);
NewMapConnection.ConnectionRefs[ii] = (unsigned int)ref;
}
@ -917,6 +934,8 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_FLY | SAMPLE_POLYFLAGS_WALLCLIMB | SAMPLE_POLYFLAGS_WELD);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
@ -930,6 +949,9 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE | SAMPLE_POLYFLAGS_TEAM2PHASEGATE | SAMPLE_POLYFLAGS_DUCKJUMP | SAMPLE_POLYFLAGS_WELD | SAMPLE_POLYFLAGS_FLY);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
@ -944,6 +966,8 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
@ -961,6 +985,8 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
@ -976,6 +1002,8 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.5f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
@ -992,6 +1020,8 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 5.0f); // Onos is a wrecking machine, structures shouldn't be such an obstacle for them!
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LADDER, 1.5f);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_LIFT, 3.0f);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
@ -1867,33 +1897,53 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
NextPathNode.requiredZ += 5.0f;
}
if (IsPlayerSkulk(pBot->Edict) && CurrFlags == SAMPLE_POLYFLAGS_LADDER)
/*if (IsPlayerSkulk(pBot->Edict) && CurrFlags == SAMPLE_POLYFLAGS_LADDER)
{
Vector NearestLadderTopPoint = UTIL_GetNearestLadderTopPoint(NextPathNode.Location);
NearestLadderTopPoint.z = NewRequiredZ;
bool bIsGoingUpLadder = (NextPathNode.Location.z > NodeFromLocation.z);
Vector CurrMoveDest = NextPathNode.Location;
Vector NearestLadderTopPoint = UTIL_GetNearestLadderTopPoint(NextPathNode.FromLocation);
Vector NearestLadderCentre = UTIL_GetNearestLadderCentrePoint(NextPathNode.FromLocation);
if (NextPathNode.Location.z > NodeFromLocation.z)
{
NextPathNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB;
CurrFlags = SAMPLE_POLYFLAGS_WALLCLIMB;
}
else
{
NextPathNode.flag = SAMPLE_POLYFLAGS_FALL;
CurrFlags = SAMPLE_POLYFLAGS_FALL;
}
NextPathNode.Location = NearestLadderTopPoint;
path.push_back(NextPathNode);
NextPathNode.Location = CurrMoveDest;
NextPathNode.FromLocation = NearestLadderTopPoint;
Vector LadderNormal = UTIL_GetVectorNormal2D(NextPathNode.FromLocation - NearestLadderCentre);
Vector OnLadderPoint = NearestLadderTopPoint;
OnLadderPoint.z = FromLocation.z;
OnLadderPoint = OnLadderPoint + (LadderNormal * 16.0f);
}
bot_path_node GetOnLadderNode;
GetOnLadderNode.area = CurrArea;
GetOnLadderNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB;
GetOnLadderNode.FromLocation = FromLocation;
GetOnLadderNode.Location = OnLadderPoint;
GetOnLadderNode.requiredZ = FromLocation.z;
path.push_back(GetOnLadderNode);
bot_path_node EndLadderNode;
EndLadderNode.area = CurrArea;
EndLadderNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB;
EndLadderNode.FromLocation = OnLadderPoint;
Vector EndClimbPoint = NearestLadderTopPoint;
EndClimbPoint.z = NewRequiredZ;
if (vDist2DSq(EndClimbPoint, OnLadderPoint) < sqrf(8.0f))
{
EndClimbPoint = EndClimbPoint - (LadderNormal * 8.0f);
}
EndLadderNode.Location = EndClimbPoint;
EndLadderNode.requiredZ = NewRequiredZ;
path.push_back(EndLadderNode);
FromLocation = EndClimbPoint;
CurrFlags = SAMPLE_POLYFLAGS_WALLCLIMB;
NextPathNode.FromLocation = FromLocation;
}*/
}
else
@ -2060,7 +2110,7 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot)
{
Vector ClosestPointToPath = vClosestPointOnLine(MoveFrom, MoveTo, pEdict->v.origin);
return vEquals(ClosestPointToPath, MoveTo, 5.0f);
return vEquals(ClosestPointToPath, MoveTo, GetPlayerRadius(pBot->Player));
}
return bAtOrPastDestination && fabs(pEdict->v.origin.z - MoveTo.z) < 50.0f;
@ -2929,7 +2979,7 @@ void NewMove(AvHAIPlayer* pBot)
{
if (IsPlayerSkulk(pBot->Edict))
{
SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, NextArea);
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, NextArea);
}
else
{
@ -2949,7 +2999,7 @@ void NewMove(AvHAIPlayer* pBot)
break;
}
if (vIsZero(pBot->LookTargetLocation))
if (vIsZero(pBot->LookTargetLocation) && vIsZero(pBot->MoveLookLocation))
{
Vector FurthestView = UTIL_GetFurthestVisiblePointOnPath(pBot);
@ -3197,16 +3247,24 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z);
// Onos is buggy as hell getting on and off ladders, ducking seems to help...
if (IsPlayerOnos(pEdict))
{
pBot->Button |= IN_DUCK;
}
if (pBot->Player->IsOnLadder())
if (IsPlayerOnLadder(pBot->Edict))
{
// We're on the ladder and actively climbing
const Vector LadderRightNormal = UTIL_GetVectorNormal(UTIL_GetCrossProduct(pBot->CurrentLadderNormal, UP_VECTOR));
Vector CurrentLadderNormal = UTIL_GetNearestLadderNormal(pBot->Edict);
CurrentLadderNormal = UTIL_GetVectorNormal2D(CurrentLadderNormal);
if (vIsZero(CurrentLadderNormal))
{
CurrentLadderNormal = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
}
const Vector LadderRightNormal = UTIL_GetVectorNormal(UTIL_GetCrossProduct(CurrentLadderNormal, UP_VECTOR));
// Stop holding crouch if we're a skulk so we can climb up/down
if (IsPlayerSkulk(pBot->Edict))
{
pBot->Button &= ~IN_DUCK;
}
Vector ClimbRightNormal = LadderRightNormal;
@ -3214,6 +3272,14 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
{
ClimbRightNormal = -LadderRightNormal;
}
else
{
if (UTIL_GetDotProduct(CurrentLadderNormal, UP_VECTOR) > 0.5f)
{
FallMove(pBot, StartPoint, EndPoint);
return;
}
}
if (bIsGoingUpLadder)
{
@ -3234,7 +3300,7 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
BotMoveLookAt(pBot, LookLocation);
// If the get-off point is opposite the ladder, then jump to get to it
if (UTIL_GetDotProduct(pBot->CurrentLadderNormal, vForward) > 0.75f)
if (UTIL_GetDotProduct(CurrentLadderNormal, vForward) > 0.75f)
{
BotJump(pBot);
}
@ -3278,7 +3344,7 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
// If we are blocked going up the ladder, face the ladder and slide left/right to avoid blockage
if (bBlockedLeft && !bBlockedRight)
{
Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f);
Vector LookLocation = pBot->Edict->v.origin - (CurrentLadderNormal * 50.0f);
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
@ -3288,7 +3354,7 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
if (bBlockedRight && !bBlockedLeft)
{
Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f);
Vector LookLocation = pBot->Edict->v.origin - (CurrentLadderNormal * 50.0f);
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
@ -3317,37 +3383,47 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
Vector LookLocation = EndPoint;
float dot = UTIL_GetDotProduct2D(vForward, LadderRightNormal);
// Get-off point is to the side of the ladder rather than right at the top
if (fabsf(dot) > 0.5f)
if (!IsPlayerSkulk(pBot->Edict))
{
if (dot > 0.0f)
float dot = UTIL_GetDotProduct2D(vForward, LadderRightNormal);
// Get-off point is to the side of the ladder rather than right at the top
if (fabsf(dot) > 0.5f)
{
LookLocation = pBot->Edict->v.origin + (LadderRightNormal * 50.0f);
if (dot > 0.0f)
{
LookLocation = pBot->Edict->v.origin + (LadderRightNormal * 50.0f);
}
else
{
LookLocation = pBot->Edict->v.origin - (LadderRightNormal * 50.0f);
}
}
else
{
LookLocation = pBot->Edict->v.origin - (LadderRightNormal * 50.0f);
// Get-off point is at the top of the ladder, so face the ladder
LookLocation = EndPoint - (CurrentLadderNormal * 50.0f);
}
LookLocation.z += 100.0f;
}
else
{
// Get-off point is at the top of the ladder, so face the ladder
LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f);
LookLocation = UTIL_GetNearestLadderTopPoint(pBot->Edict->v.origin) - (CurrentLadderNormal * 50.0f);
LookLocation.z += 100.0f;
}
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
if (RequiredClimbHeight > pBot->Edict->v.origin.z)
if (RequiredClimbHeight > pBot->Edict->v.origin.z || IsPlayerSkulk(pBot->Edict))
{
pBot->desiredMovementDir = -pBot->CurrentLadderNormal;
pBot->desiredMovementDir = -CurrentLadderNormal;
}
else
{
pBot->desiredMovementDir = pBot->CurrentLadderNormal;
pBot->desiredMovementDir = CurrentLadderNormal;
}
}
@ -3364,37 +3440,47 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
bool bBlockedLeft = !UTIL_QuickTrace(pEdict, StartLeftTrace, StartLeftTrace - Vector(0.0f, 0.0f, 32.0f));
bool bBlockedRight = !UTIL_QuickTrace(pEdict, StartRightTrace, StartRightTrace - Vector(0.0f, 0.0f, 32.0f));
if (bBlockedLeft)
if (bBlockedLeft && !bBlockedRight)
{
pBot->desiredMovementDir = LadderRightNormal;
return;
}
if (bBlockedRight)
if (bBlockedRight && !bBlockedLeft)
{
pBot->desiredMovementDir = -LadderRightNormal;
return;
}
if (EndPoint.z > pBot->Edict->v.origin.z)
if (EndPoint.z > pBot->Edict->v.origin.z || IsPlayerSkulk(pBot->Edict))
{
pBot->desiredMovementDir = -pBot->CurrentLadderNormal;
pBot->desiredMovementDir = -CurrentLadderNormal;
}
else
{
pBot->desiredMovementDir = pBot->CurrentLadderNormal;
pBot->desiredMovementDir = CurrentLadderNormal;
}
// We're going down the ladder, look ahead on the path or at the bottom of the ladder if we can't
Vector FurthestView = UTIL_GetFurthestVisiblePointOnPath(pBot);
Vector LookLocation = EndPoint;
if (vIsZero(FurthestView))
if (!IsPlayerSkulk(pBot->Edict))
{
FurthestView = EndPoint + (pBot->CurrentLadderNormal * 100.0f);
Vector FurthestView = UTIL_GetFurthestVisiblePointOnPath(pBot);
if (vIsZero(FurthestView))
{
LookLocation = EndPoint + (CurrentLadderNormal * 100.0f);
}
}
else
{
LookLocation = pBot->CurrentEyePosition - (CurrentLadderNormal * 50.0f);
LookLocation.z -= 100.0f;
}
BotMoveLookAt(pBot, FurthestView);
BotMoveLookAt(pBot, LookLocation);
}
return;
@ -3403,26 +3489,28 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
// We're not yet on the ladder
// If we're going down the ladder and are approaching it, just keep moving towards it
if (pBot->BotNavInfo.IsOnGround && !bIsGoingUpLadder)
if ((pBot->BotNavInfo.IsOnGround || IsPlayerSkulk(pBot->Edict)) && !bIsGoingUpLadder)
{
if (vDist2DSq(pEdict->v.origin, StartPoint) < sqrf(32.0f))
{
pBot->BotNavInfo.bShouldWalk = true;
}
Vector ApproachDir = UTIL_GetVectorNormal2D(EndPoint - pBot->Edict->v.origin);
Vector LadderStart = UTIL_GetNearestLadderTopPoint(StartPoint);
float Dot = UTIL_GetDotProduct2D(ApproachDir, vForward);
Vector ApproachDir = UTIL_GetVectorNormal2D(LadderStart - pBot->Edict->v.origin);
Vector IdealApproachDir = UTIL_GetVectorNormal2D(LadderStart - StartPoint);
if (Dot > 45.0f)
float DistFromLine = vDistanceFromLine2DSq(StartPoint, LadderStart, pBot->Edict->v.origin);
pBot->desiredMovementDir = IdealApproachDir;
if (DistFromLine > sqrf(4.0f))
{
pBot->desiredMovementDir = vForward;
pBot->BotNavInfo.bShouldWalk = true;
}
else
{
Vector nearestLadderPoint = UTIL_GetNearestLadderCentrePoint(pEdict);
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(nearestLadderPoint - StartPoint);
Vector vRight = UTIL_GetCrossProduct(IdealApproachDir, UP_VECTOR);
float modifier = (float)vPointOnLine(StartPoint, LadderStart, pBot->Edict->v.origin);
pBot->desiredMovementDir = IdealApproachDir + (vRight * modifier);
}
return;
@ -3460,76 +3548,6 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
}
}
void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea)
{
edict_t* pEdict = pBot->Edict;
AvHPlayer* AIPlayer = pBot->Player;
const Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z);
Vector LadderNormal = UTIL_GetNearestLadderNormal(pBot->Edict);
const Vector LadderRightNormal = UTIL_GetVectorNormal(UTIL_GetCrossProduct(LadderNormal, UP_VECTOR));
Vector ClimbRightNormal = LadderRightNormal;
if (bIsGoingUpLadder)
{
pBot->Button &= ~IN_DUCK;
Vector HullTraceTo = EndPoint;
HullTraceTo.z = pBot->CollisionHullBottomLocation.z;
// We have reached our desired climb height and want to get off the ladder
if ((pBot->Edict->v.origin.z >= RequiredClimbHeight) && UTIL_QuickHullTrace(pEdict, pEdict->v.origin, Vector(EndPoint.x, EndPoint.y, pEdict->v.origin.z), head_hull))
{
// Move directly towards the desired get-off point, looking slightly up still
pBot->desiredMovementDir = vForward;
Vector LookLocation = EndPoint;
LookLocation.z = pBot->CurrentEyePosition.z + 64.0f;
BotMoveLookAt(pBot, LookLocation);
// If the get-off point is opposite the ladder, then jump to get to it
if (UTIL_GetDotProduct(pBot->CurrentLadderNormal, vForward) > 0.75f)
{
BotJump(pBot);
}
return;
}
else
{
// This is for cases where the ladder physically doesn't reach the desired get-off point and the bot kind of has to "jump" up off the ladder.
if (pBot->CollisionHullTopLocation.z >= UTIL_GetNearestLadderTopPoint(pEdict).z)
{
pBot->desiredMovementDir = vForward;
// We look up really far to get maximum launch
BotMoveLookAt(pBot, EndPoint + Vector(0.0f, 0.0f, 100.0f));
return;
}
// Still climbing the ladder. Look up, and move left/right on the ladder to avoid any blockages
Vector LookLocation = StartPoint - (LadderNormal * 100.0f);
LookLocation.z = EndPoint.z + 50.0f;
BotMoveLookAt(pBot, LookLocation);
pBot->desiredMovementDir = -LadderNormal;
}
}
else
{
FallMove(pBot, StartPoint, EndPoint);
}
}
bool UTIL_TriggerHasBeenRecentlyActivated(edict_t* TriggerEntity)
{
return true;
@ -3605,7 +3623,12 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
if (!bFullyOnLift)
{
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(LiftPosition - StartPoint);
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(LiftPosition - pBot->Edict->v.origin);
}
if (!UTIL_QuickTrace(pBot->Edict, pBot->CollisionHullTopLocation, pBot->CollisionHullTopLocation + Vector(0.0f, 0.0f, 64.0f)))
{
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(LiftPosition - pBot->Edict->v.origin);
}
return;
@ -4062,6 +4085,8 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
Vector AdjustedTargetLocation = EndPoint + (UTIL_GetVectorNormal2D(EndPoint - StartPoint) * 1000.0f);
Vector DirectAheadView = pBot->CurrentEyePosition + (UTIL_GetVectorNormal2D(AdjustedTargetLocation - pEdict->v.origin) * 10.0f);
Vector ClimbSurfaceNormal = UTIL_GetVectorNormal(EndPoint - StartPoint);
Vector LookLocation = g_vecZero;
if (ZDiff < 1.0f)
@ -4075,18 +4100,21 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
{
if (ZDiff > 32.0f)
{
LookLocation = DirectAheadView - Vector(0.0f, 0.0f, 100.0f);
ClimbSurfaceNormal = ClimbSurfaceNormal - (2.0f * (UTIL_GetDotProduct(ClimbSurfaceNormal, UP_VECTOR) * ClimbSurfaceNormal));
LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f);
}
else
{
LookLocation = DirectAheadView - Vector(0.0f, 0.0f, 20.0f);
//LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f);
}
}
else
{
if (ZDiff > 32.0f)
{
LookLocation = DirectAheadView + Vector(0.0f, 0.0f, 100.0f);
ClimbSurfaceNormal = ClimbSurfaceNormal;
LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f);
}
else
{
@ -4966,12 +4994,14 @@ void UTIL_UpdateBotMovementStatus(AvHAIPlayer* pBot)
bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
{
if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; }
if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.CurrentPathPoint == pBot->BotNavInfo.CurrentPath.end() || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; }
Vector MoveFrom = pBot->BotNavInfo.CurrentPathPoint->FromLocation;
Vector MoveTo = pBot->BotNavInfo.CurrentPathPoint->Location;
unsigned int flag = pBot->BotNavInfo.CurrentPathPoint->flag;
if (flag == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || flag == SAMPLE_POLYFLAGS_TEAM2PHASEGATE || flag == SAMPLE_POLYFLAGS_TEAM1STRUCTURE || flag == SAMPLE_POLYFLAGS_TEAM2STRUCTURE) { return true; }
Vector ClosestPointOnLine = vClosestPointOnLine2D(MoveFrom, MoveTo, pBot->Edict->v.origin);
bool bAtOrPastMovement = (vEquals2D(ClosestPointOnLine, MoveFrom, 1.0f) && fabsf(pBot->Edict->v.origin.z - MoveFrom.z) <= 50.0f ) || (vEquals2D(ClosestPointOnLine, MoveTo, 1.0f) && fabsf(pBot->Edict->v.origin.z - MoveTo.z) <= 50.0f) ;
@ -5014,7 +5044,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
}
}
if (flag == SAMPLE_POLYFLAGS_WALK)
if (flag == SAMPLE_POLYFLAGS_WALK || flag == SAMPLE_POLYFLAGS_WELD || flag == SAMPLE_POLYFLAGS_DOOR)
{
if (UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveFrom) || UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, MoveTo))
{
@ -5047,41 +5077,29 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
{
if (bReverseCourse)
{
if (IsPlayerSkulk(pBot->Edict))
{
SkulkLadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
}
else
{
LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveTo.z > MoveFrom.z)
LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveTo.z > MoveFrom.z)
{
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
BotJump(pBot);
}
BotJump(pBot);
}
}
}
else
{
if (IsPlayerSkulk(pBot->Edict))
{
SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
}
else
{
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveFrom.z > MoveTo.z)
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPathPoint->requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveFrom.z > MoveTo.z)
{
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
BotJump(pBot);
}
BotJump(pBot);
}
}
}
@ -5092,7 +5110,7 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
return true;
}
if (flag == SAMPLE_POLYFLAGS_JUMP || flag == SAMPLE_POLYFLAGS_BLOCKED)
if (flag == SAMPLE_POLYFLAGS_JUMP || flag == SAMPLE_POLYFLAGS_DUCKJUMP || flag == SAMPLE_POLYFLAGS_BLOCKED)
{
if (bReverseCourse)
{
@ -6490,193 +6508,7 @@ bool BotRecalcPath(AvHAIPlayer* pBot, const Vector Destination)
return false;
}
Vector UTIL_GetNearestLadderNormal(edict_t* pEdict)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, pEdict->v.origin);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector CentrePoint = closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f);
CentrePoint.z = pEdict->v.origin.z;
UTIL_TraceHull(pEdict->v.origin, CentrePoint, ignore_monsters, GetPlayerHullIndex(pEdict), pEdict->v.pContainingEntity, &result);
if (result.flFraction < 1.0f)
{
return result.vecPlaneNormal;
}
}
return Vector(0.0f, 0.0f, 0.0f);
}
Vector UTIL_GetNearestLadderBottomPoint(edict_t* pEdict)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, pEdict->v.origin);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector Centre = (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
Centre.z = closestLadderRef->pev->absmin.z;
return Centre;
}
return pEdict->v.origin;
}
Vector UTIL_GetNearestLadderTopPoint(const Vector SearchLocation)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, SearchLocation);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector Centre = (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
Centre.z = closestLadderRef->pev->absmax.z;
return Centre;
}
return SearchLocation;
}
Vector UTIL_GetNearestLadderTopPoint(edict_t* pEdict)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, pEdict->v.origin);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector Centre = (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
Centre.z = closestLadderRef->pev->absmax.z;
return Centre;
}
return pEdict->v.origin;
}
Vector UTIL_GetNearestLadderCentrePoint(edict_t* pEdict)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, pEdict->v.origin);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
return (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
}
return pEdict->v.origin;
}
float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEnd, const int HullNum)
{
@ -7865,6 +7697,8 @@ void UTIL_PopulateDoors()
NewDoor.DoorEdict = DoorEnt->edict();
NewDoor.CurrentState = ToggleRef->m_toggle_state;
const char* DoorName = STRING(NewDoor.DoorEdict->v.targetname);
if (DoorEnt->pev->spawnflags & DOOR_USE_ONLY)
{
NewDoor.ActivationType = DOOR_USE;
@ -7935,7 +7769,8 @@ nav_door* UTIL_GetClosestLiftToPoints(const Vector StartPoint, const Vector EndP
distBottomPoint = fminf(distBottomPoint, vDist3DSq(UTIL_GetClosestPointOnEntityToLocation(EndPoint, it->DoorEdict, *stop), EndPoint));
}
float thisDist = fminf(distTopPoint, distBottomPoint);
// Get the average distance from our desired start and end points, whichever scores lowest is probably the lift/train/door we want to ride
float thisDist = ((distTopPoint + distBottomPoint) * 0.5f);
if (!Result || thisDist < minDist)
{
@ -8120,9 +7955,11 @@ void NAV_SetMoveMovementTask(AvHAIPlayer* pBot, Vector MoveLocation, DoorTrigger
{
AvHAIPlayerMoveTask* MoveTask = &pBot->BotNavInfo.MovementTask;
if (MoveTask->TaskType == MOVE_TASK_TOUCH && vEquals(MoveTask->TaskLocation, MoveLocation)) { return; }
if (MoveTask->TaskType == MOVE_TASK_MOVE && vEquals(MoveTask->TaskLocation, MoveLocation)) { return; }
MoveTask->TaskType = MOVE_TASK_TOUCH;
if (vDist2DSq(pBot->CurrentFloorPosition, MoveLocation) < sqrf(GetPlayerRadius(pBot->Player)) && fabsf(pBot->CollisionHullBottomLocation.z - MoveLocation.z) < 50.0f) { return; }
MoveTask->TaskType = MOVE_TASK_MOVE;
MoveTask->TaskLocation = MoveLocation;
vector<bot_path_node> Path;
@ -8294,11 +8131,16 @@ bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot)
if (MoveTask->TriggerToActivate->NextActivationTime > gpGlobals->time) { return false; }
}
if (MoveTask->TaskType == MOVE_TASK_USE)
if (MoveTask->TaskType == MOVE_TASK_MOVE)
{
return (vDist2DSq(pBot->Edict->v.origin, MoveTask->TaskLocation) > sqrf(GetPlayerRadius(pBot->Player)) || fabsf(pBot->Edict->v.origin.z - MoveTask->TaskLocation.z) > 50.0f);
}
if (MoveTask->TaskType == MOVE_TASK_USE)
{
return MoveTask->TriggerToActivate && MoveTask->TriggerToActivate->bIsActivated && MoveTask->TriggerToActivate->NextActivationTime < gpGlobals->time;
}
if (MoveTask->TaskType == MOVE_TASK_PICKUP)
{
return (!FNullEnt(MoveTask->TaskTarget) && !(MoveTask->TaskTarget->v.effects & EF_NODRAW));

View file

@ -44,7 +44,9 @@ enum SamplePolyAreas
SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall-stick, fly or blink
SAMPLE_POLYAREA_OBSTRUCTION = 5, // There is a door or weldable object in the way
SAMPLE_POLYAREA_STRUCTUREBLOCK = 6, // An enemy structure is blocking the way that must be destroyed
SAMPLE_POLYAREA_PHASEGATE = 7 // Phase gate area, for area cost calculation
SAMPLE_POLYAREA_PHASEGATE = 7, // Phase gate area, for area cost calculation
SAMPLE_POLYAREA_LADDER = 8, // Phase gate area, for area cost calculation
SAMPLE_POLYAREA_LIFT = 9, // Phase gate area, for area cost calculation
};
// Possible movement types. Swim and door are not used
@ -222,8 +224,7 @@ void StructureBlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vect
void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint
void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea);
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint as skulk, which requires different movement
void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea);
// Called by NewMove, determines the movement direction and inputs required to climb a wall to reach endpoint
void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
@ -417,11 +418,7 @@ unsigned char UTIL_GetNavAreaAtLocation(const nav_profile& NavProfile, const Vec
// For printing out human-readable nav mesh areas
const char* UTIL_NavmeshAreaToChar(const unsigned char Area);
Vector UTIL_GetNearestLadderNormal(edict_t* pEdict);
Vector UTIL_GetNearestLadderCentrePoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderTopPoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderTopPoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderBottomPoint(edict_t* pEdict);
// From the given start point, determine how high up the bot needs to climb to get to climb end. Will allow the bot to climb over railings
float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEnd, const int HullNum);

View file

@ -1864,8 +1864,17 @@ void UpdateAIPlayerCORole(AvHAIPlayer* pBot)
if (NumLerks + NumHarassers == 0)
{
SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS);
return;
float LastSeenTime;
edict_t* PreviousLerk = AITAC_GetLastSeenLerkForTeam(BotTeam, LastSeenTime);
// We only go lerk if the last lerk we had in the match was either us, or we've not had another lerk in 30 seconds
// This prevents a situation where a human is lerk, gets killed, and a bot immediately takes over.
// This is undesireable as it pressures the human to pick something else to avoid too many lerks
if (LastSeenTime > 30.0f || PreviousLerk == pBot->Edict)
{
SetNewAIPlayerRole(pBot, BOT_ROLE_HARASS);
return;
}
}
int MaxOnos = (int)(ceilf((float)(AIMGR_GetNumPlayersOnTeam(BotTeam) - 2)) * 0.3f);

View file

@ -621,7 +621,7 @@ void AIMGR_UpdateAIPlayers()
AIDEBUG_DrawBotPath(bot);
if (bot->BotNavInfo.CurrentPathPoint != bot->BotNavInfo.CurrentPath.end())
if (bot->BotNavInfo.CurrentPath.size() > 0 && bot->BotNavInfo.CurrentPathPoint != bot->BotNavInfo.CurrentPath.end())
{
UTIL_DrawLine(INDEXENT(1), bot->Edict->v.origin, bot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255);
}

View file

@ -6,6 +6,8 @@
#include "../AvHPlayerUpgrade.h"
#include "AvHAIMath.h"
#include "../AvHGamerules.h"
#include "../pm_shared/pm_shared.h"
#include "../pm_shared/pm_defs.h"
bool IsPlayerSkulk(const edict_t* Player)
{
@ -137,6 +139,25 @@ bool IsPlayerBuffed(const edict_t* Player)
bool IsPlayerOnLadder(const edict_t* Player)
{
if (IsPlayerSkulk(Player))
{
edict_t* NearestLadder = UTIL_GetNearestLadderAtPoint(Player->v.origin);
if (FNullEnt(NearestLadder)) { return false; }
if (vPointOverlaps3D(Player->v.origin, NearestLadder->v.absmin, NearestLadder->v.absmax)) { return true; }
trace_t TraceResult;
Vector TraceStart = Player->v.origin;
Vector TraceEnd = UTIL_GetCentreOfEntity(NearestLadder);
TraceEnd.z = TraceStart.z;
NS_TraceLine(TraceStart, TraceEnd, 1, PM_WORLD_ONLY, -1, true, TraceResult);
return (TraceResult.fraction < 0.01f);
}
return (Player->v.movetype == MOVETYPE_FLY);
}
@ -764,4 +785,205 @@ AvHUser3 GetPlayerActiveClass(const AvHPlayer* Player)
// Player isn't gestating, just return whatever they are now
return (AvHUser3)Player->pev->iuser3;
}
edict_t* UTIL_GetNearestLadderAtPoint(const Vector SearchLocation)
{
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = FLT_MAX;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, SearchLocation);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
return (closestLadderRef) ? closestLadderRef->edict() : nullptr;
}
Vector UTIL_GetNearestLadderNormal(edict_t* pEdict)
{
return UTIL_GetNearestLadderNormal(pEdict->v.origin);
}
Vector UTIL_GetNearestLadderNormal(Vector SearchLocation)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = FLT_MAX;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, SearchLocation);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector CentrePoint = closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f);
CentrePoint.z = SearchLocation.z;
if (vPointOverlaps3D(SearchLocation, closestLadderRef->pev->absmin, closestLadderRef->pev->absmax))
{
CentrePoint = SearchLocation;
CentrePoint.z = closestLadderRef->pev->absmin.z;
}
trace_t TraceResult;
NS_TraceLine(SearchLocation, CentrePoint, 1, PM_WORLD_ONLY, -1, true, TraceResult);
if (TraceResult.fraction < 1.0f)
{
return TraceResult.plane.normal;
}
}
return ZERO_VECTOR;
}
Vector UTIL_GetNearestLadderBottomPoint(edict_t* pEdict)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, pEdict->v.origin);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector Centre = (closestLadderRef->pev->absmin + (closestLadderRef->pev->size * 0.5f));
Centre.z = closestLadderRef->pev->absmin.z;
return Centre;
}
return pEdict->v.origin;
}
Vector UTIL_GetNearestLadderTopPoint(const Vector SearchLocation)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, SearchLocation);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
Vector Centre = (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
Centre.z = closestLadderRef->pev->absmax.z;
return Centre;
}
return SearchLocation;
}
Vector UTIL_GetNearestLadderTopPoint(edict_t* pEdict)
{
return UTIL_GetNearestLadderTopPoint(pEdict->v.origin);
}
Vector UTIL_GetNearestLadderCentrePoint(edict_t* pEdict)
{
return UTIL_GetNearestLadderCentrePoint(pEdict->v.origin);
}
Vector UTIL_GetNearestLadderCentrePoint(const Vector SearchLocation)
{
TraceResult result;
CBaseEntity* entity = NULL;
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
CBaseEntity* closestLadderRef = entity;
float lowestDist = 999999.0f;
while (entity)
{
Vector LadderMin = entity->pev->absmin;
Vector LadderMax = entity->pev->absmax;
float dist = vDistanceFromLine3D(LadderMin, LadderMax, SearchLocation);
if (dist < lowestDist)
{
closestLadderRef = entity;
lowestDist = dist;
}
entity = UTIL_FindEntityByClassname(entity, "func_ladder");
}
if (closestLadderRef)
{
return (closestLadderRef->pev->absmin + ((closestLadderRef->pev->absmax - closestLadderRef->pev->absmin) * 0.5f));
}
return SearchLocation;
}

View file

@ -152,5 +152,13 @@ bool IsPlayerReloading(const AvHPlayer* Player);
// If the player is gestating, will return the target evolution. Otherwise, returns the iuser3
AvHUser3 GetPlayerActiveClass(const AvHPlayer* Player);
edict_t* UTIL_GetNearestLadderAtPoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderNormal(edict_t* pEdict);
Vector UTIL_GetNearestLadderNormal(Vector SearchLocation);
Vector UTIL_GetNearestLadderCentrePoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderCentrePoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderTopPoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderTopPoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderBottomPoint(edict_t* pEdict);
#endif

View file

@ -60,6 +60,12 @@ extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
bool bNavMeshModified = false;
extern bool bTileCacheUpToDate;
edict_t* LastSeenLerkTeamA = nullptr; // Track who went lerk on team A last time. This ensures we don't get endless cycles of lerks
edict_t* LastSeenLerkTeamB = nullptr; // Track who went lerk on team B last time. This ensures we don't get endless cycles of lerks
float LastSeenLerkTeamATime = 0.0f;
float LastSeenLerkTeamBTime = 0.0f;
std::vector<AvHAIBuildableStructure*> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter)
{
std::vector<AvHAIBuildableStructure*> Result;
@ -1423,6 +1429,39 @@ void AITAC_UpdateMapAIData()
AITAC_RefreshMarineItems();
last_item_refresh_time = gpGlobals->time;
}
vector<AvHPlayer*> AllTeamAPlayers = AITAC_GetAllPlayersOnTeamOfClass(GetGameRules()->GetTeamANumber(), AVH_USER3_ALIEN_PLAYER3, nullptr);
edict_t* LastTeamALerk = nullptr;
for (auto it = AllTeamAPlayers.begin(); it != AllTeamAPlayers.end(); it++)
{
edict_t* PlayerEdict = (*it)->edict();
if (FNullEnt(LastTeamALerk) || IsPlayerHuman(PlayerEdict))
{
LastTeamALerk = PlayerEdict;
LastSeenLerkTeamATime = gpGlobals->time;
}
}
LastSeenLerkTeamA = LastTeamALerk;
vector<AvHPlayer*> AllTeamBPlayers = AITAC_GetAllPlayersOnTeamOfClass(GetGameRules()->GetTeamBNumber(), AVH_USER3_ALIEN_PLAYER3, nullptr);
edict_t* LastTeamBLerk = nullptr;
for (auto it = AllTeamBPlayers.begin(); it != AllTeamBPlayers.end(); it++)
{
edict_t* PlayerEdict = (*it)->edict();
if (FNullEnt(LastTeamBLerk) || IsPlayerHuman(PlayerEdict))
{
LastTeamBLerk = PlayerEdict;
LastSeenLerkTeamBTime = gpGlobals->time;
}
}
LastSeenLerkTeamB = LastTeamALerk;
}
void AITAC_CheckNavMeshModified()
@ -3060,6 +3099,28 @@ bool AITAC_AnyPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchL
return false;
}
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer)
{
vector<AvHPlayer*> Result;
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
edict_t* PlayerEdict = INDEXENT(i);
if (FNullEnt(PlayerEdict) || PlayerEdict->free || PlayerEdict == IgnorePlayer) { continue; }
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
if (PlayerRef != nullptr && (SearchClass == AVH_USER3_NONE || GetPlayerActiveClass(PlayerRef) == SearchClass) && (Team == TEAM_IND || PlayerRef->GetTeam() == Team) && IsPlayerActiveInGame(PlayerEdict))
{
Result.push_back(PlayerRef);
}
}
return Result;
}
int AITAC_GetNumPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer)
{
int Result = 0;
@ -4645,4 +4706,18 @@ int AITAC_GetNumWeaponsInPlay(AvHTeamNumber Team, AvHAIWeapon WeaponType)
}
return Result;
}
}
edict_t* AITAC_GetLastSeenLerkForTeam(AvHTeamNumber Team, float& LastSeenTime)
{
if (Team == GetGameRules()->GetTeamANumber())
{
LastSeenTime = LastSeenLerkTeamATime;
return LastSeenLerkTeamA;
}
else
{
LastSeenTime = LastSeenLerkTeamBTime;
return LastSeenLerkTeamB;
}
}

View file

@ -126,6 +126,7 @@ bool AITAC_AnyPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchL
vector<AvHPlayer*> AITAC_GetAllPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass);
int AITAC_GetNumPlayersOfTeamAndClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
int AITAC_GetNumPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer);
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer);
edict_t* AITAC_GetNearestPlayerOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
vector<edict_t*> AITAC_GetAllPlayersOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
@ -186,4 +187,6 @@ bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus Des
int AITAC_GetNumWeaponsInPlay(AvHTeamNumber Team, AvHAIWeapon WeaponType);
edict_t* AITAC_GetLastSeenLerkForTeam(AvHTeamNumber Team, float& LastSeenTime);
#endif

View file

@ -1584,6 +1584,21 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
}
}
theSuccess = true;
}
else if (FStrEq(pcmd, "amonladder"))
{
if (IsPlayerOnLadder(theAvHPlayer->edict()))
{
UTIL_SayText("TRUE\n", theAvHPlayer);
}
else
{
UTIL_SayText("FALSE\n", theAvHPlayer);
}
theSuccess = true;
}
else if (FStrEq(pcmd, "tracelift"))

View file

@ -23,6 +23,7 @@
void PM_Init( struct playermove_s *ppmove );
void PM_Move ( struct playermove_s *ppmove, int server );
char PM_FindTextureType( char *name );
void NS_TraceLine(float* start, float* end, int hull, int traceFlags, int ignore_pe, bool ignorePlayers, trace_t& result);
// Spectator Movement modes (stored in pev->iuser1, so the physics code can get at them)
#define OBS_NONE 0