2018-04-22 15:55:55 +00:00
|
|
|
#include "../util/nowarnings.h"
|
|
|
|
|
|
|
|
#ifdef AVH_SERVER
|
|
|
|
#include "../dlls/extdll.h"
|
|
|
|
#include "../dlls/util.h"
|
|
|
|
#include "../types.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
#include "cl_dll/hud.h"
|
|
|
|
#include "cl_dll/cl_util.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "AvHSelectionHelper.h"
|
|
|
|
#include "AvHConstants.h"
|
|
|
|
|
|
|
|
#ifdef AVH_SERVER
|
|
|
|
#include "AvHPlayer.h"
|
|
|
|
#include "AvHServerUtil.h"
|
|
|
|
#include "AvHEntities.h"
|
|
|
|
#include "AvHGamerules.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "../pm_shared/pm_defs.h"
|
|
|
|
#include "../pm_shared/pm_shared.h"
|
|
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
#include "../pm_shared/pm_debug.h"
|
|
|
|
//extern DebugPointListType gTriDebugLocations;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "../util/MathUtil.h"
|
|
|
|
|
|
|
|
extern playermove_t *pmove;
|
|
|
|
#include "AvHSharedUtil.h"
|
|
|
|
|
|
|
|
#include "../common/vector_util.h"
|
|
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
#include "cl_dll/eventscripts.h"
|
|
|
|
|
|
|
|
#include "../common/r_efx.h"
|
|
|
|
#include "../common/event_api.h"
|
|
|
|
#include "../common/event_args.h"
|
|
|
|
#include "cl_dll/in_defs.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "AvHSpecials.h"
|
|
|
|
|
|
|
|
AvHSelectionHelper::AvHSelectionHelper()
|
|
|
|
{
|
|
|
|
this->mQueuedTeamNumber = TEAM_IND;
|
|
|
|
this->mQueuedSelectionWaiting = false;
|
|
|
|
this->mSelectionResultsWaiting = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AvHSelectionHelper::ClearSelection()
|
|
|
|
{
|
|
|
|
ASSERT(this->mSelectionResults.size() == 0);
|
|
|
|
this->mSelectionResults.clear();
|
|
|
|
this->mSelectionResultsWaiting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AvHSelectionHelper::GetAndClearSelection(EntityListType& outSelectedList)
|
|
|
|
{
|
|
|
|
ASSERT(this->mSelectionResultsWaiting);
|
|
|
|
outSelectedList = this->mSelectionResults;
|
|
|
|
this->mSelectionResults.clear();
|
|
|
|
this->mSelectionResultsWaiting = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AvHSelectionHelper::ProcessPendingSelections()
|
|
|
|
{
|
|
|
|
if(this->mQueuedSelectionWaiting)
|
|
|
|
{
|
|
|
|
this->mSelectionResultsWaiting = this->SelectUnits(this->mQueuedPointOfView, this->mQueuedRayOne, this->mQueuedRayTwo, this->mQueuedTeamNumber, this->mSelectionResults);
|
|
|
|
this->mQueuedSelectionWaiting = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AvHSelectionHelper::QueueSelection(const Vector& inPointOfView, const Vector& inStartRay, const Vector& inEndRay, AvHTeamNumber inTeamNumber)
|
|
|
|
{
|
|
|
|
this->mQueuedPointOfView = inPointOfView;
|
|
|
|
this->mQueuedRayOne = inStartRay;
|
|
|
|
this->mQueuedRayTwo = inEndRay;
|
|
|
|
this->mQueuedTeamNumber = inTeamNumber;
|
|
|
|
|
|
|
|
this->mQueuedSelectionWaiting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AvHSelectionHelper::SelectionResultsWaiting()
|
|
|
|
{
|
|
|
|
return this->mSelectionResultsWaiting;
|
|
|
|
}
|
|
|
|
|
|
|
|
//#ifdef AVH_SERVER
|
|
|
|
//void AvHSelectionHelper::SelectLocation(const Vector& inPointOfView, const Vector& inNormRay, Vector& outVector)
|
|
|
|
//{
|
|
|
|
// TraceResult tr;
|
|
|
|
// Vector theStartPos;
|
|
|
|
// Vector theEndPos;
|
|
|
|
// bool theSuccess = false;
|
|
|
|
// bool theDone = false;
|
|
|
|
//
|
|
|
|
// VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPos);
|
|
|
|
// VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos);
|
|
|
|
//
|
|
|
|
// CBaseEntity* theEntityHit = NULL;
|
|
|
|
// edict_t* theEdictToIgnore = NULL;
|
|
|
|
//
|
|
|
|
// do
|
|
|
|
// {
|
|
|
|
// UTIL_TraceLine(theStartPos, theEndPos, ignore_monsters, theEdictToIgnore, &tr);
|
|
|
|
// //UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, dont_ignore_glass, theEdictToIgnore, &tr);
|
|
|
|
//
|
|
|
|
//// theEntityHit = CBaseEntity::Instance(tr.pHit);
|
|
|
|
//// AvHWaypoint* theGround = dynamic_cast<AvHWaypoint*>(theEntityHit);
|
|
|
|
//// if(theGround)
|
|
|
|
//// {
|
|
|
|
//// VectorCopy(tr.vecEndPos, outVector);
|
|
|
|
//// theSuccess = true;
|
|
|
|
//// theDone = true;
|
|
|
|
//// }
|
|
|
|
//
|
|
|
|
// if((tr.flFraction >= 1.0f) || (tr.flFraction < kFloatTolerance))
|
|
|
|
// {
|
|
|
|
// theDone = true;
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// if(theEntityHit)
|
|
|
|
// {
|
|
|
|
// theEdictToIgnore = ENT(theEntityHit->pev);
|
|
|
|
// }
|
|
|
|
// VectorCopy(tr.vecEndPos, theStartPos);
|
|
|
|
// }
|
|
|
|
// } while(!theDone);
|
|
|
|
//}
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
//#ifdef AVH_CLIENT
|
|
|
|
//void AvHSelectionHelper::SelectLocation(const Vector& inPointOfView, const Vector& inNormRay, Vector& outVector)
|
|
|
|
//{
|
|
|
|
// // TODO: Change this to only return location when proper entity hit
|
|
|
|
// pmtrace_t tr;
|
|
|
|
// Vector theStartPos = inPointOfView;
|
|
|
|
// Vector theEndPos = theStartPos + kSelectionEndRange*inNormRay;
|
|
|
|
// Vector theResult;
|
|
|
|
//
|
|
|
|
// tr = *pmove->PM_TraceLine( (float *)&theStartPos, (float *)&theEndPos, PM_TRACELINE_ANYVISIBLE, 2 /*point sized hull*/, -1 );
|
|
|
|
// outVector = tr.endpos;
|
|
|
|
//}
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
|
|
|
|
bool AvHSelectionHelper::IsPositionInRegion(const Vector& inPosition, const Vector& inPointOfView, const Vector& inNormRayOne, const Vector& inNormRayTwo)
|
|
|
|
{
|
|
|
|
bool theSuccess = false;
|
|
|
|
|
|
|
|
// Build normalized vector from eye to entity
|
|
|
|
Vector theEyeToEntity;
|
|
|
|
VectorSubtract(inPosition, inPointOfView, theEyeToEntity);
|
|
|
|
VectorNormalize(theEyeToEntity);
|
|
|
|
|
|
|
|
// Is vector between two other vectors?
|
|
|
|
//if(IsVectorBetweenBoundingVectors(theEyeToEntity, inNormRayOne, inNormRayTwo))
|
|
|
|
//if(IsVectorBetweenBoundingVectors(inPosition, theEyeToEntity, inNormRayOne, inNormRayTwo, thePlaneABCD))
|
|
|
|
if(IsVectorBetweenBoundingVectors(inPosition, theEyeToEntity, inNormRayOne, inNormRayTwo))
|
|
|
|
{
|
|
|
|
theSuccess = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return theSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AvHSelectionHelper::ProcessEntityForSelection(const Vector& inOrigin, const Vector& inPointOfView, const Vector& inNormRayOne, const Vector& inNormRayTwo, int inIndex, bool inIsPlayer, bool inIsMarkedSelectable, bool inSameTeam, bool inIsVisible)
|
|
|
|
{
|
|
|
|
if(this->IsPositionInRegion(inOrigin, inPointOfView, inNormRayOne, inNormRayTwo))
|
|
|
|
{
|
|
|
|
if(inIsPlayer || inIsMarkedSelectable)
|
|
|
|
{
|
|
|
|
bool theIsFriendly = inSameTeam;
|
|
|
|
|
|
|
|
if(inIsPlayer)
|
|
|
|
{
|
|
|
|
if(theIsFriendly)
|
|
|
|
{
|
|
|
|
this->mFriendlyPlayers.push_back(inIndex);
|
|
|
|
}
|
|
|
|
else if(inIsVisible)
|
|
|
|
{
|
|
|
|
//this->mNonFriendlyPlayers.push_back(inIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(theIsFriendly)
|
|
|
|
{
|
|
|
|
this->mFriendlyBuildings.push_back(inIndex);
|
|
|
|
}
|
|
|
|
else if(inIsVisible)
|
|
|
|
{
|
|
|
|
//this->mNonFriendlyBuildings.push_back(inIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// else if(inIsSelectableWorldObject)
|
|
|
|
// {
|
|
|
|
// this->mWorldObjects.push_back(inIndex);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AvHSelectionHelper::SelectUnitsInRegion(const Vector& inPointOfView, const Vector& inNormRayOne, const Vector& inNormRayTwo, AvHTeamNumber inTeamNumber, EntityListType& outEntIndexList)
|
|
|
|
{
|
|
|
|
#ifdef AVH_SERVER
|
|
|
|
// Assumes that entities won't be too far away
|
2021-01-04 18:34:39 +00:00
|
|
|
float theRadius = GetGameRules()->GetMapExtents().GetTopDownCullDistance()*4;
|
2018-04-22 15:55:55 +00:00
|
|
|
|
|
|
|
CBaseEntity* theBaseEntity = NULL;
|
|
|
|
while((theBaseEntity = UTIL_FindEntityInSphere(theBaseEntity, inPointOfView, theRadius)) != NULL)
|
|
|
|
{
|
|
|
|
const char* theClassName = STRING(theBaseEntity->pev->classname);
|
|
|
|
if(!AvHSUGetIsExternalClassName(theClassName))
|
|
|
|
{
|
|
|
|
|
|
|
|
// Check for EF_NODRAW so that recycled command stations cannot be selected.
|
|
|
|
|
|
|
|
if(!GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_TOPDOWN) && !(theBaseEntity->pev->effects & EF_NODRAW) )
|
|
|
|
{
|
|
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theBaseEntity);
|
|
|
|
bool theIsPlayer = (thePlayer && thePlayer->GetIsRelevant() && (thePlayer->GetUser3() != AVH_USER3_COMMANDER_PLAYER));
|
|
|
|
bool theIsMarkedSelectable = GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_SELECTABLE);
|
|
|
|
bool theSameTeam = (theBaseEntity->pev->team == inTeamNumber);
|
|
|
|
bool theIsVisible = (theSameTeam || GetHasUpgrade(theBaseEntity->pev->iuser4, MASK_VIS_SIGHTED));
|
|
|
|
|
|
|
|
Vector thePosition = theBaseEntity->pev->origin;
|
|
|
|
if((thePosition.x == thePosition.y) && (thePosition.y == thePosition.z) && (thePosition.z == 0.0f))
|
|
|
|
{
|
|
|
|
thePosition.x = (theBaseEntity->pev->mins.x + theBaseEntity->pev->maxs.x)/2.0f;
|
|
|
|
thePosition.y = (theBaseEntity->pev->mins.y + theBaseEntity->pev->maxs.y)/2.0f;
|
|
|
|
thePosition.z = (theBaseEntity->pev->mins.z + theBaseEntity->pev->maxs.z)/2.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
int theEntityIndex = theBaseEntity->entindex();
|
|
|
|
AvHSHUGetEntityLocation(theEntityIndex, thePosition);
|
|
|
|
|
|
|
|
this->ProcessEntityForSelection(thePosition, inPointOfView, inNormRayOne, inNormRayTwo, theEntityIndex, theIsPlayer, theIsMarkedSelectable, theSameTeam, theIsVisible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
|
|
|
|
|
|
|
// Store off the old count
|
|
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
|
|
|
|
// Now add in all of the players.
|
|
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);
|
|
|
|
|
|
|
|
physent_t* theEntity = NULL;
|
|
|
|
int theNumEnts = pmove->numphysent;
|
|
|
|
for (int i = 0; i < theNumEnts; i++)
|
|
|
|
{
|
|
|
|
theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i);
|
|
|
|
if(theEntity && !GetHasUpgrade(theEntity->iuser4, MASK_TOPDOWN))
|
|
|
|
{
|
|
|
|
int theEntityIndex = theEntity->info;
|
|
|
|
bool theIsPlayer = ((theEntityIndex >= 1) && (theEntityIndex <= gEngfuncs.GetMaxClients()));
|
|
|
|
bool theIsMarkedSelectable = GetHasUpgrade(theEntity->iuser4, MASK_SELECTABLE);
|
|
|
|
bool theSameTeam = (theEntity->team == inTeamNumber);
|
|
|
|
bool theIsVisible = (theSameTeam || GetHasUpgrade(theEntity->iuser4, MASK_VIS_SIGHTED));
|
|
|
|
|
|
|
|
Vector thePosition = theEntity->origin;
|
|
|
|
if((thePosition.x == thePosition.y) && (thePosition.y == thePosition.z) && (thePosition.z == 0.0f))
|
|
|
|
{
|
|
|
|
thePosition.x = (theEntity->mins.x + theEntity->maxs.x)/2.0f;
|
|
|
|
thePosition.y = (theEntity->mins.y + theEntity->maxs.y)/2.0f;
|
|
|
|
thePosition.z = (theEntity->mins.z + theEntity->maxs.z)/2.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->ProcessEntityForSelection(thePosition, inPointOfView, inNormRayOne, inNormRayTwo, theEntityIndex, theIsPlayer, theIsMarkedSelectable, theSameTeam, theIsVisible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool theSuccess = false;
|
|
|
|
|
|
|
|
// Our own players
|
|
|
|
if(this->mFriendlyPlayers.size() > 0)
|
|
|
|
{
|
|
|
|
outEntIndexList = this->mFriendlyPlayers;
|
|
|
|
}
|
|
|
|
// Our own buildings only one
|
|
|
|
else if(this->mFriendlyBuildings.size() > 0)
|
|
|
|
{
|
|
|
|
outEntIndexList.push_back(*mFriendlyBuildings.begin());
|
|
|
|
}
|
|
|
|
// Enemy players (only one)
|
|
|
|
else if(this->mNonFriendlyPlayers.size() > 0)
|
|
|
|
{
|
|
|
|
outEntIndexList.push_back(*this->mNonFriendlyPlayers.begin());
|
|
|
|
}
|
|
|
|
// Enemy buildings (only one)
|
|
|
|
else if(this->mNonFriendlyBuildings.size() > 0)
|
|
|
|
{
|
|
|
|
outEntIndexList.push_back(*this->mNonFriendlyBuildings.begin());
|
|
|
|
}
|
|
|
|
// World objects (only one)
|
|
|
|
// else if(this->mWorldObjects.size() > 0)
|
|
|
|
// {
|
|
|
|
// outEntIndexList.push_back(*this->mWorldObjects.begin());
|
|
|
|
// }
|
|
|
|
if(outEntIndexList.size() > 0)
|
|
|
|
{
|
|
|
|
theSuccess = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->mFriendlyBuildings.clear();
|
|
|
|
this->mFriendlyPlayers.clear();
|
|
|
|
this->mNonFriendlyBuildings.clear();
|
|
|
|
this->mNonFriendlyPlayers.clear();
|
|
|
|
// this->mWorldObjects.clear();
|
|
|
|
|
|
|
|
return theSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AvHSelectionHelper::SelectUnits(const Vector& inPointOfView, const Vector& inStartRay, const Vector& inEndRay, AvHTeamNumber inTeamNumber, EntityListType& outEntIndexList)
|
|
|
|
{
|
|
|
|
bool theSuccess = false;
|
|
|
|
|
|
|
|
// Select into new list
|
|
|
|
EntityListType theNewSelection;
|
|
|
|
|
|
|
|
Vector theStartRay = inStartRay;
|
|
|
|
Vector theEndRay = inEndRay;
|
|
|
|
|
|
|
|
// If inNormRayOne and inNormRayTwo are sufficiently close, just do a ray test
|
|
|
|
const float theXTolerance = .1f;
|
|
|
|
const float theYTolerance = .1f;
|
|
|
|
if((fabs(theStartRay.x - theEndRay.x) < theXTolerance) && (fabs(theStartRay.y - theEndRay.y) < theYTolerance))
|
|
|
|
{
|
|
|
|
// // Ignore team here, we're allowed to click select units on either team
|
|
|
|
// int theEntIndex;
|
|
|
|
// if(AvHSHUGetEntityAtRay(inPointOfView, inStartRay, theEntIndex))
|
|
|
|
// {
|
|
|
|
// theNewSelection.push_back(theEntIndex);
|
|
|
|
// theSuccess = true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Select minimum around center
|
|
|
|
Vector theCenter;
|
|
|
|
theCenter.x = (inStartRay.x + inEndRay.x)/2.0f;
|
|
|
|
theCenter.y = (inStartRay.y + inEndRay.y)/2.0f;
|
|
|
|
// theCenter.z = (inStartRay.z + inEndRay.z)/2.0f;
|
|
|
|
|
|
|
|
// Not perfect, but good enough
|
|
|
|
theStartRay.x = theCenter.x - theXTolerance/2.0f;
|
|
|
|
theStartRay.y = theCenter.y - theYTolerance/2.0f;
|
|
|
|
VectorNormalize(theStartRay);
|
|
|
|
|
|
|
|
theEndRay.x = theCenter.x + theXTolerance/2.0f;
|
|
|
|
theEndRay.y = theCenter.y + theYTolerance/2.0f;
|
|
|
|
VectorNormalize(theEndRay);
|
|
|
|
}
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
theSuccess = SelectUnitsInRegion(inPointOfView, theStartRay, theEndRay, inTeamNumber, theNewSelection);
|
|
|
|
// }
|
|
|
|
|
|
|
|
if(theSuccess)
|
|
|
|
{
|
|
|
|
// Set new selection
|
|
|
|
outEntIndexList = theNewSelection;
|
|
|
|
}
|
|
|
|
|
|
|
|
return theSuccess;
|
|
|
|
}
|