From 2fef7b83ff2ef27aa205aced6967531e33dd3ae6 Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Mon, 26 Feb 2024 23:06:02 +0000 Subject: [PATCH] Fix ladder and wall climbing --- main/source/mod/AIPlayers/AvHAIConstants.h | 1 + main/source/mod/AIPlayers/AvHAINavigation.cpp | 564 +++++++----------- main/source/mod/AIPlayers/AvHAINavigation.h | 13 +- main/source/mod/AIPlayers/AvHAIPlayer.cpp | 13 +- .../mod/AIPlayers/AvHAIPlayerManager.cpp | 2 +- main/source/mod/AIPlayers/AvHAIPlayerUtil.cpp | 222 +++++++ main/source/mod/AIPlayers/AvHAIPlayerUtil.h | 8 + main/source/mod/AIPlayers/AvHAITactical.cpp | 77 ++- main/source/mod/AIPlayers/AvHAITactical.h | 3 + main/source/mod/AvHConsoleCommands.cpp | 15 + main/source/pm_shared/pm_shared.h | 1 + 11 files changed, 546 insertions(+), 373 deletions(-) diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 0a2b7bb0..439fe0f8 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -410,6 +410,7 @@ typedef enum typedef enum { MOVE_TASK_NONE = 0, + MOVE_TASK_MOVE, MOVE_TASK_USE, MOVE_TASK_BREAK, MOVE_TASK_TOUCH, diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index b05118f4..5c4de2d8 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -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 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)); diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index 7dd0a282..1954d666 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -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); diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 442f126e..a506999e 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -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); diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index 633524ef..9404aeb3 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -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); } diff --git a/main/source/mod/AIPlayers/AvHAIPlayerUtil.cpp b/main/source/mod/AIPlayers/AvHAIPlayerUtil.cpp index 98cf7d0b..8889b145 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerUtil.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerUtil.cpp @@ -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; } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAIPlayerUtil.h b/main/source/mod/AIPlayers/AvHAIPlayerUtil.h index 4967fac7..d0e629b7 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerUtil.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerUtil.h @@ -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 \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 1a779772..e015fe44 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -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 AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter) { std::vector Result; @@ -1423,6 +1429,39 @@ void AITAC_UpdateMapAIData() AITAC_RefreshMarineItems(); last_item_refresh_time = gpGlobals->time; } + + vector 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 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 AITAC_GetAllPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer) +{ + vector 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(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; -} \ No newline at end of file +} + +edict_t* AITAC_GetLastSeenLerkForTeam(AvHTeamNumber Team, float& LastSeenTime) +{ + if (Team == GetGameRules()->GetTeamANumber()) + { + LastSeenTime = LastSeenLerkTeamATime; + return LastSeenLerkTeamA; + } + else + { + LastSeenTime = LastSeenLerkTeamBTime; + return LastSeenLerkTeamB; + } +} diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index d5300e16..f4f887f5 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -126,6 +126,7 @@ bool AITAC_AnyPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchL vector 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 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 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 \ No newline at end of file diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index 00ad3630..69c18185 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -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")) diff --git a/main/source/pm_shared/pm_shared.h b/main/source/pm_shared/pm_shared.h index 69fbfabb..1c46d094 100644 --- a/main/source/pm_shared/pm_shared.h +++ b/main/source/pm_shared/pm_shared.h @@ -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